bounded the Resizable box

This commit is contained in:
Patrick 2026-03-21 20:46:17 +01:00
parent 2ddcf21572
commit b3b7ec0293
2 changed files with 94 additions and 165 deletions

View File

@ -1,97 +1,18 @@
const devicePixelRatio = window.devicePixelRatio || 1; const devicePixelRatio = window.devicePixelRatio || 1;
class ImageCropper { function clamp(n, min, max) {
return Math.max(Math.min(n, max), min)
constructor(canvas) {
if (!(canvas instanceof HTMLCanvasElement)) {
throw TypeError('canvas must be a HTMLCanvasElement');
}
this._preview = canvas;
this._preview_ctx = canvas.getContext('2d');
this._preview_ctx.reset();
const dimensions = this._preview.getBoundingClientRect();
console.log(dimensions);
console.log(this._preview.width, this._preview.height);
this._preview.width = dimensions.width * devicePixelRatio;
this._preview.height = dimensions.height * devicePixelRatio;
this._preview_ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0);
}
showImage(image_src) {
this._image = new Image();
this._image.src = image_src;
this._image.addEventListener('load', (e) => {
const dimensions = this._preview.getBoundingClientRect();
const scale = {
x: dimensions.width / this._image.naturalWidth,
y: dimensions.height / this._image.naturalHeight
}
const ratio = Math.min(scale.x, scale.y);
const position = {
x: (dimensions.width - this._image.naturalWidth * ratio) / 2,
y: (dimensions.height - this._image.naturalHeight * ratio) / 2,
};
this._preview_ctx.drawImage(this._image, position.x, position.y, this._image.naturalWidth * ratio, this._image.naturalHeight * ratio);
})
}
doCrop() {
this._shadow = document.createElement('canvas');
this._shadow.style.display = 'none';
this._shadow_ctx = canvas.getContext('2d');
}
} }
/*jQuery(document).ready( ($) => { function getOffsetRect(element) {
return new DOMRect(element.offsetLeft, element.offsetTop, element.offsetWidth, element.offsetHeight);
let wp_file_selector_frame; }
let image_cropper;
$('#theatergf-select-image').on('click', (event) => {
// don't open browser file select
event.preventDefault();
if (wp_file_selector_frame) {
wp_file_selector_frame.open();
}
wp_file_selector_frame = wp.media({
title: 'Select Image',
button: {
text: 'Use Image'
},
multiple: false
});
wp_file_selector_frame.on('select', () => {
const file = wp_file_selector_frame.state().get('selection').first().toJSON();
image_cropper = new ImageCropper(document.getElementById('theatergf-image-editor'));
//image_cropper.showImage('http://localhost:8000/wp-content/uploads/2026/03/Kishon002.jpg');
image_cropper.showImage(file.url);
//document.getElementById('theatergf-image-preview').src = file.url;
});
wp_file_selector_frame.open();
});
});*/
class ResizableBox { class ResizableBox {
constructor(element, aspectRatio = null) { constructor(element, bounding, aspectRatio = null) {
this.element = element; this.element = element;
this.bounding = bounding;
this.aspectRatio = aspectRatio; this.aspectRatio = aspectRatio;
this.mouseStart = { this.mouseStart = {
@ -99,16 +20,7 @@ class ResizableBox {
y: 0 y: 0
} }
this.elemStart = { this.elemStart = new DOMRect()
left: 0,
top: 0,
width: element.style.width,
height: element.style.height,
corner_pos: {
x: 0,
y: 0
}
};
this.direction = undefined; this.direction = undefined;
@ -124,13 +36,20 @@ class ResizableBox {
if (this.direction && aspectRatio) { if (this.direction && aspectRatio) {
const {anchor, sign} = (() => { const handle = this.element.querySelector('.handle')
const handle_size = getOffsetRect(handle)
const { anchor, corner_pos, sign} = (() => {
switch (this.direction) { switch (this.direction) {
case 'nw': case 'nw':
return { return {
anchor: { anchor: {
x: this.elemStart.left + this.elemStart.width, x: this.elemStart.right,
y: this.elemStart.top + this.elemStart.height y: this.elemStart.bottom
},
corner_pos: {
x: this.elemStart.left,
y: this.elemStart.top
}, },
sign: { sign: {
x: -1, x: -1,
@ -141,7 +60,11 @@ class ResizableBox {
return { return {
anchor: { anchor: {
x: this.elemStart.left, x: this.elemStart.left,
y: this.elemStart.top + this.elemStart.height y: this.elemStart.bottom
},
corner_pos: {
x: this.elemStart.right,
y: this.elemStart.top
}, },
sign: { sign: {
x: +1, x: +1,
@ -151,9 +74,13 @@ class ResizableBox {
case 'sw': case 'sw':
return { return {
anchor: { anchor: {
x: this.elemStart.left + this.elemStart.width, x: this.elemStart.right,
y: this.elemStart.top y: this.elemStart.top
}, },
corner_pos: {
x: this.elemStart.left,
y: this.elemStart.bottom
},
sign: { sign: {
x: -1, x: -1,
y: +1 y: +1
@ -165,6 +92,10 @@ class ResizableBox {
x: this.elemStart.left, x: this.elemStart.left,
y: this.elemStart.top y: this.elemStart.top
}, },
corner_pos: {
x: this.elemStart.right,
y: this.elemStart.bottom
},
sign: { sign: {
x: +1, x: +1,
y: +1 y: +1
@ -173,8 +104,8 @@ class ResizableBox {
} }
})() })()
const px = this.elemStart.corner_pos.x + offset.x const px = corner_pos.x + offset.x
const py = this.elemStart.corner_pos.y + offset.y const py = corner_pos.y + offset.y
const dx = sign.x * (px - anchor.x) const dx = sign.x * (px - anchor.x)
const dy = sign.y * (py - anchor.y) const dy = sign.y * (py - anchor.y)
@ -187,38 +118,52 @@ class ResizableBox {
const s = (dv > dh) ? sv : sh const s = (dv > dh) ? sv : sh
delta.x = sign.x * (s * this.elemStart.width - this.elemStart.width) const sMaxX = (sign.x === +1)
delta.y = sign.y * (s * this.elemStart.height - this.elemStart.height) ? (this.bounding.right - anchor.x) / this.elemStart.width
: (anchor.x - this.bounding.left) / this.elemStart.width
const sMaxY = (sign.y === +1)
? (this.bounding.bottom - anchor.y) / this.elemStart.height
: (anchor.y - this.bounding.top) / this.elemStart.height
const sMin = Math.max(2*handle_size.width / this.elemStart.width, 2*handle_size.height / this.elemStart.height)
const sClamped = Math.max(Math.min(s, sMaxX, sMaxY), sMin)
delta.x = sign.x * (sClamped * this.elemStart.width - this.elemStart.width)
delta.y = sign.y * (sClamped * this.elemStart.height - this.elemStart.height)
} }
switch(this.direction) { switch(this.direction) {
case 'nw': case 'nw':
this.element.style.left = (this.elemStart.left + delta.x) + "px"; this.element.style.left = this.elemStart.left + delta.x+ "px";
this.element.style.top = (this.elemStart.top + delta.y) + "px"; this.element.style.top = this.elemStart.top + delta.y + "px";
this.element.style.width = (this.elemStart.width - delta.x) + "px"; this.element.style.width = this.elemStart.width - delta.x+ "px";
this.element.style.height = (this.elemStart.height - delta.y) + "px"; this.element.style.height = this.elemStart.height - delta.y + "px";
break; break;
case 'ne': case 'ne':
this.element.style.top = (this.elemStart.top + delta.y) + "px"; this.element.style.top = this.elemStart.top + delta.y + "px";
this.element.style.width = (this.elemStart.width + delta.x) + "px"; this.element.style.width = this.elemStart.width + delta.x + "px";
this.element.style.height = (this.elemStart.height - delta.y) + "px"; this.element.style.height = this.elemStart.height - delta.y + "px";
break; break;
case 'sw': case 'sw':
this.element.style.left = (this.elemStart.left + delta.x) + "px"; this.element.style.left = this.elemStart.left + delta.x+ "px";
this.element.style.width = (this.elemStart.width - delta.x) + "px"; this.element.style.width = this.elemStart.width - delta.x+ "px";
this.element.style.height = (this.elemStart.height + delta.y) + "px"; this.element.style.height = this.elemStart.height + delta.y + "px";
break; break;
case 'se': case 'se':
this.element.style.width = (this.elemStart.width + delta.x) + "px"; this.element.style.width = this.elemStart.width + delta.x+ "px";
this.element.style.height = (this.elemStart.height + delta.y) + "px"; this.element.style.height = this.elemStart.height + delta.y + "px";
break; break;
// moving is the default // moving is the default
default: default:
this.element.style.left = (this.elemStart.left + offset.x) + "px"; this.element.style.left = clamp(this.elemStart.left + offset.x, this.bounding.left, this.bounding.right - this.elemStart.width) + "px";
this.element.style.top = (this.elemStart.top + offset.y) + "px"; this.element.style.top = clamp(this.elemStart.top + offset.y, this.bounding.top, this.bounding.bottom - this.elemStart.height) + "px";
} }
console.log(this.bounding)
console.log(this.elemStart)
} }
@ -235,32 +180,10 @@ class ResizableBox {
this.mouseStart.x = event.clientX; this.mouseStart.x = event.clientX;
this.mouseStart.y = event.clientY; this.mouseStart.y = event.clientY;
this.elemStart.left = this.element.offsetLeft; this.elemStart = getOffsetRect(this.element)
this.elemStart.top = this.element.offsetTop;
this.elemStart.width = this.element.offsetWidth;
this.elemStart.height = this.element.offsetHeight;
this.direction = [...event.target.classList].find(c => ["nw","ne","sw","se"].includes(c)); this.direction = [...event.target.classList].find(c => ["nw","ne","sw","se"].includes(c));
switch (this.direction) {
case 'nw':
this.elemStart.corner_pos.x = this.element.offsetLeft;
this.elemStart.corner_pos.y = this.element.offsetTop;
break;
case 'ne':
this.elemStart.corner_pos.x = this.element.offsetLeft + this.element.offsetWidth;
this.elemStart.corner_pos.y = this.element.offsetTop;
break;
case 'sw':
this.elemStart.corner_pos.x = this.element.offsetLeft;
this.elemStart.corner_pos.y = this.element.offsetTop + this.element.offsetHeight;
break;
case 'se':
this.elemStart.corner_pos.x = this.element.offsetLeft + this.element.offsetWidth;
this.elemStart.corner_pos.y = this.element.offsetTop + this.element.offsetHeight;
break;
}
this.element.classList.add('moving'); this.element.classList.add('moving');
document.addEventListener('mousemove', dragMouseMove); document.addEventListener('mousemove', dragMouseMove);
@ -296,9 +219,6 @@ class ResizableBox {
this.element.appendChild(swHandle); this.element.appendChild(swHandle);
} }
getRect() {
return new DOMRect(this.element.offsetLeft, this.element.offsetTop, this.element.offsetWidth, this.element.offsetHeight);
}
} }
@ -307,45 +227,57 @@ class Editor {
constructor(editorElement, aspectRatio) { constructor(editorElement, aspectRatio) {
this._editor = editorElement; this._editor = editorElement;
this.aspectRatio = aspectRatio; this.aspectRatio = aspectRatio;
this._imgElement = undefined
} }
loadImage(url) { loadImage(url) {
const imgElement = new Image() this._imgElement = new Image()
imgElement.classList.add("editor-image"); this._imgElement.classList.add("editor-image");
imgElement.src = url; this._imgElement.src = url;
this._editor.replaceChildren(imgElement); this._editor.replaceChildren(this._imgElement);
const that = this; const that = this;
imgElement.addEventListener('load', (_) => { this._imgElement.addEventListener('load', (_) => {
const size = { this._imgElement.style.left = (this._editor.clientWidth - this._imgElement.offsetWidth) / 2 + "px";
width: imgElement.offsetWidth, this._imgElement.style.top = (this._editor.clientHeight - this._imgElement.offsetHeight) / 2 + "px";
height: imgElement.offsetHeight
};
if (size.width > size.height * that.aspectRatio) { const selector_pos = new DOMRect(0, 0, this._imgElement.offsetWidth, this._imgElement.offsetHeight);
size.width = size.height * that.aspectRatio;
console.log(selector_pos)
if (selector_pos.width > selector_pos.height * that.aspectRatio) {
selector_pos.width = selector_pos.height * that.aspectRatio;
selector_pos.x = this._imgElement.offsetLeft
selector_pos.y = (this._editor.clientHeight - selector_pos.height) / 2
} else { } else {
size.height = size.width / that.aspectRatio; selector_pos.height = selector_pos.width / that.aspectRatio;
selector_pos.x = (this._editor.clientWidth - selector_pos.width) / 2
selector_pos.y = this._imgElement.offsetTop
} }
that._createSelectionBox(size.width, size.height); console.log(selector_pos)
that._createSelectionBox(selector_pos);
}); });
} }
_createSelectionBox(width, height) { _createSelectionBox(selector_pos) {
const selector = document.createElement('div'); const selector = document.createElement('div');
selector.classList.add('selector'); selector.classList.add('selector');
selector.style.width = (width - 1) + "px"; selector.style.left = selector_pos.left + "px",
selector.style.height = (height - 1) + "px"; selector.style.top = selector_pos.top + "px"
selector.style.width = (selector_pos.width - 1) + "px";
selector.style.height = (selector_pos.height - 1) + "px";
this._cutSelector = selector; this._cutSelector = selector;
this._editor.appendChild(selector); this._editor.appendChild(selector);
this._cutArea = new ResizableBox(selector, width/height); this._cutArea = new ResizableBox(selector, getOffsetRect(this._imgElement), selector_pos.width/selector_pos.height);
} }
} }

View File

@ -15,9 +15,6 @@
max-height: 100%; max-height: 100%;
position: absolute; position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: white; background-color: white;
} }