Day 6 - 用 canvas 复刻 小画家 直线

直线

在上一篇章我们学会提取点击时的位置,本篇章也会用到相同的 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;
      ...
      }
    };

来看看效果吧

太棒了!顺利完成!


<<:  07 | WordPress 空白间隔区块 Spacer Block

>>:  Day21 Raid原理

【把玩Azure DevOps】Day22 建立自管的Azure DevOps Agent(Linux Container agent)

前一篇文章建立了Azure DevOps Agent的Windows Container Image...

Re: 新手让网页 act 起来: Day09 - 简单却不是很容易懂的 key(2)

昨天我们介绍了 key 的基本使用方式,今天我们就一起来了解为什麽需要 key 吧! 为什麽需要 k...

Azure - 储存体帐户 (Storage Accounts)

Home -> 资源群组(Resource Groups) -> 储存体帐户(Stor...

类比数位转换模组

身为一个程序撰写员 所见到的都是数位内容 如果要处理类比的内容,就需要将其转换为数位的,才能使用 像...

Day 46 (Node.js)

1.NPM版本 无须更新到最新,怕错误 2.制作专案package.json npm init np...