使用fabric.js拖拽、旋转、缩放图片

时间:2024-03-10 14:02:46

什么是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

 

Fabricjs相关方法知识点(实践)