1 Star 3 Fork 1

yangcuncun / fabricjs

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

fabricjs使用笔记

安装及引用

  • 安装: npm install fabric
  • 引用: import { fabric } from 'fabric'

声明画布

<canvas id="canvas" width="600" height="600"></canvas>
// mounted
canvasId = new fabric.Canvas('canvas')

绘制图形

Line Rect Circle Triangle Path Text Group Image

  • 直线Line
onLine () {
  const line = new fabric.Line([10, 10, 100, 100], {
    fill: 'green', //填充颜色 
    stroke: 'green', //笔触颜色
    strokeWidth: 2, //笔触宽度
  })
  canvasId.add(line)
}
  • 虚线
onLineDash () {
  const line = new fabric.Line([10, 10, 100, 100], {
    fill: 'green',
    stroke: 'green',
    strokeDashArray: [3, 1] // strokeDashArray[a,b] ==> 每隔a个像素空b个像素
  })
  canvasId.add(line)
}
  • 方形Rect
onRect () {
  const rect = new fabric.Rect({
    left: 10,
    top: 10,
    fill: 'red',
    width: 50,
    height: 50
  })
  canvasId.add(rect)
}
  • 圆形Circle
onCircle () {
  const circle = new fabric.Circle({
    radius: 100,
    fill: 'yellow',
    stroke: 'green',
    strokeWidth: 3,
    originX: 'center', // 调整中心点的X轴坐标
    originY: 'center', // 调整中心点的Y轴坐标
    top: 200,
    left: 200
  })
  circle.on('selected', function () {
    console.log('selected a circle')
  })
  canvasId.add(circle)
}
  • 三角形
onTriangle () {
  const triangle = new fabric.Triangle({
    width: 80,
    height: 100,
    fill: 'blue',
    left: 50,
    top: 50
  })
  canvasId.add(triangle)
}
  • 不规则图形Path
onPath () {
  // M => 移动命令  L => 线   z => 让图形闭合路径
  // 'M 0 0': 把画笔移动到(0, 0)点坐标
  // 'L 200 100': 从画笔的坐标画到(200, 100)坐标
  const path = new fabric.Path('M 0 0 L 200 100 L 170 200 z')
  path.set({
    left: 120,
    top: 120,
    fill: 'red'
  })
  canvasId.add(path)
}

文字Text

  • 普通文本

    • onText () {
        const textStr = new fabric.Text('HELLO WORLD!', {
          fill: 'red',
          left: 20,
          top: 20,
          fontFamily: 'Comic Sans',
          fontWeight: 'bold',
          fontSize: 40,
          fontStyle: 'italic',  // 斜体
          underline: 'true',
          shadow: 'rgba(0,0,0,0.2) 5px 5px 5px',
          stroke: '#5566ff',  // 描边
          strokeWidth: 1,
          textAlign: 'right',
          textBackgroundColor: 'yellowgreen'
        })
        canvasId.add(textStr)
      }
  • 可编辑文本

    • fInsertPrice () {
        const _this = this
        // const centerObj = this.canvasId.getCenter()
        const text = new fabric.IText('HELLO WORLD!', {
          fill: _this.fontColor,
          fontSize: _this.fontSize,
          originX: 'center',
          originY: 'center',
          left: 100,
          top: 400
        })
        this.canvasId.add(text).setActiveObject(text)
        text.enterEditing()
      }
  • 组合Group

onGroup () {
  const text = new fabric.Text('HELLO WORLD.', {
    fontSize: 30,
    fill: 'red',
    originX: 'center',
    originY: 'center'
  })
  const circle = new fabric.Circle({
    radius: 100,
    fill: 'yellow',
    stroke: 'green',
    strokeWidth: 3,
    originX: 'center',
    originY: 'center',
    scaleY: 0.5,
  })
  const group = new fabric.Group([circle, text], {
    left: 150,
    top: 150,
    angle: 10
  })
  canvasId.add(group)
}

图片Image

本地图片

