大家经常会遇到需要前端来做图形标注或者类似画板的一些需求,但是我在github上也没有找到可用的库,于是决定自己实现一个,选型方面,直接基于canvas的api的话,调用太复杂,所以决定使用Fabric.js,Fabric.js是一个可以简化Canvas程序编写的库,下面说一下我的实现。
Fabric.js安装
这里我是基于vue来使用的,先安装上Fabric.js
npm install fabric
在main.js中
import fabric from 'fabric'
Vue.use(fabric);
项目预览
可以点击这里直接在线预览:下面是预览图,大概功能和微信,qq之类的截图类似。
基础功能
基础功能可以通过点击下面的按钮,支持选中后按delete删除或者ctrl+z撤销上一步操作
- 拖拽
- 箭头
- 文字
- 圆 (按住shift画圆,否则是椭圆)
- 矩形 (按住shift画正方形,否则是椭圆)
- 带文字描述的矩形(删掉了)
- 多边形
- 画笔
- 五角星
- 图片上传
- 加载背景图
- 保存并下载
实现
Fabric.js提供了很详细的canvas操作api,这个画面也全部基于Fabric.js来实现,这里我稍微说一下遇到的坑
箭头
箭头的话,是通过绘制路径Path来实现,Fabric.js中的Path包括一系列的命令,这基本上模仿一个笔从一个点到另一个。在“移动”,“线”,“曲线”,或“弧”等命令的帮助下,路径可以形成令人难以置信的复杂形状。同组的路径(路径组的帮助),开放更多的可能性,基本的为: “M” 代表 “move” 命令, 告诉笔到 0, 0 点 “L” 代表 “line” 画一条0, 0 到 200, 100 的线 'Z' 代表闭合路径 这里的箭头就是通过一些简单的计算来描绘路径,实现箭头,mouseFrom表示鼠标起始点,mouseTo表示鼠标终点
let x1 = mouseFrom.x,x2= mouseTo.x,y1 = mouseFrom.y,y2= mouseTo.y;
let w = (x2-x1),h = (y2-y1),sh = Math.cos(Math.PI/4)*16
let sin = h/Math.sqrt(Math.pow(w,2)+Math.pow(h,2))
let cos = w/Math.sqrt(Math.pow(w,2)+Math.pow(h,2))
let w1 =((16*sin)/4),h1 = ((16*cos)/4),centerx=sh*cos,centery=sh*sin
/**
* centerx,centery 表示起始点,终点连线与箭头尖端等边三角形交点相对x,y
* w1 ,h1用于确定四个点
*/
let path = " M " + x1 + " " + (y1);
path += " L " + (x2-centerx+w1) + " " + (y2-centery-h1);
path += " L " + (x2-centerx+w1*2) + " " + (y2-centery-h1*2);
path += " L " + (x2) + " " + y2;
path += " L " + (x2-centerx-w1*2) + " " + (y2-centery+h1*2);
path += " L " + (x2-centerx-w1) + " " + (y2-centery+h1);
path += " Z";
canvasObject = new fabric.Path(
path,
{
stroke: this.color,
fill: this.color,
strokeWidth: this.drawWidth
}
);
this.canvas.add(canvasObject);
手动绘制多边形
这个在Fabric.js官网上本来是有一个demo的,但是不知为何单单这个demo页面404,其余demo都在,没办法,只能自己写一个,代码太多我就补贴上来了,我说一下实现思路(伪代码)
- 点击第一个点时,在canvas上绘制一个小圆点,作为初始点
- 点击第二下的时候,使用new fabric.Line方法绘制一条线
- 点击第三下时,根据这三个点使用new fabric.Polygon方法绘制多边形
- 当点击最开始那个小圆点的时候,重新绘制最终的多边形,否处重复第三步
事件绑定
使用Fabric.js,需要将事件绑定到canvas对象上时,需要使用Fabric.js提供的api
this.canvas.on("mouse:down", this.mousedown);
this.canvas.on("mouse:move", this.mousemove);
this.canvas.on("mouse:up", this.mouseup);