Fabricjs——玩转Canvas

5,650 阅读9分钟

转载请保留这部分内容,注明出处。
关注公众号“头号前端”,每周新鲜前端好文推送。

另外,头条号前端团队非常 期待你的加入

简介

fabricjs 在国内并不算特别出名,但是在国外应该是特别的火了。它是一个强大的js库,使用它能够很方便的操作canvas。平时使用canvas的时候,总要学习它的各个api,画简单图形还算熟练,一旦稍微复杂一点就需要不断的查文档,非常不方便。fabric.js提供在上面建立元素、编辑图形和文本、对svg和json格式的序列化和反序列化、对元素进行事件响应等等功能。fabricjs是一个完全开源的项目,由麻省理工学院授权,多年来一直发光发热。

整体结构

使用

npm install fabric
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.6/fabric.min.js"></script>

对象操作

html的body中存在一个如下的canvas元素,获取这个元素fabric对象:var canvas = new fabric.Canvas( 'main' );

<canvas width="800" height="800" id="main"></canvas>

简单图形

在canvas上画矩形:

var rect = new fabric.Rect({
    left: 100,
    top: 100,
    fill: '#f0f',
    width: 30,
    height: 30
})
canvas.add(rect);

如果使用原生canvas api创建矩形,则会是:

var ele = document.getElementById('main');
var ctx = canvasEl.getContext('2d');
ctx.fillStyle = '#f0f';
ctx.fillRect(100, 100, 30, 30);

虽然代码量差不多,但是fabric可以是的图形对象化,在之后还可以继续操作这个对象,如果想要获取rect的属性,只需要 rect.get('') ,或者想要添加其他的属性,可以使用 rect.set ,如果想要从canvas上删除这个rect,只需要 canvas.remove(rect) 。 在canvas上画圆形:

var circle = new fabric.Circle({
    radius: 50,
    fill: "#fcb",
    left: 200,
    top: 100,
})

在canvas上画三角形:

var trigle = new fabric.Triangle({
    fill: "#fcb",
    left: 300,
    top: 100,
    height: 100,
    width: 80,
})

图片

如果页面中存在img元素: <img src="./d2.png" id="img-d2" style="display: none;"> ,那么要将这个元素加载到canvas中:

var imgEle = document.getElementById('img-d2');
var imgInstance = new fabric.Image(imgEle, {
    left: 100,
    top: 100,
    width: 200,
    height: 200,
    angle: 30
})
canvas.add(imgInstance);

这个img元素如果是用于显示在页面中还好,如果只是为了给fabric添加图片素材,加一个这样的img元素会显得有点多余。fabric还提供了根据url创建图片对象的方法:

fabric.Image.fromURL('./d2.png', function(img){
    console.log(img);
    // img.scale(0.5);
    canvas.add(img);
}, {
    left: 100,
    top: 100,
    width: 200,
    height: 200,
    angle: 30 //旋转
})

创建图片之后也可以对图片对象进行操作,平常我们使用图片用于背景或者突出一个显示效果,那么可以这么使用: 背景图

canvas.setBackgroundImage('./d2.png', canvas.renderAll.bind(canvas));

也可以多个图片叠加使用作为背景图,在上面代码的基础上设置:

canvas.setOverlayImage('../assets/d1.png', canvas.renderAll.bind(canvas));

显示效果: 对图片进行添加滤镜:

                    原图 
// 添加过滤器,滤镜效果是一个数组,可以添加多个
img.filters.push(new fabric.Image.filters.Grayscale(), new fabric.Image.filters.Invert());
// 应用过滤器并重新渲染画布执行
img.applyFilters(img.filters);

                 滤镜效果图 

更多的滤镜效果可以通过查文档获得,至于图片对象的缩放、旋转之类的效果可以通过设置它的属性获得,比如设置旋转60度 img.set('angle', 60)

不规则图形

在canvas上画不规则图形:

var path = new fabric.Path('M 0 0 L 200 100 L 170 200 z');
path.set({
    left: 120,
    top: 120,
    fill: "#4040fe"
})

对于在canvas上画不规则路径或者图形,“M”表示“move”命令,在这个例子中意味着从 0,0 始。“L”代表“直线”,让钢笔画一条直线到 200,100 。另一个“L”创建一条到 177,200 的线 “z”指示绘图笔关闭当前路径并最终确定形状。同样它也可使用set来设置它的其他属性,达到更多的效果:path.set({ fill: 'red', stroke: 'green', opacity: 0.5 });

