Content Table

Fabric 笔记

Fabric 是基于 HTML5 canvas 的图形库,通过操作对象的属性来操作 canvas (叫 object model),已经提供了一些基本的图元,如 Rect, Circle, Ellipse, Line, Polygon, Image, SVG, Text, Textbox, 阴影等,支持动画、拖动放大缩小旋转等,还支持自由绘制,能够序列化为字符串方便保存,需要的时候再把字符串反序列化为 fabric.Canvas 对象。

Fabric.js is a powerful and simple Javascript HTML5 canvas library. Fabric provides interactive object model on top of canvas element. Fabric also has SVG-to-canvas (and canvas-to-SVG) parser.

下面列举一些可能会常用到的知识点:

  • 全局设置默认值: fabric.Object.prototype.transparentCorners = false (cornerSize: 7)
  • 导出时包含指定 / 自定义属性: canvas.toJSON(['name', 'lockScalingY'],默认很多属性都没有到处,根据自己的需要在这里指定
  • 设置选中 item 的属性: canvas.getActiveObject().set("fontFamily", 'Monaco')
  • 修改整个 canvas 的鼠标: canvas.defaultCursor = 'move'
  • <canvas width="100" height="100"> 的大小被 Canvas 中指定的值覆盖: new fabric.Canvas('c', { width: 600, height: 600 })
  • 控制点 controls 指的是选中图形后在图形边框上出现的 8 个用来控制图形大小的小方框,名字为 tl, tr, br, bl, ml, mt, mr, mb
  • canvas.renderAll() 会立即重绘,canvas.requestRenderAll() 则是把重绘的命令放到事件队列里,如果准备放入时已经存在则丢弃,不重复放入队列中
  • 常用属性: Well, as you would expect there are those related to
    • positioning: left, top
    • dimension: width, height
    • rendering: fill, opacity, stroke, strokeWidth
    • scaling and rotation: scaleX, scaleY, angle
    • flipping: flipX, flipY
    • skewing: skewX, skewY

Fabric Demos 里有很多例子,对学习 Fabric 非常有帮助。

多行字符串

多行字符串 Textbox 的文档里介绍说只能拖动修改宽度,高度不能拖动修改,而是根据文字的多少来进行计算的,可能是有 Bug,目前拖动能够修改高度,可以设置 lockScalingY 为 true 解决这个问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 1px solid #ccc;
}
</style>
</head>
<body>
<canvas id="c" width="400" height="400"></canvas><br>
<button>Change</button>

<script src="lib/fabric.min.js"></script>
<script>
let canvas = new fabric.Canvas('c', { width: 600, height: 600 });

// 多行字符串
let textbox = new fabric.Textbox('HTML canvas 的框架很多, 比如 Three.js 是有名的 3D 框架', {
left: 10,
top: 30,
width: 200,
fontSize: 14,
lineHeight: 1.5,
textAlign: 'left', // you can use specify the text align
splitByGrapheme: true,

lockScalingY: true,
});

canvas.add(textbox);

// 点击改变字体大小
document.querySelector('button').addEventListener('click', () => {
textbox.set('fontSize', 20);
canvas.renderAll();
});
</script>
</body>
</html>

加载图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 1px solid #ccc;
}
</style>
</head>
<body>
<canvas id="c" width="400" height="400"></canvas><br>

<script src="lib/fabric.min.js"></script>
<script>
let canvas = new fabric.Canvas('c');

fabric.Image.fromURL('img/avatar.jpg', img => {
// 避免图片太大
if (img.width > 100) {
img.scale(100 / img.width);
}

canvas.add(img);
});
</script>
</body>
</html>

坐标原点

图元的默认坐标原点 originX and originY 为左上角,可以指定为图元中心 center。

1
2
3
4
5
6
7
8
9
10
let rect = new fabric.Rect({
left: 0,
top: 0,
width: 100,
height: 100,
fill: 'red',
angle: 45,
originX: 'center',
originY: 'center',
});

设置背景图

canvas.setBackgroundImage('img/avatar.jpg', canvas.renderAll.bind(canvas)) 不会拉伸图片铺满整个 canvas,使用下面的代码实现背景图平铺满 canvas。

1
2
3
4
5
6
7
8
9
10
11
12
fabric.Image.fromURL('img/avatar.jpg', function(img) {
// 或者使用下面的代码
// img.set({
// scaleX: canvas.width / img.width,
// scaleY: canvas.height / img.height,
// });

img.scaleToWidth(canvas.width);
img.scaleToHeight(canvas.height);

canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
});

修改 canvas 的大小,需要重新适配背景图:

1
2
3
4
5
6
7
8
9
10
11
document.querySelector('#button-size').addEventListener('click', () => {
canvas.setWidth(300);
canvas.setHeight(300);

// 重新适配背景图
let img = canvas.backgroundImage;
img.scaleToWidth(canvas.width);
img.scaleToHeight(canvas.height);

canvas.requestRenderAll();
});

Canvas 生成图片

可以不需要 <canvas>,直接操作 fabric.Canvas 然后生成图片。

1
<img style="max-width: 100px">
1
2
3
4
5
6
7
8
9
10
11
// element 为 null 创建 Canvas
let canvas = new fabric.StaticCanvas(null, { width: 300, height: 300 });

// [1] 添加图形
canvas.add(rect);
document.querySelector('img').src = canvas.toDataURL('png');

// [2] 反序列化
canvas.loadFromJSON(canvasJson, () => {
document.querySelector('img').src = canvas.toDataURL('png');
});

图片导出的路径

使用 canvas.toJSON() 序列化时,图片的路径为包含了协议、域名、端口的绝对路径,如 http://127.0.0.1/img/avatar.jpg,有的时候需要其他格式的路径,例如转为 / 开头的绝对路径,可以重写 Image 的 toObject 函数实现:

1
2
3
4
5
6
7
8
fabric.Image.prototype.toObject = (function(toObject) {
return function () {
return fabric.util.object.extend(toObject.call(this), {
src: this.getSrc().replace(window.location.origin, '')
// src: (()=>{ return this.getSrc().replace(window.location.origin, '')})() // 复杂逻辑
});
};
})(fabric.Image.prototype.toObject);