关键是要把存放绘制的信息放到一个二维数组中

绘制时最好不要用定时器进行定时绘制,因为会卡顿,
最好使用 requestAnimationFrame 这个原生js的api方法,
因为是以帧的间隔绘制,所以会看起来流畅

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      html,
      body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
      .container {
        width: 100%;
        padding: 20px 0;
        display: flex;
        justify-content: center;
        align-items: center;
      }
      .canvas {
        margin: 0 auto;
        border: 1px solid #878484;
        background-color: #ccc;
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <input type="color" name="" class="js-color-picker" />
    </div>
    <div class="container">
      <canvas class="js-canvas canvas"></canvas>
    </div>
    <script src="./1.js"></script>
  </body>
</html>

js代码部分

/**
 * @author: zhangcunxia
 * @email: <EMAIL>
 * @createTime: 2021-07-20 14:59:59
 * @description: canvas绘制个性签名 里面的单位不需要更新,因为都是 已经乘过了 devicePixelRatio (dpr)px,所以不用考虑
 * @updateTime: 2021-07-20 14:59:59
 * @version: 1.0.0
 * @copyright: Copyright (c) 2021
 * @license: MIT
 *
 */

const colorPicker = document.querySelector(".js-color-picker");
const cvs = document.querySelector(".js-canvas");
const ctx = cvs.getContext("2d");

function init() {
  const w = 500;
  const h = 400;
  // devicePixelRatio 设备像素比,用于解决高清屏绘制模糊问题, 是一个全局属性,可以直接使用
  cvs.width = w * devicePixelRatio;
  cvs.height = h * devicePixelRatio;
  cvs.style.width = w + "px";
  cvs.style.height = h + "px";
}

init();

class Line {
  constructor(startX, startY, endX, endY, color) {
    this.startX = startX;
    this.startY = startY;
    this.endX = endX;
    this.endY = endY;
    this.color = color || "#000";
  }

  draw() {
    ctx.beginPath();
    ctx.moveTo(this.startX, this.startY);
    ctx.lineTo(this.endX, this.endY);
    ctx.strokeStyle = this.color;
    ctx.stroke();
  }
}
// 用于存放所有的线段
let lines = [];
// 用于存放鼠标按下并抬起完整过程的次数
let count = 0;

// function draw() {
//   ctx.clearRect(0, 0, cvs.width, cvs.height);
//   for (let i = 0; i < lines.length; i++) {
//     lines[i].draw();
//   }
// }

// const line = new Line(100, 100, 200, 200, "red");
// line.draw();

cvs.onmousedown = function (event) {
  const _startX = event.clientX - cvs.offsetLeft;
  const _startY = event.clientY - cvs.offsetTop;
  const _inLines = [];
  count++;

  cvs.onmousemove = function (event) {
    const _endX = event.clientX - cvs.offsetLeft;
    const _endY = event.clientY - cvs.offsetTop;
    if (_inLines.length === 0) {
      const _inLine = new Line(
        _startX,
        _startY,
        _endX,
        _endY,
        colorPicker.value
      );
      _inLines.push(_inLine);
    } else {
      const _inLine = new Line(
        _inLines[_inLines.length - 1].endX,
        _inLines[_inLines.length - 1].endY,
        _endX,
        _endY,
        colorPicker.value
      );
      _inLines.push(_inLine);
    }
    lines.push(_inLines);
  };

  cvs.onmouseup = function () {
    cvs.onmousemove = null;
    cvs.onmouseup = null;
  };
};

function drawLines() {
  // 因为画的动作是不断进行的,所以监听并绘制每帧位置
  // requestAnimationFrame 用于动画效果,每秒调用 60 次,每帧绘制一次
  // 这里使用 requestAnimationFrame 绘制,可以保证动画效果流畅
  // 也可以使用 setInterval 绘制,但会导致动画卡顿
  // 也可以使用 setTimeout 绘制,但会导致动画效果不流畅
  // 计算机性能损耗极小,动画效果不会影响到用户体验
  requestAnimationFrame(drawLines);
  ctx.clearRect(0, 0, cvs.width, cvs.height);
  for (let line of lines) {
    for (let item of line) {
      // 因为数组里的是 Line 对象,所以可以直接调用 draw 方法
      item.draw();
    }
  }
}

drawLines();

Tom_Li
26 声望1 粉丝

热爱学习,热爱总结,热爱广博知识