fabric

lishihuan大约 8 分钟

fabric

构建Canvas画板

类似的还有tui.image-editor 能提供 裁剪,滤镜效果

官网open in new window

fabric案例&笔记

github地址open in new window

demoopen in new window

案例open in new window

https://www.cnblogs.com/vipstone/p/8716419.htmlopen in new window

https://gitee.com/k21vin/fabricjs-demoopen in new window

首页 | fabric.js中文教程 (gitee.io)open in new window

安装配置

npm install fabric --save
import fabric from 'fabric'
Vue.use(fabric);

https://blog.csdn.net/weixin_39415598/article/details/125418559open in new windowhttps://www.cnblogs.com/liuXiaoDi/p/16454072.htmlopen in new window

//计算缩放比例

var zoom = Math.min(canvas.width/(maxX - minX), canvas.height/(maxY - minY));

//计算缩放中心

var zoomPoint = new fabric.Point(canvas.width / 2 , canvas.height / 2);

//开始缩放
canvas.zoomToPoint(zoomPoint, zoom);
//canvas.setZoom(zoom)

Fabric.js 更换图片

https://juejin.cn/post/7052719026874613773open in new window

https://gitee.com/k21vin/front-end-data-visualization/blob/master/src/views/FabricJS/Demo/pages/ChangeImage/ChangeImage.vueopen in new window

需要区分是否缓存,否则设置了可能不能正常显示

如何修改组内的图片url地址?

https://segmentfault.com/a/1190000039106084?utm_source=tag-newestopen in new window

var img = new Image()
var gpoint = this //vue的this和fabric里指向对象的this冲突(见问题2)
img.onload = function() {
gpoint.group._objects[0].setElement(img)
    // 可以使用 addWithUpdate() 但会重置 scale 和 angle(见问题1)
    // 自己写的渲染函数(见问题3)
    gpoint.canvas_real_render() 
}
img.src = src

设置背景图

// 切换背景图
toggerBackHandle (src) {
    // (解决跨域图片加载)
    const image = new Image()
    image.setAttribute('crossOrigin', 'anonymous')
    image.src = src
    image.onload = () => {
        this.initCanvasSize(image) //  根据图片定义好画布的尺寸
        const Shape = new fabric.Image(image, {
            // 通过scale来设置图片大小,这里设置和画布一样大
            scaleX: this.canvas.width / image.width,
            scaleY: this.canvas.height / image.height
        })
        this.canvas.setBackgroundImage(src, this.canvas.renderAll.bind(this.canvas), Shape)// setBackgroundImage 方法设置画布背景
        this.canvas.renderAll() // 重新渲染画布
        this.fabricObjAddEvent()
        this.initPoint()
    }
},
    // 高度是固定的,然后由图片的长宽比,计算出 画布的宽度
    initCanvasSize (image) {
        const height = this.$refs.fabWrap.offsetHeight
        const width = (image.width / image.height) * height
        this.canvas.setWidth(width)
        this.canvas.setHeight(height)
    },

自定义滚轮,鼠标中心点缩放

this.canvas.on({
    // 以鼠标当前位置为中心点,进行缩放
    'mouse:wheel': (e) => {
        console.log('mouse:wheel')
        e.e.preventDefault()
        e.e.stopPropagation()
        let zoom = (e.e.deltaY > 0 ? -0.1 : 0.1) + this.canvas.getZoom()
        zoom = Math.max(0.1, zoom)
        zoom = Math.min(5, zoom)
        if (zoom === this.relationship.zoom) return
        const [x, y] = [e.e.offsetX, e.e.offsetY]
        // 计算缩放时产生的偏移量
        this.relationship.x += x / zoom - x / this.relationship.zoom
        this.relationship.y += y / zoom - y / this.relationship.zoom
        const zoomPoint = new fabric.Point(x, y)
        this.canvas.zoomToPoint(zoomPoint, zoom)
        this.relationship.zoom = zoom
    }
})

光标设置

一、统一设置光标样式

