在上一篇章我們學會提取點擊時的位置,本篇章也會用到相同的 function,我們先將他移出。
const getClientOffset = (event: any) => {
let rect = canvasRef.current.getBoundingClientRect();
const point = {
x: event.clientX - rect.left,
y: event.clientY - rect.top,
};
return point;
};
再來操作點擊line時紀錄位置
/**
* 滑鼠點下畫布
*/
const handleMouseDown = (event: any) => {
setIsDrawing(true);
switch (tool) {
...
case "line": // 畫直線
const point = getClientOffset(event);
lastPoint = { x: point?.x, y: point?.y };
break;
default:
break;
}
};
再來在滑鼠移動時顯示直線的顯示位置
const handleDrawCanvas = (point: { x: number; y: number }) => {
const ctx = canvasRef.current.getContext("2d");
switch (tool) {
....
case "line": // 直線
ctx.strokeStyle = activeColor;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(lastPoint?.x, lastPoint?.y); // 下筆位置
ctx.lineTo(point?.x, point?.y);
ctx.stroke();
break;
default:
break;
}
};
天R!這根本不是我們要的效果!觀察看看,的確是有畫上直線,但他在每個軌跡上都畫上了直線,所以我們需要在畫的過程中去清除軌跡。
在剛才畫線時,我們要操作清除多餘的線,讓畫面保持當前繪製的單一線
const handleDrawCanvas = (point: { x: number; y: number }) => {
const ctx = canvasRef.current.getContext("2d");
switch (tool) {
....
case "line": // 直線
....
ctx.beginPath();
ctx.moveTo(lastPoint?.x, lastPoint?.y);
clearCanvas(); // 補上這行!!
ctx.lineTo(point?.x, point?.y);
ctx.stroke();
break;
default:
break;
}
};
/** 清空畫布 **/
const clearCanvas = () => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
};
看看效果
YA!!! 成功了~! 但會發現,因為每次的清空畫布,導致我們畫布上永遠只出現看到的那條線,結果本末倒置 XD...,但距離完成更近了,可以很直覺的想到:只需要儲存目前階段的畫布,再畫完線後,進行還原,是不是就可以解決這個問題呢?
帶上程式碼
const [savedData, setSavedData] = useState<HTMLImageElement>(new Image());
/** 儲存畫布 */
const saveCanvas = () => {
const canvas = canvasRef.current;
const saved = new Image();
saved.src = canvas.toDataURL("image/png");
setSavedData(saved);
};
/** 還原畫布 */
const restore = () => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
ctx.drawImage(savedData, 0, 0);
};
看到 canvas 新的用法
toDataURL 可以將目前的 canvas 畫面轉為圖片,還可以指定圖片素質,甚至支援 webp格式
drawImage 則與toDataURL為相反,將圖片繪製上canvas,並且可指定大小及繪製位置。
有了上方兩個方法就可以有了上方兩個方法就可以在下筆時,進行儲存,在每次移動畫筆時,保持前一張畫布狀態,並清空多餘的軌跡線條。
/**
* 滑鼠點下畫布
*/
const handleMouseDown = (event: any) => {
setIsDrawing(true);
switch (tool) {
case "line":
const point = getClientOffset(event);
initialPoint = { x: point?.x, y: point?.y };
saveCanvas(); // 加上儲存當前的畫布!!
break;
default:
break;
}
};
/** 滑鼠移動 **/
const handleDrawCanvas = (point: { x: number; y: number }) => {
...
case "line": // 直線
clearCanvas(); //清空畫布
restore(); //還原點擊時所儲存的畫布
// draw the current line
ctx.beginPath();
ctx.moveTo(initialPoint?.x, initialPoint?.y);
ctx.lineTo(point?.x, point?.y);
ctx.stroke();
break;
...
}
};
來看看效果吧
太棒了!順利完成!