1. The function of the drawing board
- Modify the brush color;
- Modify the thickness of the brush;
- Rubber;
- Reset the artboard;
- Undo the previous step;
Save as a picture;
2. Required knowledge
Element.getBoundingClientRect()
method returns the size of the element and its position relative to the viewport.ctx.moveTo(x, y)
moves the starting point of a new sub-path to (x, y) coordinatesctx.lineTo(x, y)
uses a straight line to connect the end point of the sub-path to the x and y coordinates3. Step by step implementation
The first step is to realize the basic functions and draw the mouse path;
<canvas id="myCanvas" width="400" height="400"></canvas>
class Board {
constructor(id) {
this.canvas = document.getElementById(id);
this.context = this.canvas.getContext('2d');
this.isDrawing = false;
this.posX = 0;
this.posY = 0;
this.init();
}
init() {
const bindDown = this.handleMouseDown.bind(this);
const bindMove = this.handleMouseMove.bind(this);
this.canvas.addEventListener('mousedown', bindDown);
this.canvas.addEventListener('mousemove', bindMove);
window.addEventListener('mouseup', () => {
this.isDrawing = false;
});
}
handleMouseDown(e) {
const rect = this.canvas.getBoundingClientRect();
this.posX = e.clientX - rect.left;
this.posY = e.clientY - rect.top;
this.isDrawing = true;
}
handleMouseMove(e) {
if (this.isDrawing === true) {
const rect = this.canvas.getBoundingClientRect();
this.drawLine(this.context, this.posX, this.posY,
e.clientX - rect.left, e.clientY - rect.top);
this.posX = e.clientX - rect.left;
this.posY = e.clientY - rect.top;
}
}
drawLine(context, x1, y1, x2, y2) {
context.beginPath();
context.strokeStyle = 'black';
context.lineWidth = 1;
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.stroke();
context.closePath();
}
}
new Board('myCanvas');
In the second step, you can modify the color of the brush;
<input id="colorPicker" type="color" />
document.getElementById('colorPicker').addEventListener('change', e => {
b.changeColor(e.target.value);
})
class Board {
constructor(id, color = '#000') {
this.penColor = color;
}
drawLine(context, x1, y1, x2, y2) {
context.strokeStyle = this.penColor;
}
changeColor(color) {
this.penColor = color;
}
}
The third step is to modify the thickness of the brush;
ctx.lineWidth = number;
The fourth step, eraser;
context.globalCompositeOperation = 'destination-out';
Refer to the scratch function.
The fifth step is to reset the drawing board;
context.clearRect(0, 0, width, height);
The sixth step is to undo the previous step;
this.canvas.toDataURL()
Save the current canvas as a base64 picture and store it in an array. Set another index, undo/restore the modified index value, and take out the corresponding picture from the array.
The seventh step, save as a picture;
Create an a tag, href is the picture generated by toDataURL(), simulate a click event, and click a link.
4. Complete code
<div class="opera">
<input id="colorPicker" type="color" />
<select id="fontsizeSelect">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
<button id="eraser">橡皮擦</button>
<button id="reset">重置</button>
<button id="revoke">撤销</button>
<button id="recover">恢复</button>
<button id="saveAsPic">保存为图片</button>
</div>
<canvas id="myCanvas" width="400" height="400"></canvas>
class Board {
constructor(id, color = '#000', fontsize = 1) {
this.canvas = document.getElementById(id);
this.context = this.canvas.getContext('2d');
this.isDrawing = false;
this.posX = 0;
this.posY = 0;
this.penColor = color;
this.fontsize = fontsize;
this.isErasing = false;
this.step = 0;
this.histroyList = [];
this.init();
}
init() {
const bindDown = this.handleMouseDown.bind(this);
const bindMove = this.handleMouseMove.bind(this);
this.canvas.addEventListener('mousedown', bindDown);
this.canvas.addEventListener('mousemove', bindMove);
window.addEventListener('mouseup', () => {
this.isDrawing = false;
});
this.canvas.addEventListener('mouseup', () => {
this.step++;
if (this.step < this.histroyList.length) {
this.histroyList.length = this.step;
}
this.histroyList.push(this.canvas.toDataURL());
});
this.histroyList.push(this.canvas.toDataURL());
}
handleMouseDown(e) {
const rect = this.canvas.getBoundingClientRect();
this.posX = e.clientX - rect.left;
this.posY = e.clientY - rect.top;
this.isDrawing = true;
}
handleMouseMove(e) {
const rect = this.canvas.getBoundingClientRect();
if (this.isErasing) {
this.context.globalCompositeOperation = 'destination-out';
this.context.beginPath();
this.context.arc(e.clientX - rect.left, e.clientY - rect.top,
10, 0, Math.PI * 2);
this.context.fill();
} else if (this.isDrawing === true) {
this.drawLine(this.context, this.posX, this.posY,
e.clientX - rect.left, e.clientY - rect.top);
this.posX = e.clientX - rect.left;
this.posY = e.clientY - rect.top;
}
}
drawLine(context, x1, y1, x2, y2) {
context.beginPath();
context.strokeStyle = this.penColor;
context.lineWidth = this.fontsize;
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.stroke();
context.closePath();
}
changeColor(color) {
this.penColor = color;
}
changeFontSize(size) {
this.fontsize = size;
}
switchEraseStatus() {
this.isErasing = !this.isErasing;
}
clearBoard() {
this.context.clearRect(0, 0, window.myCanvas.width,
window.myCanvas.height);
this.step = 0;
this.histroyList = [];
}
revoke() {
if (this.step > 0) {
this.step--;
this.context.clearRect(0, 0, window.myCanvas.width,
window.myCanvas.height);
let pic = new Image();
pic.src = this.histroyList[this.step];
pic.addEventListener('load', () => {
this.context.drawImage(pic, 0, 0);
})
} else {
console.log('不能继续撤销了')
}
}
recover() {
if (this.step < this.histroyList.length - 1) {
this.step++;
this.context.clearRect(0, 0, window.myCanvas.width,
window.myCanvas.height);
let pic = new Image();
pic.src = this.histroyList[this.step];
pic.addEventListener('load', () => {
this.context.drawImage(pic, 0, 0);
})
} else {
console.log('不能继续恢复了')
}
}
saveAsPic() {
const el = document.createElement('a');
el.href = this.canvas.toDataURL();
el.download = 'canvas';
const event = new MouseEvent('click');
el.dispatchEvent(event);
}
}
const b = new Board('myCanvas');
window.colorPicker.addEventListener('change', e => {
b.changeColor(e.target.value);
})
window.fontsizeSelect.addEventListener('change', e => {
b.changeFontSize(window.fontsizeSelect.value);
})
window.eraser.addEventListener('click', () => {
b.switchEraseStatus();
})
window.reset.addEventListener('click', () => {
b.clearBoard();
})
window.revoke.addEventListener('click', () => {
b.revoke();
})
window.recover.addEventListener('click', () => {
b.recover();
})
window.saveAsPic.addEventListener('click', () => {
b.saveAsPic();
})
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。