什么是Fabric.js?
Fabric.js 是一个强大且简单的Javascript HTML5 Canvas库。
为什么要使用Fabric.js?
Canvas提供一个好的画布能力, 但是Api不够友好。绘制简单图形其实还可以, 不过做一些复杂的图形绘制, 编写一些复杂的效果,就不是那么方便了。Fabric.js就是为此而开发,它主要用对象的方式去编写代码。
Fabric.js能做的事情
- 在Canvas上创建、填充图形(包括图片、文字、规则图形和复杂路径组成图形)。
- 给图形填充渐变颜色。
- 组合图形(包括组合图形、图形文字、图片等)。
- 设置图形动画集用户交互。
- 生成JSON、SVG数据等。
- 生成Canvas对象自带拖拉拽功能。
项目开发实战
这里基于React框架为基础,介绍Fabric.js开发实战实例。
1、安装Fabric.js
1 2 3
| npm install fabric 或 yarn add fabric
|
2、引入Fabric.js
1
| import {fabric} from 'fabric';
|
3、initCanvas 画布初始化
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
| let canvasObj = new fabric.Canvas('snackCanvas');
canvasObj.setBackgroundColor('#d5d5d5');
canvasObj.setWidth(this.state.canvasWidth);
canvasObj.setHeight(this.state.canvasHeight);
canvasObj.preserveObjectStacking = true;
fabric.Object.prototype.transparentCorners = false;
fabric.Object.prototype.cornerSize = 6;
fabric.Object.prototype.borderColor = 'rgba(83,152,248,1)';
fabric.Object.prototype.cornerColor = 'white';
fabric.Object.prototype.cornerStrokeColor = 'rgba(83,152,248,1)';
fabric.Object.prototype.originX = 'center'; fabric.Object.prototype.originY = 'center';
this.editor = canvasObj;
|
4、画布事件监听
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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| //元素点击选中事件处理 canvasObj.on('selection:created', function(options) { //console.log('selection:created'); //console.log(options); if (options.target) {
} }
//元素选中更新事件处理 canvasObj.on('selection:updated', function(options) { //console.log('selection:updated'); //console.log(options);
if (options.target) {
} }
//元素取消选中事件处理 canvasObj.on('selection:cleared', function(options) { //console.log('selection:cleared');
}
//对象移动完毕事件处理 canvasObj.on('object:moved', function(options) { //console.log('moved'); //console.log(options);
if (options.target) {
} }
//对象旋转完成事件处理 canvasObj.on('object:rotated', function(options) { //console.log('rotated'); //console.log(options);
if (options.target) { } }
//对象缩放完成事件处理 canvasObj.on('object:scaled', function(options) { //console.log('scaled'); //console.log(options);
if (options.target) {
} }
//对文本编辑修改后 canvasObj.on('text:changed', function(options) { //console.log('text:changed'); //console.log(options);
if (options.target) {
} }
|
5、画布拖拽事件处理
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 44 45 46 47 48 49 50 51 52
| document.addEventListener('dragstart', function(e){ if(e.target.className == 'img'){ scope.mouseX = e.offsetX; scope.mouseY = e.offsetY;
let sourceStr = e.target.dataset.source; if(sourceStr){ scope.dragData = JSON.parse(sourceStr); } scope.figureType = e.target.className; } }, false);
document.addEventListener('dragover', function(e){ e.preventDefault();
}, false);
this.dragObj('drop');
dragObj(eventName){ let scope = this; this.editor.on(eventName, function(opt){ if((opt.e.target.className).trim() == 'upper-canvas' ){ scope.dragEvt(eventName, opt); } }); }
dragEvt(eventName, opt){ let scope = this;
if(eventName == 'dragover'){ opt.e.preventDefault();
}else if(eventName == 'drop'){ opt.e.preventDefault(); } }
|
6、画布中的图片加载
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
| const dragImageUrl = this.dragData.imageUrl; fabric.Image.fromURL(dragImageUrl, function(image){ image.set({ id: getUUID(), left: imageLeft, top: imageTop, width: nodeWidth, height: nodeHeight, classname: 'img', source: scope.dragData, selectable, hasContorls }) .scale(scope.state.canvasScale, scope.state.canvasScale) .setCoords();
scope.editor.add(image);
scope.editor.setActiveObject(image);
scope.editor.requestRenderAll(); });
|
6、画布中的字体库加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| loadAndUse(object, fontName, scope) { let myfont = new FontFaceObserver(fontName); myfont.load(null, 5000) .then(function() { if(object){ object.source.fontFamily = fontName; object.set("fontFamily", fontName).setCoords(); scope.editor.requestRenderAll(); } }).catch(function(e) { console.log(e); alert('字体 ' + fontName + ' 加载失败。'); }); }
this.loadAndUse(null, '宋体', this);
|
7、画布内容转换成图片保存到后台
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| saveData(){ ... 省略其他代码 ... let paramData = new FormData();
let dataUrl = this.editor.toDataURL();
let blobImage = this.dataURLtoBlob(dataUrl);
blobImage.contentType = 'application/octet-stream';
paramData.append("file", blobImage);
... 省略其他代码 ... }
dataURLtoBlob(dataurl){ let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while(n--){ u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], {type:'application/octet-stream'}); }
|