onImg () {
  fabric.Image.fromURL(require('@/assets/logo.jpg'), function (oImg) {
    // oImg.scale(0.5)//图片缩小5倍
    oImg.scaleToHeight(300, false);  //缩放图片的高度到400
    oImg.scaleToWidth(300, false);
    canvasId.add(oImg);
    oImg.on('selected', function () {
      console.log('selected an image')
    })
  })
}

网络图片

onImgNet () {
  const image = new Image()
  image.setAttribute('crossOrigin', 'anonymous')	// 解决跨域
  image.src = 'https://fileservice.maimiaotech.com/image/20210114_c1e42b37-f2cf-421e-b48a-5934b2b13109-3'
  const oImg = new fabric.Image(image, {})
  oImg.scaleToHeight(300, false);  //缩放图片的高度到400
  oImg.scaleToWidth(300, false);
  canvasId.add(oImg);
  oImg.on('selected', function () {
    console.log('selected an image')
  })
},
  • 上传图片

    onImgUpload () {
      const _this = this
      var input = document.createElement('input')
      input.setAttribute('type', 'file')
      input.setAttribute('accept', 'image/*')
      input.click()
      input.onchange = function () {
        var file = this.files[0]
        // console.log(file)
        if (!file.type.match('image.*')) {
          console.log('只允许图片格式的文件,如 jpg png gif')
          return false
        }
        var reader = new FileReader();
        reader.onload = (function () {
          return function (e) {
            _this.handleImg(e.target.result)
          }
        })(file)
        reader.readAsDataURL(file);
      }
    },
    handleImg (img) {
      fabric.Image.fromURL(img, function (oImg) {
        // oImg.scale(0.5)//图片缩小5倍
        oImg.scaleToHeight(300, false);  //缩放图片的高度到400
        oImg.scaleToWidth(300, false);
        canvasId.add(oImg);
        oImg.on('selected', function () {
          console.log('selected an image')
        })
      })
    },
  • 背景图

this.canvasId.setBackgroundImage(oImg, this.canvasId.renderAll.bind(this.canvasId))

事件

画布监听事件

  • mouse:down
  • mouse:move
  • mouse:up
mounted () {
  canvasId = new fabric.Canvas('canvas')
  canvasId.on('mouse:up', function (options) {
    console.log(options)
  })
}

Object监听事件

  • after:render:画布重绘后
  • object:selected:对象被选中
  • object:moving:对象移动
  • object:rotating:对象被旋转
  • object:added:对象被加入
  • object:removed:对象被移除

画布方法

删除

  • 按键删除
canvasId.on('mouse:down', options => {
  document.onkeydown = e => {
    if (e.keyCode === 8) {
      canvasId.remove(options.target)
      this.acTarget = null
    }
  }
})
  • 按钮删除
canvasId.on('mouse:down', options => {
  this.acTarget = options.target
})
onDel () {
  canvasId.remove(this.acTarget)
  this.acTarget = null
}

删除组合

层级

  • 上/下/顶/底:

    canvasId.sendBackwards(target)

    或: target.sendBackwards()

    • 下一层: sendBackwards
    • 上一层: bringForward
    • 底层: sendToBack
    • 顶层: bringToFront
// 上移 bringForward
onForward () {
  const obj = canvasId.getActiveObject()	// 获取当前激活的Object
  canvasId.bringForward(obj) // 或 obj.bringForward()
  canvasId.renderAll()
}

选中的图层是否置顶显示

preserveObjectStacking,参数:

  • true: 选中的元素在当前层显示
  • false: 置顶显示
new fabric.Canvas('canvasId', { preserveObjectStacking: true })

置顶被激活元素

onMouseDown () {
  const obj = canvasId.getActiveObject()
  if (obj) {
    obj.bringToFront()
  }
}

生成图片

  • utils.js
    /**
    * 将base64转换为blob
    * @param {*} dataurl 
    */
    function dataURLtoBlob (dataurl) {
     var arr = dataurl.split(',')
     var mime = arr[0].match(/:(.*?);/)[1]
     var bstr = atob(arr[1])
     var n = bstr.length
     var u8arr = new Uint8Array(n)
     while (n--) {
       u8arr[n] = bstr.charCodeAt(n)
     }
     return new Blob([u8arr], { type: mime })
    }
    /**
    * 将blob转换为file
    * @param {*} theBlob 
    * @param {*} fileName 
    */
    function blobToFile (theBlob, fileName) {
     theBlob.lastModifiedDate = new Date()
     theBlob.name = fileName
     return theBlob
    }
    
    export {
     dataURLtoBlob,
     blobToFile
    }

