canvas实现涂鸦功能

本文主要实现了canvas涂鸦功能,包括涂鸦,前进后退、清除画布以及画笔大小修改。

实现思路

主要是监听鼠标按下、移动、松开事件,将鼠标按下的值赋值给moveTo的x和y值,作为起始位置。在移动事件中,将鼠标距离可视区x和y值赋给lineTo,再将路径闭合即可。

canvas 涂鸦API及属性

  • ctx.beginPath(): 开始一条路径,或重置当前的路径
  • ctx.moveTo(x,y): 把路径移动到画布中的指定点,不创建线条
  • ctx.lineTo(x,y): 添加一个新点,然后创建从该点到画布中最后指定点的线条(该方法并不会创建线条)
  • ctx.stroke(): 实际地绘制出通过 moveTo() 和 lineTo() 方法定义的路径。默认颜色是黑色
  • ctx.fillStyle: 设置或返回用于填充绘画的颜色、渐变或模式
  • ctx.strokeStyle: 设置或返回用于笔触的颜色、渐变或模式
  • ctx.lineWidth: 设置或返回当前线条的宽度,以像素计
Canvas涂鸦主要用到的方法:moveTo、lineTo

实现过程

涂鸦方法
// 实现撤销和重做的功能
let canvasHistory = [];
let step = -1;

let canX;//画布左上角的x座标
let canY;//画布左上角y座标

/*
* canvas 画布标签
* ctx 元素的context
* undo 撤销元素id
* activeColor 画笔颜色
*/

// 实现涂鸦功能
let drawing = function(canvas, ctx, undo, activeColor){
 let _ = this;
 canvas.onmousedown = function(ev){
   let domObj = this.getBoundingClientRect();
   canX = domObj.left;//获取画布左上角的x座标
   canY = domObj.top;//获取画布左上角的y座标

   // 鼠标在canvas元素区域中的座标之后对座标进行了缩放,从而获得了鼠标相对于 canvas 元素绘图表面的座标
   // 处理放大缩小时,涂鸦画不上去问题(踩了好长时间的坑~~~~~)
   let x = (ev.clientX - canX) * (canvas.width / domObj.width);
   let y = (ev.clientY - canY) * (canvas.height / domObj.height);

   // 画的时候动态修改画布颜色(默认是黑色画笔)
   ctx.fillStyle = activeColor;
   ctx.strokeStyle = activeColor;
   
   ctx.beginPath();
   ctx.moveTo(x,y);
   canvas.onmousemove = function(ev){
     let targetX = (ev.clientX - canX) * (canvas.width / domObj.width);
     let targetY = (ev.clientY - canY) * (canvas.height / domObj.height);

     ctx.lineWidth = _.penSize; // 画笔大小
     ctx.lineTo(targetX,targetY);
     ctx.stroke();
   };
   canvas.onmouseup = function(){
     canvas.onmousemove = null;
     canvas.onmouseup = null;
     canvasDraw();
   };

   // 绘制方法
   function canvasDraw() {
     step++;
     if (step < canvasHistory.length) {
       canvasHistory.length = step; // 截断数组
     }
     // 添加新的绘制到历史记录
     canvasHistory.push(canvas.toDataURL());
     if (step > 0) {
       undo.classList.add('active');
     }
   }
 };
};
撤销方法(上一步)
/*
* canvasHistory 存储橡皮擦历史记录数组
* eraserStep 步长
* undo 撤销元素id
* redo 再做元素id
* ctx  获取这个元素的context——图像稍后将在此被渲染
*/

let canvasUndo = async function(undo, redo, ctx, canvasId, imgSrc){
  let _ = this;
  if (eraserStep > 0) {
    eraserStep--;
    let canvasPic = new Image();
    canvasPic.src = canvasHistory[eraserStep];
    canvasPic.onload = function () {
      ctx.drawImage(canvasPic, 0, 0);
    };
    undo.classList.add('active');
    redo.classList.add('active');
  } else if(eraserStep === 0){
    eraserStep--;
    canvasDrawImg(canvasId, imgSrc); // 回到画布初始状态
    undo.classList.remove('active');
    Message({
      type: 'warning',
      message: '不能再继续撤销了',
      duration: 2000
    });
  }
};
重做方法(下一步)
let canvasRedo = async function(redo, ctx, canvas){
  let _ = this;
  if (step < canvasHistory.length - 1) {
    step++;
    let canvasPic = new Image();
    canvasPic.src = canvasHistory[step];
    canvasPic.onload = function () {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(canvasPic, 0, 0);
    };
  } else {
    redo.classList.remove('active');
    Message({
      type: 'warning',
      message: '已经是最新的记录了',
      duration: 2000
    });
  }
};
撤销方法
let resetEvent = async function(undo, redo, canvasId){
  let _ = this;
  canvasDrawImg(canvasId, _.matUrl);
  canvasHistory = [];
  undo.classList.remove('active');
  redo.classList.remove('active');
};
canvas上绘制画布
// canvas上绘制画布
let canvasDrawImg = function(canvasId, imgSrc){
  let canvas = document.getElementById(canvasId);
  if (canvas) {
    let ctx = canvas.getContext('2d');
    // 画布大小宽高619.5、400px
    canvas.width = 619.5;
    canvas.height = 495;
    ctx.clearRect(0, 0, canvas.width, canvas.height); // 在给定矩形内清空一个矩形
    let img = new Image();
    img.src = imgSrc;
    img.setAttribute("crossOrigin",'Anonymous');
    img.onload = function () {
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
    }
  }
};

踩过的坑

  1. 涂鸦时鼠标与画笔位置出现偏移,位置无法吻合,出现这种问题的原因可能是页面布局导致,比如涂鸦容器设置了position:relative之类的,重点可以从这块入手,尽量要用
  2. 涂鸦部分进行放大缩小之后,出现鼠标与画笔位置偏移,位置无法吻合(排查了好久~~~),原因是涂鸦时鼠标点击和鼠标移动时获取的x、y有问题,需要改成如下形式:
    在这里插入图片描述
    之前写的是下面这种:
    在这里插入图片描述
  3. 如何保证在宽高固定的canvas上绘制之后图片大小依旧保持不变,
    即可以将最终得到的base64重新画在另外一张画布上,该画布的宽高设置为图片的原始宽高即可,该canvas可以设置在页面不可见部分

整体就这么多啦,大家看时有什么问题欢迎随时沟通

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章