<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
html,body{
margin:0;
padding:0;
height:100vh;
}
.canvasTool {
width: 100%;
height: 100%;
background-color: gray;
}
.canvalParent {
height:calc(100% - 60px);
width: 100%;
/* display: flex;
justify-content: center;
align-items: center; */
}
#canvas1 {
background-color: white;
position: absolute;
z-index: 1;
}
#canvas2 {
cursor: crosshair;
position: absolute;
z-index: 2;
}
.btn>div{
margin-top:10px;
/* display: flex;
justify-content: center;
align-items: center; */
}
.btn select {
margin-right: 20px;
width: 60px;
}
</style>
<body>
<div class="canvasTool">
<div class="canvalParent">
<canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas>
</div>
<div class="btn">
<div>
操作:<select id="handlerSelect"></select>
线条宽度:<select id="lineWidthSelect"></select>
颜色:<select id="colorSelect"></select>
数据精度:<select id="accuracySelect"></select>
<button id="eraser">橡皮擦</button>
<button id="clear">清除画布</button>
</div>
</div>
</div>
<script>
const handlerList = [{
value: 'point',
desc: '点',
select: false
},
{
value: 'straightLine',
desc: '直线',
select: true
},
{
value: 'straightLineConnection',
desc: '直线连线',
select: false,
},
{
value: 'circular',
desc: '圆',
select: false
},
{
value: 'quadrilateral',
desc: '四边形',
select: false
},
{
value: 'polygon',
desc: '多边形',
select: false
},
{
value: 'line',
desc: '画笔',
select: false
},
{
value: 'eraser',
desc: '橡皮擦',
select: false
},
];
const colorList = [{
value: 'red',
desc: '红',
select: false
},
{
value: 'orange',
desc: '橙',
select: true
},
{
value: 'yellow',
desc: '黄',
select: false
},
{
value: 'green',
desc: '绿',
select: false
},
{
value: 'young',
desc: '青',
select: false
},
{
value: 'blue',
desc: '蓝',
select: false
},
{
value: 'purple',
desc: '紫',
select: false
},
];
const lineWidthList = [{
value: '1',
desc: '1',
select: true
},
{
value: '2',
desc: '2',
select: false
},
{
value: '3',
desc: '3',
select: false
},
{
value: '4',
desc: '4',
select: false
},
{
value: '5',
desc: '5',
select: false
},
{
value: '6',
desc: '6',
select: false
},
{
value: '7',
desc: '7',
select: false
},
{
value: '8',
desc: '8',
select: false
},
{
value: '9',
desc: '9',
select: false
},
{
value: '10',
desc: '10',
select: false
},
{
value: '20',
desc: '20',
select: false
},
{
value: '30',
desc: '30',
select: false
},
{
value: '50',
desc: '50',
select: false
},
{
value: '100',
desc: '100',
select: false
},
]
const accuracyList = [{
value: '0',
desc: '整数',
select: true
},
{
value: '10',
desc: '一位小数',
select: false
},
{
value: '100',
desc: '二位小数',
select: false
},
{
value: '1000',
desc: '3位小数',
select: false
},
];
class Draw {
constructor() {
this.id = '';
this.c1 = document.getElementById('canvas1');
this.c2 = document.getElementById('canvas2');
this.c1.width=this.c2.width = document.body.clientWidth;
this.c1.height=this.c2.height = document.body.clientHeight - 60;
// this.drawImage();
this.x = '';
this.y = '';
this.ctx1 = this.c1.getContext('2d');
this.ctx2 = this.c2.getContext('2d');
this.mouseType = 'up'; // 鼠标状态
this.handlerType = this.addOption('handlerSelect'); // 绘制类型
this.color = this.addOption('colorSelect'); // 绘制颜色
this.lineWidth = this.addOption('lineWidthSelect'); // 线条宽度
this.ccuracy = this.addOption('accuracySelect'); // 数据精确度
this.currentObj = null; // 临时数据
this.clickNumber = 0;
this.correct = {};
this.mouseDownData = {
point: (obj) => {
if (this.handlerType !== 'point') return;
this.currentObj = obj; // 临时数据
},
straightLine: (obj) => {
const {
x,
y
} = obj;
this.currentObj = {
startX: x,
startY: y
};
},
line: (obj) => {
const {
x,
y
} = obj;
this.ctx1.save();
this.ctx1.beginPath();
this.ctx1.moveTo(x, y);
},
straightLineConnection: (obj) => {},
circular: (obj) => {
const {
x,
y
} = obj;
this.currentObj = {
startX: x,
startY: y
};
},
quadrilateral: (obj) => {
const {
x,
y
} = obj;
this.currentObj = {
startX: x,
startY: y
};
},
polygon: (obj) => {},
eraser: (obj) => {
}
};
this.mouseMoveData = {
straightLine: (obj) => { // 直线
this.drawStraightLine(this.ctx2, obj);
},
straightLineConnection: (obj) => {
if (this.mouseType === 'down') return;
this.drawStraightLineConnection('move', this.ctx2, obj);
},
line: (obj) => { // 画笔
this.currentObj = obj; // 临时数据
this.drawLine();
},
circular: (obj) => {
this.drawCirclaur(this.ctx2, obj);
},
quadrilateral: (obj) => {
this.drawQuadrilateral(this.ctx2, obj);
},
polygon: (obj) => {
if (this.mouseType === 'down') return;
this.drawPolygon('move', this.ctx2, obj);
},
eraser: (obj) => {
this.drawEraser(obj);
}
};
this.mouseUpData = {
point: (obj) => {
this.drawPoint();
this.currentObj = null;
},
line: (obj) => {
this.currentObj = null;
},
straightLine: (obj) => { // 直线
this.ctx2.clearRect(0, 0, this.c2.width, this.c2.width);
this.drawStraightLine(this.ctx1, obj);
console.log('直线:', this.currentObj, obj);
this.currentObj = null;
},
straightLineConnection: (obj) => {
this.ctx2.clearRect(0, 0, this.c2.width, this.c2.width);
this.drawStraightLineConnection('up', this.ctx2, obj);
},
circular: (obj) => {
this.ctx2.clearRect(0, 0, this.c2.width, this.c2.width);
this.drawCirclaur(this.ctx1, obj);
console.log('圆:', this.currentObj, obj);
this.currentObj = null;
},
quadrilateral: (obj) => {
this.ctx2.clearRect(0, 0, this.c2.width, this.c2.width);
this.drawQuadrilateral(this.ctx1, obj);
console.log('四边形:', this.currentObj, obj);
this.currentObj = null;
},
polygon: (obj) => {
this.ctx2.clearRect(0, 0, this.c2.width, this.c2.width);
this.drawPolygon('up', this.ctx2, obj);
},
eraser: (obj) => {}
}
}
// drawImage(){
// let img = new Image();
// img.src = './imgs/dt.png';
// img.onload = ()=>{
// this.ctx1.drawImage(img,0,0,this.c1.width,this.c1.height);
// }
// }
mouseHandler() { // 鼠标点击、拖动事件
this.c2.onmousedown = (e) => {
this.ctx2.clearRect(0, 0, this.c2.width, this.c2.width);
this.handlerType = this.addOption('handlerSelect'); // 绘制类型
this.color = this.addOption('colorSelect'); // 绘制颜色
this.lineWidth = this.addOption('lineWidthSelect'); // 线条宽度
this.ccuracy = this.addOption('accuracySelect'); // 线条宽度
this.mouseType = 'down';
this.mouseDownData[this.handlerType] && this.mouseDownData[this.handlerType]({
x: e.offsetX,
y: e.offsetY,
c: this.color,
w: this.lineWidth,
});
}
this.c2.onmousemove = (e) => {
this.ctx2.clearRect(0, 0, this.c2.width, this.c2.width);
this.x = e.offsetX;
this.y = e.offsetY;
this.ctx2.save();
this.ctx2.beginPath();
this.ctx2.strokeStyle = 'rgba(2,2,2,.2)';
this.ctx2.lineWidth = 1;
this.ctx2.setLineDash([5, 5]);
this.ctx2.moveTo(this.x, 0);
this.ctx2.lineTo(this.x, this.c2.height);
this.ctx2.moveTo(0, this.y);
this.ctx2.lineTo(this.c2.width, this.y);
this.ctx2.stroke();
this.ctx2.restore();
this.handlerType = this.addOption('handlerSelect'); // 绘制类型
this.color = this.addOption('colorSelect'); // 绘制颜色
this.lineWidth = this.addOption('lineWidthSelect'); // 线条宽度
this.ccuracy = this.addOption('accuracySelect'); // 线条宽度
if (this.handlerType === 'eraser') {
this.mouseMoveData[this.handlerType] && this.mouseMoveData[this.handlerType]({
x: e.offsetX,
y: e.offsetY,
c: this.color,
w: this.lineWidth,
});
return;
}
if ((this.handlerType === 'polygon' || this.handlerType === 'straightLineConnection') && this
.currentObj) {
this.mouseMoveData[this.handlerType] && this.mouseMoveData[this.handlerType]({
x: e.offsetX,
y: e.offsetY,
c: this.color,
w: this.lineWidth,
});
return;
}
if (this.mouseType === 'up') {
let {
px,
py
} = this.showTextPosition(this.x, this.y);
this.ctx2.beginPath();
this.ctx2.save();
this.ctx2.fillText(`x:${this.x},y:${this.y}`, px, py);
this.ctx2.restore();
return;
}
this.mouseMoveData[this.handlerType] && this.mouseMoveData[this.handlerType]({
x: e.offsetX,
y: e.offsetY,
c: this.color,
w: this.lineWidth,
});
}
this.c2.onmouseup = (e) => {
this.mouseType = 'up';
if (e.button === 2) { // 鼠标右键
// console.log('右键1')
} else {
this.c2.onpointermove = null;
this.mouseUpData[this.handlerType] && this.mouseUpData[this.handlerType]({
x: e.offsetX,
y: e.offsetY,
c: this.color,
w: this.lineWidth,
});
}
}
this.c2.ondblclick = (e) => {
this.ctx2.clearRect(0, 0, this.c2.width, this.c2.width);
if ((this.handlerType === 'polygon')) {
this.drawPolygon('up', this.ctx1, null);
console.log('多边形:', this.currentObj);
} else if (this.handlerType === 'straightLineConnection') {
console.log('直线连接:', this.currentObj);
this.drawStraightLineConnection('up', this.ctx1, null);
}
this.currentObj = null;
}
this.c2.oncontextmenu = (e) => {
e.preventDefault();
console.log('右键2')
this.ctx2.clearRect(0, 0, this.c2.width, this.c2.width);
if ((this.handlerType === 'polygon')) {
this.drawPolygon('up', this.ctx1, null);
console.log('多边形:', this.currentObj);
} else if (this.handlerType === 'straightLineConnection') {
this.drawStraightLineConnection('up', this.ctx1, null);
console.log('直线连接:', this.currentObj);
}
this.currentObj = null;
}
document.getElementById('clear').onclick = e => {
this.ctx1.clearRect(0, 0, this.c1.width, this.c1.height);
// this.drawImage();
}
document.getElementById('eraser').onclick = e => {
handlerList.forEach(item => {
if (item.value === 'eraser') {
item.select = true;
} else {
item.select = false;
}
})
this.addOption('handlerSelect', handlerList);
}
}
drawPoint() { // 点
const {
x,
y,
w,
c,
} = this.currentObj;
this.ctx1.save();
this.ctx1.beginPath();
this.ctx1.fillStyle = c;
this.ctx1.arc(x, y, w, 0, Math.PI * 2, false);
this.ctx1.fill();
this.ctx1.closePath();
this.ctx1.restore();
console.log('点:', this.currentObj);
}
drawLine(ctx, obj) { // 线
const {
x,
y,
c,
w
} = this.currentObj;
this.ctx1.strokeStyle = c;
this.ctx1.lineWidth = w;
// 2miter : 尖角 默认 // bevel : 斜角 // round : 圆角
this.ctx1.lineJoin = 'round';
// butt 默认。向线条的每个末端添加平直的边缘。 // round 向线条的每个末端添加圆形线帽。 // square 向线条的每个末端添加正方形线帽。
this.ctx1.lineCap = 'round';
this.ctx1.lineTo(x, y);
this.ctx1.stroke();
this.ctx1.restore();
console.log('线:', obj);
}
drawStraightLine(ctx, obj) {
const {
startX,
startY
} = this.currentObj;
const {
x,
y,
c,
w
} = obj;
ctx.save();
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.strokeStyle = c;
ctx.lineWidth = w;
// miter : 尖角 默认
// bevel : 斜角
// round : 圆角
ctx.lineJoin = 'round';
// butt 默认。向线条的每个末端添加平直的边缘。
// round 向线条的每个末端添加圆形线帽。
// square 向线条的每个末端添加正方形线帽。
ctx.lineCap = 'round';
const {
angle,
length,
modifyX,
modifyY,
} = this.calculation(startX, startY, x, y);
if (ctx === this.ctx2) {
let {
px,
py
} = this.showTextPosition(modifyX, modifyY, 145);
ctx.fillText(`长度:${length},角度:${angle}`, px, py);
}
ctx.lineTo(modifyX, modifyY);
ctx.stroke();
ctx.restore();
}
drawCirclaur(ctx, obj) { // 圆
const {
startX,
startY
} = this.currentObj;
const {
x,
y,
c,
w
} = obj;
ctx.save();
ctx.beginPath();
ctx.strokeStyle = c;
ctx.lineWidth = w;
// miter : 尖角 默认
// bevel : 斜角
// round : 圆角
ctx.lineJoin = 'round';
// butt 默认。向线条的每个末端添加平直的边缘。
// round 向线条的每个末端添加圆形线帽。
// square 向线条的每个末端添加正方形线帽。
ctx.lineCap = 'round';
const {
length,
modifyX,
modifyY,
} = this.calculation(startX, startY, x, y);
if (ctx === this.ctx2) {
let {
px,
py
} = this.showTextPosition(modifyX, modifyY);
ctx.fillText(`半径:${length}`, px, py);
}
let r = Math.sqrt(Math.pow(Math.abs(x - startX), 2) + Math.pow(Math.abs(y - startY), 2));
ctx.moveTo(startX + length, startY);
ctx.arc(startX, startY, length, 0, Math.PI * 2, false);
ctx.stroke();
ctx.restore();
}
drawQuadrilateral(ctx, obj) { // 四边形
const {
startX,
startY
} = this.currentObj;
const {
x,
y,
c,
w
} = obj;
ctx.save();
ctx.beginPath();
ctx.strokeStyle = c;
ctx.lineWidth = w;
// miter : 尖角 默认
// bevel : 斜角
// round : 圆角
ctx.lineJoin = 'round';
// butt 默认。向线条的每个末端添加平直的边缘。
// round 向线条的每个末端添加圆形线帽。
// square 向线条的每个末端添加正方形线帽。
ctx.lineCap = 'round';
ctx.moveTo(startX, startY);
const {
lengthX,
lengthY,
modifyX,
modifyY,
} = this.calculation(startX, startY, x, y);
if (ctx === this.ctx2) {
let {
px,
py
} = this.showTextPosition(modifyX, modifyY, 145);
ctx.fillText(`长:${lengthX},宽:${lengthY}`, px, py);
}
ctx.strokeRect(startX, startY, lengthX, lengthY);
ctx.stroke();
ctx.restore();
}
drawStraightLineConnection(handler, ctx, obj) {
let arr = [];
if (handler === 'move') {
arr = [...this.currentObj, obj];
} else {
if (obj) {
const {
x,
y,
w,
c
} = obj;
if (Array.isArray(this.currentObj)) {
// console.log('存储的点', this.correct);
this.currentObj.push({
x: this.correct.x || x,
y: this.correct.y || y,
w,
c,
clickNumber: ++this.clickNumber
});
} else {
this.clickNumber = 0;
this.currentObj = [{
x,
y,
w,
c,
clickNumber: ++this.clickNumber
}];
}
arr = [...this.currentObj];
}
arr = [...this.currentObj];
}
ctx.save()
ctx.beginPath();
arr.forEach((item, index) => {
if (index === 0) {
ctx.moveTo(item.x, item.y);
} else if (index > 0) {
const {
x,
y,
c,
w
} = item;
ctx.strokeStyle = c;
ctx.lineWidth = w;
// 2miter : 尖角 默认 // bevel : 斜角 // round : 圆角
ctx.lineJoin = 'round';
// butt 默认。向线条的每个末端添加平直的边缘。 // round 向线条的每个末端添加圆形线帽。 // square 向线条的每个末端添加正方形线帽。
ctx.lineCap = 'round';
let previous = arr[index - 1];
const {
length,
angle,
modifyX,
modifyY,
} = this.calculation(previous.x, previous.y, x, y);
this.correct = {
x: modifyX,
y: modifyY
};
if (ctx === this.ctx2) {
let {
px,
py
} = this.showTextPosition(modifyX, modifyY, 145);
ctx.fillText(`长度:${length},角度:${angle}`, px, py);
}
ctx.lineTo(modifyX, modifyY);
}
ctx.stroke();
ctx.restore();
})
}
drawPolygon(handler, ctx, obj) { // 多边形
let arr = [];
if (handler === 'move') {
arr = [...this.currentObj, obj];
} else {
if (obj) {
const {
x,
y,
w,
c
} = obj;
if (Array.isArray(this.currentObj)) {
console.log('存储的点', this.correct);
this.currentObj.push({
x: this.correct.x || x,
y: this.correct.y || y,
w,
c,
clickNumber: ++this.clickNumber
});
} else {
this.clickNumber = 0;
this.currentObj = [{
x,
y,
w,
c,
clickNumber: ++this.clickNumber
}];
}
arr = [...this.currentObj];
}
arr = [...this.currentObj];
}
ctx.save()
ctx.beginPath();
arr.forEach((item, index) => {
if (index === 0) {
ctx.moveTo(item.x, item.y);
} else if (index > 0) {
const {
x,
y,
c,
w
} = item;
ctx.strokeStyle = c;
ctx.lineWidth = w;
// 2miter : 尖角 默认 // bevel : 斜角 // round : 圆角
ctx.lineJoin = 'round';
// butt 默认。向线条的每个末端添加平直的边缘。 // round 向线条的每个末端添加圆形线帽。 // square 向线条的每个末端添加正方形线帽。
ctx.lineCap = 'round';
let previous = arr[index - 1];
const {
length,
angle,
modifyX,
modifyY,
} = this.calculation(previous.x, previous.y, x, y);
this.correct = {
x: modifyX,
y: modifyY
};
if (ctx === this.ctx2) {
let {
px,
py
} = this.showTextPosition(this.x, this.y,145);
ctx.fillText(`长度:${length},角度:${angle}`, px, py);
}
ctx.lineTo(modifyX, modifyY);
if (arr.length > 2 && index === arr.length - 1) {
ctx.closePath();
}
}
ctx.stroke();
ctx.restore();
})
}
addOption(id, arr) { // 渲染select下拉列表以及获取下拉列表值
let dom = document.getElementById(id);
if (!arr) {
return dom.value;
} else {
for (let i = 0; i < dom.childNodes.length; i++) {
let item = dom.childNodes[i];
dom.removeChild(item)
}
for (let i = 0; i < arr.length; i++) {
const {
value,
desc,
select
} = arr[i];
const option = document.createElement('option');
option.value = value;
option.text = desc;
option.selected = select;
dom.appendChild(option);
}
}
}
calculation(startX, startY, endX, endY) {
let differenceX = endX - startX;
let differenceY = endY - startY;
let angle;
if (differenceX >= 0 && differenceY >= 0) {
angle = Math.atan(differenceY / differenceX) / Math.PI * 180;
} else if (differenceX < 0 && differenceY >= 0) {
angle = 180 + Math.atan(differenceY / differenceX) / Math.PI * 180;
} else if (differenceX < 0 && differenceY < 0) {
angle = 180 + Math.atan(differenceY / differenceX) / Math.PI * 180;
} else {
angle = 360 + Math.atan(differenceY / differenceX) / Math.PI * 180;
}
let length = Math.sqrt(Math.pow(Math.abs(differenceX), 2) + Math.pow(Math.abs(differenceY), 2));
angle = this.format(angle);
length = this.format(length);
let currentX = this.format(Math.cos(angle / 180 * Math.PI) * length);
let currentY = this.format(Math.sin(angle / 180 * Math.PI) * length);
return {
length,
angle,
modifyX: currentX + startX,
modifyY: currentY + startY,
lengthX: currentX,
lengthY: currentY,
}
}
format(value) {
let n = this.ccuracy; // 线条宽度;
if (n === '0') return Math.floor(value);
return Math.floor(value * n) / n;
}
showTextPosition(x, y, z) {
if (!z) z = 80;
let px = x,
py = y;
if (x < 14) px = 10;
if (x > this.c2.width - z) px = this.c2.width - z;
if (y < 14) py = 10;
if (y > this.c2.height - 20) py = this.c2.height - 10;
return {
px,
py
};
};
drawEraser(obj) { // 黑板擦
let {
x,
y,
w
} = obj;
w = parseFloat(w);
if (w < 2) w = 2;
w = w * 5;
this.ctx2.save();
this.ctx2.beginPath();
this.ctx2.strokeStyle = 'black';
this.ctx2.width = 1;
this.ctx2.strokeRect(x - w / 2, y - w / 2, w, w);
this.ctx1.clearRect(x - w / 2, y - w / 2, w, w)
this.ctx2.stroke();
this.ctx2.restore();
// this.drawImage();
}
};
let draw = new Draw();
draw.mouseHandler();
draw.addOption('handlerSelect', handlerList);
draw.addOption('lineWidthSelect', lineWidthList);
draw.addOption('colorSelect', colorList);
draw.addOption('accuracySelect', accuracyList);
</script>
</body>
</html>
canvas自制简易画板
最新推荐文章于 2022-07-15 10:19:46 发布