V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sugars
V2EX  ›  程序员

使用 Fabric.js 玩转 H5 Canvas

  •  1
     
  •   sugars · 2019-02-05 00:50:41 +08:00 · 2969 次点击
    这是一个创建于 2119 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    之前使用这个框架写过一个卡片 DIY 的项目,中间遇到很多问题都只能通过 google 或 github issues 才能解决,国内资料较少,所以才想写这篇文章来简单的做下总结,希望可以帮到其他人哈。

    附上个人项目地址:vue-card-diy 欢迎 star~ ✨

    什么是 Fabric.js?

    Fabric.js 是一个强大的 H5 canvas 框架,在原生 canvas 之上提供了交互式对象模型,通过简洁的 api 就可以在画布上进行丰富的操作。

    该框架是个开源项目,项目地址: github

    Fabric.js 有什么功能?

    使用 Fabric.js ,你可以在画布上创建和填充对象; 比如简单的几何形状 - 矩形,圆形,椭圆形,多边形,自定义图片或由数百或数千个简单路径组成的更复杂的形状。 另外,还可以使用鼠标缩放,移动和旋转这些对象; 修改它们的属性 - 颜色,透明度,z-index 等。也可以将画布上的对象进行组合。下面我将会介绍我常用的功能以及场景,更多功能可以参考 官方文档

    安装

    npm 安装

    npm install fabric --save
    

    通过 cdn 引用

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

    初始化

    首先在 html 页面中写一个 350 x 200 的 canvas 标签, 这里不写宽高也行,后面可以通过 js 来设置宽高

    <canvas id="canvas" width="350" height="200"></canvas>
    

    初始化 fabric 的canvas 对象,创建一个卡片(后面都用card表示画布对象)

    const card = new fabric.Canvas('canvas') 
    
    // ...这里可以写 canvas 对象的一些配置,后面将会介绍
    
    // 如果<canvas>标签没设置宽高,可以通过 js 动态设置
    card.setWidth(350)
    card.setHeight(200)
    

    就是这么简单,这样就创建了一个基本的画布。

    开始花样操作

    监听画布上的事件

    官方提供了很多事件,以下为常用的事件:

    • object:added 添加图层
    • object:modified 编辑图层
    • object:removed 移除图层
    • selection:created 初次选中图层
    • selection:updated 图层选择变化
    • selection:cleared 清空图层选中
    // 在 canvas 对象初始化后,通过以下方式监听
    // 比如监听画布的图层编辑事件
    card.on('object:modified', (e) => {
        console.log(e.target) // e.target 为当前编辑的 Object
        // ...旋转,缩放,移动等编辑图层的操作都监听到
        // 所以如果有撤销 /恢复的场景,这里可以保存编辑状态
    });
    

    设置画布背景

    // 读取图片地址,设置画布背景
    fabric.Image.fromURL('xx/xx/bg.jpg', (img) => {
      img.set({
       // 通过 scale 来设置图片大小,这里设置和画布一样大
        scaleX: card.width / img.width,
        scaleY: card.height / img.height,
      });
      // 设置背景
      card.setBackgroundImage(img, card.renderAll.bind(card));
      card.renderAll();
    });
    

    如果要设置画布的背景颜色,可以在 canvas 初始化时设置

    const card = new fabric.Canvas('canvas', {
      backgroundColor: 'blue' // 画布背景色为蓝色
    });
    
    // 或者
    card.backgroundColor = 'blue';
    
    // 或者
    card.setBackgroundColor('blue');
    

    向画布添加图层对象

    fabric.js 提供了很多对象,除了基本的 RectCircleLineEllipsePolygonPolylineTriangle对象外,还有如 ImageTextboxGroup等更高级的对象,这些都是继承自 Fabric 的Object 对象

    下面我就介绍如何添加图片和文字,其他对象大同小异

    /**
    * 如何向画布添加一个 Image 对象?
    */
    
    // 方式一(通过 img 元素添加)
    const imgElement = document.getElementById('my-image');
    const imgInstance = new fabric.Image(imgElement, {
      left: 100, // 图片相对画布的左侧距离
      top: 100, // 图片相对画布的顶部距离
      angle: 30, // 图片旋转角度
      opacity: 0.85, // 图片透明度
      // 这里可以通过 scaleX 和 scaleY 来设置图片绘制后的大小,这里为原来大小的一半
      scaleX: 0.5, 
      scaleY: 0.5
    });
    // 添加对象后, 如下图
    card.add(imgInstance);
    

    // 方式二(通过图片路径添加)
    fabric.Image.fromURL('xx/xx/vue-logo.png', (img) => {
      img.set({
        hasControls: false, // 是否开启图层的控件
        borderColor: 'orange', // 图层控件边框的颜色
      });
      // 添加对象后, 如下图
      canvas.add(img);
    });
    

    /**
    * 如何向画布添加一个 Textbox 对象?
    */
    
    const textbox = new fabric.Textbox('这是一段文字', {
        left: 50,
        top: 50,
        width: 150,
        fontSize: 20, // 字体大小
        fontWeight: 800, // 字体粗细
        // fill: 'red', // 字体颜色
        // fontStyle: 'italic',  // 斜体
        // fontFamily: 'Delicious', // 设置字体
        // stroke: 'green', // 描边颜色
        // strokeWidth: 3, // 描边宽度
        hasControls: false,
        borderColor: 'orange',
        editingBorderColor: 'blue' // 点击文字进入编辑状态时的边框颜色
    });
    // 添加文字后,如下图
    card.add(textbox);
    

    获取当前选中的图层对象

    // 方式一
    this.selectedObj = card.getActiveObject(); // 返回当前画布中被选中的图层 
    
    // 方式二
    card.on('selection:created', (e) => {
        // 选中图层事件触发时,动态更新赋值
        this.selectedObj = e.target
    })
    

    旋转图层

    // 顺时针 90°旋转
    const currAngle = this.selectedObj.angle; // 当前图层的角度
    const angle = currAngle === 360 ? 90 :currAngle + 90;
    this.selectedObj.rotate(angle);
    // 如果是通过滑块的方式控制旋转
    // this.selectedObj.rotate(slideValue);
    
    // 所有图层的操作之后,都需要调用这个方法
    card.renderAll()
    

    翻转图层

    // 水平翻转,同理垂直翻转改为 scaleY 属性
    this.selectedObj.set({
        scaleX: -this.selectedObj.scaleX,
    })
    
    card.renderAll()
    

    移除图层

    card.remove(this.selectedObj) // 传入需要移除的 object
    card.renderAll()
    

    控制画布上的图层层级

    向画布添加图层,默认是依次往上叠加,但是当你选中一个图层进入active状态时,该图层会默认置于顶层,如果像禁止选中图层时指定,可以:

    // 在画布初始化后设置
    card.preserveObjectStacking = true // 禁止选中图层时自定置于顶部
    

    设置之后,我选中 vue logo 就是这个样子,不会置顶。

    如何上移和下移图层?

    // 上移图层
    this.selectedObj.bringForward();
    
    // 下移图层
    this.selectedObj.sendBackwards();
    
    // 也可以使用 canvas 对象的 moveTo 方法,移至图层到指定位置
    card.moveTo(object, index);
    

    画布状态记录

    框架提供了如 toJSONloadFromJSON 方法,作用分别为导出当前画布的 json 信息,加载 json 画布信息来还原画布状态。

    // 导出当前画布信息
    const currState = card.toJSON(); // 导出的 Json 如下图
    

    // 加载画布信息
    card.loadFromJSON(lastState, () => {
      card.renderAll();
    });
    

    将画布导出成图片

    const dataURL = card.toDataURL({
      format: 'jpeg', // jpeg 或 png
      quality: 0.8 // 图片质量,仅 jpeg 时可用
      // 截取指定位置和大小
      //left: 100, 
      //top: 100,
      //width: 200,
      //height: 200
    });
    

    Fabric.js 的基本介绍就到这里,这个框架很强大,还有很多功能可以去试试,欢迎大家评论交流哈!

    如转载本文请注明文章作者及出处!

    8 条回复    2019-02-14 11:31:31 +08:00
    GDC
        1
    GDC  
       2019-02-05 01:40:18 +08:00
    楼主主页那在云间穿行的效果挺酷的
    hjdtl
        2
    hjdtl  
       2019-02-05 10:20:43 +08:00
    看起来应该能实现 Photoshop 的一些功能
    sugars
        3
    sugars  
    OP
       2019-02-05 12:34:51 +08:00
    大家感兴趣的话 star 一波我的 github 项目哈哈😆
    sugars
        4
    sugars  
    OP
       2019-02-05 12:35:08 +08:00
    @GDC 谢谢!新年快乐
    Dk2014
        5
    Dk2014  
       2019-02-05 20:15:57 +08:00
    @GDC
    点进去笔记本风扇就开始转了
    sugars
        6
    sugars  
    OP
       2019-02-06 15:44:44 +08:00 via iPhone
    @Dk2014 哈哈 这块不知道怎么优化
    fumeboy
        7
    fumeboy  
       2019-02-07 10:21:20 +08:00 via iPhone
    类似的还有 svg.js
    sugars
        8
    sugars  
    OP
       2019-02-14 11:31:31 +08:00
    @fumeboy svg 没看过,还有一个叫 Kinetic.js 的也差不多
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   979 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 19:56 · PVG 03:56 · LAX 11:56 · JFK 14:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.