forked from external-repos/squoosh
fix crop aspect and presets
This commit is contained in:
@@ -27,6 +27,7 @@ interface PointerTrack {
|
|||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
edges: { edge: Edge; value: number }[];
|
edges: { edge: Edge; value: number }[];
|
||||||
|
aspect: number | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@@ -101,14 +102,22 @@ export default class Cropper extends Component<Props, State> {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
let aspect;
|
||||||
const edges = edgeAttr.split(/ *, */) as Edge[];
|
const edges = edgeAttr.split(/ *, */) as Edge[];
|
||||||
// console.log(this.props.lockAspect);
|
if (this.props.lockAspect) {
|
||||||
if (this.props.lockAspect && edges.length === 1) return;
|
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, {
|
this.pointers.set(event.pointerId, {
|
||||||
x: event.x,
|
x: event.x,
|
||||||
y: event.y,
|
y: event.y,
|
||||||
edges: edges.map((edge) => ({ edge, value: this.state.crop[edge] })),
|
edges: edges.map((edge) => ({ edge, value: this.state.crop[edge] })),
|
||||||
|
aspect,
|
||||||
});
|
});
|
||||||
target.setPointerCapture(event.pointerId);
|
target.setPointerCapture(event.pointerId);
|
||||||
}
|
}
|
||||||
@@ -120,17 +129,14 @@ export default class Cropper extends Component<Props, State> {
|
|||||||
if (down && target.hasPointerCapture(event.pointerId)) {
|
if (down && target.hasPointerCapture(event.pointerId)) {
|
||||||
const { size } = this.props;
|
const { size } = this.props;
|
||||||
const oldCrop = this.state.crop;
|
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;
|
const scale = this.props.scale || 1;
|
||||||
let dx = (event.x - down.x) / scale;
|
let dx = (event.x - down.x) / scale;
|
||||||
let dy = (event.y - down.y) / 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;
|
const dir = (dx + dy) / 2;
|
||||||
dx = dir * aspect;
|
dx = dir * down.aspect;
|
||||||
dy = dir / aspect;
|
dy = dir / down.aspect;
|
||||||
}
|
}
|
||||||
const crop: Partial<CropBox> = {};
|
const crop: Partial<CropBox> = {};
|
||||||
for (const { edge, value } of down.edges) {
|
for (const { edge, value } of down.edges) {
|
||||||
@@ -151,39 +157,64 @@ export default class Cropper extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
crop[edge] = edgeValue;
|
crop[edge] = edgeValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent MOVE from resizing the cropbox:
|
// Prevent MOVE from resizing the cropbox:
|
||||||
if (crop.left && crop.right) {
|
if (crop.left && crop.right) {
|
||||||
if (crop.left < 0) crop.right += crop.left;
|
if (crop.left < 0) crop.right += crop.left;
|
||||||
if (crop.right < 0) crop.left += crop.right;
|
if (crop.right < 0) crop.left += crop.right;
|
||||||
} else {
|
} else {
|
||||||
// enforce minimum 1px cropbox width
|
// enforce minimum 1px cropbox width
|
||||||
if (crop.left)
|
if (crop.left) {
|
||||||
|
if (down.aspect) crop.left = Math.max(0, crop.left);
|
||||||
|
else
|
||||||
crop.left = Math.min(
|
crop.left = Math.min(
|
||||||
crop.left,
|
crop.left,
|
||||||
size.width - oldCrop.right - MIN_SIZE,
|
size.width - oldCrop.right - MIN_SIZE,
|
||||||
);
|
);
|
||||||
if (crop.right)
|
}
|
||||||
|
if (crop.right) {
|
||||||
|
if (down.aspect) crop.right = Math.max(0, crop.right);
|
||||||
crop.right = Math.min(
|
crop.right = Math.min(
|
||||||
crop.right,
|
crop.right,
|
||||||
size.width - oldCrop.left - MIN_SIZE,
|
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 && crop.bottom) {
|
||||||
if (crop.top < 0) crop.bottom += crop.top;
|
if (crop.top < 0) crop.bottom += crop.top;
|
||||||
if (crop.bottom < 0) crop.top += crop.bottom;
|
if (crop.bottom < 0) crop.top += crop.bottom;
|
||||||
} else {
|
} else {
|
||||||
// enforce minimum 1px cropbox height
|
// 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 = Math.min(
|
||||||
crop.top,
|
crop.top,
|
||||||
size.height - oldCrop.bottom - MIN_SIZE,
|
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 = Math.min(
|
||||||
crop.bottom,
|
crop.bottom,
|
||||||
size.height - oldCrop.top - MIN_SIZE,
|
size.height - oldCrop.top - MIN_SIZE,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
down.aspect &&
|
||||||
|
(crop.top ?? oldCrop.top) + (crop.bottom ?? oldCrop.bottom) >
|
||||||
|
size.height
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setCrop(crop);
|
this.setCrop(crop);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
@@ -152,8 +152,6 @@ export default class Transform extends Component<Props, State> {
|
|||||||
newState = cleanSet(newState, 'crop', crop);
|
newState = cleanSet(newState, 'crop', crop);
|
||||||
newState = cleanSet(newState, 'flip', flip);
|
newState = cleanSet(newState, 'flip', flip);
|
||||||
|
|
||||||
console.log('u', JSON.parse(JSON.stringify(newState)));
|
|
||||||
|
|
||||||
if (onSave) onSave({ preprocessorState: newState });
|
if (onSave) onSave({ preprocessorState: newState });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -164,29 +162,6 @@ export default class Transform extends Component<Props, State> {
|
|||||||
onSave({ preprocessorState: this.props.preprocessorState });
|
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 = () => {
|
private zoomIn = () => {
|
||||||
if (!this.pinchZoom.current) throw Error('Missing pinch-zoom element');
|
if (!this.pinchZoom.current) throw Error('Missing pinch-zoom element');
|
||||||
this.pinchZoom.current.scaleTo(this.state.scale * 1.25, scaleToOpts);
|
this.pinchZoom.current.scaleTo(this.state.scale * 1.25, scaleToOpts);
|
||||||
@@ -235,11 +210,23 @@ export default class Transform extends Component<Props, State> {
|
|||||||
|
|
||||||
private onCropPresetChange = (event: Event) => {
|
private onCropPresetChange = (event: Event) => {
|
||||||
const { value } = event.target as HTMLSelectElement;
|
const { value } = event.target as HTMLSelectElement;
|
||||||
// @ts-ignore-next
|
const cropPreset = value ? (value as keyof typeof cropPresets) : undefined;
|
||||||
const cropPreset = cropPresets[value];
|
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({
|
this.setState({
|
||||||
|
crop,
|
||||||
cropPreset,
|
cropPreset,
|
||||||
lockAspect: true,
|
lockAspect: !!cropPreset,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -318,12 +305,6 @@ export default class Transform extends Component<Props, State> {
|
|||||||
this.setState({ flip: { horizontal, vertical: !vertical } });
|
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 = () => {
|
private toggleLockAspect = () => {
|
||||||
this.setState({ lockAspect: !this.state.lockAspect });
|
this.setState({ lockAspect: !this.state.lockAspect });
|
||||||
};
|
};
|
||||||
@@ -360,17 +341,6 @@ export default class Transform extends Component<Props, State> {
|
|||||||
this.setCrop({ top, right, bottom, left });
|
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(
|
render(
|
||||||
{ mobileView, source }: Props,
|
{ mobileView, source }: Props,
|
||||||
{ scale, editingScale, rotate, flip, crop, cropPreset, lockAspect }: State,
|
{ scale, editingScale, rotate, flip, crop, cropPreset, lockAspect }: State,
|
||||||
@@ -575,12 +545,12 @@ interface BackdropProps {
|
|||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @TODO this could at least use clip-path. It's too expensive this way. */
|
||||||
class Backdrop extends Component<BackdropProps> {
|
class Backdrop extends Component<BackdropProps> {
|
||||||
shouldComponentUpdate({ width, height }: BackdropProps) {
|
shouldComponentUpdate({ width, height }: BackdropProps) {
|
||||||
return width !== this.props.width || height !== this.props.height;
|
return width !== this.props.width || height !== this.props.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @TODO this could at least use clip-path */
|
|
||||||
render({ width, height }: BackdropProps) {
|
render({ width, height }: BackdropProps) {
|
||||||
const transform =
|
const transform =
|
||||||
`transform-origin: 50% 50%; transform: translate(var(--x), var(--y)) ` +
|
`transform-origin: 50% 50%; transform: translate(var(--x), var(--y)) ` +
|
||||||
|
|||||||
Reference in New Issue
Block a user