oliver1204 / randomNotes

用来记录平时的日常总结
1 stars 0 forks source link

Fabric.js 简单介绍和使用 #80

Open oliver1204 opened 6 years ago

oliver1204 commented 6 years ago

Fabric.js 简单介绍和使用

背景

前段时间,产品突然拿着美图秀秀自由拼图模块找到我说,要做一个一模一样的功能,来分享图片。顿时我心中有千万头草泥马奔向产品。不过静下来,认真的在网上翻了翻前人经验,突然发现了一个非常好用的第三方插件,完全可以满足这个功能。话不多说,下面我们就来介绍一下,这个插件的基本功能。

前言

canvas提供一个好的画布能力, 但其api超级烂。如果你就想画个简单图形, 其实也可以, 不过做一些复杂的图形绘制, 编写一些复杂的效果,就不是那么好了。fabric就是为此而开发。

常用功能简介

画正方形代码

<canvas id="canvas"></canvas>
var canvas = new fabric.Canvas('canvas');

var rect = new fabric.Rect({
    left: 100,
    top: 100,
    fill: 'red',
    width: 20,
    height: 20,
    angle: 45,       // 旋转一下角度
    opacity: 0.7
});

canvas.add(rect);

如果我们想重新调整位置 怎么办

var canvas = new fabric.Canvas('canvas');
...
canvas.add(rect);
...

rect.set({ left: 20, top: 50 });
canvas.renderAll();       // 重新渲染

objects

  1. fabric.Circle

  2. fabric.Ellipse

  3. fabric.Line

  4. fabric.Polygon

  5. fabric.Polyline

  6. fabric.Rect

  7. fabric.Triangle

画一个三角形 和一个 圆形


var canvas = new fabric.Canvas('canvas');

var circle = new fabric.Circle({
    radius: 20, fill: 'green', left: 100, top: 100
});
var triangle = new fabric.Triangle({
    width: 20, height: 30, fill: 'blue', left: 50, top: 50
});

canvas.add(circle, triangle);

Manipulating objects

可以简单的使用set来控制对象属性

  1. positioning — left, top;
  2. dimension — width, height;
  3. rendering — fill, opacity, stroke(描边的颜色), strokeWidth(描边的宽度);
  4. scaling and rotation — scaleX, scaleY, angle;
  5. and even those related to flipping — flipX, flipY.
rect.set('fill', 'red');
rect.set({ 
    strokeWidth: 5, 
    stroke: 'rgba(100,200,200,0.5)' 
});
rect.set('angle', 15).set('flipY', true);

get

var rect = new fabric.Rect(); // notice no options passed in

rect.getWidth(); // 0
rect.getHeight(); // 0

rect.getLeft(); // 0
rect.getTop(); // 0

rect.getFill(); // rgb(0,0,0)
rect.getStroke(); // null

rect.getOpacity(); // 1

Hierarchy and Inheritance

fabric.Object 是图像基类

你可以自己扩充方法

fabric.Object.prototype.getAngleInRadians = function() {
  return this.getAngle() / 180 * Math.PI;
};

var rect = new fabric.Rect({ angle: 45 });
rect.getAngleInRadians(); // 0.785...

var circle = new fabric.Circle({ angle: 30, radius: 10 });
circle.getAngleInRadians(); // 0.523...

circle instanceof fabric.Circle; // true
circle instanceof fabric.Object; // true

Canvas

fabric.Canvas 是canvas的wrapper

var canvas = new fabric.Canvas('c');
var rect = new fabric.Rect();

canvas.add(rect); // add object

canvas.item(0); // reference fabric.Rect added earlier (first object)
canvas.getObjects(); // get all objects on canvas (rect will be first and only)

canvas.remove(rect); // remove previously-added fabric.Rect

经典的设计 有options 有对象方法


var canvas = new fabric.Canvas('c', {
  backgroundColor: 'rgb(100,100,200)',
  selectionColor: 'blue',
  selectionLineWidth: 2
  // ...
});

// or

var canvas = new fabric.Canvas('c');
canvas.setBackgroundImage(http://...');
canvas.onFpsUpdate = function(){ /* ... */ };

Images

<canvas id="c"></canvas>
<img src="my_image.png" id="my-image">
var canvas = new fabric.Canvas('c');
var imgElement = document.getElementById('my-image');
var imgInstance = new fabric.Image(imgElement, {
  left: 100,
  top: 100,
  angle: 30,
  opacity: 0.85
});
canvas.add(imgInstance);

当然也可以通过url加载一张图片到canvas

fabric.Image.fromURL('my_image.png', function(oImg) {
  canvas.add(oImg);
});

可以对加载的图片进行预处理


fabric.Image.fromURL('my_image.png', function(oImg) {
  // scale image down, and flip it, before adding it onto canvas
  oImg.scale(0.5).setFlipX(true);
  canvas.add(oImg);
});

实现拼图

// 图片
let imageLists = [ 'http://img02.tooopen.com/images/20160509/tooopen_sy_161967094653.jpg',
'http://pic.qiantucdn.com/58pic/17/86/50/76c58PICbVx_1024.jpg!qt324'
'http://pic.sc.chinaz.com/files/pic/pic9/201610/apic23847.jpg'] 地址

fabricCanvas = new fabric.Canvas('canvas');

imageLists.map((item, index) => {
    fabric.Image.fromURL(item.image, function(img) {
        img.scale(fabric.util.getRandomInt(50, 100) / 100).set({
            left: fabric.util.getRandomInt(0, 600),
            top: fabric.util.getRandomInt(0, 500),
            angle: fabric.util.getRandomInt(0, 90),
            strokeWidth: 30,
            stroke: '#fff',
            lockUniScaling: true // 不允许单向拉伸
        });     
        fabricCanvas.add(img);
    });
})

开发中遇见的问题

1. 去边框问题

如上图,单独对某张图片进行调整的时候,会出现上面的框,如不点击当前 canvas 空白处,则边框不会取消。canvas.toDataURL()保存图片的时候,操作边框也同样被保留下来。

解决这个问题,可以在将 canvas保存为图片的操作处(笔者是一个保存button),同事操作下面的代码:

let lenght = imageLists.length;

for(let i = 0; i < lenght; i++) {
    fabricCanvas.item(i).hasControls = false
    fabricCanvas.item(i).hasBorders = false

    fabricCanvas.renderAll();
}

// canvas保存为图片
var canvasDom = document.getElementById("canvas");
imageBase64 = canvasDom.toDataURL("image/png").replace("image/png", "image/octet-stream"); 

2. 图片跨域

看似都完成了,但是却忽略了一个重要的问题。

什么是“被污染”的 canvas?

尽管不通过 CORS 就可以在画布中使用图片,但是这会污染画布。一旦画布被污染,你就无法读取其数据。例如,你不能再使用画布的 toBlob(), toDataURL() 或 getImageData() 方法,调用它们会抛出安全错误。这种机制可以避免未经许可拉取远程网站信息而导致的用户隐私泄露。

此时我们需要做的是,


fabricCanvas = new fabric.Canvas('canvas');

this.state.imageLists.map((item, index) => {
    fabric.Image.fromURL(item.image, function(img) {
        img.scale(item.scale).set({
            ..
        });     
        fabricCanvas.add(img);
    }, {
        crossOrigin: 'Anonymous' // 允许跨域
    });
})

图片处于同源的或者需要图片服务器添加Access-Control-Allow-Origin: *

更多方法请参考