Fabric.js 简单介绍和使用
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
简介Fabric.js是一个可以简化canvas程序编写的库。 Fabric.js为canvas提供所缺少的对象模型, svg parser, 交互和一整套其他不可或缺的工具。基于MIT协议开源,在github上有许多人贡献代码。 Why fabric?canvas提供一个好的画布能力, 但其api超级烂。如果你就想画个简单图形, 其实也可以, 不过做一些复杂的图形绘制, 编写一些复杂的效果,就不是那么好了。 用对象的方式去编写代码 举个例子 // reference canvas element (with id="c") var canvasEl = document.getElementById('c'); // get 2d context to draw on (the "bitmap" mentioned earlier) var ctx = canvasEl.getContext('2d'); // set fill color of context ctx.fillStyle = 'red'; // create rectangle at a 100,100 point, with 20x20 dimensions ctx.fillRect(100, 100, 20, 20); 使用fabric // create a wrapper around native canvas element (with id="c") var canvas = new fabric.Canvas('c');
// create a rectangle object var rect = new fabric.Rect({ left: 100, top: 100, fill: 'red', width: 20, height: 20 });
// "add" rectangle onto canvas canvas.add(rect); 好的 其实并没有什么差别 不过我们试着旋转一下角度 var canvasEl = document.getElementById('c'); var ctx = canvasEl.getContext('2d'); ctx.fillStyle = 'red'; ctx.translate(100, 100); ctx.rotate(Math.PI / 180 * 45); ctx.fillRect(-10, -10, 20, 20); fabric var canvas = new fabric.Canvas('c'); // create a rectangle with angle=45 var rect = new fabric.Rect({ left: 100, top: 100, fill: 'red', width: 20, height: 20, angle: 45 }); canvas.add(rect); 如果我们想重新调整位置 怎么办 var canvasEl = document.getElementById('c'); ... ctx.strokRect(100, 100, 20, 20); ... // erase entire canvas area ctx.clearRect(0, 0, canvasEl.width, canvasEl.height); ctx.fillRect(20, 50, 20, 20); fabric var canvas = new fabric.Canvas('c'); ... canvas.add(rect); ... rect.set({ left: 20, top: 50 }); canvas.renderAll(); objects
画一个三角形 和一个 圆形 // create a wrapper around native canvas element (with id="c") var canvas = new fabric.Canvas('c'); 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来控制对象属性 rect.set('fill', 'red'); rect.set({ strokeWidth: 5, stroke: 'rgba(100,200,200,0.5)' }); rect.set('angle', 15).set('flipY', true); 有了set 其实也就有了get,对象可以创建时设置属性,也可以先实例化再赋值。 var rect = new fabric.Rect({ width: 10, height: 20, fill: '#f55', opacity: 0.7 }); // or functionally identical var rect = new fabric.Rect(); rect.set({ width: 10, height: 20, fill: '#f55', opacity: 0.7 }); 另外这里的fabric.Rect是函数,大家可以使用class继承。 默认值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 Inheritancefabric.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 Canvasfabric.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使用fabric.Image你可以轻松的加载一个图片 <canvas id="c"></canvas> <img src="my_image.png" id="my-image"> js 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); }); Path and PathGroup我们已经看了简单的形状,然后图像。更复杂、丰富的形状和内容呢? var canvas = new fabric.Canvas('c'); var path = new fabric.Path('M 0 0 L 200 100 L 170 200 z'); path.set({ left: 120, top: 120 }); canvas.add(path); “M” 代表 “move” 命令, 告诉笔到 0, 0 点. ... var path = new fabric.Path('M 0 0 L 300 100 L 200 300 z'); ... path.set({ fill: 'red', stroke: 'green', opacity: 0.5 }); canvas.add(path); path也可以设置canvas属性 当然,太困然了,所以你可以使用 fabric.loadSVGfromString or fabric.loadSVGfromURL 方法。 Afterword看些demo吧。 上面我们学习了基础用法,现在我们开始一些好玩的。 Animation我们先回顾设置一下正方形角度方法 rect.set('angle', 45); 这是没有动画的 Fabric object都有animate方法 rect.animate('angle', 45, { onChange: canvas.renderAll.bind(canvas) }); 那么正方形会从0到45有一个动画过度 从左到右进行变动 rect.animate('left', '+=100', { onChange: canvas.renderAll.bind(canvas) }); 逆时针转5度 rect.animate('angle', '-=5', { onChange: canvas.renderAll.bind(canvas) }); 当然animate还支持这些方法
rect.animate('left', '+=100', { onChange: canvas.renderAll.bind(canvas), duration: 3000, easing: fabric.util.ease.easeOutBounce }); Image filters图片可以使用filter效果 fabric.Image.fromURL('pug.jpg', function(img) { // add filter img.filters.push(new fabric.Image.filters.Grayscale()); // apply filters and re-render canvas when done img.applyFilters(canvas.renderAll.bind(canvas)); // add image onto canvas canvas.add(img); }); filter一次可以使用多个效果 当然 你也可以自己定义filter fabric.Image.filters.Redify = fabric.util.createClass({ type: 'Redify', applyTo: function(canvasEl) { var context = canvasEl.getContext('2d'), imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height), data = imageData.data; for (var i = 0, len = data.length; i < len; i += 4) { data[i + 1] = 0; data[i + 2] = 0; } context.putImageData(imageData, 0, 0); } }); fabric.Image.filters.Redify.fromObject = function(object) { return new fabric.Image.filters.Redify(object); }; Colorsfabric 支持 hex rgb rgba颜色 new fabric.Color('#f55'); new fabric.Color('#123123'); new fabric.Color('356735'); new fabric.Color('rgb(100,0,100)'); new fabric.Color('rgba(10, 20, 30, 0.5)'); 并且支持相互转换 new fabric.Color('#f55').toRgb(); // "rgb(255,85,85)" new fabric.Color('rgb(100,100,100)').toHex(); // "646464" new fabric.Color('fff').toHex(); // "FFFFFF" 两种颜色可以叠加 并且你可以使用一些特定效果 var redish = new fabric.Color('#f55'); var greenish = new fabric.Color('#5f5'); redish.overlayWith(greenish).toHex(); // "AAAA55" redish.toGrayscale().toHex(); // "A1A1A1" Gradients可以使用渐变色 var circle = new fabric.Circle({ left: 100, top: 100, radius: 50}); circle.setGradient('fill', { x1: 0, y1: -circle.height / 2, x2: 0, y2: circle.height / 2, colorStops: { 0: '#000', 1: '#fff' } }); 首先确定两个点 在其距离中以百分比定位颜色 circle.setGradient('fill', { x1: -circle.width / 2, y1: 0, x2: circle.width / 2, y2: 0, colorStops: { 0: "red", 0.2: "orange", 0.4: "yellow", 0.6: "green", 0.8: "blue", 1: "purple" } }); Text
如何添加文字 var text = new fabric.Text('hello world', { left: 100, top: 100 }); canvas.add(text); fontFamily var comicSansText = new fabric.Text("I'm in Comic Sans", {fontFamily: 'Comic Sans'}); fontSize var text40 = new fabric.Text("I'm at fontSize 40", {fontSize: 40}); var text20 = new fabric.Text("I'm at fontSize 20", {fontSize: 20}); fontWeight var normalText = new fabric.Text("I'm a normal text", {fontWeight: 'normal'}); var boldText = new fabric.Text("I'm at bold text", {fontWeight: 'bold'}); textDecoration var underlineText = new fabric.Text("I'm an underlined text", {textDecoration: 'underline'}); var strokeThroughText = new fabric.Text("I'm a stroke-through text", {textDecoration: 'line-through'}); var overlineText = new fabric.Text("I'm an overline text", {textDecoration: 'overline'}); shadow var shadowText1 = new fabric.Text("I'm a text with shadow", { shadow: 'rgba(0,0,0,0.3) 5px 5px 5px'}); var shadowText2 = new fabric.Text("And another shadow", { shadow: 'rgba(0,0,0,0.2) 0 0 5px'}); var shadowText3 = new fabric.Text("Lorem ipsum dolor sit", { shadow: 'green -5px -5px 3px'}); fontStyle var italicText = new fabric.Text("A very fancy italic text", { fontStyle: 'italic', fontFamily: 'Delicious'}); var anotherItalicText = new fabric.Text("another italic text", { fontStyle: 'italic', fontFamily: 'Hoefler Text'}); stroke & strokeWidth var textWithStroke = new fabric.Text("Text with a stroke", { stroke: '#ff1318', strokeWidth: 1}); var loremIpsumDolor = new fabric.Text("Lorem ipsum dolor", { fontFamily: 'Impact', stroke: '#c3bfbf', strokeWidth: 3}); textAlign var text = 'this is\na multiline\ntext\naligned right!'; var alignedRightText = new fabric.Text(text, { textAlign: 'right'}); lineHeight var lineHeight3 = new fabric.Text('Lorem ipsum ...', { lineHeight: 3}); var lineHeight1 = new fabric.Text('Lorem ipsum ...', { lineHeight: 1}); textBackgroundColor var text = 'this is\na multiline\ntext\nwith\ncustom lineheight\n&background'; var textWithBackground = new fabric.Text(text, { textBackgroundColor: 'rgb(0,200,0)'}); Events怎么可以没有事件呢 事件以on off使用 canvas 可以捕捉事件 mouseevent renderevent selectionevent objectevent var canvas = new fabric.Canvas('...'); canvas.on('mouse:down', function(options) { console.log(options.e.clientX, options.e.clientY); }); 同样这些事件也可以用任何fabric对象监听 var rect = new fabric.Rect({ width: 100, height: 50, fill: 'green' }); rect.on('selected', function() { console.log('selected a rectangle'); }); var circle = new fabric.Circle({ radius: 75, fill: 'blue' }); circle.on('selected', function() { console.log('selected a circle'); }); 上面我们学习了基础和高级特性,现在介绍更神奇的东西。 Groups话说这个功能我最喜欢,组成群组可以统一修改其中所有组件属性,如何定义: 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.item(0).setFill('red'); group.item(1).set({ text: 'trololo', fill: 'white' }); group中的元素相对于group定位 但是由于要确保之前得到却切位置 所以要异步 fabric.Image.fromURL('/assets/pug.jpg', function(img) { var img1 = img.scale(0.1).set({ left: 100, top: 100 }); fabric.Image.fromURL('/assets/pug.jpg', function(img) { var img2 = img.scale(0.1).set({ left: 175, top: 175 }); fabric.Image.fromURL('/assets/pug.jpg', function(img) { var img3 = img.scale(0.1).set({ left: 250, top: 250 }); canvas.add(new fabric.Group([ img1, img2, img3], { left: 200, top: 200 })) }); }); }); group 可以动态添加 group.add(new fabric.Rect({ ... originX: 'center', originY: 'center' })); 添加并修改group group.addWithupdate(new fabric.Rect({ ... left: group.getLeft(), top: group.getTop(), originX: 'center', originY: 'center' })); 当然 你可以使用canvas上已有的进行克隆 组合 // create a group with copies of existing (2) objects var group = new fabric.Group([ canvas.item(0).clone(), canvas.item(1).clone() ]); // remove all objects and re-render canvas.clear().renderAll(); // add group onto canvas canvas.add(group); Serialization序列化是为了相互传输 toObject, toJSONcanvas 实现了toJSON接口 可以被序列化 var canvas = new fabric.Canvas('c'); JSON.stringify(canvas); // '{"objects":[],"background":"rgba(0, 0, 0, 0)"}' canvas 可以随时被修改 json数据会被修改 canvas.backgroundColor = 'red'; JSON.stringify(canvas); // '{"objects":[],"background":"red"}' 添加新对象 也会改变json数据 canvas.add(new fabric.Rect({ left: 50, top: 50, height: 20, width: 20, fill: 'green'})); console.log(JSON.stringify(canvas)); '{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0}],"background":"rgba(0, 0, 0, 0)"}' 再添加一个 canvas.add(new fabric.Circle({ left: 100, top: 100, radius: 50, fill: 'red'})); console.log(JSON.stringify(canvas)); '{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0},{"type":"circle","left":100,"top":100,"width":100,"height":100,"fill":"red","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"radius":50}],"background":"rgba(0, 0, 0, 0)"}' toObject可以转化成js object对象 { "background" : "rgba(0, 0, 0, 0)", "objects" : [ { "angle" : 0, "fill" : "green", "flipX" : false, "flipY" : false, "hasBorders" : true, "hasControls" : true, "hasRotatingPoint" : false, "height" : 20, "left" : 50, "opacity" : 1, "overlayFill" : null, "perPixelTargetFind" : false, "scaleX" : 1, "scaleY" : 1, "selectable" : true, "stroke" : null, "strokeDashArray" : null, "strokeWidth" : 1, "top" : 50, "transparentCorners" : true, "type" : "rect", "width" : 20 } ]} 每个fabric对象有toObject方法 这和toJSON 也有关 可以自定义 var rect = new fabric.Rect(); rect.toObject = function() { return { name: 'trololo' }; }; canvas.add(rect); console.log(JSON.stringify(canvas)); '{"objects":[{"name":"trololo"}],"background":"rgba(0, 0, 0, 0)"}' 当然我们可以保留原有的数据 新增数据 var rect = new fabric.Rect();rect.toObject = (function(toObject) { return function() { return fabric.util.object.extend(toObject.call(this), { name: this.name }); }; })(rect.toObject); canvas.add(rect);rect.name = 'trololo'; console.log(JSON.stringify(canvas)); '{"objects":[{"type":"rect","left":0,"top":0,"width":0,"height":0,"fill":"rgb(0,0,0)","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0,"name":"trololo"}],"background":"rgba(0, 0, 0, 0)"}' toSvg怎么能不支持转成svg呢 canvas.add(new fabric.Rect({ left: 50, top: 50, height: 20, width: 20, fill: 'green'})); console.log(canvas.toSVG()); '<?xml version="1.0" standalone="no" ?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800" height="700" xml:space="preserve"><desc>created with Fabric.js 0.9.21</desc><rect x="-10" y="-10" rx="0" ry="0" width="20" height="20" style="stroke: none; stroke-width: 1; stroke-dasharray: ; fill: green; opacity: 1;" transform="translate(50 50)" /></svg>' Deserialization, SVG parserfabric.Canvas#loadfromJSON var canvas = new fabric.Canvas(); canvas.loadfromJSON('{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0},{"type":"circle","left":100,"top":100,"width":100,"height":100,"fill":"red","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"radius":50}],"background":"rgba(0, 0, 0, 0)"}'); 通常情况下 svg 会被序列化 但是可以使用 fabric.Canvas#toDatalessJSON canvas.item(0).sourcePath = '/assets/dragon.svg'; console.log(JSON.stringify(canvas.toDatalessJSON())); {"objects":[{"type":"path","left":143,"top":143,"width":175,"height":151,"fill":"#231F20","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":-19,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"path":"/assets/dragon.svg"}],"background":"rgba(0, 0, 0, 0)"} Subclassing构造类 var Point = fabric.util.createClass({ initialize: function(x, y) { this.x = x || 0; this.y = y || 0; }, toString: function() { return this.x + '/' + this.y; } }); 继承类 var ColoredPoint = fabric.util.createClass(Point, { initialize: function(x, y, color) { this.callSuper('initialize', x, y); this.color = color || '#000'; }, toString: function() { return this.callSuper('toString') + ' (color: ' + this.color + ')'; } }); 继承默认类 var LabeledRect = fabric.util.createClass(fabric.Rect, { type: 'labeledRect', initialize: function(options) { options || (options = { }); this.callSuper('initialize', options); this.set('label', options.label || ''); }, toObject: function() { return fabric.util.object.extend(this.callSuper('toObject'), { label: this.get('label') }); }, _render: function(ctx) { this.callSuper('_render', ctx); ctx.font = '20px Helvetica'; ctx.fillStyle = '#333'; ctx.fillText(this.label, -this.width/2, -this.height/2 + 20); } }); 不过其实没必要的。 该文章在 2023/5/23 16:49:36 编辑过 |
关键字查询
相关文章
正在查询... |