生成并下载

onSave () {
  const url = canvasId.toDataURL()
  const blob = this.$Utils.dataURLtoBlob(url)
  // a标签下载
  const elink = document.createElement('a')
  elink.download = '截图.png'
  elink.style.display = 'none'
  elink.href = URL.createObjectURL(blob)
  document.body.appendChild(elink)
  elink.click()
  document.body.removeChild(elink)
}

生成并上传

onSave () {
  const url = canvasId.toDataURL()
  const blob = this.$Utils.dataURLtoBlob(url)
  // const files = this.$Utils.blobToFile(blob, '截图.png')	// blob文件
  const files = new window.File([blob], 'tmp.png', { type: blob.type })	// file文件
  // 上传
  this.imgUpload(files, function (data) {	// ajax
    const link = IMG_HOST + 'image/' + data.images[0]
    console.log(link)
  })
}

缩放

canvasId.setZoom(0.3)

序列化

onStringfy () {
  const jsonStr = JSON.stringify(canvasId.toJSON())
  this.jsonStr = jsonStr
}	
  • 转化对象时自定义属性丢失处理:

    • canvas 的 toDatalessJSON() 、toDatalessObject()、toJSON()、toObject() 都可以有一个参数

      • propertiesToIncludeopt:要包含的属性选项,类型为 Array;
    • eg: 给对象自定义myType属性

      onRect () {
        const rect = new fabric.Rect({
          left: 10,
          top: 10,
          fill: 'red',
          width: 50,
          height: 50,
          myPrice: true
        })
        canvasId.add(rect)
      }
      onStringfy () {
        const jsonStr = JSON.stringify(canvasId.toJSON(['myPrice']))
        this.jsonStr = jsonStr
      }	

反序列化

onParse () {
  canvasId.loadFromJSON('序列化字符串')
}

若紧跟着需要设置元素,需在回调函数中操作

fLoadJson (jsonStr) {
  this.fClearCanvas()
  this.canvasId.loadFromJSON(jsonStr, () => {
    this.canvasId.forEachObject(item => { 
      if (item.myMainImg) item.set('selectable', false) 
    })
  })
}

其他

  • 获取所有元素: canvasId.getObjects()

  • 遍历 canvasId.forEachObject(item => {})

    • fUpdatePrice (price) {
        this.canvasId.forEachObject(item => {
          if (item.myPrice) {	// 自定义的myPrice属性
            this.canvasId.setActiveObject(item)
            item.set('text', price + '')
            item.set('dirty', true)
            this.canvasId.renderAll()
          }
        })
      }
  • 清空画布: this.canvasId.clear()

元素方法

设置选取框样式

  • borderColor: 边框颜色
  • editingBorderColor: 编辑框颜色
  • borderDashArray: 选取框线性 ==> 虚线
  • padding: 内边距
  • cornerSize: 控点的大小
  • cornerColor: 选取框 ==> 控点背景色 需结合 transparentCorners: false 才能生效
fInsertText (pos) {
  console.log(129)
  const _this = this
  // const centerObj = this.canvasId.getCenter()
  const text = new fabric.IText('', {
    borderColor: '#000000',
    editingBorderColor: '#000000',
    transparentCorners: false,
    cornerColor: '#ffffff',
    borderDashArray: [3, 3],
    cornerSize: 5,
    padding: 8,
    fill: _this.fontColor,
    fontSize: _this.fontSize,
    originX: 'center',
    originY: 'center',
    left: pos.x,
    top: pos.y
  })
  this.canvasId.add(text).setActiveObject(text)
  text.enterEditing()
}

更改元素内容

obj.set({})

/**
 * 设置字体颜色
 * @param {String} color 字体颜色
 */
fFontColor (color) {
  const obj = this.canvasId.getActiveObject()
  if (!obj) return
  if (obj.type === 'text' || obj.type === 'i-text') {
    obj.set({
      fill: color,
      dirty: true	// 
    })
    this.canvasId.renderAll()
  }
}

