Send original file, better offers view

This commit is contained in:
Adrian Woźniak 2022-07-19 15:09:37 +02:00
parent d69c4bb6ef
commit d922326ce3
No known key found for this signature in database
GPG Key ID: 0012845A89C7352B
2 changed files with 74 additions and 88 deletions

View File

@ -9,15 +9,26 @@ customElements.define('offer-form', class extends Component {
super(`
<style>
:host { display: block; }
section > form {
display: flex;
justify-content: space-between;
}
#imageSection {
margin-right: 16px;
}
#descriptionSection {
width: calc(100% - 230px);
}
${ FORM_STYLE }
</style>
<section>
<form action="/offers/create" method="post">
<div>
<image-input></image-input>
<div id="imageSection">
<image-input send-original="true"></image-input>
<input name="picture_url" id="picture_url" type="hidden" />
</div>
<div>
<div id="descriptionSection">
<label for="description">Opis</label>
<input name="description" id="description" type="text" />
</div>

View File

@ -1,8 +1,10 @@
import { BUTTON_STYLE, Component } from "../shared.js";
customElements.define('image-input', class extends Component {
#file;
static get observedAttributes() {
return ['width', 'height', "account-id", "url"]
return ['width', 'height', "account-id", "url", "send-original"];
}
constructor() {
@ -15,8 +17,7 @@ customElements.define('image-input', class extends Component {
}
#hidden { overflow: hidden; width: 1px; height: 1px; position: relative; }
input[type=file] { position: absolute; top: -10px; left: -10px; display: none; }
#view { height: 200px; cursor: pointer; }
canvas { width: 200px; height: 200px; }
#view { width: 200px; height: 200px; cursor: pointer; }
div > input[type=button] {
margin: 0;
width: 100%;
@ -26,15 +27,19 @@ customElements.define('image-input', class extends Component {
border-color: var(--border-light-gray-color);
}
img[src=""] { display: none; }
img {
max-width: 200px;
max-height: 200px;
display: block;
margin: auto;
}
${ BUTTON_STYLE }
</style>
<article>
<section id="hidden">
<input id="file" type="file" accept="image/*" />
<img alt="" src="" />
</section>
<div id="view">
<canvas width="200" height="200"></canvas>
</div>
<div>
<input id="save" type="button" value="Wyślij" />
@ -42,45 +47,48 @@ customElements.define('image-input', class extends Component {
</article>
`);
this.shadowRoot.querySelector('#save').addEventListener('click', ev => {
this.shadowRoot.querySelector('#save').addEventListener('click', async ev => {
ev.preventDefault();
ev.stopPropagation();
this.#uploadBase64(this.width, this.height);
await this.#sendFile(this.#file);
});
const f = new FileReader();
const input = this.shadowRoot.querySelector('#file');
const view = this.shadowRoot.querySelector('#view');
const img = this.shadowRoot.querySelector('img');
const canvas = this.shadowRoot.querySelector('canvas');
const ctx = canvas.getContext('2d');
img.addEventListener('load', () => {
let width, height;
if (img.width > img.height) {
width = 200;
height = (img.height * 200) / img.width;
} else {
width = (img.width * 200) / img.height;
height = 200;
}
this.setAttribute('width', width);
this.setAttribute('height', height);
const toFile = (canvas) =>
canvas.toBlob((blob) => {
this.#file = new File([blob], `${ crypto.randomUUID() }.webp`, { type: blob.type });
}, 'image/webp');
img.width = width;
img.height = height;
ctx.clearRect(0, 0, 200, 200);
ctx.drawImage(img, 0, 0, width, height);
});
input.addEventListener('change', ev => {
ev.stopPropagation();
f.addEventListener('loadend', (readerEvent) => {
if (readerEvent.total !== readerEvent.loaded)
return;
img.src = readerEvent.target.result || '';
});
f.readAsDataURL(ev.target.files[0]);
const image = new Image();
const canvas = document.createElement('canvas');
if (this.send_original) {
image.onload = () => {
canvas.width = image.naturalWidth;
canvas.height = image.naturalHeight;
canvas.getContext('2d').drawImage(image, 0, 0);
toFile(canvas);
};
image.src = URL.createObjectURL(input.files[0]);
} else {
image.onload = () => {
const width = image.width > image.height ? 200 : (image.width * 200) / image.height;
const height = image.width > image.height ? (image.width * 200) / image.height : 200;
canvas.width = image.width = width;
canvas.height = image.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
toFile(canvas);
};
image.src = URL.createObjectURL(input.files[0]);
}
view.innerHTML = '';
view.appendChild(image);
});
view.addEventListener('click', ev => {
@ -109,22 +117,20 @@ customElements.define('image-input', class extends Component {
get width() {
const v = parseInt(this.getAttribute('width'));
return isNaN(v) ? 0 : v;
return isNaN(v) ? 200 : v;
}
set width(v) {
this.setAttribute('width', v);
this.shadowRoot.querySelector('canvas').width = v;
}
get height() {
const v = parseInt(this.getAttribute('height'));
return isNaN(v) ? 0 : v;
return isNaN(v) ? 200 : v;
}
set height(v) {
this.setAttribute('height', v);
this.shadowRoot.querySelector('canvas').height = v;
}
get url() {
@ -134,26 +140,15 @@ customElements.define('image-input', class extends Component {
set url(v) {
if (!(v || '').startsWith("/")) v = '';
this.setAttribute('url', v);
this.shadowRoot.querySelector('img').src = v;
const view = this.shadowRoot.querySelector('#view');
view.innerHTML = `<img src="${ v }" alt=""/>`;
}
#uploadBase64(width, height) {
const canvas = this.shadowRoot.querySelector('canvas');
const ctx = canvas.getContext('2d');
const c = document.createElement('canvas');
c.width = width;
c.height = height;
c.getContext('2d').putImageData(ctx.getImageData(0, 0, width, height), 0, 0);
const blobBin = atob(c.toDataURL("image/webp", 1.0).split(',')[1]);
const array = [];
for (let i = 0; i < blobBin.length; i++) {
array.push(blobBin.charCodeAt(i));
}
const file = new Blob([new Uint8Array(array)], { type: 'image/webp' });
async #sendFile(file) {
if (!file) return;
const form = new FormData;
form.append(`${ crypto.randomUUID() }.webp`, file);
fetch("/upload", {
await fetch("/upload", {
method: "POST",
body: form,
}).then(res => res.json()).then(({ path }) => {
@ -161,36 +156,16 @@ customElements.define('image-input', class extends Component {
this.dispatchEvent(new CustomEvent('image-input:uploaded', { bubbles: true, composed: true }));
});
}
get send_original() {
return this.getAttribute('send-original') === 'true';
}
set send_original(v) {
v = v === true || v === 'true';
if (v)
this.setAttribute('send-original', 'true')
else
this.removeAttribute('send-original');
}
});
customElements.define('upload-size', class extends Component {
static get observedAttributes() {
return ['width', 'height'];
}
constructor() {
super(``);
}
get width() {
const v = parseInt(this.getAttribute('width'));
return isNaN(v) ? 0 : v;
}
set width(v) {
this.setAttribute('width', v);
}
get height() {
const v = parseInt(this.getAttribute('height'));
return isNaN(v) ? 0 : v;
}
set height(v) {
this.setAttribute('height', v);
}
get size() {
return { width: this.width, height: this.height }
}
})