9

背景:

​   在实际工作中,接触到整体的一个作业批改的流程。所以对作业的批改有部分了解,
   故做此分享,希望能够抛砖引玉,大家共同探讨

1、什么是Fabric.js

​ 一个功能强大的Javascript库,使使用HTML5 canvas变得轻而易举。
​ Fabric.js为Canvas提供所缺少的对象模型, 交互和一整套其他不可或缺的工具

2、为什么要用它而不用其他的

首先,Canvas提供了一个画布的能力, 但是api不够友好。我们在pc端的批改是用的原生canvas,但应用到小程序经过调研发现并不合适。canvas.绘制简单图形其实还可以, 不过做一些复杂的图形绘制, 编写一些复杂的效果,就不是那么方便了。所以,我们决定使用Fabric.js来开发
它主要就是用对象的方式去编写代码。

3、原生: canvas 和fabric的对比

同样条件下绘制一个矩形

  • a.原生canvas
var canvasEl = document.getElementById('c'); 
var ctx = canvasEl.getContext('2d');
ctx.fillStyle =‘红色'; //在100,100点处创建20x20尺寸的矩形 
ctx.fillRect(100,100,20,20);
  • b.fabric.js
 var canvas = new fabric.Canvas('c');//创建一个矩形对象
    var rect = new fabric.Rect({ 
     left:100,
     top:100 
     fill:“红色”, 
     width:20, 
     Height:20
    });//在画布上“添加”矩形
    canvas.add(rect);

使用自由画笔

  • a.原生canvas
const drawLine = (x1, y1, x2, y2) => 
     { 
     const { ctx } = getCanvas(); 
     ctx.lineWidth = 2; 
     ctx.lineCap = 'round'; 
     ctx.lineJoin = 'round'; 
     ctx.fillStyle = 'red'; 
     ctx.strokeStyle = 'red'; 
     ctx.moveTo(x1, y1); // lineTo(x, y) 绘制一条从当前位置到指定x以及y位置的直线 
     ctx.lineTo(x2, y2); // 通过线条来绘制图形轮廓 
     ctx.stroke(); 
     ctx.closePath(); 
     };

使用fabric 不仅仅是因为他是以对象的形式来绘制图形,更重要的是它集成了十分强大的交互功能。

  • b.使用Fabric.js

     freeDraw() { 
      canvasCtx.isDrawingMode = true; 
      canvasCtx.freeDrawingBrush.color = 'red'; 
      canvasCtx.freeDrawingBrush.width = 2; 
      };
     

4、如何使用

引入:
1、npm安装: npm install fabric --save
2、通过CDN引入:

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

3、项目中引入使用: import { fabric } from 'fabric'
创建实例:

    js:
    canvasCtx = new fabric.Canvas('my-canvas', {
     enableRetinaScaling: true,
     perPixelTargetFind: true, // 对象基于像素检测
     skipTargetFind: true,
     selection: false,
     selectable: false
     });
    ​
    dom: 
     <canvas
     id="my-canvas"
     className="canvas"
     width=375
     height=650
     ></canvas>
     

image
创建完实例后,fabric.js会构建两层 canvas 元素:lower-canvas 和 upper-canvas
lower-canvas: 只负责渲染元素
upper-canvas: 负责所有的事件处理