判断元素类型

acObj.isType(type)

  • eg: 选取多个object时,删除元素需要遍历
fDelTarget: function () {
  const acObj = this.canvasId.getActiveObject()
  if (acObj.isType('activeSelection')) {	// 多个object
    acObj.forEachObject(item => {
      this.canvasId.remove(item)
    })
  } else {	// 单个object
    this.canvasId.remove(acObj)
  }
}

快速设置元素坐标

变换originX/Y

/**
 * 处理快捷方位的坐标
 * @param {Number} index 九宫格方位 1-9
 */
fPosition (index) {
  const centerObj = this.canvasId.getCenter()
  const acObj = this.canvasId.getActiveObject()
  if (!acObj) return
  const xObj = { 1: 'left', 2: 'center', 3: 'right', 4: 'left', 5: 'center', 6: 'right', 7: 'left', 8: 'center', 9: 'right' }
  const yObj = { 1: 'top', 2: 'top', 3: 'top', 4: 'center', 5: 'center', 6: 'center', 7: 'bottom', 8: 'bottom', 9: 'bottom' }
  const posObj = {
    1: { y: 0, x: 0 },
    2: { y: 0, x: centerObj.left },
    3: { y: 0, x: centerObj.left * 2 },
    4: { y: centerObj.top, x: 0 },
    5: { y: centerObj.top, x: centerObj.left },
    6: { y: centerObj.top, x: centerObj.left * 2 },
    7: { y: centerObj.top * 2, x: 0 },
    8: { y: centerObj.top * 2, x: centerObj.left },
    9: { y: centerObj.top * 2, x: centerObj.left * 2 }
  }
  acObj.set({
    originX: xObj[index],
    originY: yObj[index],
    top: posObj[index].y,
    left: posObj[index].x,
    dirty: true
  })
  acObj.setCoords()	// 防止 编程式定位后无法在新位置被选中
  this.canvasId.renderAll()
}

固定originX/Y

方便后端绘图,所以要求固定originX = 'left' originY = 'top'

/**
 * 处理快捷方位的坐标
 * @param {Number} index 九宫格方位 1-9
 */
fPosition (index) {
  const acObj = this.canvasId.getActiveObject()
  if (!acObj) return
  const centerObj = this.canvasId.getCenter()
  // this.GCanvasSize.scaleL是画布缩放比例,若没有缩放就不需要相乘
  // const centerObjL = centerObj.left * this.GCanvasSize.scaleL
  // const centerObjT = centerObj.top * this.GCanvasSize.scaleL
  const centerObjL = centerObj.left
  const centerObjT = centerObj.top
  const acObjW = acObj.width
  const acObjH = acObj.height
  let left = 0; let top = 0
  switch (index) {
    case 1: case 4: case 7: left = 0; break
    case 2: case 5: case 8: left = centerObjL - acObjW * 0.5; break
    case 3: case 6: case 9: left = centerObjL * 2 - acObjW; break
    default: left = 0
  }
  switch (index) {
    case 1: case 2: case 3: top = 0; break
    case 4: case 5: case 6: top = centerObjT - acObjH * 0.5; break
    case 7: case 8: case 9: top = centerObjT * 2 - acObjH; break
    default: top = 0
  }
  acObj.set({
    top,
    left,
    dirty: true
  })
  acObj.setCoords()	// 防止 编程式定位后无法在新位置被选中
  this.canvasId.renderAll()
},

编程式定位后无法在新位置被选中

acObj.setCoords()

const acObj = FEB.canvasId.getActiveObject()
if (!acObj) return
acObj.set({
  top: 100,
  left: 100,
  dirty: true
})
acObj.setCoords()	// 要紧!要紧!
this.canvasId.renderAll()

参考

其他

最新笔记

空文件

简介

fabricjs项目 展开 收起
Vue 等 4 种语言
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
1
https://gitee.com/denghuoan/fabricjs.git
git@gitee.com:denghuoan/fabricjs.git
denghuoan
fabricjs
fabricjs
master

搜索帮助