有时候我们想实现一个绘画板,如果用原生的canvas来实现,代码量会比较大,而且还要处理很多细节。
这时候我们可以使用fabric.js来实现,它自带绘画板功能,只需要我们打开它的绘画板功能即可。
在初始化fabric对象之后,我们只需要将fabric对象的isDrawingMode属性设置为true即可,当然你也可以在初始化的时候就设置。
// 初始化 fabric 对象
const canvas = new fabric.Canvas("canvas", {
width: 800,
height: 600,
backgroundColor: "#f0f0f0",
});
// 打开绘画板功能
canvas.isDrawingMode = true;
/* 等同于下属代码 */
const canvas = new fabric.Canvas("canvas", {
width: 800,
height: 600,
backgroundColor: "#f0f0f0",
isDrawingMode: true,
});
现在你就可以体验fabric的绘画板功能了,一行代码搞定canvas.isDrawingMode = true;并不是大话;
贪玩的小伙伴现在就开始体验自由绘画吧,好学的小伙伴们就跟着我继续深入绘画的细节吧;
PencilBrush
fabric的绘画板功能是基于PencilBrush来实现的,我们可以通过fabric.PencilBrush来获取PencilBrush对象。
文档地址:fabric.PencilBrush
也就是说我们可以通过fabric.PencilBrush来自定义绘画板的功能,比如修改笔触的颜色、粗细等,废话不多说,我们来看看怎么自定义画笔;
// 省略初始化 canvas 的代码
const pencilBrush = new fabric.PencilBrush(canvas);
pencilBrush.color = "red"; // 修改笔触颜色
pencilBrush.width = 10; // 修改笔触粗细
canvas.freeDrawingBrush = pencilBrush; // 将自定义的笔触设置到 canvas 上
通常来说我们只需要修改笔触的颜色和粗细就可以满足我们的需求了,但是fabric还提供了很多其他的属性,让我们可以实现更加高级的功能。
降低精度
有时候我们希望我们画的线条能尽量的平滑,例如我想画一条直线,但是作为一个手残党,我画的线条总是歪歪扭扭的,这个时候我们可以通过decimate属性来实现。
// 省略初始化代码
pencilBrush.decimate = 100; // 降低精度
decimate属性默认是1,值越大,精度越低,画出来的线条越平滑,所以用这个属性不管是画直线还是画曲线都会更加顺滑。
画直线
有时候我们需要画直线,这个时候我们可以设置straightLineKey属性来实现。
// 省略初始化代码
pencilBrush.straightLineKey = "altKey"; // 按住 alt 键画直线
straightLineKey属性默认是关闭的,可选值有altKey、shiftKey、ctrlKey、none,分别对应alt、shift、ctrl、command键。
这个就不用过多解释了,大家可以自己尝试一下。
fabric并没有提供画圆等其他图形的功能,但是我们可以通过fabric的其他功能来实现,这个就不在本文的范围内了。
阴影
有时候我们需要给画的线条加上阴影,这个时候我们可以通过shadow属性来实现。
// 省略初始化代码
pencilBrush.shadow = new fabric.Shadow({
color: "rgba(0, 0, 0, 0.5)",
blur: 10,
offsetX: 5,
offsetY: 5,
});
shadow属性是一个fabric.Shadow对象,我们可以通过fabric.Shadow来自定义阴影的颜色、模糊度、偏移量等,文档地址:fabric.Shadow
一些方法
在PencilBrush对象上还有一些方法,但是都是内部自调用的方法,我们一般不会用到,我这里只讲三个方法。
有时候我们希望可以控制画笔,也就是用程序来控制绘画行为,这个时候我们可以通过三个方法来实现,pencilBrush.onMouseDown、pencilBrush.onMouseMove、pencilBrush.onMouseUp。
现在我们要同步绘画步骤到另一个画板上,我们可以通过这三个方法来实现。
const createPencilBrush = (canvas) => {
return new fabric.PencilBrush(canvas);
}
const createCanvas = (id) => {
return new fabric.Canvas(id, {
width: 500,
height: 500,
backgroundColor: "#f0f0f0",
selection: false,
isDrawingMode: true,
});
}
const syncDrawing = (canvas, pencilBrush) => {
let isDrawing = false;
canvas.on("mouse:down", (e) => {
isDrawing = true;
const pointer = canvas.getPointer(e.e);
pencilBrush.onMouseDown(pointer, {e: {}, pointer});
});
canvas.on("mouse:move", (e) => {
if (!isDrawing) return;
const pointer = canvas.getPointer(e.e);
pencilBrush.onMouseMove(pointer, {e: {}, pointer});
});
canvas.on("mouse:up", (e) => {
isDrawing = false;
const pointer = canvas.getPointer(e.e);
pencilBrush.onMouseUp({e: {}, pointer});
});
}
const canvas1 = createCanvas("canvas1");
const pencilBrush1 = createPencilBrush(canvas1);
canvas1.freeDrawingBrush = pencilBrush1;
const canvas2 = createCanvas("canvas2");
const pencilBrush2 = createPencilBrush(canvas2);
canvas2.freeDrawingBrush = pencilBrush2;
syncDrawing(canvas1, pencilBrush2);
syncDrawing(canvas2, pencilBrush1);
这里的代码有点多,抽出核心代码就是syncDrawing函数,这个函数的作用是同步两个画板的绘画行为:
/**
* 同步绘画行为
* @param {fabric.Canvas} canvas 当前绘画的画板对象
* @param {fabric.PencilBrush} pencilBrush 当前绘画的笔触对象
*/
const syncDrawing = (canvas, pencilBrush) => {
let isDrawing = false;
canvas.on("mouse:down", (e) => {
isDrawing = true;
const pointer = canvas.getPointer(e.e);
pencilBrush.onMouseDown(pointer, {e: {}, pointer});
});
canvas.on("mouse:move", (e) => {
if (!isDrawing) return;
const pointer = canvas.getPointer(e.e);
pencilBrush.onMouseMove(pointer, {e: {}, pointer});
});
canvas.on("mouse:up", (e) => {
isDrawing = false;
const pointer = canvas.getPointer(e.e);
pencilBrush.onMouseUp({e: {}, pointer});
});
}
其实onMouseDown、onMouseMove、onMouseUp这三个方法在fabric文档中并没有过多的介绍,所以内部的参数并不明确,但是可以通过源码来看这三个方法的参数。
他们的参数其实就是需要一个pointer对象,这个对象包含了x、y坐标,还有一个e对象,这个对象是fabric的事件对象,在这里我们并不需要这个对象,注意不可以传null,否则会报错。
需要注意的是同步绘画需要两个画板的大小都相同,当然不同大小也可以,只是会出现小的画板绘画的内容会被裁剪掉。
总结
fabric的绘画板功能是基于PencilBrush来实现的,我们可以通过fabric.PencilBrush来获取PencilBrush对象,然后通过PencilBrush对象来自定义绘画板的功能。
fabric还有很多其他的画笔,可用于自定义绘画线条的样式,比如CircleBrush、PatternBrush、SprayBrush等,这些都是基于fabric.BaseBrush来实现的,有兴趣的小伙伴可以自己去尝试一下。