canvas关于橡皮擦的做法以及优化

786 阅读1分钟

tutieshi_640x447_7s.gif

主要思路:

  1. 记录下最开始鼠标点击时 第一次的点 用 p1 标注
  2. 随后的点都用 p2 标注
  3. 计算 p1 和 p2 之间的距离 将两点之间的距离用圆来填充
  4. 然后将这些圆都画出来 画出的路径是带有颜色的,然后使用 globalCompositeOperation属性 让画出的部分变为透明

说明: 为什么要让画出路径部分变透明,直接让他是白色或者是背景色不好吗?

  1. 首先,如果你的背景色是一个固定的颜色当然可以让橡皮擦画出的部分填充你的背景色,这一样可以。
  2. 但是如果你希望你的背景色是可以自由切换的就需要将橡皮擦擦过的部分设为透明,因为如果你要切换背景色,你橡皮擦涂过的地方是不会跟着你的背景色一起变色的。

ps: 切换背景色不建议将canvas画板整个涂成想要的颜色,因为这样你想换背景色的时候就不好操作了,建议直接将canvas的css属性background改成想要的颜色,保持cavas背景为透明的。关于canvas透明怎么导出带有背景色的canvas图片请看专栏里的另一篇名为《关于导出带有背景色的canvas》的文章

globalCompositeOperation属性 解析

定义: globalCompositeOperation 属性设置或返回如何将一个源(新的)图像绘制到目标(已有)的图像上。 源图像 = 您打算放置到画布上的绘图。 目标图像 = 您已经放置在画布上的绘图。

image.png

image.png

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title></title>
    <style type="text/css">
      #canvas {
        margin: 0 auto;
        border: 1px solid black;
      }
    </style>
  </head>

  <body>
    <div id="red">更换背景色红色</div>
    <div id="green">更换背景色红色</div>
    <canvas id="canvas" width="1000" height="500"></canvas>
  </body>
  <script type="text/javascript">
    var oCanvas = document.getElementById("canvas");
    var width = oCanvas.width;
    var height = oCanvas.height;
    var context = oCanvas.getContext("2d");
    var state = null;
    var points = [];
    var arr = [];
    var cancel = 0;
    var x = 0;
    var y = 0;
    var w = 0;
    var h = 0;
    let eraserFlag = 0;
    let p1 = {};
    context.strokeStyle = "black";
    context.fillStyle = "black";
    context.lineWidth = 10;
    context.beginPath();
    context.rect(100, 100, 100, 100);
    context.fill();

    document.getElementById("red").addEventListener("click", function (e) {
      document.getElementById("canvas").style.background = "red";
    });
    document.getElementById("green").addEventListener("click", function (e) {
      document.getElementById("canvas").style.background = "green";
    });
    document
      .getElementById("canvas")
      .addEventListener("mousedown", function (e) {
        console.log("mousedown");
        state = "down";
        eraserFlag = 0;
      });
    document
      .getElementById("canvas")
      .addEventListener("mousemove", function (e) {
        if (state == "down") {
          if (eraserFlag.value == 0) {
            easer(true, e, 15, "");
            eraserFlag.value = eraserFlag.value + 1;
          } else {
            easer(false, e, 15, "");
          }
        }
      });
    document.getElementById("canvas").addEventListener("mouseup", function (e) {
      state = null;
      arr.push(context.getImageData(0, 0, width, height));
    });
    function easer(flag, e, width, color) {
      if (flag) {
        //第一次触发 只记录p1点
        flag = false;
        p1 = {};
        p1 = {
          x: e.offsetX,
          y: e.offsetY,
        };
        return;
      }
      let p2 = {};
      let k = null;
      let b = null;
      p2 = { x: e.offsetX, y: e.offsetY }; //后面n次触发 记录p2点
      k = (p2.y - p1.y) / (p2.x - p1.x); // p1 p2直线斜率
      b = p1.y - k * p1.x; //y = kx + b 的 b
      var d = Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)); //两点之间的距离
      var num = d * 0.2; //两点之间要画多少个圆才能看起来像条平滑直线,0.2 是平均每像素的距离 画0.2个圆,100像素的距离画20个圆足够
      //注意 这里圆点半径为15 - 25像素适应
      var x = p1.x;
      var y = p1.y; //第一个圆的位置
      var n = (p2.x - p1.x) / num; //每个圆心之间的间距
      context.globalCompositeOperation = "destination-out"; //让橡皮擦画过的地方变得透明
      for (var i = 0; i < num; i++) {
        //依次在这条直线上画 num 个圆
        context.beginPath();
        context.arc(x, y, width / 2, 0, 2 * Math.PI);
        if (color == "") {
          context.fillStyle = "white";
        } else {
          context.fillStyle = color;
        }
        context.fill();
        x += n;
        y = k * x + b;
      }
      p1 = p2; //最后 将p2 赋给 p1
    }
  </script>
</html>