iT邦幫忙

2021 iThome 鐵人賽

DAY 6
1
Modern Web

Canvas 小錦囊系列 第 6

Day 6 - 用 canvas 復刻 小畫家 直線

  • 分享至 

  • xImage
  •  

直線

在上一篇章我們學會提取點擊時的位置,本篇章也會用到相同的 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 and drawImage

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;
      ...
      }
    };

來看看效果吧

太棒了!順利完成!


上一篇
Day 5 - 用 canvas 復刻 小畫家 挑選顏色(顏色選取器)
下一篇
Day 7 - 用 canvas 復刻 小畫家 繪製矩形與圓角矩形
系列文
Canvas 小錦囊30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言