From: Mischa POSLAWSKY Date: Mon, 10 Jan 2022 16:13:17 +0000 (+0100) Subject: word/edit: custom crop interface superior to croppie X-Git-Tag: v1.13~42 X-Git-Url: http://git.shiar.nl/sheet.git/commitdiff_plain/8f91d2a391ec8c0b38a6f657cf3815143234d090 word/edit: custom crop interface superior to croppie Some minimal js and absolute positioning can replace v1.12-226-gca5f2bee8e (2022-02-07) [word/edit: croppie js thumbnail selection interface] to exact demands, without rounding errors and restricted zoom control. --- diff --git a/word/edit.plp b/word/edit.plp index 0f212b8..40bf8af 100644 --- a/word/edit.plp +++ b/word/edit.plp @@ -10,8 +10,6 @@ Html({ raw => <<'EOT', - - EOT }); @@ -262,7 +260,7 @@ for my $col (@wordcols) { printf ''; print $row->input($col => $attr); if (my $imgsrc = $attr->{-src}) { - printf('%s', + printf('%s', $col, $_, $row->{form}, $col eq 'source' && ' hidden' ) for grep { -e } $imgsrc->($row); } diff --git a/word/editor.css b/word/editor.css index 2e89bb5..c3717d5 100644 --- a/word/editor.css +++ b/word/editor.css @@ -40,8 +40,11 @@ select { } #convertpreview { width: 300px; + height: 200px; align-self: start; flex-shrink: 0; + position: relative; + overflow: hidden; } .popup { diff --git a/word/editor.js b/word/editor.js index 0e54c62..5fc9854 100644 --- a/word/editor.js +++ b/word/editor.js @@ -120,26 +120,74 @@ document.addEventListener('DOMContentLoaded', () => { let thumbpreview = document.getElementById('convertpreview'); if (thumbpreview && imgpreview) { thumbpreview.onclick = e => { + thumbpreview.onclick = null; // setup once const cropinput = document.getElementById('crop32'); - let border = { width: 600, height: 400 }; - let crop = new Croppie(thumbpreview, { - boundary: { width: border.width * 1.3, height: border.height * 1.2 }, - viewport: border, - update: e => { - cropinput.value = e.points.map((pos, axis) => { - Math.round(1000 * pos / (axis % 2 ? border.height : border.width)) - }).join(','); - }, - }); - crop.bind({ - url: imgpreview.src, - points: cropinput.value.split(/[^0-9]/).map((rel, axis) => { - return rel * (axis % 2 ? border.height : border.width) / 1000 - }), - }); - crop.elements.overlay.addEventListener('dblclick', e => { - crop.destroy(); - }); + const imgselect = thumbpreview.children[0]; + const border = [300,200]; + let crop = cropinput.value.split(/[^0-9]/).map(pos => pos / 1000); + let scale = 1 / (crop[2] - crop[0]) || 1; + crop.push(0); // defined y dimension + crop.splice(2); // end coordinates applied to zoom + crop = crop.map((rel, axis) => rel * border[axis % 2] * scale); + + let drag = false; + function applydrag(e) { + const pos = [e.pageX, e.pageY]; + if (drag) { + // apply drag delta to crop position + crop[0] += drag[0] - pos[0]; + crop[1] += drag[1] - pos[1]; + recrop(); + } + drag = pos; + } + function recrop() { + [0, 1].forEach(axis => { + if (crop[axis] > border[axis] * (scale - 1)) { + crop[axis] = border[axis] * (scale - 1); + } + if (crop[axis] < 0) { + crop[axis] = 0; + } + }); + imgselect.style.left = -crop[0]+'px'; + imgselect.style.top = -crop[1]+'px'; + imgselect.style.width = (scale * 100)+'%'; + cropinput.value = [ + crop[0] / border[0], crop[1] / border[1], + crop[0] / border[0] + 1, crop[1] / border[1] + 1, + ].map(pos => Math.round(1000 * pos / scale)); + } + + imgselect.src = imgpreview.children[0].src; + imgselect.style.position = 'absolute'; + recrop(); + + imgselect.onmousedown = e => { + e.preventDefault(); + applydrag(e); + }; + imgselect.onmouseup = e => { + e.preventDefault(); + drag = false; + }; + imgselect.onmousemove = e => { + if (!drag) return; + applydrag(e); + }; + imgselect.onwheel = e => { + e.preventDefault(); + let zoom = e.deltaY * -.05 * scale; + if (scale + zoom < 1) { + zoom = 1 - scale; // scale = 1 + } + [0, 1].forEach(axis => { + // same area center at altered scale + crop[axis] += (crop[axis] + border[axis] / 2) / scale * zoom; + }); + scale += zoom; + recrop(); + }; }; }