revert: image editor ux
This commit is contained in:
@@ -34,9 +34,6 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
|
|||||||
onSave,
|
onSave,
|
||||||
uploadButton
|
uploadButton
|
||||||
}) => {
|
}) => {
|
||||||
const MIN_SCALE = 0.4;
|
|
||||||
const MAX_SCALE = 10;
|
|
||||||
const zoomFactor = 1.1;
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
const overlayCanvasRef = useRef<HTMLCanvasElement>(null);
|
const overlayCanvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
@@ -50,20 +47,25 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
|
|||||||
const autoScale = useRef<number>(1);
|
const autoScale = useRef<number>(1);
|
||||||
const cursorRef = useRef<HTMLDivElement>(null);
|
const cursorRef = useRef<HTMLDivElement>(null);
|
||||||
const [imgLoaded, setImgLoaded] = useState(false);
|
const [imgLoaded, setImgLoaded] = useState(false);
|
||||||
const offsetRef = useRef({ x: 0, y: 0 });
|
|
||||||
const originPosition = useRef({ x: 0, y: 0 });
|
let scale = 1;
|
||||||
|
let offsetX = 0;
|
||||||
|
let offsetY = 0;
|
||||||
|
|
||||||
|
const MIN_SCALE = 0.5;
|
||||||
|
const MAX_SCALE = 5;
|
||||||
|
|
||||||
const getTransformedPoint = (event: React.MouseEvent<HTMLCanvasElement>) => {
|
const getTransformedPoint = (event: React.MouseEvent<HTMLCanvasElement>) => {
|
||||||
const overlayCanvas = overlayCanvasRef.current!;
|
const overlayCanvas = overlayCanvasRef.current!;
|
||||||
const rect = overlayCanvas.getBoundingClientRect();
|
const rect = overlayCanvas.getBoundingClientRect();
|
||||||
|
|
||||||
// 获取鼠标在画布上的原始坐标
|
|
||||||
const x = event.clientX - rect.left;
|
const x = event.clientX - rect.left;
|
||||||
const y = event.clientY - rect.top;
|
const y = event.clientY - rect.top;
|
||||||
|
|
||||||
// 考虑缩放比例和偏移量
|
const transformedX = x - overlayCanvas.width / 2;
|
||||||
const transformedX = (x - offsetRef.current.x) / autoScale.current;
|
const transformedY = y - overlayCanvas.height / 2;
|
||||||
const transformedY = (y - offsetRef.current.y) / autoScale.current;
|
|
||||||
|
console.log('Mouse Coordinates (Transformed):', transformedX, transformedY);
|
||||||
|
|
||||||
return { x: transformedX, y: transformedY };
|
return { x: transformedX, y: transformedY };
|
||||||
};
|
};
|
||||||
@@ -111,10 +113,6 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
|
|||||||
overlayCtx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
|
overlayCtx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
|
||||||
ctx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
|
ctx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
|
||||||
offscreenCtx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
|
offscreenCtx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
|
||||||
originPosition.current = {
|
|
||||||
x: ctx!.canvas.width / 2,
|
|
||||||
y: ctx!.canvas.height / 2
|
|
||||||
};
|
|
||||||
}, [canvasRef.current, overlayCanvasRef.current]);
|
}, [canvasRef.current, overlayCanvasRef.current]);
|
||||||
|
|
||||||
const scaleCanvasSize = useCallback(() => {
|
const scaleCanvasSize = useCallback(() => {
|
||||||
@@ -441,8 +439,8 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
|
|||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
canvas!.width = img.width;
|
canvas!.width = img.width * scale;
|
||||||
canvas!.height = img.height;
|
canvas!.height = img.height * scale;
|
||||||
|
|
||||||
autoScale.current = scale / autoScale.current;
|
autoScale.current = scale / autoScale.current;
|
||||||
|
|
||||||
@@ -459,7 +457,6 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
|
|||||||
canvas!.width,
|
canvas!.width,
|
||||||
canvas!.height
|
canvas!.height
|
||||||
);
|
);
|
||||||
canvas!.style.transform = `scale(${scale})`;
|
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -505,102 +502,61 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
|
|||||||
const y = event.clientY - rect.top;
|
const y = event.clientY - rect.top;
|
||||||
|
|
||||||
// 考虑缩放比例和偏移量
|
// 考虑缩放比例和偏移量
|
||||||
const transformedX = (x - offsetRef.current.x) / autoScale.current;
|
const transformedX = (x - offsetX) / scale;
|
||||||
const transformedY = (y - offsetRef.current.y) / autoScale.current;
|
const transformedY = (y - offsetY) / scale;
|
||||||
|
|
||||||
return { x: transformedX, y: transformedY };
|
return { x: transformedX, y: transformedY };
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMousePosition = (event: any) => {
|
|
||||||
const canvas = overlayCanvasRef.current!;
|
|
||||||
const rect = canvas.getBoundingClientRect();
|
|
||||||
const mouseX = event.clientX - rect.left;
|
|
||||||
const mouseY = event.clientY - rect.top;
|
|
||||||
|
|
||||||
const { x: originX, y: originY } = originPosition.current;
|
|
||||||
const canvasX = (mouseX - originX) / autoScale.current;
|
|
||||||
const canvasY = (mouseY - originY) / autoScale.current;
|
|
||||||
|
|
||||||
return { x: canvasX, y: canvasY };
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateCavasTransform = (event: any) => {
|
|
||||||
const canvas = overlayCanvasRef.current!;
|
|
||||||
const rect = canvas.getBoundingClientRect();
|
|
||||||
const mouseX = event.clientX - rect.left;
|
|
||||||
const mouseY = event.clientY - rect.top;
|
|
||||||
|
|
||||||
// 鼠标在中心坐标系中的位置
|
|
||||||
const { x: originX, y: originY } = originPosition.current;
|
|
||||||
const canvasX = (mouseX - originX) / autoScale.current;
|
|
||||||
const canvasY = (mouseY - originY) / autoScale.current;
|
|
||||||
|
|
||||||
// 更新缩放比例
|
|
||||||
// max scale: 2, min scale: 0.3
|
|
||||||
const zoomIn = event.deltaY < 0;
|
|
||||||
let newScale = autoScale.current;
|
|
||||||
if (zoomIn) {
|
|
||||||
newScale = Math.min(MAX_SCALE, autoScale.current * zoomFactor);
|
|
||||||
} else {
|
|
||||||
newScale = Math.max(MIN_SCALE, autoScale.current / zoomFactor);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('New Scale:', newScale);
|
|
||||||
|
|
||||||
autoScale.current = newScale;
|
|
||||||
// 更新原点
|
|
||||||
const neworiginX = mouseX - canvasX * newScale;
|
|
||||||
const neworiginY = mouseY - canvasY * newScale;
|
|
||||||
originPosition.current = { x: neworiginX, y: neworiginY };
|
|
||||||
|
|
||||||
const overlayCtx = overlayCanvasRef.current!.getContext('2d')!;
|
|
||||||
const canvasCtx = canvasRef.current!.getContext('2d')!;
|
|
||||||
// 更新 Canvas 的变换
|
|
||||||
overlayCtx.setTransform(
|
|
||||||
autoScale.current,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
autoScale.current,
|
|
||||||
neworiginX,
|
|
||||||
neworiginY
|
|
||||||
);
|
|
||||||
canvasCtx.setTransform(
|
|
||||||
autoScale.current,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
autoScale.current,
|
|
||||||
neworiginX,
|
|
||||||
neworiginY
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnWheel = (event: WheelEvent) => {
|
const handleOnWheel = (event: WheelEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
updateCavasTransform(event);
|
const zoomFactor = event.deltaY < 0 ? 1.1 : 0.9;
|
||||||
|
const newScale = Math.min(
|
||||||
|
MAX_SCALE,
|
||||||
|
Math.max(MIN_SCALE, scale * zoomFactor)
|
||||||
|
);
|
||||||
|
|
||||||
overlayCanvasRef.current!.style.transform = `scale(${autoScale.current})`;
|
const rect = canvasRef.current!.getBoundingClientRect();
|
||||||
canvasRef.current!.style.transform = `scale(${autoScale.current})`;
|
const mouseX = event.clientX - rect.left;
|
||||||
|
const mouseY = event.clientY - rect.top;
|
||||||
|
|
||||||
|
// 计算新的偏移量
|
||||||
|
offsetX = mouseX - (mouseX - offsetX) * (newScale / scale);
|
||||||
|
offsetY = mouseY - (mouseY - offsetY) * (newScale / scale);
|
||||||
|
|
||||||
|
// 更新缩放比例
|
||||||
|
scale = newScale;
|
||||||
|
|
||||||
|
// 设置画布的变换
|
||||||
|
const overlayCtx = overlayCanvasRef.current!.getContext('2d')!;
|
||||||
|
overlayCtx.setTransform(scale, 0, 0, scale, offsetX, offsetY);
|
||||||
|
const canvasCtx = canvasRef.current!.getContext('2d')!;
|
||||||
|
canvasCtx.setTransform(scale, 0, 0, scale, offsetX, offsetY);
|
||||||
|
overlayCanvasRef.current!.style.transform = `scale(${scale})`;
|
||||||
|
canvasRef.current!.style.transform = `scale(${scale})`;
|
||||||
|
|
||||||
|
console.log('Zoom:', scale, offsetX, offsetY);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initializeImage();
|
initializeImage();
|
||||||
}, [initializeImage]);
|
}, [initializeImage]);
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
const container = containerRef.current;
|
// const container = containerRef.current;
|
||||||
if (!container) return;
|
// if (!container) return;
|
||||||
if (container) {
|
// if (container) {
|
||||||
resizeObserver.current = new ResizeObserver(
|
// resizeObserver.current = new ResizeObserver(
|
||||||
_.throttle(handleResize, 100)
|
// _.throttle(handleResize, 100)
|
||||||
);
|
// );
|
||||||
resizeObserver.current.observe(container);
|
// resizeObserver.current.observe(container);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return () => {
|
// return () => {
|
||||||
resizeObserver.current?.disconnect();
|
// resizeObserver.current?.disconnect();
|
||||||
};
|
// };
|
||||||
}, [handleResize, containerRef.current]);
|
// }, [handleResize, containerRef.current]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
createOffscreenCanvas();
|
createOffscreenCanvas();
|
||||||
|
|||||||
@@ -184,7 +184,6 @@ body {
|
|||||||
font-family: 'noto sans', sans-serif;
|
font-family: 'noto sans', sans-serif;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
overflow-y: initial !important;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 0;
|
width: 0;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export default function useOverlayScroller(options?: any) {
|
|||||||
clickScroll: 'instant'
|
clickScroll: 'instant'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
defer: true
|
defer: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,29 +4,36 @@ import 'simplebar-react/dist/simplebar.min.css';
|
|||||||
import '../style/column-wrapper.less';
|
import '../style/column-wrapper.less';
|
||||||
|
|
||||||
const ColumnWrapper: React.FC<any> = ({ children, footer, height }) => {
|
const ColumnWrapper: React.FC<any> = ({ children, footer, height }) => {
|
||||||
if (footer) {
|
const handleOnWheel = (e: React.WheelEvent) => {
|
||||||
return (
|
console.log('handleOnWheel', e);
|
||||||
<div className="column-wrapper-footer">
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{footer ? (
|
||||||
|
<div className="column-wrapper-footer">
|
||||||
|
<div className="column-wrapper">
|
||||||
|
<SimpleBar
|
||||||
|
style={{
|
||||||
|
maxHeight: height || 'calc(100vh - 89px)',
|
||||||
|
paddingBottom: '50px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</SimpleBar>
|
||||||
|
</div>
|
||||||
|
{<div className="footer">{footer}</div>}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<div className="column-wrapper">
|
<div className="column-wrapper">
|
||||||
<SimpleBar
|
<SimpleBar style={{ maxHeight: height || 'calc(100vh - 89px)' }}>
|
||||||
style={{
|
|
||||||
maxHeight: height || 'calc(100vh - 89px)',
|
|
||||||
paddingBottom: '50px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</SimpleBar>
|
</SimpleBar>
|
||||||
</div>
|
</div>
|
||||||
{<div className="footer">{footer}</div>}
|
)}
|
||||||
</div>
|
</>
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="column-wrapper">
|
|
||||||
<SimpleBar style={{ maxHeight: height || 'calc(100vh - 89px)' }}>
|
|
||||||
{children}
|
|
||||||
</SimpleBar>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -189,8 +189,9 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
value: item.quantization
|
value: item.quantization
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
setQuantizationOptions(quantizationList);
|
const result = _.uniqBy(quantizationList, 'value');
|
||||||
return quantizationList;
|
setQuantizationOptions(result);
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSetBackendOptions = () => {
|
const handleSetBackendOptions = () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user