fix crop aspect and presets

This commit is contained in:
Jason Miller
2020-12-09 11:22:22 -05:00
parent a7a991ae45
commit ee8ea539e7
2 changed files with 64 additions and 63 deletions

View File

@@ -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<Props, State> {
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<Props, State> {
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<CropBox> = {};
for (const { edge, value } of down.edges) {
@@ -151,39 +157,64 @@ export default class Cropper extends Component<Props, State> {
}
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();

View File

@@ -152,8 +152,6 @@ export default class Transform extends Component<Props, State> {
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<Props, State> {
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<Props, State> {
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<Props, State> {
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<Props, State> {
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<BackdropProps> {
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)) ` +