使用fabric.js拖拽、旋转、缩放图片
什么是Fabric.js?
Fabric.js 是一个强大的H5 canvas框架,在原生canvas之上提供了交互式对象模型,通过简洁的api就可以在画布上进行丰富的操作。
Fabric.js有什么功能?
使用Fabric.js,你可以在画布上创建和填充对象; 比如简单的几何形状 - 矩形,圆形,椭圆形,多边形,自定义图片或由数百或数千个简单路径组成的更复杂的形状。 另外,还可以使用鼠标缩放,移动和旋转这些对象; 修改它们的属性 - 颜色,透明度,z-index等。也可以将画布上的对象进行组合。
安装
npm 安装
npm install fabric --save
通过cdn引用
<script src="https://cdn.bootcdn.net/ajax/libs/fabric.js/4.2.0/fabric.js"></script>
初始化
首先在 html 页面中写一个800 x 800的 canvas 标签,这里不写宽高也行,后面可以通过js来设置宽高
<canvas id="canvas" width="800" height="800"></canvas>
初始化fabric的 canvas 对象,创建一个卡片(后面都用 canvas 表示画布对象)
const canvas = new fabric.Canvas('canvas'); // ...这里可以写canvas对象的一些配置,后面将会介绍 // 如果<canvas>标签没设置宽高,可以通过js动态设置 canvas.setWidth(350); canvas.setHeight(200);
这样就创建了一个基本的画布。
开始其他操作
向画布添加图层对象
fabric.js提供了很多对象,除了基本的 Rect、Circle、Line、Ellipse、Polygon、Polyline、Triangle 对象外,还有如 Image、Textbox、Group 等更高级的对象,这些都是继承自 Fabric 的 Object对象。
下面我就介绍如何添加图片和文字,其他对象大同小异
/* * 如何向画布添加一个Image对象? */ // 方式一 (通过img元素添加) const imgElement = document.getElementById('img'); const imgInstance = new fabric.Image(imgElement, { left: 100, // 图片相对画布的左侧距离 top: 100, // 图片相对画布的顶部距离 angle: 30, // 图片旋转角度 opacity: 0.85, // 图片透明度 // 这里可以通过scaleX和scaleY来设置图片绘制后的大小,这里为原来大小的一半 scaleX: 0.5, scaleY: 0.5 }); // 添加对象后, 如下图 canvas.add(imgInstance);
// 方式二(通过图片路径添加) fabric.Image.fromURL('img/2.png', (img) => { img.set({ left: 100, // 图片相对画布的左侧距离 top: 100, // 图片相对画布的顶部距离 angle: 30, // 图片旋转角度 opacity: 0.85, // 图片透明度 // 这里可以通过scaleX和scaleY来设置图片绘制后的大小,这里为原来大小的一半 scaleX: 0.5, scaleY: 0.5 }); // 添加对象 canvas.add(img); });
设置图层控件的样式
imgInstance.set({ transparentCorners: false, cornerColor: 'blue', cornerStrokeColor: 'red', borderColor: 'red', cornerSize: 12, padding: 10, cornerStyle: 'circle', borderDashArray: [3, 3] });
如下图:
导出下载图片
<button onclick="downloadFabric(canvas, new Date().getTime())">导出</button>
function download(url,name){ $('<a>').attr({href:url,download:name})[0].click(); } function downloadFabric(canvas,name){ download(canvas.toDataURL(),name+'.png'); }
设置画布背景
fabric.Image.fromURL('img/forest.jpg', (img) => { img.set({ // 通过scale来设置图片大小,这里设置和画布一样大 scaleX: canvas.width / img.width, scaleY: canvas.height / img.height, }); // 设置背景 canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas)); canvas.renderAll(); });
鼠标滚动缩放
var zoom;
canvas.on({ // 鼠标滚动缩放 "mouse:wheel": (e) => { zoom = (event.deltaY > 0 ? -0.1 : 0.1) + canvas.getZoom(); zoom = Math.max(0.1, zoom); //最小为原来的1/10 zoom = Math.min(3, zoom); //最大是原来的3倍 zoomPoint = new fabric.Point(400, 400); // 中心点 canvas.zoomToPoint(zoomPoint, zoom); }, })
鼠标拖动旋转
canvas.on({ // 鼠标旋转 "object:rotating": (e) => { tag.style.display = 'block'; var offsetX = e.e.offsetX; var offsetY = e.e.offsetY; tag.style.left = offsetX + 30 + 'px'; // 离鼠标太近,可能会出现抖动,闪现 tag.style.top = offsetY + 30 + 'px'; }, "object:rotated": (e) => { tag.style.display = 'none'; } })
画布状态记录
框架提供了如 toJSON 和 loadFromJSON 方法,作用分别为导出当前画布的 json 信息,加载json画布信息来还原画布状态。
// 导出当前画布信息 const currState = canvas.toJSON();
// 加载画布信息 canvas.loadFromJSON(lastState, () => { card.renderAll(); });
删除某个图层
<img src="img/close.svg" id="deleteBtn" style="position:absolute;top: 0px;left: 0px;cursor:pointer;width:20px;height:20px;display: none;"/>
// 删除某个图层 var deleteBtn = document.getElementById('deleteBtn'); function addDeleteBtn(x, y){ deleteBtn.style.display ='none'; deleteBtn.style.left = x + 30 + 'px'; deleteBtn.style.top = y - 15 + 'px'; deleteBtn.style.display ='block'; } canvas.on('selection:created', function(e){ addDeleteBtn(e.target.lineCoords.tr.x, e.target.lineCoords.tr.y); }); canvas.on('selection:updated', function(e){ addDeleteBtn(e.target.lineCoords.tr.x, e.target.lineCoords.tr.y); }); canvas.on('mouse:down', function(e){ if(!canvas.getActiveObject()){ deleteBtn.style.display ='none'; } }); canvas.on('object:modified', function(e){ addDeleteBtn(e.target.lineCoords.tr.x, e.target.lineCoords.tr.y); }); canvas.on('object:scaling', function(e){ deleteBtn.style.display ='none'; }); canvas.on('object:moving', function(e){ deleteBtn.style.display ='none'; }); canvas.on('object:rotating', function(e){ deleteBtn.style.display ='none'; }); canvas.on('mouse:wheel', function(e){ deleteBtn.style.display ='none'; }) $(document).on('click',"#deleteBtn", function(){ if(canvas.getActiveObject()){ canvas.remove(canvas.getActiveObject()); deleteBtn.style.display ='none'; } });
效果如下:
全部代码
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style> *{padding: 0;margin: 0;} .backgrounds{ display: flex; } .backgrounds img{ width: 200px; height: 200px; } #CanvasContainer { width: 270px; height: 519px; margin-left: 15px; } #Canvas { overflow: hidden; } .tag{ position: absolute; z-index: 15; padding: 0 5px; min-width: 48px; height: 16px; line-height: 16px; text-align: center; font-size: 12px; color: #505050; border: 1px solid #fff; background: hsla(0,0%,86.3%,.8); border-radius: 10px; -webkit-border-radius: 10px; display: none; } </style> </head> <body> <div id="Backgrounds" class="backgrounds"> <img src="img/shoe1.jpg" alt="" id="img1"/> <img src="img/shoe2.jpg" alt="" id="img2"/> <img src="img/shoe3.png" alt="" id="img3"/> </div> <div class="container" style="position: relative;"> <div id="CanvasContainer" style="width: 800px;height: 800px;border: 1px solid #ccc;"> <canvas id="Canvas" width="800" height="800"></canvas> </div> <div class="tag" id="tag">3</div> <img src="img/close.svg" id="deleteBtn" style="position:absolute;top: 0px;left: 0px;cursor:pointer;width:20px;height:20px;display: none;"/> </div> <button onclick="downloadFabric(canvas, new Date().getTime())">导出</button> <button onclick="unload()">离开</button> <script src="fabric.js"></script> <script src="js/jquery.min.js"></script> <script> var canvas = new fabric.Canvas('Canvas'); $(document).ready(function () { $("#Backgrounds img").click(function () { var getId = $(this).attr("id"); var imgElement = document.getElementById(getId); var imgInstance = new fabric.Image(imgElement, { left: 0, top: 0 }); imgInstance.set({ transparentCorners: false, cornerColor: 'black', cornerStrokeColor: 'black', borderColor: '#686666', cornerSize: 12, padding: 10, cornerStyle: 'circle', borderDashArray: [3, 3], }); canvas.add(imgInstance); }); }); // 设置画布背景 fabric.Image.fromURL('img/forest.jpg', (img) => { img.set({ // 通过scale来设置图片大小,这里设置和画布一样大 scaleX: canvas.width / img.width, scaleY: canvas.height / img.height, }); // 设置背景 canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas)); canvas.renderAll(); }); // 导出下载图片 function download(url,name){ $('<a>').attr({href:url,download:name})[0].click(); } function downloadFabric(canvas,name){ // 导出合并后的图片 download(canvas.toDataURL(),name+'.png'); // 导出单独的图片 // download(canvas._objects[0].toDataURL(),name+'.png'); } var tag = document.getElementById('tag'); var zoom,zoomPoint; canvas.on({ // 鼠标滚动缩放 "mouse:wheel": (e) => { zoom = (event.deltaY > 0 ? -0.1 : 0.1) + canvas.getZoom(); zoom = Math.max(0.1, zoom); //最小为原来的1/10 zoom = Math.min(3, zoom); //最大是原来的3倍 // zoomPoint = new fabric.Point(e.pointer.x, e.pointer.y); zoomPoint = new fabric.Point(400, 400); // 中心点 canvas.zoomToPoint(zoomPoint, zoom); }, // 鼠标旋转 "object:rotating": (e) => { tag.style.display = 'block'; var offsetX = e.e.offsetX; var offsetY = e.e.offsetY; tag.style.left = offsetX + 30 + 'px'; // 离鼠标太近,可能会出现抖动,闪现 tag.style.top = offsetY + 30 + 'px'; }, "object:rotated": (e) => { tag.style.display = 'none'; }, }) // 离开页面,保存当前的画布信息 function unload(){ // 导出当前画布信息 const currState = canvas.toJSON(); sessionStorage.setItem('img', JSON.stringify(currState)); sessionStorage.setItem('zoomObj',JSON.stringify({zm: zoom, zmpoint: zoomPoint})); } // 刷新,恢复之前的画布信息 var sessionImg = sessionStorage.getItem('img'); var lastState = sessionImg? JSON.parse(sessionImg): ''; var zoomObj = sessionStorage.getItem('zoomObj')? JSON.parse(sessionStorage.getItem('zoomObj')): ''; // 加载画布信息 canvas.loadFromJSON(lastState, () => { // 设置缩放点 zoomPoint = zoomObj.zmpoint; zoom = zoomObj.zm; canvas.zoomToPoint({x: zoomPoint.x,y: zoomPoint.y}, zoom); // 给每一个图层设置边框圆角样式 刷新后重绘,需要重新设置之前的一些样式 var objects = canvas._objects; if(objects.length > 0){ objects.map(item => { item.set({ transparentCorners: false, cornerColor: 'black', cornerStrokeColor: 'black', borderColor: '#686666', cornerSize: 12, padding: 10, cornerStyle: 'circle', borderDashArray: [3, 3], }) }) } // 重绘 canvas.renderAll(); }); // 删除某个图层 var deleteBtn = document.getElementById('deleteBtn'); function addDeleteBtn(x, y){ deleteBtn.style.display ='none'; deleteBtn.style.left = x + 30 + 'px'; deleteBtn.style.top = y - 15 + 'px'; deleteBtn.style.display ='block'; } canvas.on('selection:created', function(e){ addDeleteBtn(e.target.lineCoords.tr.x, e.target.lineCoords.tr.y); }); canvas.on('selection:updated', function(e){ addDeleteBtn(e.target.lineCoords.tr.x, e.target.lineCoords.tr.y); }); canvas.on('mouse:down', function(e){ if(!canvas.getActiveObject()){ deleteBtn.style.display ='none'; } }); canvas.on('object:modified', function(e){ addDeleteBtn(e.target.lineCoords.tr.x, e.target.lineCoords.tr.y); }); canvas.on('object:scaling', function(e){ deleteBtn.style.display ='none'; }); canvas.on('object:moving', function(e){ deleteBtn.style.display ='none'; }); canvas.on('object:rotating', function(e){ deleteBtn.style.display ='none'; }); canvas.on('mouse:wheel', function(e){ deleteBtn.style.display ='none'; }) $(document).on('click',"#deleteBtn", function(){ if(canvas.getActiveObject()){ canvas.remove(canvas.getActiveObject()); deleteBtn.style.display ='none'; } }); </script> </body> </html>
fabricjs使用笔记
参考文章:使用Fabric.js玩转canvas