代码拉取完成,页面将自动刷新
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
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)
}
onRect () {
const rect = new fabric.Rect({
left: 10,
top: 10,
fill: 'red',
width: 50,
height: 50
})
canvasId.add(rect)
}
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)
}
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)
}
普通文本
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)
}
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))
mounted () {
canvasId = new fabric.Canvas('canvas')
canvasId.on('mouse:up', function (options) {
console.log(options)
})
}
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()
// 上移 bringForward
onForward () {
const obj = canvasId.getActiveObject() // 获取当前激活的Object
canvasId.bringForward(obj) // 或 obj.bringForward()
canvasId.renderAll()
}
preserveObjectStacking,参数:
new fabric.Canvas('canvasId', { preserveObjectStacking: true })
onMouseDown () {
const obj = canvasId.getActiveObject()
if (obj) {
obj.bringToFront()
}
}
/**
* 将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() 都可以有一个参数
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()
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)
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)
}
}
/**
* 处理快捷方位的坐标
* @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 = '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()
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。