fabricjs学习

893 阅读3分钟

介绍:Fabric.js 是一个强大而简单的 Javascript HTML5 画布库 Fabric 在画布元素之上提供交互式对象模型 Fabric 还具有 SVG-to-canvas(和 canvas-to-SVG)解析器

官网:fabricjs.com/

安装配置:

npm install fabric

全局引入main.js:

import { fabric } from 'fabric'
Vue.use(fabric);

局部引入:

import { fabric } from 'fabric' 

初始化:

<canvas id="canvas"></canvas>
this.canvas = new fabric.Canvas('canvas');

修改canvas宽高:

this.canvas.setWidth(dragBox.offsetWidth);
this.canvas.setHeight(dragBox.offsetHeight);

全局配置(选中时的样式):

// 全局配置
fabric.Object.prototype.transparentCorners = false;
fabric.Object.prototype.cornerColor = 'black';
fabric.Object.prototype.cornerStrokeColor = '#409EFF';
fabric.Object.prototype.cornerSize = 7;
fabric.Object.prototype.cornerStyle = 'circle';
fabric.Object.prototype.lockScalingX = true;
fabric.Object.prototype.lockScalingY = true;
fabric.Object.prototype.borderDashArray = [3, 3];
fabric.Object.prototype.padding = 0;

锁定尺寸(不能放到缩小):

scaleX: 1,
scaleY: 1,
minScaleLimit: 1,
maxScaleLimit: 1,

绘制简单图形: Fabric 提供了 7 种基础形状:

  • fabric.Circle (圆)
  • fabric.Ellipse (椭圆)
  • fabric.Line (线)
  • fabric.Polyline (多条线绘制成图形)
  • fabric.triangle (三角形)
  • fabric.Rect (矩形)
  • fabric.Polygon (多边形)

添加矩形:

function Add() {
  var rect = new fabric.Rect({
    left: 100,
    top: 50,
    fill: 'yellow',
    width: 200,
    height: 100,
    objectCaching: false,
    stroke: 'lightgreen',
    strokeWidth: 4,
  });
  that.canvas.add(rect);
  that.canvas.setActiveObject(rect);
}

动画:

rect.animate("left", 100, {
  onChange: canvas.renderAll.bind(canvas),
  duration: 1000,
});
rect.animate("angle", "+=90", {
  onChange: canvas.renderAll.bind(canvas),
  duration: 2000,
});

添加文字:

_this.text = new fabric.Text(
  '我是文字',
  {
    right: 10,
    top: 0,
    fontSize: 12, //字号
    color: 'grey',
    hasControls: false,
  }
);
_this.canvas.add(_this.text);

设置背景:

const imgUrl = `${_this.bg}?${Date.parse(new Date())}`;
fabric.Image.fromURL(imgUrl, (img) => {
  img.set({
    scaleX: _this.canvas.width / img.width,
    scaleY: _this.canvas.height / img.height,
  });

  // 设置背景
  _this.canvas.setBackgroundImage(img, _this.canvas.renderAll.bind(_this.canvas));
  _this.canvas.renderAll();
});

绘制图片:主要有通过 url 和 img 标签绘制两种方式

//通过url绘制图片
fabric.Image.fromURL(
    //本地图片需要通过require来引入,require("./xxx.jpeg")
    "<https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.thaihot.com.cn%2Fuploadimg%2Fico%2F2021%2F0711%2F1625982535739193.jpg&refer=http%3A%2F%2Fimg.thaihot.com.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1630940858&t=e1d24ff0a7eaeea2ff89cedf656a9374>",
    (img) => {
        img.scale(0.5);
        canvas.add(img);
    }
);

//也可以通过标签绘制
let img = document.getElementById("img");
let image = new fabric.Image(img, {
    left100,
    top100,
    opacity0.8,
});
canvas.add(image);

添加图片:

const imgInstance = fabric.Image.fromURL(
        this.imageList[index].url,
        (img) => {
          img.set({
            id: 3,
            amount: this.imageList[index].amount || 1,
            category: _this.parentList[+_this.parentName].id,
            categoryName: _this.parentList[+_this.parentName].name,
            left: _this.originLeft,
            top: _this.originTop,
            scaleX: 0.1,
            scaleY: 0.1,
            minScaleLimit: 0.1,
            maxScaleLimit: 0.1,
            lockScalingX: true,
            lockScalingY: true,
          });
          _this.canvas.add(img).setActiveObject(img);
  }, {crossOrigin: 'anonymous'}
);

图像滤镜: 目前 Fabric 为我们提供了以下内置滤镜- BaseFilter 基本过滤器

  • Blur 模糊
  • Brightness 亮度
  • ColorMatrix 颜色矩阵
  • Contrast 对比
  • Convolute 卷积
  • Gamma 伽玛
  • Grayscale 灰度
  • HueRotation 色调旋转
  • Invert 倒置
  • Noise 噪音
  • Pixelate 像素化
  • RemoveColor 移除颜色
  • Resize 调整大小
  • Saturation 饱和