通过 canvas 对象的光标相关属性可以对所有的指针样式进行统一设置:

  • defaultCursor:默认光标,即鼠标在画布上的样式(默认值:default
  • hoverCursor:鼠标移动到对象上的样式(默认值:move
  • moveCursor:对象拖动时的鼠标样式(默认值:move
  • freeDrawingCursor:自由绘制时的鼠标样式(默认值:crosshair
  • rotationCursor:旋转时的鼠标样式(默认值:crosshair
var canvas = new fabric.Canvas('canvas');
canvas.defaultCursor = 'crosshair'; //默认光标改成十字
canvas.hoverCursor = 'pointer'; //悬浮光标改成手型

二、为对象单独设置

1,基本介绍

我们也可以通过对象的光标相关属性给不同对象分别设置不同的光标。对象的光标属性和 canvas 是一样的:

  • defaultCursor:默认光标(默认值:default
  • hoverCursor:鼠标移动到对象上的样式(默认值:move
  • moveCursor:对象拖动时的鼠标样式(默认值:move
  • freeDrawingCursor:自由绘制时的鼠标样式(默认值:crosshair
  • rotationCursor:旋转时的鼠标样式(默认值:crosshair
var canvas = new fabric.Canvas('canvas');
 
//添加两个方块
var rect1 = new fabric.Rect({top: 50, left: 50, width: 70, height: 70, fill: 'red'});
canvas.add(rect1);
var rect2 = new fabric.Rect({top: 50, left: 150, width: 70, height: 70, fill: 'red'});
canvas.add(rect2);
 
//为两个方块分别设置不同的鼠标样式
rect1.hoverCursor = 'crosshair'; //默认光标改成十字
rect2.hoverCursor = 'pointer'; //默认光标改成手型

讲图片作为对象添加到画布中,高度适中,并且居中

initBaseImg (src) {
    // 以高度为基数,然后获取图片缩放比,计算图片的宽度
    fabric.Image.fromURL(src, (bckImg) => {
        var { width, height } = bckImg.getOriginalSize()
        // 计算缩放比,以浏览器高度/图片的实际高度(这样的尺寸,会保证高度正好,为了不影响美观,所以乘以0.8 使图片 大小合适的现在在正中间)
        this.imgScale = this.canvas.height / height * 0.8 // 屏幕高度作为基准,计算出当前 背景图需要缩放的比例 scaleY
         this.canvas.add(bckImg.scale(this.imgScale))
         bckImg.centerH().centerV()
    })
},

设置画布尺寸

  let canvas = null
 
  // 设置画布宽度
  function setWidth(width) {
    canvas.setWidth(width)
  }
 
  // 设置画布高度
  function setHeight(height) {
    canvas.setHeight(height)
  }
 
  // 一键设置宽高
  function setDimensions(width, height) {
    canvas.setDimensions({
      width,
      height
    })
  }
 
  window.onload = function() {
    // 使用 元素id 创建画布,此时可以在画布上框选
    canvas = new fabric.Canvas('canvasBox', {
      width: 100,
      height: 100
    })
  }
<script>
	let imageEffectCanvas = '';

	// 初始化画布控制器
	fabric.Object.prototype.set({
	    cornerStrokeColor:'#ff4555', //边角描边颜色
	    cornerColor:'#ff4555', //边角颜色
	    cornerStyle:'circle', //边角形状
	    cornerSize:28, //边角大小
	    borderScaleFactor:6, //描边边框大小
	    borderColor:'#ff4555', //描边边框颜色
	});
	methods: {
		// 初始化画布
		initImageEffectCanvas() {
			imageEffectCanvas = new fabric.Canvas('imageEffectCanvas',
				{
	                width: '1080', //画布宽度
	                height: '1920', //画布高度
	                backgroundColor: '#ffffff', //画布默认背景颜色
	            });
	            imageEffectCanvas.preserveObjectStacking=true; //选中对象不会到最高层(解决选中图片层级提升覆盖文字等其它元素问题)
		},
	},
</script>

使用笔记

参考open in new window

常用属性

canvas.isDrawingMode = true; 可以自由绘制
canvas.selectable = false; 控件不能被选择,不会被操作
canvas.selection = true; 画板显示选中
canvas.skipTargetFind = true; 整个画板元素不能被选中
canvas.freeDrawingBrush.color = "#E34F51" 设置自由绘画笔的颜色
freeDrawingBrush.width 自由绘笔触宽度
canvas.setZoom(2); 设置画板缩放比例



方法

add(object) 添加
insertAt(object,index) 添加
remove(object) 移除
canvas.clear(); // 清空画布内容
forEachObject 循环遍历
getObjects() 获取所有对象
item(int) 获取子项
isEmpty() 判断是否空画板
size() 画板元素个数
contains(object) 查询是否包含某个元素
fabric.util.cos
fabric.util.sin
fabric.util.drawDashedLine 绘制虚线
getWidth() setWidth()
getHeight()?
clear() 清空
renderAll() 重绘
requestRenderAll() 请求重新渲染
rendercanvas() 重绘画板?
getCenter().top/left 获取中心坐标
toDatalessJSON() 画板信息序列化成最小的json
toJSON() 画板信息序列化成json
moveTo(object,index) 移动?
dispose() 释放?
setCursor() 设置手势图标
getSelectionContext()获取选中的context
getSelectionElement()获取选中的元素
getActiveObject() 获取选中的对象
getActiveObjects() 获取选中的多个对象
discardActiveObject()取消当前选中对象
isType() 图片的类型?
setColor(color) = canvas.set("full","");
rotate() 设置旋转角度
setCoords() 设置坐标

事件

object:added
object:removed
object:modified
object:rotating
object:scaling
object:moving
object:selected 这个方法v2已经废弃,使用selection:created替代,多选不会触发
before:selection:cleared
selection:cleared
selection:updated
selection:created
path:created
mouse:down
mouse:move
mouse:up
mouse:over
mouse:out
mouse:dblclick

IText的方法

selectAll() 选择全部
getSelectedText() 获取选中的文本
exitEditing() 退出编辑模式?

绘制直线

var line = new fabric.Line([10, 10, 100, 100], {
fill: 'green',
stroke: 'green', //笔触颜色
strokeWidth: 2,//笔触宽度
});
canvas.add(line);

绘制虚线

在绘制直线的基础上添加属性strokeDashArray:Array example:

var line = new fabric.Line([10, 10, 100, 100], {
fill: 'green',
stroke: 'green',
strokeDashArray:[3,1]
});
canvas.add(line);

strokeDashArray[a,b] =》 每隔a个像素空b个像素。


可绘制对象

fabric.Circle 圆
fabric.Ellipse 椭圆
fabric.Line 直线
fabric.Polygon
fabric.Polyline
fabric.Rect 矩形
fabric.Triangle 三角形

图片去掉选中边框和旋转,且只能移动,不可操作

oImg.hasControls = false; 只能移动不能(编辑)操作
oImg.hasBorders = false; 去掉边框,可以正常操作
hasRotatingPoint = false; 不能被旋转
hasRotatingPoint 控制旋转点不可见
scaleToHeight(value, absolute) 缩放图片高度到value scaleToWidth(value, absolute) 缩放图片宽度到value

示例代码如下:

fabric.Image.fromURL("img.jpg", function (oImg) {
	img.scaleToHeight(400, false);  //缩放图片的高度到400
	img.scaleToWidth(400, false);   //缩放图片的宽度到400
	canvas.add(oImg);
	oImg.hasControls = oImg.hasBorders = false;
});

https://cloud.tencent.com/developer/article/2119146open in new window

 // 通过鼠标滚轮缩放画布
  canvas.on('mouse:wheel', opt => {
    const delta = opt.e.deltaY // 滚轮,向上滚一下是 -100,向下滚一下是 100
    let zoom = canvas.getZoom() // 获取画布当前缩放值
    zoom *= 0.999 ** delta
    if (zoom > 20) zoom = 20
    if (zoom < 0.01) zoom = 0.01
    canvas.zoomToPoint(
      { // 关键点
        x: opt.e.offsetX,
        y: opt.e.offsetY
      },
      zoom
    )
    opt.e.preventDefault()
    opt.e.stopPropagation()
  })


  // 鼠标拖拽画布
  canvas.on('mouse:down', opt => { // 鼠标按下时触发
    let evt = opt.e
    canvas.isDragging = true // isDragging 是自定义的
    canvas.lastPosX = evt.clientX // lastPosX 是自定义的
    canvas.lastPosY = evt.clientY // lastPosY 是自定义的
  })

  canvas.on('mouse:move', opt => { // 鼠标移动时触发
    if (canvas.isDragging) {
      let evt = opt.e
      let vpt = canvas.viewportTransform // 聚焦视图的转换
      vpt[4] += evt.clientX - canvas.lastPosX
      vpt[5] += evt.clientY - canvas.lastPosY
      canvas.requestRenderAll()
      canvas.lastPosX = evt.clientX
      canvas.lastPosY = evt.clientY
    }
  })

  canvas.on('mouse:up', opt => { // 鼠标松开时触发
    canvas.setViewportTransform(canvas.viewportTransform) // 设置此画布实例的视口转换  
    canvas.isDragging = false
  })

添加图片

FabricJs使用 -- 图片 - 知乎 (zhihu.com)open in new window


/**
             *  标记点 (标记点由2部分组装成)
             *  1. 背景圈-通过 fabric.Gradient 属性设置渐变色
             *  1. 标记图- 图片
             * @param item
             */
            addPoint (item) {
                const imgSrc = this.tagIconMap.default; // item.warnFlag === '0' ? this.tagIconMap.default : this.tagIconMap.alarm
                const radius = 20;
                fabric.Image.fromURL(imgSrc, bckImg => {
                    bckImg.originX = 'center'; // 设置中心点为图形中心
                    bckImg.originY = 'center';
                    // bckImg.left = 0; bckImg.top = 0;
                    bckImg.scale(0.5);
                    // 图标外围的背景圈    通过 new fabric.Gradient 对渐变色设置
                    var circle = new fabric.Circle({
                        radius: radius,
                        // fill: '#007AFF',
                        originX: 'center',
                        originY: 'center',
                        level: '1' // 自定义变量,记录 当前背景圈 层级为 1  后面实现选中
                    });
                    // 设置渐变色
                    const fillGradient = new fabric.Gradient({
                        type: 'radial',
                        coords: { r1: 50, r2: 0, x1: 50, y1: 50, x2: 50, y2: 50 },
                        colorStops: [
                            { offset: 0, color: '#007AFF' },
                            { offset: 1, color: '#59ffff' }
                        ]
                    })
                    circle.set('fill', fillGradient); // 给标记点设置渐变色
                    var group = new fabric.Group([circle, bckImg], {}); // bckImg,
                    group.left = Number(item.xPosition) * this.imgScale2 - radius; //  + bckImg.width / 2
                    group.top = Number(item.yPosition) * this.imgScale2 - radius; //  + bckImg.height / 2;
                    group.extData = item; // 拓展属性
                    group.extData.fillGradient = fillGradient; // 拓展属性中记录下 原始渐变色
                    group.hoverCursor = 'pointer' // 设置鼠标悬停样式
                    group.lockMovementX = true; // 不允许水平移动
                    group.lockMovementY = true;
                    group.hasControls = false;
                    group.hasBorders = false;
                    // 鼠标经过的时候变红色
                    this.canvas.on('mouse:over', opt => {
                        const checkGroup = opt.target; // 当前组对象
                        if (checkGroup) {
                            const checkItem = checkGroup.getObjects().find(itemObj => itemObj.level === '1'); // 用于取出 底部背景圈
                            const fillGradient = new fabric.Gradient({
                                type: 'radial',
                                coords: { r1: 50, r2: 0, x1: 50, y1: 50, x2: 50, y2: 50 },
                                colorStops: [
                                    { offset: 0, color: '#26AF00FF' },
                                    { offset: 1, color: '#26AF0080' }
                                ]
                            })
                            checkItem.set('fill', fillGradient);
                            this.showWindowPopup(opt.e, checkGroup.extData);
                            this.canvas.renderAll()
                        }
                    })

                    // 鼠标移开后变回绿色
                    this.canvas.on('mouse:out', opt => {
                        const checkGroup = opt.target; // 当前组对象
                        if (checkGroup) {
                            const checkItem = checkGroup.getObjects().find(itemObj => itemObj.level === '1'); // 用于取出 底部背景圈
                            checkItem.set('fill', checkGroup.extData.fillGradient);
                            this.canvas.renderAll()
                        }
                    })
                    this.canvas.add(group);
                })
            },
                
                
                //搜索 定位 标记点
                chechPoint (item) {
                    this.canvas._objects.forEach(obj => {
                        if (obj.extData.id === item.id) {
                            obj.__eventListeners.click[0]()
                        }
                    })
                },