vue中使用canvas画图,vue 图片标注
这篇文章主要介绍了某视频剪辑软件下如何利用帆布实现在线图片标注,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
目录
组件代码如下在开发过程中遇到的问题网端实现在线图片标注在此做下记录,功能类似微信截图时的标注,包含画线、框、箭头和文字输入,思路是利用帆布画布,先把要标注的图片使用绘图图像方法画在画布上,然后定义画线、框、箭头和文字输入的方法调用
组件代码如下
模板
div class=" draw "
div class= drawTop ref= drawTop v-if= line step==lineNum
差异
埃尔按钮类型@click=resetAll 清空/el-button
埃尔按钮类型@ click=废除撤销/el-button
埃尔按钮类型@click=canvasRedo 恢复/el-button
埃尔按钮类型@click=下载下载/el-button
/div
div style=宽度:22%
选择绘制类型:
El-radio-group v-model= type size= medium
埃尔单选按钮
typeOption中的v-for=(项目,索引)
:key=index
:label=item.value
@点击。native=单选按钮单击(项目。值)’
{{item.label}}
/El-单选按钮
/el-radio-group
/div
div style=宽度:15%
边框粗细:
El滑块v-model= line width :min= 0 :max= 10 :step= 1 style= width:70% /El滑块
/div
差异
线条颜色:
埃尔颜色选择器v-model=strokeStyle/el颜色选择器
/div
差异
文字颜色:
埃尔-颜色选择器v-model= font color /El-颜色选择器
/div
div style=宽度:15%
文字大小:
El-slider v-model= font size :min= 14 :max= 36 :step= 2 style= width:70% /El-slider
/div
/div
div style= height:100%;宽度:100%;位置:相对;
div class=content/div
输入 v-show= is show type= text @ blur= txt blue ref= txt id= txt
style= z-index:9999;位置:绝对;边框:0;背景:无;大纲:无;/
/div
/div
/模板
脚本
导出默认值{
名称:标注,
道具:{
imgPath:未定义,
},
data() {
返回{
isShow:假,
画布: ,
ctx: ,
ctxX: 0,
ctxY: 0,
线宽:1,
类型: L ,
typeOption: [
{标签: 线,值: L},
{标签: 矩形,值: R},
{标签: 箭头,值: A},
{标签: 文字,值: T},
],
canvasHistory: [],
步骤:0,
加载:假,
填充样式:“# CB 0707”,
strokeStyle: #CB0707 ,
lineNum: 2,
线峰值:[],
线步:2,
埃利普瑟:0.5,
dialogVisible: false,
isUnfold:没错,
fontSize: 24,
fontColor: #CB0707 ,
fontFamily:微软雅黑,
img: new Image(),
};
},
已安装(){
让这个=这个
let Image=new Image();
形象。设置属性(“交叉起源”、“匿名”);
形象。src=这个。img路径;
image.onload=function () {//图片加载完,再画和今天陶尔
if (image.complete) {
_this.img=image
让内容=文档。getelementsbyclassname( content )[0];
_这个。画布=文档。createelement(“canvas”);
_这个。画布。身高=_这个。img。高度
_这个。画布。width=_ this。img。宽度
_这个。CTX=_这个。画布。获取上下文(“2d”);
_这个。CTX。全局alpha=1;
_this.ctx.drawImage(_this.img,0,0)
_这个。画布历史。推(_这个。画布。toda taurl());
_这个。CTX。globalcompositeoperation=_ this。类型;
内容。appendchild(_ this。画布);
_这个。bindeventlistner();
}
}
},
方法:{
单选按钮(项目){
如果(项!=T) {
this.txtBlue()
this.resetTxt()
}
},
//下载画布
下载(){
让这个=这个
让URL=_ this。画布。toda taurl( image/png );
让文件名= canvas.png
if(文档中的“下载”)。createelement( a ){
//非工业管理学(工业工程)下载
const elink=文档。createelement( a );
elink.download=文件名
伊琳克。风格。display= none
elink.href=url
文档。身体。appendchild(elink);
伊琳克。单击();
文档。身体。删除子对象(elink);
}否则{
//IE10下载
领航员。mssavelob(URL,文件名);
}
},
//清空画布及历史记录
resetAll() {
this.ctx.clearRect(0,0,this.canvas.width,this。画布。身高);
这个。画布历史=[];
this.ctx.drawImage(this.img,0,0);
这个。画布历史。推(这个。画布。toda taurl());
这个。步长=0;
这个。reset XT();
},
//清空当前画布
重置(){
this.ctx.clearRect(0,0,this.canvas.width,this。画布。身高);
this.ctx.drawImage(this.img,0,0);
这个。reset XT();
},
//撤销方法
废除(){
让这个=这个
如果(this.isShow) {
_这个。重置XT();
_这个. revolution();
}否则{
_这个. revolution();
}
},
_ revolution(){
如果(this.step=1) {
这个。step=这个。第一步;
let canvasPic=new Image();
canvaspic。src=这个。画布历史【这个。步骤];
canvaspic。addevent侦听器( load ,()={
this.ctx.clearRect(0,0,this.canvas.width,this。画布。身高);
this.ctx.drawImage(canvasPic,0,0);
this.loading=true
});
}否则{
这个message.warning(不能再继续撤销了);
}
},
//恢复方法
canvasRedo() {
如果(这个。踩这个。画布历史。长度-1){
if (this.step==0) {
这个。step=1;
}否则{
这一步;
}
let canvasPic=new Image();
canvaspic。src=这个。画布历史【这个。步骤];
canvaspic。addevent侦听器( load ,()={
this.ctx.clearRect(0,0,this.canvas.width,this。画布。身高);
this.ctx.drawImage(canvasPic,0,0);
});
}否则{
这个message.warning(已经是最新的记录了);
}
},
//绘制历史数组中的最后一个
转播(){
let canvasPic=new Image();
canvaspic。src=这个。画布历史【这个。步骤];
canvaspic。addevent侦听器( load ,()={
this.ctx.clearRect(0,0,this.canvas.width,this。画布。身高);
this.ctx.drawImage(canvasPic,0,0);
this.loading=true
});
},
//绑定事件,判断分支
bindEventLisner() {
让这个=这个
设r1,R2;//绘制圆形,矩形需要
这个。画布。onmousedown=function(e){
控制台。log( onmousedown );
if (_this.type==L) {
_this.createL(e, begin );
} else if (_this.type==R) {
r1=e.layerX
r2=e.layerY
_this.createR(e, begin ,r1,R2);
} else if (_this.type==A) {
_this.drawArrow(e,“begin”)
} else if (_this.type==T) {
_this.createT(e, begin )
}
};
这个。画布。onmouseup=函数(e){
控制台。log( onmouseup );
if (_this.type==L) {
_this.createL(e, end );
} else if (_this.type==R) {
_this.createR(e, end ,r1,R2);
r1=空;
r2=空;
} else if (_this.type==A) {
_this.drawArrow(e," end ")
} else if (_this.type==T) {
_this.createT(e, end )
}
};
},
//绘制线条
创建(状态)
让这个=这个
if (status==begin) {
_这个。CTX。begin path();
_this.ctx.moveTo(e.layerX,e . layery);
_这个。画布。onmousemove=function(e){
控制台。log( onmousemove );
_this.ctx.lineTo(e.layerX,e . layery);
_这个。CTX。笔画风格=_ this。笔画风格;
_这个。CTX。线宽=_ this。线宽;
_这个。CTX。笔画();
};
} else if (status==end) {
_这个。CTX。关闭路径();
_这个。step=_ this。第一步;
如果(_这个。这一步。画布历史。长度-1){
_这个。画布历史。长度=_ this。步;//截断数组
}
_这个。画布历史。推(_这个。画布。toda taurl());
_这个。画布。onmousemove=null
}
},
//绘制矩形
创建者(e,状态,r1,r2) {
让这个=这个
设r;
if (status==begin) {
控制台。log( onmousemove );
_这个。画布。onmousemove=function(e){
_这个。reset();
设rx=e . layerx-R1;
设ry=e . layery-R2;
//保留之前绘画的图形
if (_this.step!==0) {
let canvasPic=new Image();
canvaspic。src=_ this。画布历史[_ this。步骤];
_this.ctx.drawImage(canvasPic,0,0);
}
_这个。CTX。begin path();
_this.ctx.strokeRect(r1,r2,rx,ry);
_这个。CTX。笔画风格=_ this。笔画风格;
_这个。CTX。线宽=_ this。线宽;
_这个。CTX。关闭路径();
_这个。CTX。笔画();
};
} else if (status==end) {
_这个。转播();
让interval=setInterval(()={
if (_this.loading) {
间隙(区间);
_ this.loading=false
}否则{
返回;
}
设rx=e . layerx-R1;
设ry=e . layery-R2;
_这个。CTX。begin path();
_this.ctx.rect(r1,r2,rx,ry);
_这个。CTX。笔画风格=_ this。笔画风格;
_这个。CTX。线宽=_ this。线宽;
_这个。CTX。关闭路径();
_这个。CTX。笔画();
_这个。step=_ this。第一步;
如果(_这个。这一步。画布历史。长度-1){
_这个。画布历史。长度=_ this。步;//截断数组
}
_这个。画布历史。推(_这个。画布。toda taurl());
_这个。画布。onmousemove=null
}, 1);
}
},
//绘制箭头
绘制箭头(e,状态){
让这个=这个
if (status==begin) {
//获取起始位置
_这个。arrow fromx=e . layerx
_这个。arrow fromy=e . layery
_这个。CTX。begin path();
_this.ctx.moveTo(e.layerX,e . layery);
} else if (status==end) {
//计算箭头及画线
设toX=e.layerX
让玩具=e.layerY
设=30;
设headlen=10
让这个=这个
let fromX=this.arrowFromX
let fromY=this.arrowFromY
//计算各角度和对应的P3P2坐标
设angle=Math.atan2(fromY - toY,fromX - toX) * 180/Math .PI,
角度1=(角度)*数学。/180,
角度2=(角度-)*数学。/180,
topX=headlen * Math.cos(angle1),
topY=headlen * Math.sin(angle1),
botX=headlen * Math.cos(angle2),
botY=头len *数学。sin(角度2);
设arrowX=fromX - topX,
arroy=fromY-topY;
_this.ctx.moveTo(arrowX,arrow y);
_this.ctx.moveTo(fromX,fromY);
_this.ctx.lineTo(toX,toY);
arrowX=toX topX
arrowY=玩具陀螺
_this.ctx.moveTo(arrowX,arrow y);
_this.ctx.lineTo(toX,toY);
arrowX=toX botX
箭头=玩具机器人;
_this.ctx.lineTo(arrowX,arrow y);
_这个。CTX。笔画风格=_ this。笔画风格;
_这个。CTX。线宽=_ this。线宽;
_这个。CTX。笔画();
_这个。CTX。关闭路径();
_这个。step=_ this。第一步;
如果(_这个。这一步。画布历史。长度-1){
_这个。画布历史。长度=_ this。步;//截断数组
}
_这个。画布历史。推(_这个。画布。toda taurl());
_这个。画布。onmousemove=null
}
},
//文字输入
创建(状态)
让这个=这个
if (status==begin) {
} else if (status==end) {
设偏移=0;
if (_this.fontSize=28) {
offset=(_this.fontSize/2) - 3
}否则{
offset=(_this.fontSize/2) - 2
}
_这个。ctxx=e . layer x2;
_this.ctxY=e.layerY偏移量;
设指数=这个。getpointoncanvas(e);
_这个参考文献。txt。风格。左=索引。x px
_这个参考文献。txt。风格。top=索引。y-(_这个。字体大小/2) px ;
_这个参考文献。txt。值=" ";
_这个参考文献。txt。风格。身高=_这个。字体大小“px”;
_这个参考文献。txt。风格。width=_ this。画布。宽度-e . layerx-1 像素,
_这个参考文献。txt。风格。font size=_ this。字体大小“px”;
_这个参考文献。txt。风格。font family=_ this。字体系列;
_这个参考文献。txt。风格。color=_这个。字体颜色;
_这个参考文献。txt。风格。maxlength=math。地板((_这个。画布。width-e . layerx)/_ this。字体大小);
_ this.isShow=true
setTimeout(()={
_这个参考文献。txt。焦点();
})
}
},
//文字输入框失去光标时在画布上生成文字
txtBlue() {
让这个=这个
让txt=_this .$参考文献。txt。价值;
if (txt) {
_this.ctx.font=_this .$参考文献。txt。风格。字号 _ this .$参考文献。txt。风格。字体系列;
_this.ctx.fillStyle=_this .$参考文献。txt。风格。颜色;
_this.ctx.fillText(txt,_this.ctxX,_this。ctxy);
_这个。step=_ this。第一步;
如果(_这个。这一步。画布历史。长度-1){
_这个。画布历史。长度=_ this。步;//截断数组
}
_这个。画布历史。推(_这个。画布。toda taurl());
_这个。画布。onmousemove=null
}
},
//计算文字框定位位置
getPointOnCanvas(e) {
设cs=this .画布
让内容=文档。getelementsbyclassname( content )[0];
返回{
x:e . layerx(内容。客户端宽度。宽度)/2,
y: e.layerY
};
},
//清空文字
resetTxt() {
让这个=这个
_这个参考文献。txt。值=" ";
_ this.isShow=false
}
}
};
/脚本
样式范围
* {
框大小:边框-框;
}
身体,
html,
#app {
溢出:隐藏;
}。绘制{
身高:100%;
最小宽度:420像素;
显示器:flex
伸缩方向:列;
}。内容{
伸缩增长:1;
身高:100%;
宽度:100%;
}。抽屉顶部{
显示器:flex
对齐-内容:灵活开始;
对齐-项目:居中;
填充:5px
高度:52px
}。抽屉分区{
显示器:flex
对齐-项目:居中;
填充:5px 5px
}
div。drawtopcontrolllor {
显示:无;
}
@媒体屏幕和(最大宽度:1200像素){。抽屉顶部{
位置:绝对;
背景色:白色;
宽度:100%;
伸缩方向:列;
align-items:flex-start;
高度:30px
溢出:隐藏;
}。绘图控制器{
显示:flex!重要;
高度:30px
宽度:100%;
对齐-内容:居中;
对齐-项目:居中;
填充:0!重要;
}
}
/风格
然后在页面中引入组件,传入图片链接。
在开发过程中遇到的问题
文字输入功能在用户输入文字后,如果不再点击别的地方直接点击别的功能按钮的话,最后输入的文字将不会再画布上生成,通过监控输入框的虚化事件来在画布上生成文字,避免这个问题。
文字输入时字体的大小会影响生成文字的位置,这里发现文字的大小和位置有一个偏移量:
设偏移=0;
if (_this.fontSize=28) {
offset=(_this.fontSize/2) - 3
}否则{
offset=(_this.fontSize/2) - 2
}
在画布上生成文字的时候需要加上这个偏移量,这里字体范围是14~36,别的字体大小没有校验,不一定适用这个计算方式。
绘制矩形的时候需要先清空画布,在清空之前先保存一次画布然后再清空再重新画一下画布,负责矩形框会不停的出现轨迹,并且之前画的元素会消失。
撤销的时候需要考虑文字输入,判断投入得虚拟展示是否为没错,如果是真实的需要先清空文字,再撤销,否则画布上会一直存在一个输入框。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。