赞
踩
目录
演示Demo:前端可视化demo
Demo源码:https://gitee.com/k21vin/front-end-data-visualization
本文章所有的gif图片由于录屏软件问题,上面的鼠标位置都是错位显示的!!
Fabric.js是一个对canvas进行封装的Javascript库,在原生canvas之上提供了交互式对象模型,通过简洁的api就可以在画布上进行丰富的操作。
它主要的功能包括在canvas上创建和填充图形,比如矩形、圆形、多边形;生成的图像自带缩放、旋转、拖拽等功能;还可以给图形填充渐变颜色;各个图形可以相互组合等等。
npm i fabric --save
- // main.js
- import fabric from "fabric"
- Vue.use(fabric)
- <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数据
- mouse: down // 鼠标按下事件
- mouse: move // 鼠标移动事件
- mouse: up // 鼠标移动事件
- mouse: over // 鼠标移入事件
- mouse: out // 鼠标移出事件
- mouse: dblclick // 鼠标双击事件
- object: added // 对象被添加事件
- object: removed // 对象被删除事件
- object: modified // 对象被修改事件
- object: rotating // 对象被旋转事件
- object: scaling // 对象被缩放事件
- object: moving // 对象被移动事件
- canvas.on('mouse: wheel', (opt) => {
- console.log(opt)
- })
canvas.off('mouse: wheel')
- 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轴方向)
- 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列表的索引!!!!!
- 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) // 移动到指定层
- handleCopy() {
- if (!canvas.getActiveObject()) {
- this.$message.warning('请先选择元素')
- return
- }
- this.canvas.getActiveObject().clone(cloned => {
- this.cloneObjects = cloned
- })
- }
- 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 += 10
- this.cloneObjects.left += 10
- this.canvas.setActiveObject(cloned)
- this.canvas.requestRenderAll()
- })
- }
Fabric对象可以添加一些属性进行锁定,例如静止水平移动、静止垂直移动,静止缩放等等
-
- let rect = new fabric.Rect({
- width: 100,
- height: 50,
- fill: '#ffde7d',
- top: 20,
- left: 20
- })
- rect.lockMovementX = true
- canvas.add(rect)
-
- let rect = new fabric.Rect({
- width: 100,
- height: 50,
- fill: '#ffde7d',
- top: 20,
- left: 20
- })
- rect.lockMovementY = true
-
- let rect = new fabric.Rect({
- width: 100,
- height: 50,
- fill: '#ff9a3c',
- top: 60,
- left: 160
- })
- rect.lockRotation = true
-
- let rect = new fabric.Rect({
- width: 100,
- height: 50,
- fill: '#ffde7d',
- top: 20,
- left: 20
- })
- rect.lockScalingX = true
-
- let rect = new fabric.Rect({
- width: 100,
- height: 50,
- fill: '#f95959',
- top: 20,
- left: 20
- })
- rect.lockScalingY = true
-
- 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、easeOutExpo
- onChange: 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支持在所有对象上设置填充或描边属性的渐变,首先创建渐变,然后将其分配给填充或描边。
-
- // 圆
- let circle = new fabric.Circle({
- left: 100,
- top: 100,
- radius: 50,
- })
- let gradient = new fabric.Gradient({
- type: 'linear', // linear or radial
- gradientUnits: '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)
-
- 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)
-
- <script>
- export default {
- data() {
- return {
- lastPosX: 0, // 上次鼠标位置X坐标
- lastPosY: 0, // 上次鼠标位置Y坐标
- isDragging: false, // 是否可以拖拽画布
- }
- },
- mounted() {
- ... // 初始化canvas
- this.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.clientX
- this.lastPosY = opt.e.clientY
- this.isDragging = true
- },
- // 监听鼠标移动事件
- onMouseMove(opt) {
- if (this.isDragging) {
- this.canvas.viewportTransform[4] += opt.e.clientX - this.lastPosX
- this.canvas.viewportTransform[5] += opt.e.clientY - this.lastPosY
- this.canvas.requestRenderAll()
- this.lastPosX = opt.e.clientX
- this.lastPosY = opt.e.clientY
- }
- },
- // 监听鼠标松开事件
- onMouseUp(opt) {
- if (this.isDragging) {
- this.canvas.setViewportTransform(this.canvas.viewportTransform)
- this.isDragging = false
- }
- }
- }
- }
- </script>
- <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 ** delta
- zoom = 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>
-
- this.canvas.on('mouse:wheel', this.onMouseWheel)
-
- // 监听鼠标放大缩小事件
- onMouseWheel(opt) {
- let delta = opt.e.deltaY // 滚轮,向上滚一下是 -100,向下滚一下是 100
- let zoom = this.canvas.getZoom() // 获取画布当前缩放值
- zoom *= 0.999 ** delta
- zoom = 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的数字为3
- stopContextMenu: 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 = true
- this.$nextTick(() => {
- this.delMenuStyle = this.getMenuStyle(this.$refs.delMenu, opt)
- })
- } else {
- // 否则隐藏删除弹窗
- this.hiddenMenu()
- }
- // 按下鼠标左键
- } else {
- this.hiddenMenu()
- }
- },
- // 获取弹窗坐标
- getMenuStyle(ele, opt) {
- let menuWidth = ele.offsetWidth
- let menuHeight = ele.offsetHeight
- let pointX = opt.pointer.x + 2
- let pointY = opt.pointer.y + 2
- if (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 = null
- this.isShowDelMenu = false
- },
- // 删除选中元素
- handleDeleteMenu() {
- this.canvas.remove(this.activeEle)
- this.canvas.requestRenderAll()
- this.hiddenMenu()
- }
- }
- }
-
- 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'
- })
-
- canvas.isDrawingMode = false
-
- <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)
- }
-
- 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上键入文本
-
- 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 或 斜体 italic
- charSpacing: 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, // 能否被选中,默认true
- shadow: 'rgba(0, 0, 0, 0.5) 5px 5px 5px', // 投影
- })
- canvas.add(text)
-
- // 下划线
- 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)
-
- let IText = new fabric.IText('雷猴啊,双击打几个字试下~', {
- fontFamily: 'Comic Sans'
- })
- canvas.add(IText)
-
- let line = new fabric.Line([0, 100, 100, 100], {
- fill: 'green', // 填充色
- stroke: 'green', // 笔触颜色
- strokeWidth: 2, // 笔触宽度
- });
- canvas.add(line);
在绘制直线的基础上添加属性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);
-
- let path = new fabric.Path('M 0 0 L 200 100 L 170 200 z')
- path.set({
- top: 120, // 距离容器顶部距离 120px
- left: 120, // 距离容器左侧距离 120px
- fill: '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 || null
- this.strokeColor = '#00FF64'
- }
- },
- // 监听鼠标松开事件
- onMouseUp(opt) {
- if (this.isActiveRect) {
- this.drawRect(opt.absolutePointer)
- }
- },
- // 绘制完成元素事件
- onObjectAdded(opt) {
- let target = opt.target
- if (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 = null
- this.strokeColor = 'transparent'
- },
- // 激活绘制矩形
- handleActiveRect() {
- this.isActiveRect = !this.isActiveRect
- if(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.absolutePointer
- this.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)) / 2
- let top = opt.absolutePointer.y > this.downPoint.y ? this.downPoint.y : this.downPoint.y - radius * 2
- let left = opt.absolutePointer.x > this.downPoint.x ? this.downPoint.x : this.downPoint.x - radius * 2
- this.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.target
- if (target.stroke === '#00FF64') {
- this.isActiveCircle && this.circleList.push(target)
- }
- },
- // 激活绘制圆形
- handleActiveCircle() {
- this.isActiveCircle = !this.isActiveCircle
- if(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.absolutePointer
- this.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) / 2
- let ry = Math.abs(this.downPoint.y - opt.absolutePointer.y) / 2
- let top = opt.absolutePointer.y > this.downPoint.y ? this.downPoint.y : this.downPoint.y - ry * 2
- let left = opt.absolutePointer.x > this.downPoint.x ? this.downPoint.x : this.downPoint.x - rx * 2
- this.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.target
- if (target.stroke === '#00FF64') {
- this.isActiveEllipse && this.ellipseList.push(target)
- }
- },
- // 激活绘制椭圆形
- handleActiveEllipse() {
- this.isActiveEllipse = !this.isActiveEllipse
- if(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.absolutePointer
- this.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.y
- let left = opt.absolutePointer.x > this.downPoint.x ? this.downPoint.x : opt.absolutePointer.x
- this.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.target
- if (target.stroke === '#00FF64') {
- this.isActiveTriangle && this.triangleList.push(target)
- }
- },
- // 激活绘制矩形
- handleActiveTriangle() {
- this.isActiveTriangle = !this.isActiveTriangle
- if(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.target
- if (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.points
- points.push({
- x: opt.absolutePointer.x,
- y: opt.absolutePointer.y
- })
- this.canvas.requestRenderAll()
- },
- // 多边形橡皮带
- changePolygonBelt(opt) {
- let points = this.canvasPolygon.points
- points[points.length - 1].x = opt.absolutePointer.x
- points[points.length - 1].y = opt.absolutePointer.y
- this.canvas.requestRenderAll()
- },
- // 完成多边形绘制
- finishPolygon(opt) {
- let points = this.canvasPolygon.points
- points[points.length - 1].x = opt.absolutePointer.x
- points[points.length - 1].y = opt.absolutePointer.y
- points.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 = null
- this.canvas.requestRenderAll()
- this.strokeColor = 'transparent'
- },
- // 激活绘制多边形
- handleActivePolygon() {
- this.isActivePolygon = !this.isActivePolygon
- if(this.isActivePolygon) {
- this.canvas.selectionBorderColor = '#00FF64'
- }
- }
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。