最近有图片批改作业主观题的需求,所以研究了一下canvas
实现了PC端和基于vue的移动端
PC端效果图如下
核心代码如下:
//矩形
function rect(){
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
//当前绘制线段的起点
var startPoint = {};
//是否进行绘制的开关
var isDraw = false;
//保存鼠标拖动之前canvas的数据
var contextDate = null;
//绘制的图形,默认是线段
var drawType = "line";
function drawHorizontalLine(y) {
context.beginPath();
context.moveTo(0, y);
context.lineTo(canvas.width, y);
context.stroke();
}
//绘制竖直线,竖直线只有x坐标会变,纵坐标一直是从0到canvas.height
function drawVerticalLine(x) {
context.beginPath();
context.moveTo(x, 0);
context.lineTo(x, canvas.height);
context.stroke();
}
function getLocation(e) {
var bbox = document.getElementById("canvas").getBoundingClientRect();
return {
x: e.clientX - bbox.left * (canvas.width/bbox.width),
y: e.clientY - bbox.top * (canvas.height/bbox.height)
};
}
//绘制矩形方法
function drawRect(startPoint, endPoint) {
context.save();
context.beginPath();
context.strokeRect(startPoint.x, startPoint.y,
endPoint.x-startPoint.x, endPoint.y-startPoint.y);
context.stroke();
context.closePath();
}
function saveImageData() {
contextDate = context.getImageData(0, 0, canvas.width, canvas.height);
}
function restoreImageData() {
context.putImageData(contextDate, 0, 0);
}
canvas.onmousedown = function(e) {
imgData.push(ctx.getImageData(0,0,canvas.width,canvas.height));
context.strokeStyle = "black";
startPoint = getLocation(e);
isDraw = true;
saveImageData();
};
canvas.onmousemove = function(e) {
if(isDraw) {
restoreImageData();
drawRect(startPoint, getLocation(e));
}
};
canvas.onmouseup = function(e) {
isDraw = false;
};
}
// 对号
function true1(){
Txt.style.display="none";
ctx.closePath();
canvas.onmousedown=function (ev) {
imgData.push(ctx.getImageData(0,0,canvas.width,canvas.height));
ctx.beginPath();
ctx.strokeStyle = "#50f596";
ctx.lineWidth = 3;
ctx.lineJoin="round";
canvas.onmouseup=function (ev) {
//鼠标按下的位置
var oldX = ev.offsetX;
var oldY = ev.offsetY;
console.log(oldX,oldY);
ctx.moveTo(oldX,oldY);
ctx.lineTo(ev.offsetX+20,ev.offsetY+20);
ctx.lineTo(ev.offsetX+60,ev.offsetY-10);
ctx.stroke();
}
canvas.ondragstart='return false';
};
};
// 叉号
function error1(){
Txt.style.display="none";
ctx.closePath();
canvas.onmousedown=function (ev) {
imgData.push(ctx.getImageData(0,0,canvas.width,canvas.height));
ctx.beginPath();
ctx.strokeStyle = "#e42343";
ctx.lineWidth = 3;
ctx.lineJoin="round";
//鼠标按下的位置
canvas.onmouseup=function (ev) {
var oldX = ev.offsetX;
var oldY = ev.offsetY;
ctx.moveTo(oldX,oldY);
ctx.lineTo(ev.offsetX+30,ev.offsetY+30);
ctx.moveTo(ev.offsetX+30,ev.offsetY);
ctx.lineTo(ev.offsetX,ev.offsetY+30);
ctx.stroke();
};
ctx.closePath();
};
};
// 画笔
function pen(){
Txt.style.display="none";
var last;
// 鼠标按下
canvas.onmousedown = function(){
//保存快照
imgData.push(ctx.getImageData(0,0,canvas.width,canvas.height));
// 在鼠标按下后触发鼠标移动事件
canvas.onmousemove = move;
}
// 鼠标抬起取消鼠标移动的事件
canvas.onmouseup = function(){
canvas.onmousemove = null;
last = null;
}
// 鼠标移出画布时 移动事件取消
canvas.onmouseout = function(){
canvas.onmousemove = null;
last = null;
}
// 鼠标移动函数
function move(e){
// console.log(e.offsetX);
if(last != null){
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = "#000000";
ctx.moveTo(last[0],last[1]);
ctx.lineTo(e.offsetX,e.offsetY);
ctx.stroke();
}
// 第一次触发这个函数,只做一件事,把当前 鼠标的 x , y 的位置记录下来
// 做下一次 线段的 起始点。
last = [e.offsetX,e.offsetY];
}
};
// 箭头
function arrow(){
Txt.style.display="none";
var beginPoint = {},
stopPoint = {},
polygonVertex = [],
CONST = {
edgeLen:100,
angle: 50
};
var Plot = {
angle: "",
//短距离画箭头的时候会出现箭头头部过大,修改:
dynArrowSize: function() {
var x = stopPoint.x - beginPoint.x,
y = stopPoint.y - beginPoint.y,
length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
if (length < 250) {
CONST.edgeLen = CONST.edgeLen/2;
CONST.angle = CONST.angle/2;
}
else if(length<500){
CONST.edgeLen=CONST.edgeLen*length/500;
CONST.angle=CONST.angle*length/500;
}
// console.log(length);
},
//getRadian 返回以起点与X轴之间的夹角角度值
getRadian: function(beginPoint, stopPoint) {
Plot.angle = Math.atan2(stopPoint.y - beginPoint.y, stopPoint.x - beginPoint.x) / Math.PI * 180;
console.log(Plot.angle);
paraDef(50,25);
Plot.dynArrowSize();
},
///获得箭头底边两个点
arrowCoord: function(beginPoint, stopPoint) {
polygonVertex[0] = beginPoint.x;
polygonVertex[1] = beginPoint.y;
polygonVertex[6] = stopPoint.x;
polygonVertex[7] = stopPoint.y;
Plot.getRadian(beginPoint, stopPoint);
polygonVertex[8] = stopPoint.x - CONST.edgeLen * Math.cos(Math.PI / 180 * (Plot.angle + CONST.angle));
polygonVertex[9] = stopPoint.y - CONST.edgeLen * Math.sin(Math.PI / 180 * (Plot.angle + CONST.angle));
polygonVertex[4] = stopPoint.x - CONST.edgeLen * Math.cos(Math.PI / 180 * (Plot.angle - CONST.angle));
polygonVertex[5] = stopPoint.y - CONST.edgeLen * Math.sin(Math.PI / 180 * (Plot.angle - CONST.angle));
},
//获取另两个底边侧面点
sideCoord: function() {
var midpoint = {};
// midpoint.x = polygonVertex[6] - (CONST.edgeLen * Math.cos(Plot.angle * Math.PI / 180));
// midpoint.y = polygonVertex[7] - (CONST.edgeLen * Math.sin(Plot.angle * Math.PI / 180));
midpoint.x=(polygonVertex[4]+polygonVertex[8])/2;
midpoint.y=(polygonVertex[5]+polygonVertex[9])/2;
polygonVertex[2] = (polygonVertex[4] + midpoint.x) / 2;
polygonVertex[3] = (polygonVertex[5] + midpoint.y) / 2;
polygonVertex[10] = (polygonVertex[8] + midpoint.x) / 2;
polygonVertex[11] = (polygonVertex[9] + midpoint.y) / 2;
},
//画箭头
drawArrow: function() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(polygonVertex[0], polygonVertex[1]);
ctx.lineTo(polygonVertex[2], polygonVertex[3]);
ctx.lineTo(polygonVertex[4], polygonVertex[5]);
ctx.lineTo(polygonVertex[6], polygonVertex[7]);
ctx.lineTo(polygonVertex[8], polygonVertex[9]);
ctx.lineTo(polygonVertex[10], polygonVertex[11]);
// ctx.lineTo(polygonVertex[0], polygonVertex[1]);
ctx.closePath();
ctx.fillStyle='#e42343';
ctx.fill();
}
};
//记录起点beginPoint
canvas.onmousedown=function (e) {
imgData.push(ctx.getImageData(0,0,canvas.width,canvas.height));
console.log(e.offsetX,e.offsetY);
beginPoint.x = e.offsetX;
beginPoint.y = e.offsetY;
console.log(beginPoint.x+"+"+beginPoint.y);
// alert(beginPoint.x+"+"+beginPoint.y);
};
//记录终点stopPoint,绘图
canvas.onmouseup = function(e){
stopPoint.x = e.offsetX;
stopPoint.y = e.offsetY;
// alert(stopPoint.x+"+"+stopPoint.y);
Plot.arrowCoord(beginPoint, stopPoint);
Plot.sideCoord();
Plot.drawArrow();
};
//自定义参数
function paraDef(edgeLen, angle) {
CONST.edgeLen = edgeLen;
CONST.angle = angle;
}
};
// 文字
function text(){
Txt.style.display="block";
ctx.font="16px Microsoft Yahei";
canvas.onmousedown=function (ev) {
imgData.push(ctx.getImageData(0,0,canvas.width,canvas.height));
var v = Txt.value;
console.log(v);
if (v != '') {
var oldX = ev.offsetX;
var oldY = ev.offsetY;
console.log(oldX,oldY);
ctx.moveTo(oldX,oldY);
canvas.onmouseup=function (ev) {
ctx.fillText(v,oldX,oldY);
// Txt.value = "";
Txt.style.display="none";
}
}
}
}
移动端效果图如下
selectColor(color){
this.color = color;
this.ctx.strokeStyle = color;
this.showColor = false;
},
eraser(){
var eraser = document.querySelector('.eraser');
var canvas = this.canvas;
var ctx = this.ctx;
//鼠标按下去,获取鼠标在canvas内的相对位置
canvas.ontouchstart = function(ent){
canvas.ontouchmove = function(e){
//判断是否需要显示橡皮擦
eraser.style.position="absolute"
eraser.style.top = e.touches[0].clientY-20+'px'
eraser.style.left = e.touches[0].clientX-20+'px'
eraser.style.display = 'block';
ctx.clearRect(e.touches[0].clientX,e.touches[0].clientY,20,20);
}
}
canvas.ontouchend = function(){
canvas.ontouchmove = null;
eraser.style.display = 'none'
}
},
drawImg(){
this.canvas.style.backgroundImage = 'url('+this.imgs.src+')'
this.canvas.style.backgroundRepeat = 'round'
//this.ctx.drawImage(this.imgs,0,0,this.widths,this.heights);
},
rect(){
var this_ = this;
var imgData = this.imgData;
var Txt = this.Txt;
var ctx = this.ctx;
var canvas = this.canvas;
ctx.strokeStyle=this.color;
var imgData = this.imgData;
this.clearEvent();
//当前绘制线段的起点
var startPoint = {};
//是否进行绘制的开关
var isDraw = false;
//保存鼠标拖动之前canvas的数据
var contextDate = null;
//绘制的图形,默认是线段
var drawType = "line";
function drawHorizontalLine(y) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
//绘制竖直线,竖直线只有x坐标会变,纵坐标一直是从0到canvas.height
function drawVerticalLine(x) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
}
function getLocation(e) {
var bbox = document.getElementById("canvas").getBoundingClientRect();
return {
x: e.touches[0].clientX - bbox.left * (canvas.width/bbox.width),
y: e.touches[0].clientY - bbox.top * (canvas.height/bbox.height)
};
}
//绘制矩形方法
function drawRect(startPoint, endPoint) {
ctx.save();
ctx.beginPath();
ctx.strokeRect(startPoint.x, startPoint.y,
endPoint.x-startPoint.x, endPoint.y-startPoint.y);
ctx.stroke();
ctx.closePath();
}
function saveImageData() {
contextDate = ctx.getImageData(0, 0, canvas.width, canvas.height);
}
function restoreImageData() {
ctx.putImageData(contextDate, 0, 0);
}
canvas.ontouchstart = function(e) {
//保存上一次的绘图记录
imgData.push(ctx.getImageData(0,0,canvas.width,canvas.height))
ctx.strokeStyle = this_.color;
startPoint = getLocation(e);
isDraw = true;
saveImageData();
};
canvas.ontouchmove = function(e) {
if(isDraw) {
restoreImageData();
drawRect(startPoint, getLocation(e));
}
};
canvas.ontouchend = function(e) {
isDraw = false;
};
},
true1(){
var this_ = this;
var imgData = this.imgData;
var Txt = this.Txt;
var ctx = this.ctx;
var canvas = this.canvas;
Txt.style.display="none";
var canvas = this.canvas;
ctx.closePath();
this.clearEvent();
canvas.onmousedown=function (ev) {
imgData.push(ctx.getImageData(0,0,canvas.width,canvas.height))
ctx.beginPath();
ctx.strokeStyle = this_.color;
ctx.lineWidth = 3;
ctx.lineJoin="round";
canvas.onmouseup=function (ev) {
//鼠标按下的位置
var oldX = ev.offsetX;
var oldY = ev.offsetY;
console.log(oldX,oldY);
ctx.moveTo(oldX,oldY);
ctx.lineTo(ev.offsetX+20,ev.offsetY+20);
ctx.lineTo(ev.offsetX+60,ev.offsetY-10);
ctx.stroke();
}
canvas.ondragstart='return false';
};
},
error1(){
var this_ = this;
var imgData = this.imgData;
var Txt = this.Txt;
var ctx = this.ctx;
var canvas = this.canvas;
Txt.style.display="none";
ctx.closePath();
this.clearEvent();
canvas.onmousedown=function (ev) {
imgData.push(ctx.getImageData(0,0,canvas.width,canvas.height))
ctx.beginPath();
ctx.strokeStyle = this_.color;
ctx.lineWidth = 3;
ctx.lineJoin="round";
//鼠标按下的位置
canvas.onmouseup=function (ev) {
var oldX = ev.offsetX;
var oldY = ev.offsetY;
ctx.moveTo(oldX,oldY);
ctx.lineTo(ev.offsetX+30,ev.offsetY+30);
ctx.moveTo(ev.offsetX+30,ev.offsetY);
ctx.lineTo(ev.offsetX,ev.offsetY+30);
ctx.stroke();
};
ctx.closePath();
};
},
pen(){
var this_ = this;
var imgData = this.imgData;
var Txt = this.Txt;
var ctx = this.ctx;
var canvas = this.canvas;
Txt.style.display="none";
var last;
this.clearEvent();
// 鼠标按下
canvas.ontouchstart = function(){
imgData.push(ctx.getImageData(0,0,canvas.width,canvas.height))
// 在鼠标按下后触发鼠标移动事件
canvas.ontouchmove = move;
}
// 鼠标抬起取消鼠标移动的事件
canvas.ontouchend = function(){
canvas.ontouchmove = null;
last = null;
}
// 鼠标移动函数
function move(e){
// console.log(e.offsetX);
if(last != null){
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = this_.color;
ctx.moveTo(last[0],last[1]);
ctx.lineTo(e.touches[0].clientX,e.touches[0].clientY);
ctx.stroke();
}
// 第一次触发这个函数,只做一件事,把当前 鼠标的 x , y 的位置记录下来
// 做下一次 线段的 起始点。
last = [e.touches[0].clientX,e.touches[0].clientY];
}
},
arrow(){
var this_ = this;
var imgData = this.imgData;
var Txt = this.Txt;
var ctx = this.ctx;
var canvas = this.canvas;
Txt.style.display="none";
var beginPoint = {},
stopPoint = {},
polygonVertex = [],
CONST = {
edgeLen:100,
angle: 50
};
this.clearEvent();
var Plot = {
angle: "",
//短距离画箭头的时候会出现箭头头部过大,修改:
dynArrowSize: function() {
var x = stopPoint.x - beginPoint.x,
y = stopPoint.y - beginPoint.y,
length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
if (length < 250) {
CONST.edgeLen = CONST.edgeLen/2;
CONST.angle = CONST.angle/2;
}
else if(length<500){
CONST.edgeLen=CONST.edgeLen*length/500;
CONST.angle=CONST.angle*length/500;
}
},
//getRadian 返回以起点与X轴之间的夹角角度值
getRadian: function(beginPoint, stopPoint) {
Plot.angle = Math.atan2(stopPoint.y - beginPoint.y, stopPoint.x - beginPoint.x) / Math.PI * 180;
console.log(Plot.angle);
paraDef(50,25);
Plot.dynArrowSize();
},
///获得箭头底边两个点
arrowCoord: function(beginPoint, stopPoint) {
polygonVertex[0] = beginPoint.x;
polygonVertex[1] = beginPoint.y;
polygonVertex[6] = stopPoint.x;
polygonVertex[7] = stopPoint.y;
Plot.getRadian(beginPoint, stopPoint);
polygonVertex[8] = stopPoint.x - CONST.edgeLen * Math.cos(Math.PI / 180 * (Plot.angle + CONST.angle));
polygonVertex[9] = stopPoint.y - CONST.edgeLen * Math.sin(Math.PI / 180 * (Plot.angle + CONST.angle));
polygonVertex[4] = stopPoint.x - CONST.edgeLen * Math.cos(Math.PI / 180 * (Plot.angle - CONST.angle));
polygonVertex[5] = stopPoint.y - CONST.edgeLen * Math.sin(Math.PI / 180 * (Plot.angle - CONST.angle));
},
//获取另两个底边侧面点
sideCoord: function() {
var midpoint = {};
midpoint.x=(polygonVertex[4]+polygonVertex[8])/2;
midpoint.y=(polygonVertex[5]+polygonVertex[9])/2;
polygonVertex[2] = (polygonVertex[4] + midpoint.x) / 2;
polygonVertex[3] = (polygonVertex[5] + midpoint.y) / 2;
polygonVertex[10] = (polygonVertex[8] + midpoint.x) / 2;
polygonVertex[11] = (polygonVertex[9] + midpoint.y) / 2;
},
//画箭头
drawArrow: function() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(polygonVertex[0], polygonVertex[1]);
ctx.lineTo(polygonVertex[2], polygonVertex[3]);
ctx.lineTo(polygonVertex[4], polygonVertex[5]);
ctx.lineTo(polygonVertex[6], polygonVertex[7]);
ctx.lineTo(polygonVertex[8], polygonVertex[9]);
ctx.lineTo(polygonVertex[10], polygonVertex[11]);
// ctx.lineTo(polygonVertex[0], polygonVertex[1]);
ctx.closePath();
ctx.fillStyle=this_.color;
ctx.fill();
}
};
canvas.ontouchmove = null;
//记录起点beginPoint
canvas.ontouchstart=function (e) {
imgData.push(ctx.getImageData(0,0,canvas.width,canvas.height))
beginPoint.x = e.touches[0].clientX;
beginPoint.y = e.touches[0].clientY;
};
//记录终点stopPoint,绘图
canvas.ontouchend = function(e){
stopPoint.x = e.changedTouches[0].clientX;
stopPoint.y = e.changedTouches[0].clientY;
Plot.arrowCoord(beginPoint, stopPoint);
Plot.sideCoord();
Plot.drawArrow();
};
//自定义参数
function paraDef(edgeLen, angle) {
CONST.edgeLen = edgeLen;
CONST.angle = angle;
}
},
text(){
this.clearEvent();
var this_ = this;
var imgData = this.imgData;
var Txt = null;
var ctx = this.ctx;
var canvas = this.canvas;
canvas.onmousedown= createTxt;
var oldX = null;
var oldY = null;
function createTxt(ev) {
if(Txt !=null){
return;
}
oldX = ev.offsetX;
oldY = ev.offsetY;
Txt = document.createElement("textArea");
Txt.style.display="block";
ctx.font="16px Microsoft Yahei";
Txt.style.position = 'absolute'
Txt.style.top = ev.offsetY+'px'
Txt.style.left = ev.offsetX+'px'
Txt.style.background = 'rgb(0,0,0,0)'
Txt.style.border = '1px solid '+this_.color
Txt.style.color = this_.color
Txt.style.width = canvas.width-oldX-20+"px"
document.body.append(Txt)
setTimeout(() => {
Txt.focus()
}, "50");
setTimeout(() => {
Txt.onblur = fillTxt;
}, 50);
canvas.onmousedown=null;
}
function fillTxt(ev){
imgData.push(ctx.getImageData(0,0,canvas.width,canvas.height))
var v = Txt.value;
if(v !=''){
ctx.fillStyle = this_.color
ctx.moveTo(oldX,oldY);
let row = []
let temp = ""
for(var a = 0; a < v.length; a++){
if( ctx.measureText(temp).width < canvas.width-oldX-20 ){
;
}
else{
row.push(temp);
temp = "";
}
temp += v[a];
}
row.push(temp);
for(var b = 0; b < row.length; b++){
ctx.fillText(row[b],oldX,oldY+(b+1)*14);
}
}
Txt.remove();
Txt = null;
canvas.onmousedown=createTxt;
}
},
restuya(){
var this_ = this;
var imgData = this.imgData;
var Txt = this.Txt;
var ctx = this.ctx;
var canvas = this.canvas;
Txt.style.display="none";
this.imgData = [];
ctx.clearRect(0, 0, canvas.width,canvas.height);
},
put(){
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var imgData = this.imgData;
if(imgData.length>0){
let data = imgData.pop()
// ctx.clearRect(0, 0, canvas.width,canvas.height);
ctx.putImageData(data,0,0,0,0,canvas.width,canvas.height);
}
},
saveTu(){
let tempCanvas = document.createElement("canvas");
tempCanvas.width = this.widths
tempCanvas.height = this.heights
let tempCtx = tempCanvas.getContext('2d');
if(this.imgData.length==0){
return;
}
var image = new Image();
image = this.canvas.toDataURL('Image/png');
let imageObj = document.createElement('img');
imageObj.src = image;
setTimeout(() => {
tempCtx.drawImage(this.imgs,0,0,this.widths,this.heights);
tempCtx.drawImage(imageObj,0,0,this.widths,this.heights);
var tempImage = new Image();
tempImage = tempCanvas.toDataURL('Image/png')
document.getElementById('res').innerHTML = '<img src="'+tempImage+'">';
}, 100);
},
clearEvent(){
var canvas = this.canvas;
canvas.ontouchstart = null;
canvas.ontouchmove =null;
canvas.ontouchend = null;
canvas.onmousedown = null;
canvas.onmouseup = null;
}
总结一下啊。
canvas在PC端和移动端的开发中要注意监听画布的事件是不一样的
PC端主要是监听 onmousedown ,onmousemove,onmouseup事件
移动端主要是监听 ontouchstart,ontouchmove,ontouchend事件
每次在点击新的画笔功能后,要注意清除之前监听的事件,不然有会有坑
canvas.ontouchstart = null;
canvas.ontouchmove =null;
canvas.ontouchend = null;
canvas.onmousedown = null;
canvas.onmouseup = null;
最后附上源码下载地址:https://download.csdn.net/download/qq_28800347/12612863