上传的图片可以进行拖拽调整大小和方向
目录
0. 前言
1. 安装fabric与引入
2. fabric组件的使用
3. 属性相关设置
4. 初始化加载
4. 方法
5. 全代码
利用fabric组件,实现图片上传、图片”裁剪“、自定义的区域标记一系列操作
先放一张效果图吧
npm i fabric -S
我用的是全局引入方式,视情况调整
import fabric from 'fabric';
Vue.use(fabric);
先放一个fabric.js API地址☞Api | Fabric中文文档 (gitee.io)
定义容器id=canvas,注意宽高
图片上传
清除
{{ item }}
上传的图片可以进行拖拽调整大小和方向
累了,不想写了
data() {
return {
bgImgFlag: true,
bgImgSrc: '',
imgFile: {},
width: 800,
height: 400,
alarmLevel: ['一级风险', '二级风险', '三级风险', '四级风险'],
colorGrounp: ['rgba(51, 164, 255, 1)', 'rgba(255, 200, 89, 1)', 'rgba(255, 160, 89, 1)', 'rgba(196,43, 1, 1)'],
colorGrounpFill: ['rgba(51, 164, 255, 0.3)', 'rgba(255, 200, 89, 0.3)', 'rgba(255, 160, 89, 0.3)', 'rgba(196,43, 1, 0.3)'],
canvas: {},
mouseFrom: {},
mouseTo: {},
drawType: '', // 当前绘制图像ROI
drawWidth: 2, // 笔触宽度
drawingObject: null, // 当前绘制对象
moveCount: 1, // 绘制移动计数器
doDrawing: false, // 绘制状态
// polygon 相关参数
polygonMode: false,
pointArray: [],
lineArray: [],
savePointsGroup: [],
activeShape: false,
activeLine: '',
line: {},
deleteIconURL: require('@/assets/screen/icon-close.png') // 区域标记取消的x号图标
};
},
this.canvas = new fabric.Canvas('canvas', {
skipTargetFind: false, // 当为真时,跳过目标检测。目标检测将返回始终未定义。点击选择将无效
selectable: false, // 为false时,不能选择对象进行修改
selection: false // 是否可以多个对象为一组
});
this.canvas.selectionColor = 'rgba(0,0,0,0.05)';
this.canvas.on('mouse:down', this.mousedown);
this.canvas.on('mouse:move', this.mousemove);
document.onkeydown = e => {
// 键盘 delect删除所选元素
if (e.keyCode == 46) {
this.deleteObj();
}
// ctrl+z 删除最近添加的元素
if (e.keyCode == 90 && e.ctrlKey) {
this.canvas.remove(
this.canvas.getObjects()[this.canvas.getObjects().length - 1]
);
}
};
this.$nextTick(() => {
this.loadDraw(); // 回显之前标注过的内容,底图和区域标记内容
});
methods: {
// 保存当前画布为png图片
save() {
var canvas = document.getElementById('canvas');
var imgData = canvas.toDataURL('png');
imgData = imgData.replace('image/png', 'image/octet-stream');
// 下载后的问题名,可自由指定
var filename = 'drawingboard_' + (new Date()).getTime() + '.' + 'png';
this.saveFile(imgData, filename);
},
saveFile(data, filename) {
var save_link = document.createElement('a');
save_link.href = data;
save_link.download = filename;
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
save_link.dispatchEvent(event);
},
// 提交绘制内容
submitDraw() {
const params = {
pointInfo: [],
imgInfo: '',
img: ''
};
this.canvas.toJSON(['alarmLevel', 'isBgImg']).objects.forEach(item => {
const element = {
alarmLevel: item.alarmLevel,
pointInfo: ''
};
if (item?.points) {
element.pointInfo = item.points;
params.pointInfo.push(element);
}
if (item?.isBgImg) {
params.imgInfo = item;
params.img = item.src;
delete params.imgInfo.src;
}
});
this.$emit('saveDraw', params);
},
// 清除画布
clean() {
this.$confirm('是否清除图片和标记?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.canvas.clear();
this.bgImgSrc = '';
this.bgImgFlag = true;
});
},
// 从已渲染的DOM元素加载图片至canvas
loadExpImg() {
const imgElement = document.getElementById('expImg');
imgElement.onload = () => {
// eslint-disable-next-line new-cap
new fabric.Image.fromURL(
imgElement.src,
img => {
img.scale(0.3);
img.set({
originX: 'center',
originY: 'center'
}, { crossOrigin: 'anonymous' });
img.on('scaling', e => { // 拉伸事件
const h = img.scaleY;
const w = img.scaleX;
if (h !== w || w == h) { // 判断缩放值相等或不相等后执行图片等比缩放
if (e.e.movementY == -1 || e.e.movementY == 1) {
img.scale(h);// 缩放
} else {
img.scale(w);
}
}
});
img.setCoords();
img.centeredScaling = true;
img.centerTransform = true;
this.canvas.add(img);
this.canvas.centerObject(img);
this.canvas.renderAll();
}, {
selectable: true,
hasControls: true,
centeredScaling: false,
zIndex: -99,
isBgImg: true
});
};
},
// 上传确认
uploadImgConfirm() {
if (this.bgImgSrc !== '') {
this.$confirm('是否重新上传标记?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.canvas.clear();
this.bgImgFlag = true;
document.getElementById('imgInput').click();
});
} else {
document.getElementById('imgInput').click();
}
},
// 从文件加载图片至canvas
uploadImgChange() {
// 获取文件
var eleImportInput = document.getElementById('imgInput');
this.imgFile = eleImportInput.files[0];
var imgTitle = '';
// 从reader中获取选择文件的src
if (/\.(jpe?g|png|gif)$/i.test(this.imgFile.name)) {
var reader = new FileReader();
var _this = this;
reader.addEventListener(
'load',
function() {
imgTitle = _this.imgFile.name;
_this.bgImgSrc = this.result;
},
false
);
reader.readAsDataURL(this.imgFile);
}
this.loadExpImg();
},
// 鼠标按下时触发
mousedown(e) {
if (undefined === e) return;
// 记录鼠标按下时的坐标
var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);
this.mouseFrom.x = xy.x;
this.mouseFrom.y = xy.y;
this.doDrawing = true;
this.canvas.skipTargetFind = false;
try {
// 此段为判断是否闭合多边形,点击红点时闭合多边形
if (this.pointArray.length > 1) {
// e.target.id == this.pointArray[0].id 表示点击了初始红点
if (e.target && e.target.id == this.pointArray[0].id) {
this.generatePolygon();
return;
}
}
// 未点击红点则继续作画
if (this.polygonMode && this.pointArray.length < 4) {
this.addPoint(e);
} else if (this.polygonMode && this.pointArray.length > 0) {
this.$message.warning('最多设置四个点');
}
} catch (error) {
console.log(error);
}
},
// 鼠标松开执行
mouseup(e) {
if (undefined === e) return;
var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);
this.mouseTo.x = xy.x;
this.mouseTo.y = xy.y;
this.drawingObject = null;
this.moveCount = 1;
},
// 鼠标移动过程中已经完成了绘制
mousemove(e) {
if (undefined === e) return;
if (this.moveCount % 2 && !this.doDrawing) {
// 减少绘制频率
return;
}
this.moveCount++;
var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);
this.mouseTo.x = xy.x;
this.mouseTo.y = xy.y;
if (this.activeLine && this.activeLine.class == 'line') {
var pointer = this.canvas.getPointer(e.e);
this.activeLine.set({
x2: pointer.x,
y2: pointer.y
});
var points = this.activeShape.get('points');
points[this.pointArray.length] = {
x: pointer.x,
y: pointer.y,
zIndex: 1
};
this.activeShape.set({
points: points
});
this.canvas.renderAll();
}
this.canvas.renderAll();
},
deleteObj() {
this.canvas.getActiveObjects().map(item => {
this.canvas.remove(item);
});
},
transformMouse(mouseX, mouseY) {
return {
x: mouseX / 1,
y: mouseY / 1
};
},
// 绘制多边形开始
drawPolygon(data) {
if (this.bgImgFlag) {
this.canvas.getObjects().forEach(obj => {
if (obj.isBgImg) {
obj.hasControls = false;
obj.selectable = false;
obj.evented = false;
}
});
this.bgImgFlag = false;
this.canvas.renderAll();
}
this.drawType = data;
this.polygonMode = true;
this.pointArray = []; // 顶点集合
this.lineArray = []; // 线集合
this.canvas.isDrawingMode = false;
},
addPoint(e) {
var random = Math.floor(Math.random() * 10000);
var id = new Date().getTime() + random;
var circle = new fabric.Circle({
radius: 5,
fill: '#ffffff',
stroke: '#333333',
strokeWidth: 0.5,
left: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
top: (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
selectable: false,
hasBorders: false,
hasControls: false,
originX: 'center',
originY: 'center',
id: id,
objectCaching: false
});
if (this.pointArray.length == 0) {
circle.set({
fill: this.colorGrounp[this.drawType]
});
}
var points = [
(e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
(e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
(e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
(e.pointer.y || e.e.layerY) / this.canvas.getZoom()
];
this.line = new fabric.Line(points, {
strokeWidth: 2,
fill: this.colorGrounp[this.drawType],
stroke: this.colorGrounp[this.drawType],
class: 'line',
originX: 'center',
originY: 'center',
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false
});
if (this.activeShape) {
var pos = this.canvas.getPointer(e.e);
var points = this.activeShape.get('points');
points.push({
x: pos.x,
y: pos.y
});
var polygon = new fabric.Polygon(points, {
stroke: '#333333',
strokeWidth: 1,
fill: this.colorGrounpFill[this.drawType],
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false
});
this.canvas.remove(this.activeShape);
this.canvas.add(polygon);
this.activeShape = polygon;
this.canvas.renderAll();
} else {
var polyPoint = [
{
x: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
y: (e.pointer.y || e.e.layerY) / this.canvas.getZoom()
}
];
var polygon = new fabric.Polygon(polyPoint, {
stroke: '#333333',
strokeWidth: 1,
fill: '#cccccc',
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false
});
this.activeShape = polygon;
this.canvas.add(polygon);
}
this.activeLine = this.line;
this.pointArray.push(circle);
this.lineArray.push(this.line);
this.canvas.add(this.line);
this.canvas.add(circle);
},
generatePolygon() {
var points = [];
this.pointArray.map((point, index) => {
points.push({
x: point.left,
y: point.top
});
this.canvas.remove(point);
});
this.lineArray.map((line, index) => {
this.canvas.remove(line);
});
this.canvas.remove(this.activeShape).remove(this.activeLine);
var polygon = new fabric.Polygon(points, {
stroke: this.colorGrounp[this.drawType],
strokeWidth: this.drawWidth,
fill: this.colorGrounpFill[this.drawType],
opacity: 1,
selectable: false,
hasBorders: false,
hasControls: false,
alarmLevel: this.drawType
});
let max = 0;
for (let i = 1; i < this.canvas._objects.length; i++) {
if (this.canvas._objects[i].index > max) max = this.canvas._objects[i].index;
}
polygon.index = max + 1;
this.canvas.add(polygon);
this.activeLine = null;
this.activeShape = null;
this.polygonMode = false;
this.doDrawing = false;
// this.drawType = null;
fabric.Image.fromURL(this.deleteIconURL, this.deletecallback);
},
// 从画布中删除当前选中的对象
deleteObject() {
const activeObject = this.canvas.getActiveObject();
if (activeObject) {
this.canvas._objects.forEach(item => {
if (item.index === activeObject.index) {
this.canvas.remove(item);
}
});
this.canvas.remove(activeObject);
this.canvas.renderAll();
}
},
// 渲染删除按钮
async deletecallback(img) {
const self = this;
let max = 0;
for (let i = 1; i < this.canvas._objects.length; i++) {
if (this.canvas._objects[i].index > max) max = this.canvas._objects[i].index;
}
img.index = max;
const oImg = await img.set({
left: this.pointArray[0].left - 20,
top: this.pointArray[0].top - 20,
width: 40,
height: 40,
angle: 0
}).scale(0.8);
this.canvas.add(oImg).renderAll();
this.canvas.setActiveObject(oImg);
oImg.on('mousedown', function() {
self.deleteObject();
});
},
// 回显详情信息
loadDraw() {
const self = this;
if (self.drawinfo.id === '') return;
const pointGroup = JSON.parse(self.drawinfo.pointInfo);
const imgInfo = JSON.parse(self.drawinfo.imgInfo);
self.bgImgSrc = self.drawinfo.img;
imgInfo.src = self.drawinfo.img;
// 1、加载底图
fabric.util.enlivenObjects([imgInfo], objects => {
objects.forEach(o => {
o.selectable = false;
o.hasControls = false;
o.centeredScaling = false;
this.canvas.add(o);
});
// 2、处理多边形绘制回显操作
pointGroup.forEach(async (item, index) => {
if (item.pointInfo !== '') {
const polygon = new fabric.Polygon(item.pointInfo, {
stroke: self.colorGrounp[item.alarmLevel],
strokeWidth: self.drawWidth,
fill: self.colorGrounpFill[item.alarmLevel],
opacity: 1,
selectable: false,
hasBorders: false,
hasControls: false,
alarmLevel: item.alarmLevel
});
polygon.index = index;
self.canvas.add(polygon);
self.activeLine = null;
self.activeShape = null;
self.polygonMode = false;
self.doDrawing = false;
if (!self.readstate) {
fabric.Image.fromURL(self.deleteIconURL, async img => {
const _self = this;
img.index = index;
const oImg = await img.set({
left: item.pointInfo[0].x - 20,
top: item.pointInfo[0].y - 20,
width: 40,
height: 40,
angle: 0
}).scale(0.8);
this.canvas.add(oImg);
oImg.on('mousedown', function() {
_self.deleteObject();
});
});
}
}
});
});
self.canvas.renderAll();
}
}
累了累了,开始摆烂,以后再调整,直接放全代码吧
图片上传
清除
{{ item }}
上传的图片可以进行拖拽调整大小和方向