chore: image edit scale

This commit is contained in:
jialin
2025-01-14 17:11:46 +08:00
parent c323b6a8a9
commit e089ddb223
4 changed files with 211 additions and 186 deletions
+186 -167
View File
@@ -1,5 +1,6 @@
import { import {
DownloadOutlined, DownloadOutlined,
ExpandOutlined,
FormatPainterOutlined, FormatPainterOutlined,
SyncOutlined, SyncOutlined,
UndoOutlined UndoOutlined
@@ -34,6 +35,9 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
onSave, onSave,
uploadButton uploadButton
}) => { }) => {
const MIN_SCALE = 0.5;
const MAX_SCALE = 8;
const ZOOM_SPEED = 0.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);
@@ -45,43 +49,66 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
const strokesRef = useRef<Stroke[]>([]); const strokesRef = useRef<Stroke[]>([]);
const offscreenCanvasRef = useRef<HTMLCanvasElement | null>(null); const offscreenCanvasRef = useRef<HTMLCanvasElement | null>(null);
const autoScale = useRef<number>(1); const autoScale = useRef<number>(1);
const baseScale = useRef<number>(1);
const cursorRef = useRef<HTMLDivElement>(null); const cursorRef = useRef<HTMLDivElement>(null);
const [imgLoaded, setImgLoaded] = useState(false); const [imgLoaded, setImgLoaded] = useState(false);
const translatePos = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
const contentPos = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
const animationFrameIdRef = useRef<number | null>(null);
const originRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
const preAutoScale = useRef<number>(1);
let scale = 1; const getTransformedPoint = (offsetX: number, offsetY: number) => {
let offsetX = 0; const { current: scale } = autoScale;
let offsetY = 0;
const MIN_SCALE = 0.5; const { x: translateX, y: translateY } = translatePos.current;
const MAX_SCALE = 5;
const getTransformedPoint = (event: React.MouseEvent<HTMLCanvasElement>) => { const transformedX = (offsetX + lineWidth / 2 - translateX) / scale;
const overlayCanvas = overlayCanvasRef.current!; const transformedY = (offsetY + lineWidth / 2 - translateY) / scale;
const rect = overlayCanvas.getBoundingClientRect();
const x = event.clientX - rect.left; return {
const y = event.clientY - rect.top; x: Math.round(transformedX),
y: Math.round(transformedY)
};
};
const transformedX = x - overlayCanvas.width / 2; const getTransformLineWidth = (lineWidth: number) => {
const transformedY = y - overlayCanvas.height / 2; return lineWidth / autoScale.current;
};
console.log('Mouse Coordinates (Transformed):', transformedX, transformedY); const setCanvasTransformOrigin = (e: React.MouseEvent<HTMLCanvasElement>) => {
if (autoScale.current <= MIN_SCALE) {
return;
}
return { x: transformedX, y: transformedY }; if (autoScale.current >= MAX_SCALE) {
return;
}
console.log('Setting transform origin:', autoScale.current);
const rect = overlayCanvasRef.current!.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
const originX = mouseX / rect.width;
const originY = mouseY / rect.height;
overlayCanvasRef.current!.style.transformOrigin = `${originX * 100}% ${originY * 100}%`;
canvasRef.current!.style.transformOrigin = `${originX * 100}% ${originY * 100}%`;
}; };
const handleMouseEnter = (e: React.MouseEvent<HTMLCanvasElement>) => { const handleMouseEnter = (e: React.MouseEvent<HTMLCanvasElement>) => {
overlayCanvasRef.current!.style.cursor = 'none'; overlayCanvasRef.current!.style.cursor = 'none';
cursorRef.current!.style.display = 'block'; cursorRef.current!.style.display = 'block';
cursorRef.current!.style.top = `${e.clientY - lineWidth / 2}px`; cursorRef.current!.style.top = `${e.clientY}px`;
cursorRef.current!.style.left = `${e.clientX - lineWidth / 2}px`; cursorRef.current!.style.left = `${e.clientX}px`;
}; };
const handleMouseMove = (e: React.MouseEvent<HTMLCanvasElement>) => { const handleMouseMove = (e: React.MouseEvent<HTMLCanvasElement>) => {
overlayCanvasRef.current!.style.cursor = 'none'; overlayCanvasRef.current!.style.cursor = 'none';
cursorRef.current!.style.display = 'block'; cursorRef.current!.style.display = 'block';
cursorRef.current!.style.top = `${e.clientY - lineWidth / 2}px`; cursorRef.current!.style.top = `${e.clientY}px`;
cursorRef.current!.style.left = `${e.clientX - lineWidth / 2}px`; cursorRef.current!.style.left = `${e.clientX}px`;
}; };
const handleMouseLeave = () => { const handleMouseLeave = () => {
@@ -94,28 +121,11 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
offscreenCanvasRef.current = document.createElement('canvas'); offscreenCanvasRef.current = document.createElement('canvas');
offscreenCanvasRef.current.width = overlayCanvasRef.current!.width; offscreenCanvasRef.current.width = overlayCanvasRef.current!.width;
offscreenCanvasRef.current.height = overlayCanvasRef.current!.height; offscreenCanvasRef.current.height = overlayCanvasRef.current!.height;
const offscreenCtx = offscreenCanvasRef.current.getContext('2d')!;
offscreenCtx.translate(
overlayCanvasRef.current!.width / 2,
overlayCanvasRef.current!.height / 2
);
} }
}; };
const setCanvasCenter = useCallback(() => { // update the canvas size
if (!canvasRef.current || !overlayCanvasRef.current) return; const updateCanvasSize = useCallback(() => {
const overlayCtx = overlayCanvasRef.current!.getContext('2d');
const ctx = canvasRef.current!.getContext('2d');
const offscreenCtx = offscreenCanvasRef.current!.getContext('2d');
// Set the origin to the center
overlayCtx!.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);
}, [canvasRef.current, overlayCanvasRef.current]);
const scaleCanvasSize = useCallback(() => {
const canvas = canvasRef.current!; const canvas = canvasRef.current!;
const offscreenCanvas = offscreenCanvasRef.current!; const offscreenCanvas = offscreenCanvasRef.current!;
const overlayCanvas = overlayCanvasRef.current!; const overlayCanvas = overlayCanvasRef.current!;
@@ -131,10 +141,6 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
strokesRef.current = strokes; strokesRef.current = strokes;
}; };
const scaleLineWidth = useCallback(() => {
// setLineWidth(lineWidth * autoScale.current);
}, [lineWidth]);
const generateMask = useCallback(() => { const generateMask = useCallback(() => {
const overlayCanvas = overlayCanvasRef.current!; const overlayCanvas = overlayCanvasRef.current!;
const maskCanvas = document.createElement('canvas'); const maskCanvas = document.createElement('canvas');
@@ -207,11 +213,12 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
ctx.beginPath(); ctx.beginPath();
stroke.forEach((point, i) => { stroke.forEach((point, i) => {
ctx.lineWidth = point.lineWidth; const { x, y } = getTransformedPoint(point.x, point.y);
ctx.lineWidth = getTransformLineWidth(point.lineWidth);
if (i === 0) { if (i === 0) {
ctx.moveTo(point.x, point.y); ctx.moveTo(x, y);
} else { } else {
ctx.lineTo(point.x, point.y); ctx.lineTo(x, y);
} }
}); });
if (compositeOperation === 'source-over') { if (compositeOperation === 'source-over') {
@@ -234,12 +241,14 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
) => { ) => {
const { lineWidth, color, compositeOperation } = options; const { lineWidth, color, compositeOperation } = options;
ctx.lineWidth = lineWidth; ctx.lineWidth = getTransformLineWidth(lineWidth);
ctx.lineCap = 'round'; ctx.lineCap = 'round';
ctx.lineJoin = 'round'; ctx.lineJoin = 'round';
ctx.globalCompositeOperation = compositeOperation; ctx.globalCompositeOperation = compositeOperation;
ctx.lineTo(point.x, point.y); const { x, y } = getTransformedPoint(point.x, point.y);
ctx.lineTo(x, y);
if (compositeOperation === 'source-over') { if (compositeOperation === 'source-over') {
ctx.strokeStyle = color; ctx.strokeStyle = color;
} }
@@ -248,14 +257,29 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
[lineWidth] [lineWidth]
); );
const setTransform = useCallback(() => {
const ctx = canvasRef.current?.getContext('2d');
const overlayCtx = overlayCanvasRef.current?.getContext('2d');
if (!ctx || !overlayCtx) return;
ctx!.resetTransform();
overlayCtx!.resetTransform();
const { current: scale } = autoScale;
const { x: translateX, y: translateY } = translatePos.current;
ctx!.setTransform(scale, 0, 0, scale, translateX, translateY);
overlayCtx!.setTransform(scale, 0, 0, scale, translateX, translateY);
}, []);
const draw = (e: React.MouseEvent<HTMLCanvasElement>) => { const draw = (e: React.MouseEvent<HTMLCanvasElement>) => {
if (!isDrawing.current) return; if (!isDrawing.current) return;
const { x, y } = getTransformedPoint(e); const { offsetX, offsetY } = e.nativeEvent;
console.log('Drawing:', e.nativeEvent, { x, y });
currentStroke.current.push({ currentStroke.current.push({
x, x: offsetX,
y, y: offsetY,
lineWidth lineWidth
}); });
@@ -265,12 +289,12 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
drawLine( drawLine(
ctx!, ctx!,
{ x, y, lineWidth }, { x: offsetX, y: offsetY, lineWidth },
{ lineWidth, color: COLOR, compositeOperation: 'destination-out' } { lineWidth, color: COLOR, compositeOperation: 'destination-out' }
); );
drawLine( drawLine(
ctx!, ctx!,
{ x, y, lineWidth }, { x: offsetX, y: offsetY, lineWidth },
{ lineWidth, color: COLOR, compositeOperation: 'source-over' } { lineWidth, color: COLOR, compositeOperation: 'source-over' }
); );
@@ -281,14 +305,16 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
isDrawing.current = true; isDrawing.current = true;
currentStroke.current = []; currentStroke.current = [];
const { x, y } = getTransformedPoint(e); const { offsetX, offsetY } = e.nativeEvent;
currentStroke.current.push({ currentStroke.current.push({
x, x: offsetX,
y, y: offsetY,
lineWidth lineWidth
}); });
const ctx = overlayCanvasRef.current!.getContext('2d'); const ctx = overlayCanvasRef.current!.getContext('2d');
setTransform();
const { x, y } = getTransformedPoint(offsetX, offsetY);
ctx!.beginPath(); ctx!.beginPath();
ctx!.moveTo(x, y); ctx!.moveTo(x, y);
@@ -312,9 +338,10 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
const clearOverlayCanvas = useCallback(() => { const clearOverlayCanvas = useCallback(() => {
const ctx = overlayCanvasRef.current!.getContext('2d'); const ctx = overlayCanvasRef.current!.getContext('2d');
ctx!.resetTransform();
ctx!.clearRect( ctx!.clearRect(
-overlayCanvasRef.current!.width / 2, 0,
-overlayCanvasRef.current!.height / 2, 0,
overlayCanvasRef.current!.width, overlayCanvasRef.current!.width,
overlayCanvasRef.current!.height overlayCanvasRef.current!.height
); );
@@ -323,24 +350,18 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
const clearCanvas = useCallback(() => { const clearCanvas = useCallback(() => {
const canvas = canvasRef.current!; const canvas = canvasRef.current!;
const ctx = canvasRef.current!.getContext('2d'); const ctx = canvasRef.current!.getContext('2d');
ctx!.clearRect( ctx!.resetTransform();
-canvas.width / 2, ctx!.clearRect(0, 0, canvas.width, canvas.height);
-canvas.height / 2,
canvas.width,
canvas.height
);
}, []); }, []);
const clearOffscreenCanvas = useCallback(() => { const clearOffscreenCanvas = useCallback(() => {
const offscreenCanvas = offscreenCanvasRef.current!; const offscreenCanvas = offscreenCanvasRef.current!;
const offscreenCtx = offscreenCanvas.getContext('2d')!; const offscreenCtx = offscreenCanvas.getContext('2d')!;
offscreenCtx.resetTransform();
offscreenCtx.clearRect( offscreenCtx.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
-offscreenCanvas.width / 2, const { current: scale } = autoScale;
-offscreenCanvas.height / 2, const { x: translateX, y: translateY } = translatePos.current;
offscreenCanvas.width, offscreenCtx!.setTransform(scale, 0, 0, scale, translateX, translateY);
offscreenCanvas.height
);
}, []); }, []);
const onReset = useCallback(() => { const onReset = useCallback(() => {
@@ -373,6 +394,8 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
clearOverlayCanvas(); clearOverlayCanvas();
setTransform();
strokes?.forEach((stroke: Point[], index) => { strokes?.forEach((stroke: Point[], index) => {
overlayCtx.save(); overlayCtx.save();
drawStroke(overlayCtx, stroke, { drawStroke(overlayCtx, stroke, {
@@ -409,21 +432,6 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
link.remove(); link.remove();
}; };
const scaleStrokes = (scale: number): Stroke[] => {
const strokes: Stroke[] = _.cloneDeep(strokesRef.current);
const newStrokes = strokes.map((stroke) => {
return stroke.map((point) => {
return {
x: point.x * scale,
y: point.y * scale,
lineWidth: point.lineWidth
};
});
});
setStrokes(newStrokes);
return newStrokes;
};
const drawImage = useCallback(async () => { const drawImage = useCallback(async () => {
if (!containerRef.current || !canvasRef.current) return; if (!containerRef.current || !canvasRef.current) return;
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
@@ -433,53 +441,46 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
const canvas = canvasRef.current!; const canvas = canvasRef.current!;
const ctx = canvas!.getContext('2d'); const ctx = canvas!.getContext('2d');
const container = containerRef.current; const container = containerRef.current;
const scale = Math.min( baseScale.current = Math.min(
container!.offsetWidth / img.width, container!.offsetWidth / img.width,
container!.offsetHeight / img.height, container!.offsetHeight / img.height,
1 1
); );
canvas!.width = img.width * scale; canvas!.width = img.width * baseScale.current;
canvas!.height = img.height * scale; canvas!.height = img.height * baseScale.current;
autoScale.current = scale / autoScale.current; // fit the image to the container
autoScale.current = autoScale.current || 1;
scaleLineWidth(); updateCanvasSize();
scaleCanvasSize();
setCanvasCenter();
clearCanvas(); clearCanvas();
ctx!.drawImage( ctx!.drawImage(img, 0, 0, canvas!.width, canvas!.height);
img,
-canvas.width / 2,
-canvas.height / 2,
canvas!.width,
canvas!.height
);
resolve(); resolve();
}; };
}); });
}, [ }, [imageSrc, containerRef.current, canvasRef.current, updateCanvasSize]);
imageSrc,
containerRef.current,
canvasRef.current,
scaleCanvasSize,
setCanvasCenter,
scaleLineWidth
]);
const handleResize = useCallback( const resetCanvas = useCallback(() => {
async (entries: ResizeObserverEntry[]) => { const canvas = canvasRef.current!;
const contentRect = entries[0].contentRect; const overlayCanvas = overlayCanvasRef.current!;
if (!contentRect.width || !contentRect.height || !imgLoaded) return; const ctx = canvas.getContext('2d');
await drawImage(); const overlayCtx = overlayCanvas.getContext('2d');
if (imageStatus.isOriginal) {
redrawStrokes(strokesRef.current, 'resize'); autoScale.current = 1;
} baseScale.current = 1;
}, translatePos.current = { x: 0, y: 0 };
[drawImage, scaleStrokes, redrawStrokes, onReset, imageStatus, imgLoaded] contentPos.current = { x: 0, y: 0 };
); canvas.style.transform = 'scale(1)';
overlayCanvas.style.transform = 'scale(1)';
cursorRef.current!.style.width = `${lineWidth}px`;
cursorRef.current!.style.height = `${lineWidth}px`;
ctx!.resetTransform();
overlayCtx!.resetTransform();
}, []);
const initializeImage = useCallback(async () => { const initializeImage = useCallback(async () => {
setImgLoaded(false); setImgLoaded(false);
@@ -490,74 +491,71 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
redrawStrokes(strokesRef.current, 'initialize'); redrawStrokes(strokesRef.current, 'initialize');
} else if (imageStatus.isResetNeeded) { } else if (imageStatus.isResetNeeded) {
onReset(); onReset();
resetCanvas();
} }
}, [drawImage, onReset, redrawStrokes, imageStatus]); }, [drawImage, onReset, redrawStrokes, imageStatus]);
const calcTransformedPoint = (event: React.MouseEvent<HTMLCanvasElement>) => { const updateZoom = (scaleChange: number, mouseX: number, mouseY: number) => {
const overlayCanvas = overlayCanvasRef.current!; const newScale = _.round(autoScale.current + scaleChange, 2);
const rect = overlayCanvas.getBoundingClientRect();
// 获取鼠标在画布上的原始坐标 if (newScale < MIN_SCALE || newScale > MAX_SCALE) return;
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
// 考虑缩放比例和偏移量 const { current: oldScale } = autoScale;
const transformedX = (x - offsetX) / scale; const { x: oldTranslateX, y: oldTranslateY } = translatePos.current;
const transformedY = (y - offsetY) / scale;
return { x: transformedX, y: transformedY }; const centerX = (mouseX - oldTranslateX) / oldScale;
const centerY = (mouseY - oldTranslateY) / oldScale;
autoScale.current = newScale;
const newTranslateX = mouseX - centerX * newScale;
const newTranslateY = mouseY - centerY * newScale;
translatePos.current = { x: newTranslateX, y: newTranslateY };
}; };
const handleOnWheel = (event: WheelEvent) => { const handleZoom = (event: React.WheelEvent<HTMLCanvasElement>) => {
event.preventDefault(); const scaleChange = event.deltaY > 0 ? -ZOOM_SPEED : ZOOM_SPEED;
const zoomFactor = event.deltaY < 0 ? 1.1 : 0.9; // current mouse position
const newScale = Math.min( const canvas = overlayCanvasRef.current!;
MAX_SCALE, const rect = canvas.getBoundingClientRect();
Math.max(MIN_SCALE, scale * zoomFactor)
);
const rect = canvasRef.current!.getBoundingClientRect();
const mouseX = event.clientX - rect.left; const mouseX = event.clientX - rect.left;
const mouseY = event.clientY - rect.top; const mouseY = event.clientY - rect.top;
// 计算新的偏移量 setCanvasTransformOrigin(event);
offsetX = mouseX - (mouseX - offsetX) * (newScale / scale);
offsetY = mouseY - (mouseY - offsetY) * (newScale / scale);
// 更新缩放比例 updateZoom(scaleChange, mouseX, mouseY);
scale = newScale;
// 设置画布的变换 overlayCanvasRef.current!.style.transform = `scale(${autoScale.current})`;
const overlayCtx = overlayCanvasRef.current!.getContext('2d')!; canvasRef.current!.style.transform = `scale(${autoScale.current})`;
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); const updateCursorSize = () => {
cursorRef.current!.style.width = `${lineWidth * autoScale.current}px`;
cursorRef.current!.style.height = `${lineWidth * autoScale.current}px`;
};
const handleOnWheel = (event: any) => {
handleZoom(event);
updateCursorSize();
};
const handleFitView = () => {
autoScale.current = baseScale.current;
translatePos.current = { x: 0, y: 0 };
setTransform();
overlayCanvasRef.current!.style.transform = `scale(${autoScale.current})`;
canvasRef.current!.style.transform = `scale(${autoScale.current})`;
updateCursorSize();
redrawStrokes(strokesRef.current);
}; };
useEffect(() => { useEffect(() => {
initializeImage(); initializeImage();
}, [initializeImage]); }, [initializeImage]);
// useEffect(() => {
// const container = containerRef.current;
// if (!container) return;
// if (container) {
// resizeObserver.current = new ResizeObserver(
// _.throttle(handleResize, 100)
// );
// resizeObserver.current.observe(container);
// }
// return () => {
// resizeObserver.current?.disconnect();
// };
// }, [handleResize, containerRef.current]);
useEffect(() => { useEffect(() => {
createOffscreenCanvas(); createOffscreenCanvas();
const handleUndoShortcut = (e: KeyboardEvent) => { const handleUndoShortcut = (e: KeyboardEvent) => {
@@ -569,6 +567,9 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
window.addEventListener('keydown', handleUndoShortcut); window.addEventListener('keydown', handleUndoShortcut);
return () => { return () => {
window.removeEventListener('keydown', handleUndoShortcut); window.removeEventListener('keydown', handleUndoShortcut);
if (animationFrameIdRef.current !== null) {
cancelAnimationFrame(animationFrameIdRef.current);
}
}; };
}, []); }, []);
@@ -581,7 +582,12 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
}, [disabled]); }, [disabled]);
return ( return (
<div className="editor-wrapper"> <div
className="editor-wrapper"
style={{
border: '1px solid #ddd'
}}
>
<div className="flex-between"> <div className="flex-between">
<div className="tools"> <div className="tools">
<Tooltip <Tooltip
@@ -636,6 +642,18 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
<SyncOutlined className="font-size-14" /> <SyncOutlined className="font-size-14" />
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip
title={intl.formatMessage({ id: 'playground.image.fitview' })}
>
<Button
onClick={handleFitView}
size="middle"
type="text"
disabled={disabled}
>
<ExpandOutlined className="font-size-14" />
</Button>
</Tooltip>
</div> </div>
<div className="tools"> <div className="tools">
<Tooltip <Tooltip
@@ -671,6 +689,7 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
onMouseDown={startDrawing} onMouseDown={startDrawing}
onMouseUp={endDrawing} onMouseUp={endDrawing}
onMouseEnter={handleMouseEnter} onMouseEnter={handleMouseEnter}
onWheel={handleOnWheel}
onMouseMove={(e) => { onMouseMove={(e) => {
handleMouseMove(e); handleMouseMove(e);
draw(e); draw(e);
@@ -690,7 +709,7 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
backgroundColor: COLOR, backgroundColor: COLOR,
borderRadius: '50%', borderRadius: '50%',
pointerEvents: 'none', pointerEvents: 'none',
zIndex: 3 zIndex: 100
}} }}
/> />
</div> </div>
+2 -1
View File
@@ -136,5 +136,6 @@ export default {
'playground.image.brushSize': 'Brush Size', 'playground.image.brushSize': 'Brush Size',
'playground.image.download': 'Download Image', 'playground.image.download': 'Download Image',
'playground.image.generate': 'Generate', 'playground.image.generate': 'Generate',
'playground.image.edit': 'Edit' 'playground.image.edit': 'Edit',
'playground.image.fitview': 'Fit View'
}; };
+2 -1
View File
@@ -131,5 +131,6 @@ export default {
'playground.image.brushSize': '画笔大小', 'playground.image.brushSize': '画笔大小',
'playground.image.download': '下载图片', 'playground.image.download': '下载图片',
'playground.image.generate': '生成图片', 'playground.image.generate': '生成图片',
'playground.image.edit': '编辑图片' 'playground.image.edit': '编辑图片',
'playground.image.fitview': '适应视图'
}; };
+21 -17
View File
@@ -215,10 +215,12 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
}, [parameters.n]); }, [parameters.n]);
const imageFile = useMemo(() => { const imageFile = useMemo(() => {
if (!image) return null;
return base64ToFile(image, 'image'); return base64ToFile(image, 'image');
}, [image]); }, [image]);
const maskFile = useMemo(() => { const maskFile = useMemo(() => {
if (!mask) return null;
return base64ToFile(mask, 'mask'); return base64ToFile(mask, 'mask');
}, [mask]); }, [mask]);
@@ -664,23 +666,25 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
); );
} }
return ( return (
<UploadImg <>
accept="image/png" <UploadImg
drag={true} accept="image/png"
multiple={false} drag={true}
handleUpdateImgList={handleUpdateImageList} multiple={false}
> handleUpdateImgList={handleUpdateImageList}
<div
className="flex-column flex-center gap-10 justify-center"
style={{ width: 150, height: 150 }}
> >
<IconFont <div
type="icon-upload_image" className="flex-column flex-center gap-10 justify-center"
className="font-size-24" style={{ width: 150, height: 150 }}
></IconFont> >
<h3>{intl.formatMessage({ id: 'playground.image.edit.tips' })}</h3> <IconFont
</div> type="icon-upload_image"
</UploadImg> className="font-size-24"
></IconFont>
<h3>{intl.formatMessage({ id: 'playground.image.edit.tips' })}</h3>
</div>
</UploadImg>
</>
); );
}, [image, loading, imageStatus, handleOnSave, handleUpdateImageList]); }, [image, loading, imageStatus, handleOnSave, handleUpdateImageList]);
@@ -911,7 +915,7 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
</span> </span>
} }
loading={loading} loading={loading}
disabled={!parameters.model || mask === ''} disabled={!parameters.model}
isEmpty={!imageList.length} isEmpty={!imageList.length}
handleSubmit={handleSendMessage} handleSubmit={handleSendMessage}
handleAbortFetch={handleStopConversation} handleAbortFetch={handleStopConversation}