5、事件绑定

    
    mouseEvent() {
     canvasCtx.on('mouse:down', (options) => {
     TODO:获取点坐标
     });
     canvasCtx.on('mouse:move', (options) => {
     TODO:
     });
     canvasCtx.on('mouse:up', (options) => {
     TODO:
     };
    }
    

最新版本的Fabric已经不需要判断手动还是点击事件,兼容的Events.js能够兼容在移动端的手势操作:
根据下图可以看到,在不同平台下所触发的事件是不同的,所以可以开箱即用。
pc端:
image
移动端:
image

6、绘制图片

      
         creatImg() {
         const imageUrl = new Image();
         imageUrl.setAttribute('crossOrigin', 'Anonymous'); // 图片跨域
         imageUrl.src = object.url;
         imageUrl.onload = () => {
         const imageBg = new fabric.Image(imageUrl, {
         angle: 90, // 旋转角度
         hasBorders: false,  // 去掉边框,可以正常操作
         selectable: false, 
         hasControls: false,  // 只能移动不能(编辑)操作
         crossOrigin: 'Anonymous' // 图片跨域
         });
         canvasCtx.add(imageBg);
         };
         }
        

7、移动图片

        
        handleMoveCnavas(options) {
                const {
                    x, y
                } = movePosition;
                const delta = new fabric.Point(options.x - x, options.y - y);
                canvasCtx.relativePan(delta);
                movePosition = options;
            }
        

### 8、缩放事件
下图为在移动端的示意图:
image

        
        // options --- 坐标点
        // targetTouches 双指触发,长度为2
        handleDoubleFinger(options) {
                const {
                    clientX: finger1X,
                    clientY: finger1Y
                } = options.e.targetTouches[0];
                const {
                    clientX: finger2X,
                    clientY: finger2Y
                } = options.e.targetTouches[1];
                const powX = (finger2X - finger1X) * (finger2X - finger1X);
                const powY = (finger2Y - finger1Y) * (finger2Y - finger1Y);
                // 计算两个手指之间的距离
                const distance = Math.sqrt(powX + powY);
                // 每次缩放的比例
                let ratio = -0.05;
                if (distance > preDistance) {
                    ratio = 0.05;
                }
                preDistance = distance;
                const x = scaleCenter.x || (Math.abs(finger1X + finger2X)) / 2;
                const y = scaleCenter.y || (Math.abs(finger1Y + finger2Y)) / 2;
                scaleCenter = {
                    x, y
                };
                // 计算当前缩放的大小
                let zoom = ratio + canvasCtx.getZoom(); // 获取当前缩放比
                zoom = Math.max(0.5, zoom);
                zoom = Math.min(3, zoom);
                const zoomPoint = new fabric.Point(x, y);
                canvasCtx.zoomToPoint(zoomPoint, zoom);
            }
       

9、旋转保存

旋转前:
image
旋转后:
image
总结:
fabric主要是canvas基础上的一次创新,能够解决很多原生canvas处理处理起来特别麻烦的地方,在整个实例对象下,有一个_objects数组,是用来存储在画板上的所有元素,元素的数量直接决定数组的长度,在启用撤销功能时,本质上是维护这个数组来实现。当然,Fabric的功能远不止这些,更深一层的理解需要日后不断的积累与总结


附录:
常用的一些API
add(object) 添加 ​
insertAt(object,index) 添加 ​
remove(object) 移除 ​
forEachObject 循环遍历 ​
getObjects() 获取所有对象 ​
item(int) 获取子项
isEmpty() 判断是否空画板
​size() 画板元素个数 ​
fabric.util.drawDashedLine 绘制虚线 ​
clear() 清空 ​
renderAll() 重绘 ​
requestRenderAll() 请求重新渲染 ​
rendercanvas() 重绘画板 ​
getCenter().top/left 获取中心坐标 ​
toDatalessJSON() 画板信息序列化成最小的json ​
toJSON() 画板信息序列化成json ​
moveTo(object,index) 移动 ​
setCursor() 设置手势图标 ​
getSelectionContext()获取选中的context ​
getSelectionElement()获取选中的元素 ​
getActiveObject() 获取选中的对象 ​
getActiveObjects() 获取选中的多个对象 ​
discardActiveObject()取消当前选中对象 ​
rotate() 设置旋转角度 ​
setCoords() 设置坐标

Fabric.js中文: 中文链接
Fabric.js原文: 官网文档


bug提交记录仪
47 声望4 粉丝

万丈高楼平地起