除了上面的M、L之类还可以用C,C表示贝赛尔曲线,它的作用是画出曲线, var path = new fabric.Path('M 0 0 L 200 100 L 170 200 C36.67,0,29.5,3.22,24.31,8.41 Z'); 但是使用自己去设置点,得到想要的不规则是非常困难的。fabric提供了加载svg来代替这种方式进行实现, loadSVGFromString 或者 loadSVGFromURL 方法来加载SVG文件,让Fabric的SVG解析器来遍历所有SVG元素并创建相应的Path对象。

fabric.loadSVGFromURL('./tab.svg', function(objects){
    var SVG = fabric.util.groupSVGElements(objects);
    canvas.add(SVG).centerObject(SVG).renderAll();
    SVG.setCoords();
})

颜色

fabric提供了很方便的方式创建颜色,这些颜色都可以创建的图形中去:

var col1 = new fabric.Color('#f55');
var col2 = new fabric.Color('#123123');
var col3 = new fabric.Color('356735');
var col4 = new fabric.Color('rgb(100,0,100)');
var col5 = new fabric.Color('rgba(10, 20, 30, 0.5)');

rgb和hex色值可以相互进行转换:

col1.toRgb()
col4.toHex()

颜色变换: 覆盖: var colo5 = col2 .overlayWith( col3 ); 如果col2进行了这个操作,不仅会得到一个新的颜色col5,col2也会被覆盖。 转换: col2.toGrayscale() 转换成灰度图颜色,其他变换可以查api得到。 颜色应用:

//简单使用色值
new fabric.Rect({
    left: 100,
    top: 100,
    width: 100,
    height: 100,
    fill: col2.toRgb()
})

// 渐变填充 横向填充效果
rect.setGradient('fill',{
    x1: 0,
    y1: 0,
    x2: circle.width,
    y2: 0,
    colorStops:{
        0: col1.toRgb(),
        1: col3.roRgb()
    }
})

动画

对于图形对象的属性,通过 set 进行设置,如果想要将这个属性设置动画效果,类似css中的transition,可以指定属性进行设置。

 rect.animate('left', "500", { //设置距离左边的偏移量500
     onChange: canvas.renderAll.bind(canvas),
    duration: 2000,
     easing: fabric.util.ease.easeInOutBounce //动画效果
 })

不想计算动画最终的数值,你可以使用 +=``-= 计算符号

rect.animate('left', "+=500", { //设置距离相对当前左边的偏移量加500
     onChange: canvas.renderAll.bind(canvas),
    duration: 2000,
     easing: fabric.util.ease.easeInOutBounce //动画效果
 })

animate函数的第三个参数中常用的属性有:

  • from :允许指定动画属性的起始值(如果我们不希望使用当前值)。

  • duration :动画持续时间

  • onComplete :完成时的回调函数

  • onChange :数值变化时调用的函数

  • easing :动画变化的曲线

文本

fabric不仅以对象的形式操作文本,还提供了比canvas操作文本 更多的功能

支持多行输入、文字对齐方式选择、文字背景设置、文本修饰、文本行高、设置字间距、修改样式、富文本(支持标签)、在 canvas 进行输入编辑。 常用的创建文本的方法有下面两种,它们的参数:第一个参数是文本内容,第二个则是文本的属性对象

var txt = new fabric.Text('aker', {
    left: 140, // 位置
    top: 140,
    shadow: 'rgba(0,0,0,0.3) 15px 15px 15px', // 阴影
    fontFamily: 'Hoefler Text', // 字体
    stroke: '#ff1318', // 画笔,用于字体边框
    strokeWidth: 2, // 画笔粗度
    fill: '#0f0', //填充颜色
    fontSize: 60, // 字体大小
});
var tex = new fabric.IText('click',{left:100,top:400, minWidth: 50}); //继承自Text,实现设置焦点、可编辑

对于文本的操作属性和css中的属性特别类似,还可以对文本进行背景色等等设置。常用的属性除了上面几个,还有: fontWeight、textDecoration、fontStyle、textAlign、textBackgroundColor、lineHeight 等等

组合

前面讲的都是对单个元素的创建以及操作,fabric也提供了将创建的对象进行组合的方法 group ,将几个对象进行组合成为一个新的对象之后进行操作:

var canvas = new fabric.Canvas('main');
var circle = new fabric.Circle({
    radius: 100,
    fill: '#eef',
    scaleY: 0.5,
    originX: 'center',
    originY: 'center'
});
var text = new fabric.Text('hello world', {
    fontSize: 30,
    originX: 'center',
    originY: 'center'
});
var group = new fabric.Group([ circle, text ], {
    left: 150,
    top: 100,
    angle: -10
});
canvas.add(group);

                    效果图 

如果在创建对象的时候,没有对每个元素的位置进行设置,组合之后默认是在group的中心点进行放置对象,如果需要设置对象在组合中的位置,可以在创建对象的时候设置位置,如:

var circle3 = new fabric.Circle({
    radius: 50,
    fill: 'blue',
    left: 200
});

group可以看作是一个小型的canvas,因为它和canvas操作对象很类似。在创建完group之后,可以对添加的对象进行操作:

group.item(0).set('fill', 'red');
group.item(1).set({
  text: 'trololo',
  fill: 'white'
});

常用的操作items方法还有: size() 表示组中所有对象的数量。 contains() 允许检查特定对象是否在组中, add 添加对象, remove 移除对象, addWithUpdate 组的中心添加对象并更新组的属性。

事件

fabric不仅提供了创建了对象的方法,也提供对创建的对象进行事件监听的方法。在fabric官网中也给出了对鼠标移动事件的demo。fabric事件监听类似jq里面对事件的处理。下面是fabric对鼠标点击事件监听demo:

var canvas = new fabric.Canvas('main');
canvas.on('mouse:down', function(options) {
    console.log(options.e.clientX, options.e.clientY);
    if (options.target) { //加入点在图层对象上面
        console.log('an object was clicked! ', options.target.type);
    }
});
var rect = new fabric.Rect({
    left: 100,
    top: 100,
    fill: '#f0f',
    width: 30,
    height: 30
});
canvas.add(rect);

                    输出结果 

fabric提供的事件比较少,不过相对于自己用原生去写肯定方便很多,提供的事件有:

  • 鼠标响应事件:"mouse:down", "mouse:move",和 "mouse:up".

  • 一般性事件:"after:render"

  • 选择事件:"before:selection:cleared", "selection:created", "selection:cleared".

  • 对象事件:"object:modified", "object:selected", "object:moving", "object:scaling", "object:rotating", "object:added", and "object:removed"

官方文档上对于object:moving"和"object:scaling有特别备注,这两个事件特别灵敏,哪怕只是1px的变化也会产生响应,所以如果使用这两个事件的话,需要添加防抖。使用好这些事件应该可以满足许多的场景,比如画布的保存,可以对 object:modified 进行监听,如果发生了就存储,那么可以避免修改丢失。 前三种事件都是在画布上面,对于事件对象可以通过options参数获得,后面的对象事件也可以用于图层对象,加入创建了rect对象,监听选中时间:

rect.on('selected', function() {
    console.log('selected a rectangle');
});。

我的实验结果是这样的,上面的对象on事件的时候并没有反应(也许是使用问题)。

序列化

对于一个应用程序,如果只是提供在上面进行操作的方法并不够,要不然就会成为一个纯展示用的应用,还需要提供一个能够保存数据或者提取数据的方法。fabric提供了很好的序列化和反序列化方法,可以保存数据为json、base64的图片、svg、对象object、字符串toString。

var canvas = new fabric.Canvas('main');
console.log(canvas.toString())

对于所有的画布对象都有toString方法,我们自己也可以创建对象进行toString覆盖。

对于toString只能得到简单的输出结果,并不能直观获取到对象详细信息,我们可以使用toObject或者toJson(这两个类似):

console.log(rect.toObject())
console.log(rect.toJSON())
console.log(rect.toString())

输出结果如下,展开结果可以看到对于没设置的属性会默认给0或者null。

对于反序列化需要注意的是loadFromJSON对应toJSON、loadFormDatalessJSON对应toDatalessJSON。但是由于性能考虑,不是所有属性都可以进行序列化, fabric 只是包装了一些常用的属性,所以如果设置了一些比较少用的属性,在加载JSON数据的时候需要重新修改图像的属性。更多关于序列化可以查看 文档

体验

图片编辑器

资料

fabricjs.com/articles/ www.321332211.com/thread?topi…

注:随便写的一些 demo ,有兴趣的玩一下