基于canvas画布的实用类Fabric.js的使用

目录

前言

一、Fabric.js简介

二、开始

1、引入Fabric.js

2、在main.js中使用

3、初始化画布

三、方法

四、事件

1、常用事件

2、事件绑定

3、事件解绑

五、canvas常用属性

六、对象属性

1、基本属性

2、扩展属性

七、图层层级操作

八、复制和粘贴

1、复制

2、粘贴

九、锁定

1、静止水平移动(lockMovementX)

2、静止垂直移动(lockMovementY)

3、静止旋转(lockRotation)

4、静止水平缩放(lockScalingX)

5、静止垂直缩放(lockScalingY)

6、限制拖动区域

十、分组

十一、动画

十二、图像滤镜

十三、渐变

1、线性渐变

2、径向渐变

十四、拖拽和缩放画布

1、拖拽画布

2、以画布中心点为基准手动缩放

3、以鼠标指针位置为基准缩放

十五、右键菜单删除

十六、自由绘画

1、开启绘图模式

2、关闭绘图模式

十七、绘制背景图片

1、方式一:通过img元素添加

2、方式二:通过图片路径添加

十八、绘制文本

1、基础用法

2、文本修饰

3、可编辑文本

十九、绘制线和路径

1、绘制直线

2、绘制虚线

3、绘制路径

二十、自由绘制矩形

二十一、自由绘制圆形

二十二、自由绘制椭圆形

二十三、自由绘制三角形

二十四、自由绘制多边形


前言

        演示Demo:前端可视化demo

        Demo源码:https://gitee.com/k21vin/front-end-data-visualization

        本文章所有的gif图片由于录屏软件问题,上面的鼠标位置都是错位显示的!!

一、Fabric.js简介

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

        它主要的功能包括在canvas上创建和填充图形,比如矩形、圆形、多边形;生成的图像自带缩放、旋转、拖拽等功能;还可以给图形填充渐变颜色;各个图形可以相互组合等等。

二、开始

1、引入Fabric.js

npm i fabric --save

2、在main.js中使用

// main.js
import fabric from "fabric"
Vue.use(fabric)

3、初始化画布

