From ee8ea539e7a91804b72cd4a5fcdfb134eaa40925 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Wed, 9 Dec 2020 11:22:22 -0500 Subject: [PATCH] fix crop aspect and presets --- .../Compress/Transform/Cropper/index.tsx | 65 ++++++++++++++----- .../lazy-app/Compress/Transform/index.tsx | 62 +++++------------- 2 files changed, 64 insertions(+), 63 deletions(-) diff --git a/src/client/lazy-app/Compress/Transform/Cropper/index.tsx b/src/client/lazy-app/Compress/Transform/Cropper/index.tsx index bb0f1c13..a50eae86 100644 --- a/src/client/lazy-app/Compress/Transform/Cropper/index.tsx +++ b/src/client/lazy-app/Compress/Transform/Cropper/index.tsx @@ -27,6 +27,7 @@ interface PointerTrack { x: number; y: number; edges: { edge: Edge; value: number }[]; + aspect: number | undefined; } interface State { @@ -101,14 +102,22 @@ export default class Cropper extends Component { event.stopPropagation(); event.preventDefault(); + let aspect; const edges = edgeAttr.split(/ *, */) as Edge[]; - // console.log(this.props.lockAspect); - if (this.props.lockAspect && edges.length === 1) return; + if (this.props.lockAspect) { + if (edges.length === 1) return; + const { size } = this.props; + const oldCrop = this.state.crop; + aspect = + (size.width - oldCrop.left - oldCrop.right) / + (size.height - oldCrop.top - oldCrop.bottom); + } this.pointers.set(event.pointerId, { x: event.x, y: event.y, edges: edges.map((edge) => ({ edge, value: this.state.crop[edge] })), + aspect, }); target.setPointerCapture(event.pointerId); } @@ -120,17 +129,14 @@ export default class Cropper extends Component { if (down && target.hasPointerCapture(event.pointerId)) { const { size } = this.props; const oldCrop = this.state.crop; - const aspect = - (size.width - oldCrop.left - oldCrop.right) / - (size.height - oldCrop.top - oldCrop.bottom); const scale = this.props.scale || 1; let dx = (event.x - down.x) / scale; let dy = (event.y - down.y) / scale; - // console.log(this.props.lockAspect, aspect); - if (this.props.lockAspect) { + + if (down.aspect && down.edges.length === 2) { const dir = (dx + dy) / 2; - dx = dir * aspect; - dy = dir / aspect; + dx = dir * down.aspect; + dy = dir / down.aspect; } const crop: Partial = {}; for (const { edge, value } of down.edges) { @@ -151,39 +157,64 @@ export default class Cropper extends Component { } crop[edge] = edgeValue; } + // Prevent MOVE from resizing the cropbox: if (crop.left && crop.right) { if (crop.left < 0) crop.right += crop.left; if (crop.right < 0) crop.left += crop.right; } else { // enforce minimum 1px cropbox width - if (crop.left) - crop.left = Math.min( - crop.left, - size.width - oldCrop.right - MIN_SIZE, - ); - if (crop.right) + if (crop.left) { + if (down.aspect) crop.left = Math.max(0, crop.left); + else + crop.left = Math.min( + crop.left, + size.width - oldCrop.right - MIN_SIZE, + ); + } + if (crop.right) { + if (down.aspect) crop.right = Math.max(0, crop.right); crop.right = Math.min( crop.right, size.width - oldCrop.left - MIN_SIZE, ); + } + + if ( + down.aspect && + (crop.left ?? oldCrop.left) + (crop.right ?? oldCrop.right) > + size.width + ) + return; } if (crop.top && crop.bottom) { if (crop.top < 0) crop.bottom += crop.top; if (crop.bottom < 0) crop.top += crop.bottom; } else { // enforce minimum 1px cropbox height - if (crop.top) + if (crop.top) { + if (down.aspect) crop.top = Math.max(0, crop.top); crop.top = Math.min( crop.top, size.height - oldCrop.bottom - MIN_SIZE, ); - if (crop.bottom) + } + if (crop.bottom) { + if (down.aspect) crop.bottom = Math.max(0, crop.bottom); crop.bottom = Math.min( crop.bottom, size.height - oldCrop.top - MIN_SIZE, ); + } + + if ( + down.aspect && + (crop.top ?? oldCrop.top) + (crop.bottom ?? oldCrop.bottom) > + size.height + ) + return; } + this.setCrop(crop); event.stopPropagation(); event.preventDefault(); diff --git a/src/client/lazy-app/Compress/Transform/index.tsx b/src/client/lazy-app/Compress/Transform/index.tsx index 0d7c6fc3..eb16ecea 100644 --- a/src/client/lazy-app/Compress/Transform/index.tsx +++ b/src/client/lazy-app/Compress/Transform/index.tsx @@ -152,8 +152,6 @@ export default class Transform extends Component { newState = cleanSet(newState, 'crop', crop); newState = cleanSet(newState, 'flip', flip); - console.log('u', JSON.parse(JSON.stringify(newState))); - if (onSave) onSave({ preprocessorState: newState }); }; @@ -164,29 +162,6 @@ export default class Transform extends Component { onSave({ preprocessorState: this.props.preprocessorState }); }; - // private fitToViewport = () => { - // const pinchZoom = this.pinchZoom.current; - // const img = this.props.source?.preprocessed; - // if (!img || !pinchZoom) return; - // const scale = Number(Math.min( - // (window.innerWidth - 20) / img.width, - // (window.innerHeight - 20) / img.height - // ).toFixed(2)); - // pinchZoom.scaleTo(Number(scale.toFixed(2)), { allowChangeEvent: true }); - // this.recenter(); - // }; - - // private recenter = () => { - // const pinchZoom = this.pinchZoom.current; - // const img = this.props.source?.preprocessed; - // if (!img || !pinchZoom) return; - // pinchZoom.setTransform({ - // x: (img.width - img.width * pinchZoom.scale) / 2, - // y: (img.height - img.height * pinchZoom.scale) / 2, - // allowChangeEvent: true - // }); - // }; - private zoomIn = () => { if (!this.pinchZoom.current) throw Error('Missing pinch-zoom element'); this.pinchZoom.current.scaleTo(this.state.scale * 1.25, scaleToOpts); @@ -235,11 +210,23 @@ export default class Transform extends Component { private onCropPresetChange = (event: Event) => { const { value } = event.target as HTMLSelectElement; - // @ts-ignore-next - const cropPreset = cropPresets[value]; + const cropPreset = value ? (value as keyof typeof cropPresets) : undefined; + const crop = { ...this.state.crop }; + if (cropPreset) { + const preset = cropPresets[cropPreset]; + const { width, height } = this.props.source.decoded; + const w = width - crop.left - crop.right; + const h = w / preset.ratio; + crop.bottom = height - crop.top - h; + if (crop.bottom < 0) { + crop.top += crop.bottom; + crop.bottom = 0; + } + } this.setState({ + crop, cropPreset, - lockAspect: true, + lockAspect: !!cropPreset, }); }; @@ -318,12 +305,6 @@ export default class Transform extends Component { this.setState({ flip: { horizontal, vertical: !vertical } }); }; - // private update = (event: Event) => { - // const { name, value } = event.target as HTMLInputElement; - // const state = cleanSet(this.state, name, value); - // this.setState(state); - // }; - private toggleLockAspect = () => { this.setState({ lockAspect: !this.state.lockAspect }); }; @@ -360,17 +341,6 @@ export default class Transform extends Component { this.setCrop({ top, right, bottom, left }); }; - // private onRotateClick = () => { - // const { preprocessorState: inputProcessorState } = this.props; - // if (!inputProcessorState) return; - // const newState = cleanSet( - // inputProcessorState, - // 'rotate.rotate', - // (inputProcessorState.rotate.rotate + 90) % 360, - // ); - // this.props.onPreprocessorChange(newState); - // }; - render( { mobileView, source }: Props, { scale, editingScale, rotate, flip, crop, cropPreset, lockAspect }: State, @@ -575,12 +545,12 @@ interface BackdropProps { height: number; } +/** @TODO this could at least use clip-path. It's too expensive this way. */ class Backdrop extends Component { shouldComponentUpdate({ width, height }: BackdropProps) { return width !== this.props.width || height !== this.props.height; } - /** @TODO this could at least use clip-path */ render({ width, height }: BackdropProps) { const transform = `transform-origin: 50% 50%; transform: translate(var(--x), var(--y)) ` +