历时一周开发了一个简单的canvas实现的画板功能,这是在vue+elementui项目里开发的,支持画笔、橡皮擦、回退、清除、保存图片、切换颜色、设置宽度等。先看最终效果:
在这里插入图片描述
1.我们先创建一个canvas元素

<div class="canvas">
   <canvas ref="canvas" id="canvas"></canvas>
</div>

2.初始化画布

initCanvas() {
	 let that = this;
	 // 获取canvas元素
	 this.canvas = this.$refs.canvas;
	 // 指定了画布上绘制的类型为2d
	 this.ctx = this.canvas.getContext('2d');
	 // 获取画布相对于视窗的位置集合
	 let rect = this.canvas.getBoundingClientRect();
	 let initX = rect.x;
	 let initY = rect.y;
	 this.pageWidth = document.documentElement.clientWidth - rect.x - 50;
	 this.pageHeight = document.documentElement.clientHeight - rect.y - 50;
	 this.canvas.width = this.pageWidth;
	 this.canvas.height = this.pageHeight;
}

3.画笔功能
我们给一个画笔button,点击时isPainting为true,active表示正在使用的按钮index

<el-button :type=buttonType(0)
           icon="iconfont icon-pen" round
            @click="painting">
</el-button>
painting() {
  this.isPainting = true;
  this.active = 0;
},
buttonType(param) {
  if (param === this.active) {
    return "primary"
  }
}

我们需要监听鼠标onmousedownonmousemoveonmouseup事件
onmousedown时,paint属性为true,记录鼠标按下的坐标为lastPoint,moveTo移动到目标坐标,lineTo将两个坐标之间连起来
onmousemove时,设置paint属性为false,表示一次绘画结束

// 鼠标按下事件
 this.canvas.onmousedown = function (e) {
   that.paint = this.isPainting;
   // e是浏览器坐标系上的点,必须减去canvas的原点坐标,才是准确的画布上的坐标
   let x = e.clientX - initX;
   let y = e.clientY - initY;
   that.lastPoint = {x: x, y: y};
 };
  // 鼠标移动事件
 this.canvas.onmousemove = function (e) {
   if (that.paint) {
     let x = e.clientX - initX;
     let y = e.clientY - initY;
     that.newPoint = {x: x, y: y};
     that.drawLine();
     that.lastPoint = that.newPoint;
   }
 };
 // 鼠标松开事件
 this.canvas.onmouseup = function () {
   that.paint = false;
 };

drawLine() {
  this.ctx.lineWidth = 1;
  this.ctx.lineCap = "round";
  this.ctx.lineJoin = "round";
  this.ctx.moveTo(this.lastPoint.x, this.lastPoint.y);
  this.ctx.lineTo(this.newPoint.x, this.newPoint.y);
  this.ctx.stroke();
},

4.橡皮擦功能
加入eraser按钮

<el-button :type="buttonType(1)"
           icon="iconfont icon-eraser"
           @click="eraser">
</el-button>

点击eraser时,isPainting为false,clear属性为true,clearRect清除区域

this.canvas.onmousedown = function (e) {
   that.paint = that.isPainting;
   that.clear = !that.isPainting;
   let x = e.clientX - initX;
   let y = e.clientY - initY;
   that.lastPoint = {x: x, y: y};
};
this.canvas.onmousemove = function (e) {
 if (that.clear) {
   let x = e.clientX - initX;
   let y = e.clientY - initY;
   that.ctx.save();
   that.ctx.clearRect(x, y, 10,10);
   that.ctx.restore();
 }
};

5.撤回
撤回功能的思路是通过getImageData()复制画布上指定矩形的像素数据,将每次操作的像素保存在history数组中。当点击撤回按钮时,通过putImageData()将数组末尾的像素数据放回画布

this.canvas.onmouseup = function () {
  let image = that.ctx.getImageData(0, 0, that.pageWidth, that.pageHeight);
  that.history.push(image);
};
lastStep() {
	this.history.pop();
	this.ctx.putImageData(this.history[this.history.length - 1], 0, 0);
	this.active = 2;
},

6.清空画布
context.clearRect(x,y,width,height);

参数描述
x要清除的矩形左上角的 x 坐标
y要清除的矩形左上角的 y 坐标
width要清除的矩形的宽度,以像素计
height要清除的矩形的高度,以像素计
clearAll() {
  this.paint = this.clear = this.isPainting = false;
  this.ctx.clearRect(0, 0, this.pageWidth, this.pageHeight);
  this.history.length = 1;
  this.active = 3;
}

7.设置线条宽度
我们加入lineWidth属性,初始值为1,加入一个slider组件双向绑定lineWidth,你也可以用其他更改数值的组件

<el-slider v-model="lineWidth"
           :min="1"
           :max="50">
</el-slider>

画线时,只需要设置画布内容的lineWidth即可
this.ctx.lineWidth = this.lineWidth;
使用橡皮擦时,设置清空矩形的宽高为lineWidth
that.ctx.clearRect(x, y, that.lineWidth, that.lineWidth);
8.切换画笔颜色
初始化一个颜色数组colorArr,给定color和style,click颜色时设置画布内容的strokeStyle
colorSize方法设置按钮的大小

<el-button v-for="(item, index) in colorArr"
           :size="colorSize(index)"
           :style="item.style" circle
           @click="checkColor(index)">
</el-button>
checkColor(param) {
 this.ctx.strokeStyle = this.colorArr[param].color;
 this.colorActive = param;
},
colorSize(param) {
 if (param === this.colorActive) {
   return "default"
 } else {
   return "medium"
 }
},

直接修改strokeStyle,虽然画笔的颜色改了,但之前的线条颜色也一起发生了变化,所以我们需要在每次绘制时闭合路径

this.canvas.onmousedown = function (e) {
  that.ctx.beginPath();
};
this.canvas.onmouseup = function () {
  that.ctx.closePath();
};

9.保存画布
toDataURL()方法把画布里的图案转变成base64编码格式的png,然后返回 Data URL数据。
创建一个a标签,设置a的href、download和target

<el-button :type="buttonType(4)"
           icon="iconfont icon-save"
           round @click="save">
</el-button>
save() {
  this.active = 4;
  this.paint = this.clear = this.isPainting = false;
  let imgUrl = this.canvas.toDataURL("image/png");
  let saveImg = document.createElement("a");
  document.body.appendChild(saveImg);
  saveImg.href = imgUrl;
  saveImg.download = "canvas" + (new Date).getTime();
  saveImg.target = "_blank";
  saveImg.click();
},

基本功能已经全部实现啦,后续可以优化样式、添加更多画笔效果,加入拖拽功能、画矩形圆形等功能~

Logo

前往低代码交流专区

更多推荐