<template><canvas id="canvas"></canvas>
</template><script>
export default {data () {canvas: null, // 画布对象},mounted() {this.canvas = new fabric.Canvas('canvas', {width: 300,height: 200})}
</script>

三、方法

canvas.add(object)                   // 添加对象
canvas.remove(object)                // 删除对象
canvas.setWidth(width)               // 设置canvas宽度
canvas.setHeight(height)             // 设置canvas高度
canvas.setDimensions({width, height})// 一键设置宽高
canvas.getObjects()                  // 获取所有对象
canvas.getActiveObject()             // 获取选中的对象
canvas.clear()                       // 清除画布中所有对象
canvas.renderAll()                   // 重绘
canvas.requestRenderAll()            // 请求重新渲染
canvas.getZoom()                     // 获取画布当前缩放值
canvas.sendToBack(object)            // 移到对象到最底层
canvas.viewportCenterObjectH(object) // 水平居中对象
canvas.viewportCenterObjectV(object) // 垂直居中对象
canvas.viewportCenterObject(object)  // 垂直水平居中对象
canvas.fxCenterObjectH(object)       // 动画水平居中对象
canvas.fxCenterObjectV(object)       // 动画垂直居中对象
canvas.fxCenterObject(object)        // 动画垂直水平居中对象let canvasJsonData = JSON.stringify(canvas.toJSON()) // 将画布序列化成json数据
let canvasSvgData = canvas.toSVG() // 将画布序列化成svg数据
canvas.loadFromJSON(canvasJsonData)  // 反序列化Json数据

四、事件

1、常用事件

mouse: down         // 鼠标按下事件
mouse: move         // 鼠标移动事件
mouse: up           // 鼠标移动事件
mouse: over         // 鼠标移入事件
mouse: out          // 鼠标移出事件
mouse: dblclick     // 鼠标双击事件
object: added       // 对象被添加事件
object: removed     // 对象被删除事件
object: modified    // 对象被修改事件
object: rotating    // 对象被旋转事件
object: scaling     // 对象被缩放事件
object: moving      // 对象被移动事件

2、事件绑定

canvas.on('mouse: wheel', (opt) => {console.log(opt)
})

3、事件解绑

canvas.off('mouse: wheel')

五、canvas常用属性

canvas.selection = true                  // 画布是否可选中 默认为true、false:不可选中
canvas.selectionColor = 'transparent'    // 画布鼠标框选时的背景色
canvas.selectionBorderColor = 'transparent'// 画布鼠标框选时的边框颜色
canvas.selectionLineWidth = 6            // 画布鼠标框选时的边框厚度
canvas.selectionDashArray = [30, 4, 10] // 画布鼠标框选时边框虚线规则
canvas.selectionFullyContained = true   // 只选择完全包含在拖动选择矩形中的形状
canvas.backgroundColor = '#2E3136'      // 画布背景色
canvas.hoverCursor = 'pointer'          // 鼠标光标样式 default、pointer、move等
canvas.skipTargetFind = true            // 整个画板元素不能被选中
canvas.fireRightClick = true            // 启用右键,options.button的数字为3
canvas.stopContextMenu = true           // 禁止默认右键菜单

        Fabric.js 可以通过 viewportTransform 属性配置画布的视窗属性

canvas.viewportTransform[4] = 100
canvas.viewportTransform[5] = 100

        viewportTransform 是一个数组,里面有6个元素,默认值是 [1, 0, 0, 1, 0, 0]。从下标0开始,它们分别代表:

[0]: 水平缩放(x轴方向)
[1]: 水平倾斜(x轴方向)
[2]: 垂直倾斜(y轴方向)
[3]: 垂直缩放(y轴方向)
[4]: 水平移动(x轴方向)
[5]: 垂直移动(y轴方向)

六、对象属性

1、基本属性

let circle = new fabric.Circle({top: 100,    // y坐标left: 100,    // x坐标fill: '#17b978', // 填充色radius: 50  // 半径
})
rect.top = 100                       // y坐标
rect.left = 100                      // x坐标
rect.width = 100                     // 矩形宽度
rect.height = 100                    // 矩形高度
circle.radius = 50                   // 圆半径
rect.fill = '#17b978'                // 填充色
rect.stroke = '#FE5332'              // 线条颜色
rect.strokeWidth = 10                // 线条宽度
rect.strokeMiterLimit = index        // 可以用来记录当前选中的rectList列表的索引!!!!!

2、扩展属性

circle.set({hasBorders: false
}
circle.selectable = false                 // 控件不能被选择,不会被操作
circle.hasControls = false                // 只能移动不能(编辑)操作
circle.hasBorders = false                 // 选中时,是否显示边,true:显示(默认)
circle.borderColor = 'red'                // 选中时,边的颜色
circle.borderScaleFactor = 5              // 选中时,边的粗细
circle.borderDashArray = [20, 5, 10, 7]  // 选中时,虚线边的规则
circle.transparentCorners = false         // 选中时,角是否是空心 true:空心  false:实心
circle.cornerColor = "#a1de93",           // 选中时,角的颜色
circle.cornerStrokeColor = 'pink'         // 选中时,角的边框的颜色
circle.cornerStyle = 'circle'             // 选中时,角的属性  rect:矩形(默认)、circle:圆形
circle.cornerSize = 20                    // 选中时,角的大小为20
circle.cornerDashArray = [10, 2, 6]       // 选中时,虚线角的规则
circle.selectionBackgroundColor = '#ffc300' // 选中时,选框的背景色
circle.padding = 20                       // 选中时,选框离图形的距离
circle.borderOpacityWhenMoving = 0.6      // 当对象活动和移动时,对象控制边界的不透明度
triangle.perPixelTargetFind = true        // 选择三角形空白位置的时候无法选中,false:可以选中(默认)

七、图层层级操作

canvas.bringToFront(rect)    // 移到顶层
canvas.sendToBack(rect)      // 移到底层
canvas.bringForward(rect)    // 上移一层
canvas.sendBackwards(rect)   // 下移一层
canvas.moveTo(0)          // 移动到指定层

八、复制和粘贴

1、复制

handleCopy() {if (!canvas.getActiveObject()) {this.$message.warning('请先选择元素')return}this.canvas.getActiveObject().clone(cloned => {this.cloneObjects = cloned})
}

2、粘贴

handlePaste() {if (!this.cloneObjects) {return this.$message.warning('还没复制过任何内容')}this.cloneObjects.clone(cloned => {this.canvas.discardActiveObject() // 取消选择// 设置新内容的坐标位置cloned.set({left: cloned.left + 10,top: cloned.top + 10,evented: true})if (cloned.type === 'activeSelection') { // 如果复制的是多个对象,则需要遍历克隆对象cloned.canvas = this.canvas;cloned.forEachObject(obj => {this.canvas.add(obj)})cloned.setCoords()} else {this.canvas.add(cloned)}this.cloneObjects.top += 10this.cloneObjects.left += 10this.canvas.setActiveObject(cloned)this.canvas.requestRenderAll()})
}

九、锁定

        Fabric对象可以添加一些属性进行锁定,例如静止水平移动、静止垂直移动,静止缩放等等

1、静止水平移动(lockMovementX)


let rect = new fabric.Rect({width: 100,height: 50,fill: '#ffde7d',top: 20,left: 20
})
rect.lockMovementX = true
canvas.add(rect)

2、静止垂直移动(lockMovementY)


let rect = new fabric.Rect({width: 100,height: 50,fill: '#ffde7d',top: 20,left: 20
})
rect.lockMovementY = true

3、静止旋转(lockRotation)


let rect = new fabric.Rect({width: 100,height: 50,fill: '#ff9a3c',top: 60,left: 160
})
rect.lockRotation = true

4、静止水平缩放(lockScalingX)


let rect = new fabric.Rect({width: 100,height: 50,fill: '#ffde7d',top: 20,left: 20
})
rect.lockScalingX = true

5、静止垂直缩放(lockScalingY)


let rect = new fabric.Rect({width: 100,height: 50,fill: '#f95959',top: 20,left: 20
})
rect.lockScalingY = true

6、限制拖动区域


let boundingBox = new fabric.Rect({top: 100,left: 100,width: 600,height: 400,fill: '#f95959',selectable: false
})
let movingBox = new fabric.Rect({top: 150,left: 150,width: 100,height: 100,fill: 'yellow',hasBorders: false,hasControls: false,hoverCursor: 'move'
})
this.canvas.add(boundingBox);
this.canvas.add(movingBox);
this.canvas.on("object:moving", (opt) => {let top = movingBox.top;let left = movingBox.left;let topBound = boundingBox.top;let bottomBound = topBound + boundingBox.height;let leftBound = boundingBox.left;let rightBound = leftBound + boundingBox.width;opt.target.left = Math.min(Math.max(left, leftBound), rightBound - movingBox.width)opt.target.top = Math.min(Math.max(top, topBound), bottomBound - movingBox.height)
})

十、分组

        Groups是Fabric最强大的功能之一,它可以将任意数量的Fabric对象组合在一起,形成一个小组,分组后,所有对象都可以一起移动、修改、缩放、旋转甚至更改其外观等


let group = new fabric.Group([circle, text], {left: 100,top: 100,angle: -10
})
canvas.add(group)

        修改分组的某个对象的属性:


group.item(0).set("fill","red");
group.item(1).set({text:"trololo",fill:"white"
})

        分组时要记住的另一件事是对象的状态。例如,在与图像组成组时,需要确保这些图像已完全加载:


fabric.Image.fromURL(logo, img => {let img1 = img.scale(0.3).set({left: 0, top: 0})fabric.Image.fromURL(logo, img => {let img2 = img.scale(0.3).set({left: 80, top: 0})fabric.Image.fromURL(logo, img => {let img3 = img.scale(0.3).set({left: 160, top: 0})let group = new fabric.Group([img1, img2, img3], {left: 10,top: 400})canvas.add(group)})})
})

十一、动画

        每个Fabric对象都有一个animate方法,该方法可以动画化该对象,animate(动画属性,动画的结束值,[动画的详细信息])


let rect = new fabric.Rect({left: 100,top: 100,width: 100,height: 100,fill: 'red'
})
rect.animate("angle", 45, {onChange: canvas.renderAll.bind(canvas)
})
canvas.add(rect)

        第一个参数是要设置动画的属性。第二个参数是动画的结束值。如果矩形具有-15°的角度,并且我们传递了45,则动画将从-15°变为45°。第三个参数是一个可选对象,用于指定动画的详细信息-持续时间,回调,缓动等


rect.animate("angle", 45, {from: 0, // 允许指定可设置动画的属性的起始值(如果我们不希望使用当前值)duration: 1000, // 默认为500(ms),可用于更改动画的持续时间easing: fabric.util.ease.easeOutBounce, // 缓动功能 easeOutBounce、easeInCubic、easeOutCubic、easeInElastic、easeOutElastic、easeInBounce、easeOutExpoonChange: canvas.renderAll.bind(canvas), // 在每次刷新时都会执行onComplete: (e) => { console.log(e) } // 在动画结束时调用的回调
})

        animate的一个方便之处在于它还支持相对值


// 向右移动100px
rect.animate('left', '+=100', {onChange: canvas.renderAll.bind(canvas)
})// 逆时针旋转5度
rect.animate('angle', '-=5', {onChange: canvas.renderAll.bind(canvas)
})

十二、图像滤镜

        fabric.Image的每个实例都具有“ filters”属性,该属性是一个简单的过滤器数组。该阵列中的每个过滤器都是Fabric过滤器之一的实例。或您自己的自定义过滤器的实例。


let url = 'http://localhost:82/public/img/5.png' // 图片url
let base64Url = await this.imgUrlToBase64(url) // 图片base64 url
// 正常照片
fabric.Image.fromURL(url, img => {img.scale(0.3)canvas.add(img)
})// 单个滤镜
fabric.Image.fromURL(base64Url, img => {img.scale(0.3)img.left = 300// 添加滤镜img.filters.push(new fabric.Image.filters.Grayscale())// 图片加载完成之后,应用滤镜效果img.applyFilters()canvas.add(img)
})// 叠加滤镜
fabric.Image.fromURL(base64Url, img => {img.scale(0.3)img.set({left: 300,top: 250,})img.filters.push(new fabric.Image.filters.Grayscale(),new fabric.Image.filters.Sepia(), //色偏new fabric.Image.filters.Brightness({ brightness: 0.2 }) //亮度)img.applyFilters()canvas.add(img)
})

        (说明:这里图片可能会出错,放本地图片地址会报“Cannot read property 'naturalWidth' of null”的错误,直接放网络图片地址会报“Failed to execute 'texImage2D' on 'WebGLRenderingContext': The image element contains cross-origin data, and may not be loaded.”的错误。解决方法就是将转为Base64格式

十三、渐变

        Fabric支持在所有对象上设置填充或描边属性的渐变,首先创建渐变,然后将其分配给填充或描边。

1、线性渐变


// 圆
let circle = new fabric.Circle({left: 100,top: 100,radius: 50,
})
let gradient = new fabric.Gradient({type: 'linear', // linear or radialgradientUnits: 'pixels', // pixels or pencentage 像素 或者 百分比coords: { x1: 0, y1: 0, x2: circle1.width, y2: 0 }, // 至少2个坐标对(x1,y1和x2,y2)将定义渐变在对象上的扩展方式colorStops:[ // 定义渐变颜色的数组{ offset: 0, color: 'red' },{ offset: 0.2, color: 'orange' },{ offset: 0.4, color: 'yellow' },{ offset: 0.6, color: 'green' },{ offset: 0.8, color: 'blue' },{ offset: 1, color: 'purple' },]
})
circle.set('fill', gradient);
canvas.add(circle)

2、径向渐变


let circle = new fabric.Circle({left: 100,top: 100,radius: 50
})
let gradient = new fabric.Gradient({type: 'radial',coords: {r1: 50,r2: 0,x1: 50,y1: 50,x2: 50,y2: 50,},colorStops: [{ offset: 0, color: '#fee140' },{ offset: 1, color: '#fa709a' }]
})
circle.set('fill', gradient);
canvas.add(circle)

十四、拖拽和缩放画布

1、拖拽画布


<script>export default {data() {return {lastPosX: 0,       // 上次鼠标位置X坐标lastPosY: 0,       // 上次鼠标位置Y坐标isDragging: false, // 是否可以拖拽画布}},mounted() {... // 初始化canvasthis.canvas.on('mouse:down', this.onMouseDown)this.canvas.on('mouse:move', this.onMouseMove)this.canvas.on('mouse:up', this.onMouseUp)},methods: {// 监听鼠标按下事件onMouseDown(opt) {this.lastPosX = opt.e.clientXthis.lastPosY = opt.e.clientYthis.isDragging = true},// 监听鼠标移动事件onMouseMove(opt) {if (this.isDragging) {this.canvas.viewportTransform[4] += opt.e.clientX - this.lastPosXthis.canvas.viewportTransform[5] += opt.e.clientY - this.lastPosYthis.canvas.requestRenderAll()this.lastPosX = opt.e.clientXthis.lastPosY = opt.e.clientY}},// 监听鼠标松开事件onMouseUp(opt) {if (this.isDragging) {this.canvas.setViewportTransform(this.canvas.viewportTransform)this.isDragging = false}}}}
</script>

2、以画布中心点为基准手动缩放

<template><el-tooltip content="放大" placement="bottom-start"><span class="iconfont icon-fangda" @click="onManualScale(-100)"></span></el-tooltip><el-tooltip content="缩小" placement="bottom-start"><span class="iconfont icon-suoxiao" @click="onManualScale(100)"></span></el-tooltip>
</template>
<script>
export default {// 中心点缩放画布onManualScale(delta) {let zoom = canvas.getZoom() // 获取画布当前缩放值zoom *= 0.999 ** deltazoom = zoom > 10 ? 10 : (zoom < 0.1 ? 0.1 : zoom) // 最大放大10倍,最小缩小至10%canvas.zoomToPoint({ // 以画布中心点为基准缩放x: this.canvasBoxWidth / 2,  // canvasBoxWidth 画布宽度y: this.canvasBoxHeight / 2  // canvasBoxHeight 画布高度}, zoom)}
}
</script>

3、以鼠标指针位置为基准缩放


this.canvas.on('mouse:wheel', this.onMouseWheel)// 监听鼠标放大缩小事件
onMouseWheel(opt) {let delta = opt.e.deltaY // 滚轮,向上滚一下是 -100,向下滚一下是 100let zoom = this.canvas.getZoom() // 获取画布当前缩放值zoom *= 0.999 ** deltazoom = zoom > 10 ? 10 : (zoom < 0.1 ? 0.1 : zoom) // 最大放大10倍,最小缩小至10%this.canvas.zoomToPoint({ // 以鼠标指针位置为基准缩放x: opt.e.offsetX,y: opt.e.offsetY}, zoom)opt.e.preventDefault()opt.e.stopPropagation()
}

十五、右键菜单删除

<template><div class="canvas-box" ref="canvasBox"><canvas id="canvas"></canvas><div id="delMenu" ref="delMenu" v-show="isShowDelMenu" :style="delMenuStyle" @contextmenu.prevent=""><el-button type="iconButton" icon="h-icon-delete" @click="handleDeleteMenu">删除</el-button></div></div>
</template>
<script>
export default {name: 'PointerDetail',data () {return {canvas: null,activeEle: null, // 上次选中元素isShowDelMenu: false, // 是否显示删除弹窗delMenuStyle: '' // 删除弹窗定位样式}},mounted() {this.init()},methods: {// 初始化init() {this.canvas = new fabric.Canvas('canvas', {fireRightClick: true, // 启用右键,button的数字为3stopContextMenu: true, // 禁止默认右键菜单})this.canvas.on('mouse:down', this.onMouseDown)},// 监听鼠标按下事件onMouseDown(opt) {// 还原上次选中状态if (this.activeEle) {this.activeEle.set('fill', 'transparent')this.canvas.renderAll()}this.activeEle = opt.target || null// 按下鼠标右键if (opt.button === 3) {// 点击到非图片控件 显示删除弹窗和填充控件背景色if (opt.target && opt.target.type !== 'image') {this.activeEle.set('fill', 'rgba(100, 100, 255, 0.3)')this.canvas.renderAll()this.isShowDelMenu = truethis.$nextTick(() => {this.delMenuStyle = this.getMenuStyle(this.$refs.delMenu, opt)})} else {// 否则隐藏删除弹窗this.hiddenMenu()}// 按下鼠标左键} else {this.hiddenMenu()}},// 获取弹窗坐标getMenuStyle(ele, opt) {let menuWidth = ele.offsetWidthlet menuHeight = ele.offsetHeightlet pointX = opt.pointer.x + 2let pointY = opt.pointer.y + 2if (this.$refs.canvasBox.offsetWidth - pointX <= menuWidth) {pointX -= menuWidth}if (this.$refs.canvasBox.offsetHeight - pointY <= menuHeight) {pointY -= menuHeight}return `left: ${pointX}px; top: ${pointY}px;`},// 隐藏菜单hiddenMenu() {this.activeEle = nullthis.isShowDelMenu = false},// 删除选中元素handleDeleteMenu() {this.canvas.remove(this.activeEle)this.canvas.requestRenderAll()this.hiddenMenu()}}
}

十六、自由绘画

1、开启绘图模式


let canvas = new fabric.Canvas('canvas', {isDrawingMode: true // 开启绘图模式
})
canvas.freeDrawingBrush.color = '#11999e' // 设置画笔颜色
canvas.freeDrawingBrush.width = 10 // 设置画笔粗细
canvas.freeDrawingBrush.shadow = new fabric.Shadow({ // 设置画笔投影blur: 10,offsetX: 10,offsetY: 10,affectStroke: true,color: '#30e3ca'
})

2、关闭绘图模式


canvas.isDrawingMode = false

十七、绘制背景图片

1、方式一:通过img元素添加


<img src="@/assets/images/logo.png" id="logo">let img = document.getElementById('logo')
img.onload = () => {let canvasImage = new fabric.Image(imgElement, {left: 100, // 距离画布左侧距离top: 100, // 距离画布顶部距离width: 200, // 图片宽度height: 200, // 图片高度angle: 50, // 旋转opacity: 1 // 透明度})canvas.add(canvasImage)
}

2、方式二:通过图片路径添加


let url = 'http://localhost:82/public/img/logo.png'
fabric.Image.fromURL(url, img => {let canvasImage = img.set({scaleX: 0.5,scaleY: 0.5})canvas.add(canvasImage)
}) 

十八、绘制文本

        Fabric也提供了文本的相关功能,Fabric文本允许以面向对象的方式处理文本,原生canvas方法,只允许在非常低的级别上填充或描边文本,通过实例化fabric.Text实例,我们就可以使用文本,就像我们将使用任何其他Fabric对象:移动它,缩放它,更改其属性等, 其次它提供比canvas给我们更丰富的功能,包括:


Multiline support   // 支持多行
Text alignment      // 文本对齐 Left、center、right
Text background     // 文本背景 背景也遵循文本对齐
Text decoration     // 文字装饰 下划线Underline、上划线overline、贯穿线strike-through
Line height         // 行高 使用多行文字时出错
Char spacing        // 字符间距 使文本更紧凑或间距更大
Subranges           // 子范围 将颜色和属性应用于文本对象的子范围
Multibyte           // 多字节 支持表情符号
On canvas editing   // 交互式画布编辑 可以直接在canvas上键入文本

1、基础用法


let text = new fabric.Text('Hello World!', {left: 40,top: 10,fontFamily: 'Comic Sans', // 字体fontSize: 60, // 字号fontWeight: 600, // 字体重量(粗细),normal、bold 或 数字(100、200、400、600、800)fontStyle: 'normal', // 字体风格 正常 normal 或 斜体 italiccharSpacing: 100, // 字距fill: 'red', // 字体颜色cornerColor: 'pink', // 角的颜色(被选中时)angle: 30, // 旋转backgroundColor: '#ffd460', // 背景色borderColor: 'yellowGreen', // 边框颜色(被选中时)borderScaleFactor: 4, // 边框粗细(被选中时)borderDashArray: [10, 4, 20], // 创建边框虚线stroke: '#3f72af', // 文字描边颜色(蓝色)strokeWidth: 2, // 文字描边粗细textAlign: 'left', // 对齐方式:left 左对齐; right 右对齐; center 居中opacity: 0.8, // 不透明度// text: '雷猴', // 文字内容,会覆盖之前设置的值selectable: true, // 能否被选中,默认trueshadow: 'rgba(0, 0, 0, 0.5) 5px 5px 5px', // 投影
})
canvas.add(text)

2、文本修饰


// 下划线
let underlineText = new fabric.Text("I am an undrline text", {underline: true
})
canvas.add(underlineText)// 贯穿线
let strokeThroughText = new fabric.Text("I am a stroke-through text", {linethrough: true,top: 40
})
canvas.add(strokeThroughText)// 上划线
let overlineText = new fabric.Text("I am overline text", {overline:true,top: 80
})
canvas.add(overlineText)

3、可编辑文本


let IText = new fabric.IText('雷猴啊,双击打几个字试下~', {fontFamily: 'Comic Sans'
})
canvas.add(IText)

十九、绘制线和路径

1、绘制直线


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

2、绘制虚线

        在绘制直线的基础上添加属性strokeDashArray[a,b],表示每隔a个像素空b个像素。


let line = new fabric.Line([0, 100, 100, 100], {fill: 'green', // 填充色stroke: 'green', // 笔触颜色strokeWidth: 2, // 笔触宽度strokeDashArray:[3,1]
});
canvas.add(line);

3、绘制路径

  • Fabric.js使用 new fabric.Path 创建路径。
  • M:可以理解为新的起始点 x,y 坐标
  • L:每个折点的 x,y 坐标
  • z:自动闭合(自动把结束点和起始点连接起来)


let path = new fabric.Path('M 0 0 L 200 100 L 170 200 z')
path.set({top: 120, // 距离容器顶部距离 120pxleft: 120, // 距离容器左侧距离 120pxfill: 'hotpink', // 填充 亮粉色opacity: 0.5, // 不透明度 50%stroke: 'black', // 描边颜色 黑色strokeWidth: 10 // 描边粗细 10px
})

        上述代码第一行“M”代表“移动”命令,“M 0 0” 代表把画笔移动到(0, 0)点坐标。“L”代表“线”,“L 200 100 ”的意思是使用钢笔画一条线,从(0, 0)坐标画到(200, 100)坐标。“z” 代表让图形闭合路径。这样就画出了一个三角形。画好三角形后,我们可以用set( )方法对三角形的位置、颜色、角度、透明度等属性进行设置。

二十、自由绘制矩形

<template><div class="canvas-box" ref="canvasBox"><canvas id="canvas"></canvas><el-button @click="handleActiveRect">绘制矩形</el-button></div>
</template>
<script>
export default {name: 'PointerDetail',data () {return {canvas: null,lastPoint: null, // 上次鼠标点位坐标strokeColor: 'transparent', // 轮廓填充颜色isActiveRect: false, // 是否激活绘制矩形rectList: [] // 绘制的矩形列表}},mounted() {this.init()},methods: {// 初始化init() {this.canvas = new fabric.Canvas('canvas', {width: this.$refs.canvasBox.offsetWidth,height: this.$refs.canvasBox.offsetHeight,backgroundColor: '#2E3136',selectionColor: 'transparent',selectionBorderColor: 'transparent',hoverCursor: 'default'})this.canvas.on('mouse:down', this.onMouseDown)this.canvas.on('mouse:up', this.onMouseUp)this.canvas.on('object:added', this.onObjectAdded)},// 监听鼠标按下事件onMouseDown(opt) {if (this.isActiveRect) {this.lastPoint = opt.absolutePointer || nullthis.strokeColor = '#00FF64'}},// 监听鼠标松开事件onMouseUp(opt) {if (this.isActiveRect) {this.drawRect(opt.absolutePointer)}},// 绘制完成元素事件onObjectAdded(opt) {let target = opt.targetif (target.stroke === '#00FF64') {this.isActiveRect && this.rectList.push(target)}},// 绘制矩形drawRect(pointer) {if (!this.lastPoint || JSON.stringify(this.lastPoint) === JSON.stringify(pointer)) { // 点击事件,不生成矩形return}let top = Math.min(this.lastPoint.y, pointer.y)let left = Math.min(this.lastPoint.x, pointer.x)let width = Math.abs(this.lastPoint.x - pointer.x)let height = Math.abs(this.lastPoint.y - pointer.y)let rect = new fabric.Rect({ top, left,width,height,fill: 'transparent',stroke: this.strokeColor,selectable: false})this.canvas.add(rect)this.lastPoint = nullthis.strokeColor = 'transparent'},// 激活绘制矩形handleActiveRect() {this.isActiveRect = !this.isActiveRectif(this.isActiveRect) {this.canvas.selectionBorderColor = '#00FF64'}}}
}

二十一、自由绘制圆形

<template><div class="canvas-box" ref="canvasBox"><canvas id="canvas"></canvas><el-button @click="handleActiveCircle">绘制圆形</el-button></div>
</template>
<script>
export default {name: 'PointerDetail',data () {return {canvas: null,canvasCircle: null,downPoint: null,strokeColor: 'transparent', // 轮廓填充颜色isActiveCircle: false, // 是否激活绘制圆形circleList: [] // 绘制的圆形列表}},mounted() {this.init()},methods: {// 初始化init() {this.canvas = new fabric.Canvas('canvas', {width: this.$refs.canvasBox.offsetWidth,height: this.$refs.canvasBox.offsetHeight,backgroundColor: '#2E3136',selectionColor: 'transparent',selectionBorderColor: 'transparent',hoverCursor: 'default'})this.canvas.on('mouse:down', this.onMouseDown)this.canvas.on('mouse:move', this.onMouseMove)this.canvas.on('mouse:up', this.onMouseUp)this.canvas.on('object:added', this.onObjectAdded)},// 监听鼠标按下事件onMouseDown(opt) {if (this.isActiveCircle) {this.downPoint = opt.absolutePointerthis.strokeColor = '#00FF64'this.canvasCircle = new fabric.Circle({top: this.downPoint.y,left: this.downPoint.x,radius: 0,fill: 'transparent',stroke: this.strokeColor,strokeWidth: 2,selectable: false,})this.canvas.add(this.canvasCircle)}},// 监听鼠标移动事件onMouseMove(opt) {if (this.isActiveCircle && this.canvasCircle) {let radius = Math.min(Math.abs(this.downPoint.x - opt.absolutePointer.x), Math.abs(this.downPoint.y - opt.absolutePointer.y)) / 2let top = opt.absolutePointer.y > this.downPoint.y ? this.downPoint.y : this.downPoint.y - radius * 2let left = opt.absolutePointer.x > this.downPoint.x ? this.downPoint.x :  this.downPoint.x - radius * 2this.canvasCircle.set('radius', radius)this.canvasCircle.set('top', top)this.canvasCircle.set('left', left)this.canvas.requestRenderAll()}},// 监听鼠标松开事件onMouseUp(opt) {if (this.isActiveCircle) {if (JSON.stringify(this.downPoint) === JSON.stringify(opt.absolutePointer)) {this.canvas.remove(this.canvasCircle)} else {if (this.canvasCircle){this.canvasCircle.set('stroke', this.strokeColor)}}this.canvasCircle = null}},// 绘制完成元素事件onObjectAdded(opt) {let target = opt.targetif (target.stroke === '#00FF64') {this.isActiveCircle && this.circleList.push(target)}},// 激活绘制圆形handleActiveCircle() {this.isActiveCircle = !this.isActiveCircleif(this.isActiveCircle) {this.canvas.selectionBorderColor = '#00FF64'}}}
}

二十二、自由绘制椭圆形

<template><div class="canvas-box" ref="canvasBox"><canvas id="canvas"></canvas><el-button @click="handleActiveEllipse">绘制椭圆形</el-button></div>
</template>
<script>
export default {name: 'PointerDetail',data () {return {canvas: null,canvasEllipse: null,downPoint: null,strokeColor: 'transparent', // 轮廓填充颜色isActiveEllipse: false, // 是否激活绘制椭圆形ellipseList: [] // 绘制的椭圆形列表}},mounted() {this.init()},methods: {// 初始化init() {this.canvas = new fabric.Canvas('canvas', {width: this.$refs.canvasBox.offsetWidth,height: this.$refs.canvasBox.offsetHeight,backgroundColor: '#2E3136',selectionColor: 'transparent',selectionBorderColor: 'transparent',hoverCursor: 'default'})this.canvas.on('mouse:down', this.onMouseDown)this.canvas.on('mouse:move', this.onMouseMove)this.canvas.on('mouse:up', this.onMouseUp)this.canvas.on('object:added', this.onObjectAdded)},// 监听鼠标按下事件onMouseDown(opt) {if (this.isActiveEllipse) {this.downPoint = opt.absolutePointerthis.strokeColor = '#00FF64'this.canvasEllipse = new fabric.Ellipse({top: this.downPoint.y,left: this.downPoint.x,rx: 0,ry: 0,fill: 'transparent',stroke: this.strokeColor,strokeWidth: 2,selectable: false,})this.canvas.add(this.canvasEllipse)}},// 监听鼠标移动事件onMouseMove(opt) {if (this.isActiveEllipse && this.canvasEllipse) {let rx = Math.abs(this.downPoint.x - opt.absolutePointer.x) / 2let ry = Math.abs(this.downPoint.y - opt.absolutePointer.y) / 2let top = opt.absolutePointer.y > this.downPoint.y ? this.downPoint.y : this.downPoint.y - ry * 2let left = opt.absolutePointer.x > this.downPoint.x ? this.downPoint.x :  this.downPoint.x - rx * 2this.canvasEllipse.set('rx', rx)this.canvasEllipse.set('ry', ry)this.canvasEllipse.set('top', top)this.canvasEllipse.set('left', left)this.canvas.requestRenderAll()}},// 监听鼠标松开事件onMouseUp(opt) {if (this.isActiveEllipse) {if (JSON.stringify(this.downPoint) === JSON.stringify(opt.absolutePointer)) {this.canvas.remove(this.canvasEllipse)} else {if (this.canvasEllipse){this.canvasEllipse.set('stroke', this.strokeColor)}}this.canvasEllipse = null}},// 绘制完成元素事件onObjectAdded(opt) {let target = opt.targetif (target.stroke === '#00FF64') {this.isActiveEllipse && this.ellipseList.push(target)}},// 激活绘制椭圆形handleActiveEllipse() {this.isActiveEllipse = !this.isActiveEllipseif(this.isActiveEllipse) {this.canvas.selectionBorderColor = '#00FF64'}}}
}

二十三、自由绘制三角形

<template><div class="canvas-box" ref="canvasBox"><canvas id="canvas"></canvas><el-button @click="handleActiveTriangle">绘制三角形</el-button></div>
</template>
<script>
export default {name: 'PointerDetail',data () {return {canvas: null,canvasTriangle: null,downPoint: null,strokeColor: 'transparent', // 轮廓填充颜色isActiveTriangle: false, // 是否激活绘制三角形triangleList: [] // 绘制的三角形列表}},mounted() {this.init()},methods: {// 初始化init() {this.canvas = new fabric.Canvas('canvas', {width: this.$refs.canvasBox.offsetWidth,height: this.$refs.canvasBox.offsetHeight,backgroundColor: '#2E3136',selectionColor: 'transparent',selectionBorderColor: 'transparent',hoverCursor: 'default'})this.canvas.on('mouse:down', this.onMouseDown)this.canvas.on('mouse:move', this.onMouseMove)this.canvas.on('mouse:up', this.onMouseUp)this.canvas.on('object:added', this.onObjectAdded)},// 监听鼠标按下事件onMouseDown(opt) {if (this.isActiveTriangle) {this.downPoint = opt.absolutePointerthis.strokeColor = '#00FF64'this.canvasTriangle = new fabric.Triangle({top: this.downPoint.y,left: this.downPoint.x,width: 0,height: 0,fill: 'transparent',stroke: this.strokeColor,strokeWidth: 2,selectable: false,})this.canvas.add(this.canvasTriangle)}},// 监听鼠标移动事件onMouseMove(opt) {if (this.isActiveTriangle && this.canvasTriangle) {let width = Math.abs(this.downPoint.x - opt.absolutePointer.x)let height = Math.abs(this.downPoint.y - opt.absolutePointer.y)let top = opt.absolutePointer.y > this.downPoint.y ? this.downPoint.y : opt.absolutePointer.ylet left = opt.absolutePointer.x > this.downPoint.x ? this.downPoint.x : opt.absolutePointer.xthis.canvasTriangle.set('width', width)this.canvasTriangle.set('height', height)this.canvasTriangle.set('top', top)this.canvasTriangle.set('left', left)this.canvas.requestRenderAll()}},// 监听鼠标松开事件onMouseUp(opt) {if (this.isActiveTriangle) {if (JSON.stringify(this.downPoint) === JSON.stringify(opt.absolutePointer)) {this.canvas.remove(this.canvasTriangle)} else {if (this.canvasTriangle){this.canvasTriangle.set('stroke', this.strokeColor)}}this.canvasTriangle = null}},// 绘制完成元素事件onObjectAdded(opt) {let target = opt.targetif (target.stroke === '#00FF64') {this.isActiveTriangle && this.triangleList.push(target)}},// 激活绘制矩形handleActiveTriangle() {this.isActiveTriangle = !this.isActiveTriangleif(this.isActiveTriangle) {this.canvas.selectionBorderColor = '#00FF64'}}}
}

二十四、自由绘制多边形

<template><div class="canvas-box" ref="canvasBox"><canvas id="canvas"></canvas><el-button @click="handleActivePolygon">绘制多边形</el-button></div>
</template>
<script>
export default {name: 'PointerDetail',data () {return {canvas: null,canvasPolygon: null,strokeColor: 'transparent', // 轮廓填充颜色isActivePolygon: false, // 是否激活绘制多边形polygonList: [] // 绘制的多边形列表}},mounted() {this.init()},methods: {// 初始化init() {this.canvas = new fabric.Canvas('canvas', {width: this.$refs.canvasBox.offsetWidth,height: this.$refs.canvasBox.offsetHeight,backgroundColor: '#2E3136',selectionColor: 'transparent',selectionBorderColor: 'transparent',hoverCursor: 'default'})this.canvas.on('mouse:down', this.onMouseDown)this.canvas.on('mouse:move', this.onMouseMove)this.canvas.on('mouse:dblclick', this.onDblclick)this.canvas.on('object:added', this.onObjectAdded)},// 监听鼠标按下事件onMouseDown(opt) {if (this.isActivePolygon) {this.strokeColor = '#00FF64'if (this.canvasPolygon === null) {this.createPolygon(opt)} else {this.changeCurrentPolygon(opt)}}},// 监听鼠标移动事件onMouseMove(opt) {if (this.isActivePolygon && this.canvasPolygon) {this.changePolygonBelt(opt)}},// 鼠标双击事件onDblclick(opt) {this.finishPolygon(opt)},// 绘制完成元素事件onObjectAdded(opt) {let target = opt.targetif (target.stroke === '#00FF64') {this.isActivePolygon && this.polygonList.push(target)}},// 创建多边形createPolygon(opt) {this.canvasPolygon = new fabric.Polygon([{ x: opt.absolutePointer.x, y: opt.absolutePointer.y },{ x: opt.absolutePointer.x, y: opt.absolutePointer.y }], {fill: 'transparent',stroke: this.strokeColor,objectCaching: false})this.canvas.add(this.canvasPolygon)},// 修改当前正在创建的多边形changeCurrentPolygon(opt) {let points = this.canvasPolygon.pointspoints.push({x: opt.absolutePointer.x,y: opt.absolutePointer.y})this.canvas.requestRenderAll()},// 多边形橡皮带changePolygonBelt(opt) {let points = this.canvasPolygon.pointspoints[points.length - 1].x = opt.absolutePointer.xpoints[points.length - 1].y = opt.absolutePointer.ythis.canvas.requestRenderAll()},// 完成多边形绘制finishPolygon(opt) {let points = this.canvasPolygon.pointspoints[points.length - 1].x = opt.absolutePointer.xpoints[points.length - 1].y = opt.absolutePointer.ypoints.pop()points.pop()this.canvas.remove(this.canvasPolygon)if (points.length > 2) {let polygon = new fabric.Polygon(points, {stroke: this.strokeColor,fill: 'transparent',selectable: false})this.canvas.add(polygon)} else {this.$message.warning('标记框小于最小标定像素!')}this.canvasPolygon = nullthis.canvas.requestRenderAll()this.strokeColor = 'transparent'},// 激活绘制多边形handleActivePolygon() {this.isActivePolygon = !this.isActivePolygonif(this.isActivePolygon) {this.canvas.selectionBorderColor = '#00FF64'}}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/75428.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Collections工具类(java)

文章目录 7.1 常用方法 参考操作数组的工具类&#xff1a;Arrays&#xff0c;Collections 是一个操作 Set、List 和 Map 等集合的工具类。 7.1 常用方法 Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作&#xff0c;还提供了对集合对象设置不可变、…

C#控制台程序+Window增加右键菜单

有时候我们可能会想定制一些自己的右键菜单功能&#xff0c;帮我们减少重复的操作。那么使用控制台程序加自定义右键菜单&#xff0c;就可以很好地满足我们的需求。 1 编写控制台程序 因为我只用到了在文件夹中空白处的右键菜单&#xff0c;所以这里提供了一个对应的模板&…

计算机视觉实验:图像处理综合-路沿检测

目录 实验步骤与过程 1. 路沿检测方法设计 2. 路沿检测方法实现 2.1 视频图像提取 2.2 图像预处理 2.3 兴趣区域提取 2.4 边缘检测 ​​​​​​​2.5 Hough变换 ​​​​​​​2.6 线条过滤与图像输出 3. 路沿检测结果展示 4. 其他路沿检测方法 实验结论或体会 实…

推荐一款非常简单实用的数据库连接工具Navicat Premium

Navicat Premium是一款非常实用的数据库连接工具&#xff0c;别再用HeidiSQL和idea自带的数据库连接了&#xff0c;看完这篇文章&#xff0c;赶紧把Navicat Premium用起来吧。 首先&#xff0c;需要获取Navicat Premium的安装包&#xff0c;可以通过以下网盘链接下载&#xff0…

计算机二级Python基本操作题-序号45

1. 键盘输入一组水果名称并以空格分隔&#xff0c;共一行。 示例格式如下&#xff1a; 苹果 芒果 草莓 芒果 苹果 草莓 芒果 香蕉 芒果 草莓 统计各类型的数量&#xff0c;从数量多到少的顺序输出类型及对应数量&#xff0c;以英文冒号分隔&#xff0c;每个类型行。输出结果保存…

复亚智能打造全新云平台:让无人机任务管理更智能、更简单

复亚智能全新升级的MindView云平台&#xff0c;对航线规划、任务管理、自动飞行、数据管理等各个环节开展可视化、数字化、智能化监管&#xff0c;从任务到结果的“看得清”、“管得住”、“查得准”&#xff0c;带来更轻松的操作&#xff0c;改善作业效率、安全保障和用户体验…

使用Gunicorn+Nginx部署Flask项目

部署-开发机上的准备工作 确认项目没有bug。用pip freeze > requirements.txt将当前环境的包导出到requirements.txt文件中&#xff0c;方便部署的时候安装。将项目上传到服务器上的/srv目录下。这里以git为例。使用git比其他上传方式&#xff08;比如使用pycharm&#xff…

网络安全进阶学习第九课——SQL注入介绍

文章目录 一、什么是注入二、什么是SQL注入三、SQL注入产生的原因四、SQL注入的危害五、SQL注入在渗透中的利用1、绕过登录验证&#xff1a;使用万能密码登录网站后台等。2、获取敏感数据3、文件系统操作4、注册表操作5、执行系统命令 六、如何挖掘SQL注入1、SQL注入漏洞分类按…

微信小程序wx.getlocation接口权限申请总结

先附上申请通过截图 插播内容&#xff1a;可代开通&#xff0c;保证通过。wx.getLocation接口&#xff08;获取当前的地址位置&#xff09; qq&#xff1a; 308205428 如何申请 当申请微信小程序的wx.getLocation接口权限时&#xff0c;你可以…

8.4 day05软件学习

文章目录 微服务的概念微服务的原则微服务的特征&#xff1a;集群介绍 spring aop 在家学习效率真不高&#xff0c;下午好兄弟喊出去玩&#xff0c;一直到晚上才回来&#xff0c;赶紧总结一下早上学习的内容。 继续看java基础进阶的思想&#xff0c;之前学的很多都忘了。 微服…

SQL基础复习与进阶

SQL进阶 文章目录 SQL进阶关键字复习ALLANYEXISTS 内置函数ROUND&#xff08;四舍五入&#xff09;TRUNCATE&#xff08;截断函数&#xff09;SEILING&#xff08;向上取整&#xff09;FLOOR&#xff08;向下取整&#xff09;ABS&#xff08;获取绝对值&#xff09;RAND&#x…

谷歌联合CMU提出全新语义金字塔概念,无需额外训练使LLMs学会执行视觉任务

​ 论文链接&#xff1a;https://arxiv.org/abs/2306.17842 代码仓库&#xff1a;https://github.com/google-research/magvit/ 在目前的大模型社区中&#xff0c;发展较为成熟的当属以ChatGPT为代表的纯语言模型&#xff08;LLMs&#xff09;&#xff0c;以GPT-4为代表的多模态…

iOS——Block one

块类似于匿名函数或闭包&#xff0c;在许多其他编程语言中也存在类似的概念。 可以访问上下文&#xff0c;运行效率高 Block 以下是块的一些基本知识&#xff1a; 块的定义&#xff1a;块是由一对花括号 {} 包围的代码片段&#xff0c;可以包含一段可执行的代码。块的定义使…

银河麒麟v10 vnc环境配置

方法一、启用自带远程桌面 银河麒麟默认已经自带远程桌面&#xff0c;如下图。此时即可用Realvnc Viewer访问该终端&#xff0c;仔细查看后自带的远程桌面是开源组件gnome-remote-desktopGNOME / gnome-remote-desktop GitLabhttps://gitlab.gnome.org/GNOME/gnome-remote-de…

13个ChatGPT类实用AI工具汇总

在ChatGPT爆火后&#xff0c;各种工具如同雨后春笋一般层出不穷。以下汇总了13种ChatGPT类实用工具&#xff0c;可以帮助学习、教学和科研。 01 / ChatGPT for google/ 一个浏览器插件&#xff0c;可搭配现有的搜索引擎来使用 最大化搜索效率&#xff0c;对搜索体验的提升相…

Kindling the Darkness: A Practical Low-light Image Enhancer论文阅读笔记

这是ACMMM2019的一篇有监督暗图增强的论文&#xff0c;KinD其网络结构如下图所示&#xff1a; 首先是一个分解网络分解出R和L分量&#xff0c;然后有Restoration-Net和Adjustment-Net分别去对R分量和L分量进一步处理&#xff0c;最终将处理好的R分量和L分量融合回去。这倒是很常…

【机器学习】Gradient Descent for Logistic Regression

Gradient Descent for Logistic Regression 1. 数据集&#xff08;多变量&#xff09;2. 逻辑梯度下降3. 梯度下降的实现及代码描述3.1 计算梯度3.2 梯度下降 4. 数据集&#xff08;单变量&#xff09;附录 导入所需的库 import copy, math import numpy as np %matplotlib wi…

备战秋招 | 笔试强训19

目录 一、选择题 二、编程题 三、选择题题解 四、编程题题解 一、选择题 1、二分查找的时间复杂度&#xff08;&#xff09; A. O(N*log(N)) B. O(N) C. O(log(N)) D. O(N^2) 2、有一个单向链表中有一个A、B两个相邻元素&#xff0c;有一个指针p指向元素A&#xff0c;现将…

谷歌云 | 电子商务 | 如何更好地管理客户身份以支持最佳的用户体验

【本文由Cloud Ace整理发布。Cloud Ace是谷歌云全球战略合作伙伴&#xff0c;拥有 300 多名工程师&#xff0c;也是谷歌最高级别合作伙伴&#xff0c;多次获得 Google Cloud 合作伙伴奖。作为谷歌托管服务商&#xff0c;我们提供谷歌云、谷歌地图、谷歌办公套件、谷歌云认证培训…

台式机/工控机通过网线共享笔记本电脑无线网络linux系统下 usb网卡的驱动安装

一、台式机/工控机通过网线共享笔记本电脑无线网络 1、 将台式机通过网线和笔记本连接。 2、 将笔记本的“本地连接”和“无线网络连接”的ipv4均设置为自动获取。 4.修改台式机的IP地址为如下&#xff08;对应笔记本信息&#xff09; IP地址为192.168.XXX.12 子网掩码为255.2…