【Konva 实践】实现一个简单的线条画布功能

发布时间 2023-07-25 02:13:52作者: Himmelbleu

完整代码

以下是完整代码,从本实践中了解 Konva 的多事件处理,以及灵活运用 Konva 的 API。打破被文档从上到下的基础知识浅层了解,以实践达到灵活地对 Konva 的使用。

点击在线浏览

<body>
  <div id="container"></div>
</body>
<script>
  const stage = new Konva.Stage({
    container: "container",
    width: window.innerWidth,
    height: window.innerHeight
  });

  const layer = new Konva.Layer();

  let lastLine,
    isPainting = false;

  stage.on("mousedown touchstart", function (e) {
    const pos = stage.getPointerPosition();
    isPainting = true;
    lastLine = new Konva.Line({
      stroke: "#df4b26",
      strokeWidth: 5,
      globalCompositeOperation: "source-over",
      lineCap: "round",
      lineJoin: "round",
      // 添加两次 x 和 y,否则绘制的 Line 不会显示
      points: [pos.x, pos.y, pos.x, pos.y]
    });

    layer.add(lastLine);
  });

  stage.on("mouseup touchup", function () {
    isPainting = false;
  });

  stage.on("mousemove touchmove", function (e) {
    e.evt.preventDefault();

    if (lastLine && isPainting) {
      const pos = stage.getPointerPosition();
      // 在这个 Line 的 points 数组中,拼接数组,并覆盖原本的 points,从而实现线条绘制功能。
      // const newPos = lastLine.points().concat([pos.x, pos.y]);
      // lastLine.points(newPos);
      lastLine.points([...lastLine.points(), ...[pos.x, pos.y]]);
    }
  });

  stage.add(layer);
</script>

业务分析

  1. 鼠标点击之后创建一个 Konva.Line 类。
  2. 鼠标在 Konva.Stage 上移动,更行 Line 的 points。
  3. 由,1 和 2 点得出,每一次鼠标点击之后的 Line 都是一个新的 Line,在鼠标点击之后移动是一个连续的过程,因此,需要记录在点击的回调函数中的 Line,以便于在移动事件回调函数中可以修改本次创建的 Line 的 points。
  4. points 是一个数组,记录了一条线从开始到结束的连续的点信息,因此,在每一次移动过后的区域,将原有的数组与鼠标最新的点数组合并起来。

代码分析

创建 Stage

Stage 是 Konva 一切的开始,它是一个容器,装载所有的图层(Layer),图层中装载许许多多的图形(Shape)。具体阅读博文:初入 H5 Canvas 框架 Konva.js

const stage = new Konva.Stage({
  container: "container",
  width: window.innerWidth,
  height: window.innerHeight
});

const layer = new Konva.Layer();

实际上 stage 相对于一个 div,div 下面是一个个 canvas 标签,即 layer。

定义全局变量

let lastLine,
  isPainting = false;
  1. lastLine:一个绘制线条的整个流程所对应的 Line 对象,鼠标松开之后再次点击 stage 是重新创建一个新的 Line 对象,即一个流程结束。
  2. isPainting:表示一个线条绘制的流程是否结束,以便于下次移动时是否断开本次绘制,从而等待进入下一个绘制流程。

鼠标按下事件

Konva 支持一个 on 函数中添加多个事件,因此,为了适配多端,所以鼠标按下事件代码如下:

stage.on("mousedown touchstart", function (e) {
  const pos = stage.getPointerPosition();
  isPainting = true;
  lastLine = new Konva.Line({
    stroke: "#df4b26",
    strokeWidth: 5,
    globalCompositeOperation: "source-over",
    lineCap: "round",
    lineJoin: "round",
    // 添加两次 x 和 y,否则绘制的 Line 不会显示
    points: [pos.x, pos.y, pos.x, pos.y]
  });

  layer.add(lastLine);
});
  1. stage.getPointerPosition():获取 stage(整个屏幕)的鼠标的坐标(x 和 y)。
  2. points: [pos.x, pos.y, pos.x, pos.y]:点击之后,如果没有移动鼠标,至少留下一个点代表这一个线条绘制结束,但是没有移动,需要添加开始和结尾(即便是同一个位置),以便于 Konva 会渲染这一个 Line。
  3. layer.add(lastLine);:这个 Line 要加入到 layer(图层)中才会被渲染到 H5 Canvas 中进行显示。

鼠标移动事件

stage.on("mousemove touchmove", function (e) {
  e.evt.preventDefault();

  if (lastLine && isPainting) {
    const pos = stage.getPointerPosition();
    // 在这个 Line 的 points 数组中,拼接数组,并覆盖原本的 points,从而实现线条绘制功能。
    // const newPos = lastLine.points().concat([pos.x, pos.y]);
    // lastLine.points(newPos);
    lastLine.points([...lastLine.points(), ...[pos.x, pos.y]]);
  }
});

lastLine.points([...lastLine.points(), ...[pos.x, pos.y]]);:把原本的数组的 point 点信息与鼠标最新移动到的位置的 x 和 y 信息合并起来,并重新设置(覆盖)线条原本的 points 属性,从而实现和完成一条线的绘制流程。