添加滤镜:

// 添加滤镜
img.filters.push(
  new fabric.Image.filters.Grayscale(),
  new fabric.Image.filters.Sepia(), //色偏
  new fabric.Image.filters.Brightness({ brightness: 0.2 }) //亮度
);

// 图片加载完成之后,应用滤镜效果
img.applyFilters();

添加复制删除按钮到图片上:

const deleteIcon = "data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' '<http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'%3E%3Csvg> version='1.1' id='Ebene_1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink>' x='0px' y='0px' width='595.275px' height='595.275px' viewBox='200 215 230 470' xml:space='preserve'%3E%3Ccircle style='fill:%23F44336;' cx='299.76' cy='439.067' r='218.516'/%3E%3Cg%3E%3Crect x='267.162' y='307.978' transform='matrix(0.7071 -0.7071 0.7071 0.7071 -222.6202 340.6915)' style='fill:white;' width='65.545' height='262.18'/%3E%3Crect x='266.988' y='308.153' transform='matrix(0.7071 0.7071 -0.7071 0.7071 398.3889 -83.3116)' style='fill:white;' width='65.544' height='262.179'/%3E%3C/g%3E%3C/svg%3E";

const cloneIcon = "data:image/svg+xml,%3C%3Fxml version='1.0' encoding='iso-8859-1'%3F%3E%3Csvg version='1.1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink>' viewBox='0 0 55.699 55.699' width='100px' height='100px' xml:space='preserve'%3E%3Cpath style='fill:%23010002;' d='M51.51,18.001c-0.006-0.085-0.022-0.167-0.05-0.248c-0.012-0.034-0.02-0.067-0.035-0.1 c-0.049-0.106-0.109-0.206-0.194-0.291v-0.001l0,0c0,0-0.001-0.001-0.001-0.002L34.161,0.293c-0.086-0.087-0.188-0.148-0.295-0.197 c-0.027-0.013-0.057-0.02-0.086-0.03c-0.086-0.029-0.174-0.048-0.265-0.053C33.494,0.011,33.475,0,33.453,0H22.177 c-3.678,0-6.669,2.992-6.669,6.67v1.674h-4.663c-3.678,0-6.67,2.992-6.67,6.67V49.03c0,3.678,2.992,6.669,6.67,6.669h22.677 c3.677,0,6.669-2.991,6.669-6.669v-1.675h4.664c3.678,0,6.669-2.991,6.669-6.669V18.069C51.524,18.045,51.512,18.025,51.51,18.001z M34.454,3.414l13.655,13.655h-8.985c-2.575,0-4.67-2.095-4.67-4.67V3.414z M38.191,49.029c0,2.574-2.095,4.669-4.669,4.669H10.845 c-2.575,0-4.67-2.095-4.67-4.669V15.014c0-2.575,2.095-4.67,4.67-4.67h5.663h4.614v10.399c0,3.678,2.991,6.669,6.668,6.669h10.4 v18.942L38.191,49.029L38.191,49.029z M36.777,25.412h-8.986c-2.574,0-4.668-2.094-4.668-4.669v-8.985L36.777,25.412z M44.855,45.355h-4.664V26.412c0-0.023-0.012-0.044-0.014-0.067c-0.006-0.085-0.021-0.167-0.049-0.249 c-0.012-0.033-0.021-0.066-0.036-0.1c-0.048-0.105-0.109-0.205-0.194-0.29l0,0l0,0c0-0.001-0.001-0.002-0.001-0.002L22.829,8.637 c-0.087-0.086-0.188-0.147-0.295-0.196c-0.029-0.013-0.058-0.021-0.088-0.031c-0.086-0.03-0.172-0.048-0.263-0.053 c-0.021-0.002-0.04-0.013-0.062-0.013h-4.614V6.67c0-2.575,2.095-4.67,4.669-4.67h10.277v10.4c0,3.678,2.992,6.67,6.67,6.67h10.399 v21.616C49.524,43.26,47.429,45.355,44.855,45.355z'/%3E%3C/svg%3E%0A"

var deleteImg = document.createElement('img');
deleteImg.src = deleteIcon;
var cloneImg = document.createElement('img');
cloneImg.src = cloneIcon;

function renderIcon(icon) {
  return function renderIcon(ctx, left, top, styleOverride, fabricObject) {
    var size = this.cornerSize;
    ctx.save();
    ctx.translate(left, top);
    ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle));
    ctx.drawImage(icon, -size/2, -size/2, size, size);
    ctx.restore();
  }
}

fabric.Object.prototype.controls.deleteControl = new fabric.Control({
  x: 0.5,
  y: -0.5,
  offsetY: -16,
  offsetX: 16,
  cursorStyle: 'pointer',
  mouseUpHandler: deleteObject,
  render: renderIcon(deleteImg),
  cornerSize: 24
});

