之前项目有做过前端截图工具,利用的是html2canvas完成截图开发,就想自己完成一些截图工具中的画画。
入门canvas学习,就边学习边记录,用的还是vue框架,有直线,框,圆,箭头,画线,撤销,清空等等。“箭头”是网上copy过来的。。其他就较为简单。还是直接贴代码吧
vue-template
<template>
<div class="canvasCurve">
<canvas
id="canvasCurve"
:width="canvasWidth"
:height="canvasHeight"
@mousedown="handleMouseDown($event)"
@mousemove="handleMouseMove($event)"
@mouseup="handleMouseUp($event)"
></canvas>
<select v-model="lineWidth">
<option v-for="item in 10" :key="item">
{{ item }}
</option>
</select>
<button @click="handleChange('line')">线</button>
<button @click="handleChange('rect')">框</button>
<button @click="handleChange('ellipse')">圆</button>
<button @click="handleChange('arrow')">箭头</button>
<button @click="handleChange('moveLine')">移动线</button>
<div class="color">
<button @click="colorShow = !colorShow">
<span> 颜色 </span>
</button>
<div class="color-select" v-if="colorShow">
<span
class="color-item"
v-for="(item, index) in colorList"
:key="index"
:style="{ 'background-color': item }"
@click="handleChangeColor(item)"
></span>
</div>
</div>
<button id="undo" @click="undo">撤销</button>
<button id="undo" @click="clear">清空</button>
<span>{{ getType }}</span>
</div>
</template>
vue-js
<script>
export default {
data() {
return {
ctx: null,
canvasWidth: 800,
canvasHeight: 800,
moving: false,
startCanvasX: 0,
startCanvasY: 0,
endCanvasX: 0,
endCanvasY: 0,
type: "ellipse",
getType: "圆",
history: [],
mousePosition: {
x: Number,
y: Number
},
beginPoint: {},
stopPoint: {},
polygonVertex: [],
CONST: {
edgeLen: 50,
angle: 25
},
angle: "",
lineWidth: 1,
colorList: [
"#5e1c0c",
"#f1946b",
"#6178aa",
"#5a6626",
"#a282c3",
"#a282c3",
"#ff7314",
"#082e79",
"#df1c2a",
"#460046",
"#baff11",
"#ff8e01",
"#000090",
"#41ad26",
"#cc0001",
"#ffd900",
"#cf037e",
"#0093be",
"#ffffff",
"#fafafa",
"#b5b5b5",
"#757575",
"#363636",
"#000000"
],
ctxColor: "#cc0001",
colorShow: false
};
},
watch: {
type() {
if (this.type === "line") {
this.getType = "线";
} else if (this.type === "rect") {
this.getType = "框";
} else if (this.type === "ellipse") {
this.getType = "圆";
} else if (this.type === "arrow") {
this.getType = "箭头";
} else if (this.type === "moveLine") {
this.getType = "移动线";
}
}
},
mounted() {
this.initChart();
// this.drawCurveChart();
this.addHistoy();
},
methods: {
handleChange(type) {
this.type = type;
},
initChart() {
this.ctx = document.querySelector("#canvasCurve").getContext("2d");
},
// 画曲线
drawCurveChart() {
this.ctx.beginPath();
this.ctx.moveTo(400, 200);
this.ctx.quadraticCurveTo(400, 400, 500, 200);
this.ctx.stroke();
this.ctx.closePath();
},
handleMouseDown(e) {
this.moving = true;
const { target } = e;
const canvasX = e.clientX - target.offsetLeft;
const canvasY = e.clientY - target.offsetTop;
this.startCanvasX = canvasX;
this.startCanvasY = canvasY;
this.beginPoint.x = e.pageX;
this.beginPoint.y = e.pageY;
if (this.type === "moveLine") {
this.ctx.beginPath();
this.ctx.moveTo(this.startCanvasX, this.startCanvasY);
}
},
handleMouseMove(e) {
if (this.moving) {
this.showLastHistory();
const start = { x: this.startCanvasX, y: this.startCanvasY };
var w = e.layerX - this.startCanvasX;
var h = e.layerY - this.startCanvasY;
this.ctx.lineWidth = this.lineWidth;
this.ctx.strokeStyle = this.ctxColor;
if (this.type === "line") {
this.drawMoveLine(start, w, h);
} else if (this.type === "rect") {
this.drawMoveRect(start, w, h);
} else if (this.type === "ellipse") {
this.drawMoveEllipse(start, w, h);
} else if (this.type === "arrow") {
this.stopPoint.x = e.pageX;
this.stopPoint.y = e.pageY;
this.arrowCoord(this.beginPoint, this.stopPoint);
this.sideCoord();
this.drawArrow();
} else if (this.type === "moveLine") {
this.mousePosition = {
x: e.clientX,
y: e.clientY
};
this.drawMoveLineMove();
}
}
},
handleMouseUp(e) {
//添加画布
this.addHistoy();
this.moving = false;
},
///获得箭头底边两个点
arrowCoord(beginPoint, stopPoint) {
this.polygonVertex[0] = beginPoint.x;
this.polygonVertex[1] = beginPoint.y;
this.polygonVertex[6] = stopPoint.x;
this.polygonVertex[7] = stopPoint.y;
this.getRadian(beginPoint, stopPoint);
this.polygonVertex[8] =
stopPoint.x -
this.CONST.edgeLen *
Math.cos((Math.PI / 180) * (this.angle + this.CONST.angle));
this.polygonVertex[9] =
stopPoint.y -
this.CONST.edgeLen *
Math.sin((Math.PI / 180) * (this.angle + this.CONST.angle));
this.polygonVertex[4] =
stopPoint.x -
this.CONST.edgeLen *
Math.cos((Math.PI / 180) * (this.angle - this.CONST.angle));
this.polygonVertex[5] =
stopPoint.y -
this.CONST.edgeLen *
Math.sin((Math.PI / 180) * (this.angle - this.CONST.angle));
},
//getRadian 返回以起点与X轴之间的夹角角度值
getRadian(beginPoint, stopPoint) {
this.angle =
(Math.atan2(stopPoint.y - beginPoint.y, stopPoint.x - beginPoint.x) /
Math.PI) *
180;
this.paraDef(50, 25);
this.dynArrowSize();
},
//在CONST中定义的edgeLen以及angle参数
//短距离画箭头的时候会出现箭头头部过大,修改
dynArrowSize() {
var x = this.stopPoint.x - this.beginPoint.x,
y = this.stopPoint.y - this.beginPoint.y,
length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
if (length < 250) {
this.CONST.edgeLen = this.CONST.edgeLen / 2;
this.CONST.angle = this.CONST.angle / 2;
} else if (length < 500) {
this.CONST.edgeLen = (this.CONST.edgeLen * length) / 500;
this.CONST.angle = (this.CONST.angle * length) / 500;
}
// console.log(length);
},
//获取另两个底边侧面点
sideCoord() {
var midpoint = {};
midpoint.x = (this.polygonVertex[4] + this.polygonVertex[8]) / 2;
midpoint.y = (this.polygonVertex[5] + this.polygonVertex[9]) / 2;
this.polygonVertex[2] = (this.polygonVertex[4] + midpoint.x) / 2;
this.polygonVertex[3] = (this.polygonVertex[5] + midpoint.y) / 2;
this.polygonVertex[10] = (this.polygonVertex[8] + midpoint.x) / 2;
this.polygonVertex[11] = (this.polygonVertex[9] + midpoint.y) / 2;
},
//画箭头
drawArrow() {
this.ctx.fillStyle = this.ctxColor;
this.ctx.beginPath();
this.ctx.moveTo(this.polygonVertex[0], this.polygonVertex[1]);
this.ctx.lineTo(this.polygonVertex[2], this.polygonVertex[3]);
this.ctx.lineTo(this.polygonVertex[4], this.polygonVertex[5]);
this.ctx.lineTo(this.polygonVertex[6], this.polygonVertex[7]);
this.ctx.lineTo(this.polygonVertex[8], this.polygonVertex[9]);
this.ctx.lineTo(this.polygonVertex[10], this.polygonVertex[11]);
this.ctx.closePath();
this.ctx.fill();
},
// 跟随鼠标的移动线
drawMoveLineMove() {
this.ctx.lineTo(this.mousePosition.x, this.mousePosition.y);
this.ctx.stroke();
},
// 移动画线
drawMoveLine(start, w, h) {
this.ctx.beginPath();
this.ctx.moveTo(start.x, start.y);
this.ctx.lineTo(start.x + w, start.y + h);
this.ctx.stroke();
this.ctx.closePath();
},
// 移动画框
drawMoveRect(start, w, h) {
this.ctx.save();
this.ctx.beginPath();
this.ctx.strokeRect(start.x, start.y, w, h);
this.ctx.stroke();
},
// 移动画圆
drawMoveEllipse(start, w, h) {
this.ctx.beginPath();
this.ctx.ellipse(
start.x + w / 2,
start.y + h / 2,
Math.abs(w / 2),
Math.abs(h / 2),
0,
0,
Math.PI * 2
);
this.ctx.stroke();
this.ctx.closePath();
},
// 画线
drawLine(start, line, lineColor = "#ccc") {
this.ctx.beginPath();
this.ctx.strokeStyle = lineColor;
this.ctx.moveTo(start.x, start.y);
this.ctx.lineTo(line.x, line.y);
this.ctx.stroke();
this.ctx.closePath();
},
// 显示最后一次画布
showLastHistory() {
if (this.history.length > 0) {
this.ctx.putImageData(
this.history[this.history.length - 1]["data"],
0,
0
);
}
},
// 鼠标up弹起保存
addHistoy(data) {
this.history.push({
data: this.ctx.getImageData(0, 0, this.canvasWidth, this.canvasHeight)
});
},
paraDef(edgeLen, angle) {
this.CONST.edgeLen = edgeLen;
this.CONST.angle = angle;
},
// 撤销
undo() {
if (this.history.length > 1) {
this.history.pop();
this.showLastHistory();
}
},
clear() {
this.history = [];
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
this.history.push({
data: this.ctx.getImageData(0, 0, this.canvasWidth, this.canvasHeight)
});
},
handleChangeColor(color) {
this.ctxColor = color;
}
}
};
</script>
vue-css
<style>
#canvasCurve {
border: 1px solid #ccc;
}
.color {
position: relative;
display: inline-block;
}
.color-select {
position: absolute;
top: 30px;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
background-color: #322b2b;
width: 200px;
padding: 10px;
cursor: pointer;
}
.color-item {
display: inline-block;
width: 20px;
height: 20px;
margin: 2px;
}
</style>
效果图: