Chào các bạn, trong bài viết này chúng ta sẽ cùng nhau làm ứng dụng Paint brush
. Nếu các bạn chưa biết Paint brush
là gì thì đây là ứng dụng cho phép chúng ta vẽ các đường nét trên 1 vùng không gian nhất định tương tự như ứng dụng Paint trong window. Các bạn có thể mở ứng dụng Paint để tham khảo
Ứng dụng mà chúng ta làm lần này sẽ không phức tạp như Paint
mà sẽ đơn giản hơn bao gồm các chức năng sau:
- Vẽ các nét
- Reset nét vẽ
- Chọn màu cho nét vẽ
- Chọn kích thước cho nét vẽ
Phần tạo giao diện cho ứng dụng, mình để sources code ở đây để các bạn tham khảo, hoặc các bạn có thể tự tạo giao diện cho ứng dụng của mình
Link tham khảo: https://github.com/buihien0109/buihien0109.github.io/tree/master/HTML5-Games/game/paint/part-1
Đây là giao diện ban đầu của chúng ta
1. Ý tưởng thực hiện
1. Thành phần
Trong ứng dụng Paint có 2 thành phần:
Canvas
: Khu vực thực hiện công việc vẽMemory canvas
: Ghi nhớ nét vẽ từ canvas khi kết thúc 1 nét vẽ, và render lại tất cả các nét vẽ sang canvas để thực hiện nét vẽ tiếp theo
2. Quá trình thực hiện
- Đầu tiên chúng ta sẽ có mảng
points
để lưu giá trị tọa độ của các điểm - Nhấn chuột xuống để bắt đầu vẽ, chúng ta tiến hành xóa toàn bộ canvas, sau đó copy các nét vẽ từ bên
memory canvas
➡➡canvas
, sau đó tiến hành vẽ nét mới, trong quá trình di chuyển chuột để vẽ nét mới lấy tọa độ của các điểm sau đó thêm vào mảngpoints
- Từ mảng
point
chúng ta sẽ vẽ nên các nét trên bề mặt canvas - Khi nhả chuột ra để kết thúc nét vẽ, lúc này xóa toàn bộ các nét vẽ trên memory canvas đồng thời đưa tất cả các nét trên bề mặt
canvas
➡➡canvas memory
, sau đó chopoints = []
; - Chúng ta sẽ sẽ lặp đi lặp lại quá trình này
2. Xử lý khi vẽ
Đầu tiên chúng ta sẽ truy cập vào canvas thông qua id của nó
const canvas = document.getElementById("canvas");
Tiếp theo chúng ta định nghĩa class FreeHand
, với constructor đầu vào là canvas element
class FreeHand {
constructor(canvas) {
this.canvas = canvas;
// Lưu context để vẽ 2D
this.context = canvas.getContext("2d");
// Kiểm tra khi nào được draw, khi nào không
this.isDraw = false;
// Lưu lại tọa độ các point đã vẽ
this.points = [];
// Style nét vẽ. Mặc định nét vẽ có chiều rộng 2px, màu trắng
this.context.lineWidth = 2;
this.context.lineJoin = "round";
this.context.lineCap = "round";
this.context.strokeStyle = "#FFF";
// Memmory canvas, dùng để ghi nhớ các nét vẽ
this.memCanvas = document.createElement("canvas");
this.memCanvas.width = canvas.width;
this.memCanvas.height = canvas.height;
this.memCtx = this.memCanvas.getContext("2d");
}
}
Để vẽ 1 nét chúng ta có các thao tác sau:
- Nhấn chuột xuống để bắt đầu vẽ
- Vừa nhấn chuột và di chuyển chuột để thực hiện vẽ
- Nhả chuột ra để kết thúc nét vẽ
Với mỗi thao tác trên chúng ta sẽ định nghĩa method trong class FreeHand
để thực hiên
1. Nhấn chuột xuống để bắt đầu vẽ
Chúng ta thực hiện thao tác này bằng các định nghĩa method onmousedown
Chúng ta sẽ lưu lại tọa độ tại vị trí nhấn chuột xuống và push vào mảng this.points
để lưu lại, đồng thời this.isDraw = true
để đặt trạng thái bắt đầu vẽ
onmousedown(event) {
// Lấy tạo độ x,y của chuột tạo vị trí bắt đầu vẽ
this.x = event.offsetX;
this.y = event.offsetY;
// Lưu cặp tọa độ vào mảng points
this.points.push({
x: this.x,
y: this.y,
});
// Đặt trạng thái bắt đầu vẽ
this.isDraw = true;
}
2. Vừa nhấn chuột và di chuyển chuột để thực hiện vẽ
Trong lúc vẽ, chúng ta sẽ xóa hết this.canvas
thông qua method this.context.clearRect
. Đồng thời vẽ lại các nét từ this.memCtx
sang this.canvas
Tọa độ của các điểm trên đường đi của bút vẽ sẽ được push vào mảng this.points
để lưu lại
Cuối cùng gọi methods this.drawPoints
để nối các điểm trong mảng this.points
lại và hiển thị lên thành nét vẽ
Phần tiếp theo chúng ta sẽ định nghĩa method this.drawPoints
để tạo nét vẽ từ mảng this.points
onmousemove(event) {
if(this.isDraw) {
// Xóa tất cả trên canvas
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
// Vẽ memory canvas lên trên canvas
this.context.drawImage(this.memCanvas, 0, 0);
// Lưu tọa độ khi di chuyển chuột vào mảng points
this.points.push({
x: event.offsetX,
y: event.offsetY,
});
// Nối các tọa độ trong mảng points lại thành 1 đường
this.drawPoints();
}
}
3. Nhả chuột ra để kết thúc nét vẽ
Khi nhả chuột ra chúng ta set giá trị this.isDraw = false
để kết thúc 1 chu trình vẽ
Đồng thời clear this.memCtx
và vẽ lại các nét từ this.canvas
sang this.memCtx
Clear this.points = []
để tiếp tục cho lần vẽ tiếp theo
onmouseup() {
if (this.isDraw) {
// Đặt lại trạng vẽ => kết thúc nét vẽ
this.isDraw = false;
// Xóa toàn bộ memory canvas
this.memCtx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// Vẽ canvas lên trên memory canvas
this.memCtx.drawImage(this.canvas, 0, 0);
// Clear mảng point cho nét vẽ khác
this.points = [];
}
}
3. Làm mượt nét vẽ
Để tạo nét vẽ chúng ta sẽ nối lần lượt tọa độ của các điểm trong mảng this.points
lại với nhau. Nhưng nếu đơn thuần chỉ nối thì các nét vẽ này sẽ không mượt và bị gấp khúc
Để làm mượt nét vẽ chúng ta sẽ sử dụng method quadraticCurveTo()
Cụ thể phương thức này như thế nào, các bạn có thể tham khảo tại đây: https://www.w3schools.com/tags/canvas_quadraticcurveto.asp
drawPoints() {
let ctx = this.context;
// Nếu mảng point có 1 điểm -> bỏ qua không vẽ
if(this.points.length <= 1) {
return
}
// Nếu mảng point có 2 điểm -> nối 2 điểm đó với nhau
if(this.points.length == 2) {
ctx.beginPath();
ctx.moveTo(this.points[0].x, this.points[0].y);
ctx.lineTo(this.points[1].x, this.points[1].y);
ctx.stroke();
return
}
// Trường hợp mảng points có nhiều điểm -> sử dụng quadraticCurveTo để nối các điểm lại
ctx.beginPath();
ctx.moveTo(this.points[0].x, this.points[0].y);
for (var i = 1; i < this.points.length - 2; i++) {
var c = (this.points[i].x + this.points[i + 1].x) / 2;
var d = (this.points[i].y + this.points[i + 1].y) / 2;
ctx.quadraticCurveTo(this.points[i].x, this.points[i].y, c, d);
}
ctx.quadraticCurveTo(
this.points[i].x,
this.points[i].y,
this.points[i + 1].x,
this.points[i + 1].y
);
ctx.stroke();
}
4. Khởi tạo đối tượng và lắng nghe sự kiện
Sau khi định nghĩa class FreeHand
với 1 số phương thức cơ bản để có thể vẽ các nét. Bây giờ chúng ta sẽ thực hiện khởi tạo đối tượng từ class FreeHand
và lắng nghe các sự kiện của đối tượng canvas
và gọi ra method tương ứng
- onmousedown event ➡➡ gọi method onmousedown
- onmousemove event ➡➡ gọi method onmousemove
- onmouseup event ➡➡ gọi method onmouseup
let freehand = new FreeHand(canvas);
// Lắng nghe sự kiện onmousedown (ấn chuột xuống) của đối tượng canvas để bắt đầu vẽ
canvas.onmousedown = (event) => {
freehand.onmousedown(event);
};
// Lắng nghe sự kiện onmousemove (di chuyển chuột) của đối tượng canvas để vẽ nét
canvas.onmousemove = (event) => {
freehand.onmousemove(event);
};
// Lắng nghe sự kiện onmouseup (nhả chuột ra) của đối tượng canvas để kết thúc nét vẽ
canvas.onmouseup = () => {
freehand.onmouseup();
};
và đây là kết quả của chúng ta
Ở bài viết tiếp theo chúng ta sẽ thực hiện các chức năng còn lại của ứng dụng Paintbrush
- Chọn nét vẽ
- Chọn màu vẽ
- Xóa toàn bộ nét vẽ
Sources phần này các bạn có thể tham khảo tại đây: https://github.com/buihien0109/buihien0109.github.io/tree/master/HTML5-Games/game/paint/part-2
Các bạn có thể tham khảo thêm khóa học này nhé:
Bình luận
test