fabric.Object.prototype.controls.clone = new fabric.Control({
  x: -0.5,
  y: -0.5,
  offsetY: -16,
  offsetX: -16,
  cursorStyle: 'pointer',
  mouseUpHandler: cloneObject,
  render: renderIcon(cloneImg),
  cornerSize: 24
});

function deleteObject(eventData, transform) {
  var target = transform.target;
  var canvas = target.canvas;
  canvas.remove(target);
  canvas.requestRenderAll();
}

function cloneObject(eventData, transform) {
  var target = transform.target;
  var canvas = target.canvas;
  target.clone(function(cloned) {
    cloned.left += 10;
    cloned.top += 10;
    canvas.add(cloned);
  });
}

自定义删除事件:

deleteBtn() {
      const el = this.canvas.getActiveObject(); // 获取active元素
      console.log(el);
      if (el) {
        this.$confirm('确定删除?''提示', {
          confirmButtonText: ‘确定’,
          cancelButtonText: ‘取消’
        })
          .then(() => {
           // 选中多个的删除
            if (el && el._objects && el._objects.length) {
              el._objects.forEach(item => {
                this.canvas.remove(item);
              });
              this.$message.success('删除成功')
              return;
            }

          // 单个的删除
            this.canvas.remove(el);
            this.$message.success('删除成功')
          })
          .catch(() => {

          })
      } else {
        this.$message.error(‘请选中删除的产品')
      }
    },

自定义复制事件:

copyBtn() {
      const _this = this
      const el = _this.canvas.getActiveObject()
      if (!el) {
        _this.$message.error('请选择要复制的元素')      } else {
        el.clone(function (clonedObj) {
          console.log(el);
          const attr = {
            left: clonedObj.left + 10,
            top: clonedObj.top + 10,
            id: el.id,
            evented: true,
            amount: el.amount,
            category: el.category,
            categoryName: el.categoryName,
            transparentCorners: false,
            cornerColor: 'black',
            cornerStrokeColor: '#409EFF',
            borderColor: '#409EFF',
            cornerSize: el.cornerSize,
            padding: 0,
            cornerStyle: 'circle',
            borderDashArray: [3, 3],
            minScaleLimit: 1,
            maxScaleLimit: 1,
            lockRotation: el.lockRotation,
            lockScalingX: true,
            lockScalingY: true,
            angle: el.angle,
            lockUniScaling: el.lockUniScaling,
            hasControls: el.hasControls,
          }
          _this.canvas.discardActiveObject();
          clonedObj.set(attr, {crossOrigin: 'anonymous'});
          if (clonedObj.type === 'activeSelection') {
            // active selection needs a reference to the canvas.
            clonedObj.canvas = _this.canvas;
            clonedObj.forEachObject(function (obj, index) {
              obj.id = el._objects[index].id;
              obj.amount = el._objects[index].amount;
              obj.category = el._objects[index].category
              obj.categoryName = el._objects[index].categoryName
              _this.canvas.add(obj);
            });
            // this should solve the unselectability
            clonedObj.setCoords();
          } else {
            _this.canvas.add(clonedObj).setActiveObject(clonedObj);
          }
          clonedObj.top += 10;
          clonedObj.left += 10;
          _this.canvas.setActiveObject(clonedObj);
          _this.canvas.requestRenderAll();
        });
      }
    },

canvas添加鼠标事件:

this.canvas.on('mouse:down', opt => {
  this.mouseDown = true
  this.getMouseCoords(opt);
})

this.canvas.on('mouse:up', opt => {
  this.mouseDown = false
  this.getMouseLeft(opt);
})

this.canvas.on('mouse:move', opt => {
  var delta = new fabric.Point(opt.e.movementX, opt.e.movementY);
  if (this.panning && this.mouseDown){
    this.canvas.relativePan(delta)
  }
})

自由绘画:

// 自由绘画
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = "blue";
canvas.freeDrawingBrush.width = 5;

生成预览图片:

image.png

preview() {
  let _this = this;
  _this.canvas.discardActiveObject().renderAll(); // 取消所有选中
  const currState = this.canvas.toJSON(['id', 'category', 'categoryName', 'amount']); // 获取所有参数,包括自定义的
  if (!currState.backgroundImage || !currState.backgroundImage.src || !currState.objects || !currState.objects.length) {
    this.$message.error('请制图')
    return;
  }

  // 不带尺子
  const scale = 3
  html2canvas(document.getElementById('dragBox'), {useCORS: true, scale: scale}).then(function (canvas) {
    _this.imgBlob = canvas.toDataURL('image/jpeg', 1.0) // 将图片转为base64
    _this.previewDialogShow = true;
    const data = {
      purpose: 'game',
      file: _this.imgBlob
    };
    _this.uploading = false
  });
}