Bạn có thể vẽ hình trong HTML không?

Trình duyệt cung cấp cho chúng tôi một số cách để hiển thị đồ họa. Cách đơn giản nhất là sử dụng các kiểu để định vị và tô màu các phần tử DOM thông thường. Điều này có thể đưa bạn đi khá xa, như trò chơi trong chương trước đã chỉ ra. Bằng cách thêm các hình nền trong suốt một phần vào các nút, chúng ta có thể làm cho chúng trông chính xác theo cách chúng ta muốn. Thậm chí có thể xoay hoặc nghiêng các nút với kiểu

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
0

Nhưng chúng tôi sẽ sử dụng DOM cho thứ mà ban đầu nó không được thiết kế cho. Một số tác vụ, chẳng hạn như vẽ một đường thẳng giữa các điểm tùy ý, cực kỳ khó thực hiện với các phần tử HTML thông thường

Có hai lựa chọn thay thế. Cái đầu tiên dựa trên DOM nhưng sử dụng Đồ họa vectơ có thể mở rộng (SVG), thay vì HTML. Hãy nghĩ về SVG như một phương ngữ đánh dấu tài liệu tập trung vào hình dạng hơn là văn bản. Bạn có thể nhúng tài liệu SVG trực tiếp vào tài liệu HTML hoặc bao gồm tài liệu đó bằng thẻ

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
1

Phương án thứ hai được gọi là canvas. Canvas là một phần tử DOM duy nhất gói gọn một bức tranh. Nó cung cấp một giao diện lập trình để vẽ các hình dạng lên không gian được chiếm bởi nút. Sự khác biệt chính giữa canvas và ảnh SVG là trong SVG, mô tả ban đầu của các hình dạng được giữ nguyên để chúng có thể được di chuyển hoặc thay đổi kích thước bất kỳ lúc nào. Mặt khác, canvas chuyển đổi hình dạng thành pixel (các chấm màu trên raster) ngay khi chúng được vẽ và không nhớ những pixel này đại diện cho cái gì. Cách duy nhất để di chuyển một hình dạng trên khung vẽ là xóa khung vẽ (hoặc một phần của khung vẽ xung quanh hình dạng đó) và vẽ lại nó bằng hình dạng ở vị trí mới

SVG

Cuốn sách này sẽ không đi sâu vào SVG, nhưng tôi sẽ giải thích ngắn gọn về cách thức hoạt động của nó. Ở cuối chương này, tôi sẽ quay lại với những đánh đổi mà bạn phải cân nhắc khi quyết định cơ chế vẽ nào phù hợp với một ứng dụng nhất định

Đây là một tài liệu HTML với một hình ảnh SVG đơn giản trong đó

<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>

Thuộc tính

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
2 thay đổi một phần tử (và các phần tử con của nó) thành một không gian tên XML khác. Không gian tên này, được xác định bởi một URL, chỉ định phương ngữ mà chúng tôi hiện đang nói. Các thẻ
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
3 và
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
4, không tồn tại trong HTML, có ý nghĩa trong SVG—chúng vẽ các hình bằng cách sử dụng kiểu và vị trí được chỉ định bởi các thuộc tính của chúng

Các thẻ này tạo các phần tử DOM, giống như các thẻ HTML, mà tập lệnh có thể tương tác với. Ví dụ: điều này thay đổi phần tử

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
3 thành màu lục lam thay thế

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");

phần tử canvas

Đồ họa canvas có thể được vẽ trên phần tử

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
6. Bạn có thể cung cấp các thuộc tính
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
7 và
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
8 cho một phần tử như vậy để xác định kích thước của nó theo pixel

Một khung vẽ mới trống, có nghĩa là nó hoàn toàn trong suốt và do đó hiển thị dưới dạng khoảng trống trong tài liệu

Thẻ

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
6 nhằm mục đích cho phép các kiểu vẽ khác nhau. Để có quyền truy cập vào một giao diện bản vẽ thực tế, trước tiên chúng ta cần tạo một bối cảnh, một đối tượng có các phương thức cung cấp giao diện bản vẽ. Hiện tại có hai kiểu vẽ được hỗ trợ rộng rãi.
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
20 cho đồ họa hai chiều và
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
21 cho đồ họa ba chiều thông qua giao diện OpenGL

Cuốn sách này sẽ không thảo luận về WebGL—chúng ta sẽ tập trung vào hai chiều. Nhưng nếu bạn quan tâm đến đồ họa ba chiều, tôi khuyến khích bạn xem xét WebGL. Nó cung cấp giao diện trực tiếp cho phần cứng đồ họa và cho phép bạn hiển thị hiệu quả ngay cả những cảnh phức tạp, sử dụng JavaScript

Bạn tạo ngữ cảnh bằng phương thức

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
22 trên phần tử DOM
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
6

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
6

Sau khi tạo đối tượng bối cảnh, ví dụ vẽ một hình chữ nhật màu đỏ rộng 100 pixel và cao 50 pixel, với góc trên cùng bên trái của nó tại tọa độ (10,10)

Giống như trong HTML (và SVG), hệ tọa độ mà canvas sử dụng đặt (0,0) ở góc trên cùng bên trái và trục y dương đi xuống từ đó. Vì vậy (10,10) là 10 pixel bên dưới và ở bên phải của góc trên cùng bên trái

Đường nét và bề mặt

Trong giao diện canvas, hình dạng có thể được lấp đầy, nghĩa là khu vực của nó được cung cấp một màu hoặc mẫu nhất định hoặc nó có thể được vuốt, nghĩa là một đường được vẽ dọc theo cạnh của nó. Thuật ngữ tương tự được sử dụng bởi SVG

Phương thức

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
24 điền vào một hình chữ nhật. Trước tiên, nó lấy tọa độ x và y của góc trên cùng bên trái của hình chữ nhật, sau đó là chiều rộng và sau đó là chiều cao của nó. Một phương pháp tương tự,
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
25, vẽ đường viền của một hình chữ nhật

Cả hai phương thức đều không nhận thêm bất kỳ tham số nào. Màu tô, độ dày của nét, v.v., không được xác định bởi một đối số của phương thức (như bạn có thể mong đợi một cách hợp lý) mà thay vào đó là bởi các thuộc tính của đối tượng bối cảnh

Thuộc tính

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
26 kiểm soát cách điền vào các hình dạng. Nó có thể được đặt thành một chuỗi chỉ định màu, sử dụng ký hiệu màu được sử dụng bởi CSS

Thuộc tính

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
27 hoạt động tương tự nhưng xác định màu được sử dụng cho đường nét. Độ rộng của dòng đó được xác định bởi thuộc tính
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
28, thuộc tính này có thể chứa bất kỳ số dương nào

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>

Khi không chỉ định thuộc tính

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
7 hoặc
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
8, như trong ví dụ, phần tử canvas có chiều rộng mặc định là 300 pixel và chiều cao là 150 pixel

đường dẫn

Đường dẫn là một dãy các đường. Giao diện canvas 2D có một cách tiếp cận đặc biệt để mô tả một đường dẫn như vậy. Nó được thực hiện hoàn toàn thông qua các tác dụng phụ. Đường dẫn không phải là giá trị có thể được lưu trữ và chuyển qua. Thay vào đó, nếu bạn muốn làm gì đó với một đường dẫn, bạn thực hiện một chuỗi các lệnh gọi phương thức để mô tả hình dạng của nó

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
2

Ví dụ này tạo một đường dẫn với một số đoạn đường ngang và sau đó vuốt nó bằng phương pháp

<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
61. Mỗi đoạn được tạo bằng
<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
62 bắt đầu tại vị trí hiện tại của đường dẫn. Vị trí đó thường là phần cuối của phân đoạn cuối cùng, trừ khi
<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
63 được gọi. Trong trường hợp đó, phân đoạn tiếp theo sẽ bắt đầu ở vị trí được chuyển đến
<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
63

Khi điền vào một đường dẫn (sử dụng phương pháp

<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
65), mỗi hình dạng được điền riêng. Một đường dẫn có thể chứa nhiều hình—mỗi chuyển động
<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
63 bắt đầu một hình mới. Nhưng đường dẫn cần phải được đóng (có nghĩa là bắt đầu và kết thúc của nó ở cùng một vị trí) trước khi nó có thể được lấp đầy. Nếu đường dẫn chưa bị đóng, một đường sẽ được thêm từ cuối đến đầu và hình được bao quanh bởi đường dẫn đã hoàn thành sẽ được lấp đầy

<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
6

Ví dụ này vẽ một hình tam giác đầy. Lưu ý rằng chỉ có hai cạnh của tam giác được vẽ rõ ràng. Thứ ba, từ góc dưới cùng bên phải trở lại trên cùng, được ngụ ý và sẽ không ở đó khi bạn vuốt đường dẫn

Bạn cũng có thể sử dụng phương thức

<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
67 để đóng một đường dẫn một cách rõ ràng bằng cách thêm một đoạn đường thực trở lại điểm bắt đầu của đường dẫn. Đoạn này được vẽ khi vuốt đường dẫn

đường cong

Một đường dẫn cũng có thể chứa các đường cong. Thật không may, đây là một chút tham gia nhiều hơn để vẽ

Phương pháp

<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
68 vẽ một đường cong đến một điểm nhất định. Để xác định độ cong của đường, phương pháp được cung cấp một điểm kiểm soát cũng như điểm đích. Hãy tưởng tượng điểm kiểm soát này đang thu hút đường thẳng, tạo cho nó đường cong. Đường thẳng sẽ không đi qua điểm kiểm soát, nhưng hướng của nó tại điểm bắt đầu và điểm kết thúc sẽ sao cho một đường thẳng theo hướng đó sẽ hướng về điểm kiểm soát. Ví dụ sau đây minh họa điều này

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
5

Chúng tôi vẽ một đường cong bậc hai từ trái sang phải, với (60,10) là điểm kiểm soát, sau đó vẽ hai đoạn thẳng đi qua điểm kiểm soát đó và quay lại điểm bắt đầu của đường. Kết quả hơi giống phù hiệu Star Trek. Bạn có thể thấy tác dụng của điểm kiểm soát. các đường rời khỏi các góc dưới bắt đầu theo hướng của điểm kiểm soát và sau đó cong về phía mục tiêu của chúng

Phương pháp

<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
69 vẽ một loại đường cong tương tự. Thay vì một điểm kiểm soát duy nhất, điểm kiểm soát này có hai—một điểm cho mỗi điểm cuối của đường. Đây là một bản phác thảo tương tự để minh họa hành vi của một đường cong như vậy

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
7

Hai điểm kiểm soát xác định hướng ở cả hai đầu của đường cong. Chúng càng ở xa điểm tương ứng, đường cong sẽ càng “phình ra” theo hướng đó

Những đường cong như vậy có thể khó làm việc với—không phải lúc nào cũng rõ ràng cách tìm các điểm kiểm soát cung cấp hình dạng mà bạn đang tìm kiếm. Đôi khi bạn có thể tính toán chúng và đôi khi bạn sẽ phải tìm một giá trị phù hợp bằng cách thử và sai

Phương pháp

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
50 là một cách để vẽ một đường cong dọc theo cạnh của hình tròn. Nó nhận một cặp tọa độ cho tâm của cung, bán kính, sau đó là góc bắt đầu và góc kết thúc

Hai tham số cuối cùng đó chỉ có thể vẽ một phần của vòng tròn. Các góc được đo bằng radian, không phải độ. Điều này có nghĩa là một vòng tròn đầy đủ có một góc là 2π hoặc

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
51, tức là khoảng 6. 28. Góc bắt đầu đếm tại điểm bên phải tâm của vòng tròn và đi theo chiều kim đồng hồ từ đó. Bạn có thể sử dụng điểm bắt đầu bằng 0 và điểm cuối lớn hơn 2π (ví dụ: 7) để vẽ một vòng tròn đầy đủ

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
0

Hình ảnh kết quả chứa một đường kẻ từ bên phải của vòng tròn đầy đủ (lần gọi đầu tiên đến

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
50) ở bên phải của vòng tròn một phần tư (lần gọi thứ hai). Giống như các phương pháp vẽ đường dẫn khác, một đường được vẽ bằng
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
50 được kết nối với đoạn đường dẫn trước đó. Bạn có thể gọi
<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
63 hoặc bắt đầu một con đường mới để tránh điều này

Vẽ biểu đồ hình tròn

Hãy tưởng tượng bạn vừa nhận một công việc tại EconomCorp, Inc. và nhiệm vụ đầu tiên của bạn là vẽ biểu đồ hình tròn về kết quả khảo sát mức độ hài lòng của khách hàng

Liên kết

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
55 chứa một mảng các đối tượng đại diện cho các câu trả lời khảo sát

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
5

Để vẽ một biểu đồ hình tròn, chúng ta vẽ một số lát cắt hình tròn, mỗi lát cắt được tạo thành từ một cung và một cặp đường kẻ đến tâm của cung đó. Chúng ta có thể tính góc của mỗi cung bằng cách chia một vòng tròn đầy đủ (2π) cho tổng số câu trả lời rồi nhân số đó (góc trên mỗi câu trả lời) với số người đã chọn một lựa chọn nhất định.

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
0

Nhưng một biểu đồ không cho chúng ta biết ý nghĩa của các lát cắt không hữu ích lắm. Chúng tôi cần một cách để vẽ văn bản vào canvas

Chữ

Bối cảnh vẽ canvas 2D cung cấp các phương thức

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
56 và
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
57. Cái sau có thể hữu ích cho việc phác thảo các chữ cái, nhưng thường thì
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
56 là thứ bạn cần. Nó sẽ lấp đầy dàn ý của văn bản đã cho với
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
26 hiện tại

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
1

Bạn có thể chỉ định kích thước, kiểu và phông chữ của văn bản bằng thuộc tính

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
70. Ví dụ này chỉ cung cấp cỡ chữ và họ. Cũng có thể thêm
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
71 hoặc
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
72 vào đầu chuỗi để chọn kiểu

Hai đối số cuối cùng của

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
56 và
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
57 cung cấp vị trí mà phông chữ được vẽ. Theo mặc định, chúng cho biết vị trí bắt đầu của đường cơ sở chữ cái của văn bản, là dòng mà các chữ cái "đứng" trên đó, không tính các phần treo trong các chữ cái như j hoặc p. Bạn có thể thay đổi vị trí nằm ngang bằng cách đặt thuộc tính
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
75 thành
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
76 hoặc
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
77 và vị trí thẳng đứng bằng cách đặt
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
78 thành
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
79,
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
00 hoặc
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
01

Chúng ta sẽ quay lại biểu đồ hình tròn và vấn đề đặt tên cho các lát cắt trong các bài tập ở cuối chương.

Hình ảnh

Trong đồ họa máy tính, người ta thường phân biệt giữa đồ họa vector và đồ họa bitmap. Đầu tiên là những gì chúng ta đã làm cho đến giờ trong chương này—xác định một bức tranh bằng cách đưa ra một mô tả hợp lý về các hình dạng. Mặt khác, đồ họa bitmap không chỉ định hình dạng thực tế mà hoạt động với dữ liệu pixel (các chấm màu)

Phương thức

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
02 cho phép chúng tôi vẽ dữ liệu pixel lên canvas. Dữ liệu pixel này có thể bắt nguồn từ phần tử
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
1 hoặc từ một canvas khác. Ví dụ sau tạo một phần tử
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
1 tách rời và tải một tệp hình ảnh vào đó. Nhưng nó không thể ngay lập tức bắt đầu vẽ từ bức tranh này vì có thể trình duyệt chưa tải nó. Để giải quyết vấn đề này, chúng tôi đăng ký trình xử lý sự kiện
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
05 và thực hiện thao tác vẽ sau khi hình ảnh được tải xong

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
2

Theo mặc định,

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
02 sẽ vẽ hình ảnh ở kích thước ban đầu. Bạn cũng có thể cung cấp cho nó hai đối số bổ sung để đặt chiều rộng và chiều cao khác nhau

Khi

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
02 được cung cấp chín đối số, nó có thể được sử dụng để chỉ vẽ một phần của hình ảnh. Các đối số thứ hai đến thứ năm cho biết hình chữ nhật (x, y, chiều rộng và chiều cao) trong hình ảnh nguồn sẽ được sao chép và các đối số thứ sáu đến thứ chín cho biết hình chữ nhật (trên canvas) mà nó sẽ được sao chép vào đó

Điều này có thể được sử dụng để đóng gói nhiều họa tiết (phần tử hình ảnh) vào một tệp hình ảnh duy nhất và sau đó chỉ vẽ phần bạn cần. Ví dụ, chúng ta có bức ảnh này chứa một nhân vật game trong nhiều tư thế

Bạn có thể vẽ hình trong HTML không?

Bằng cách xen kẽ tư thế mà chúng ta vẽ, chúng ta có thể hiển thị hoạt ảnh trông giống như một nhân vật đang đi bộ

Để tạo hoạt ảnh trên canvas, phương pháp

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
08 rất hữu ích. Nó giống như
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
24, nhưng thay vì tô màu hình chữ nhật, nó làm cho nó trong suốt, loại bỏ các điểm ảnh đã vẽ trước đó

Chúng tôi biết rằng mỗi sprite, mỗi bức ảnh con, rộng 24 pixel và cao 30 pixel. Đoạn mã sau tải hình ảnh và sau đó thiết lập một khoảng thời gian (bộ đếm thời gian lặp lại) để vẽ khung tiếp theo

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
3

Liên kết

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
50 theo dõi vị trí của chúng tôi trong hoạt ảnh. Đối với mỗi khung hình, nó được tăng lên và sau đó được cắt bớt về phạm vi từ 0 đến 7 bằng cách sử dụng toán tử phần dư. Liên kết này sau đó được sử dụng để tính toán tọa độ x mà sprite cho tư thế hiện tại có trong ảnh

chuyển đổi

Nhưng nếu chúng ta muốn nhân vật của mình đi bên trái thay vì bên phải thì sao? . Nhưng chúng ta cũng có thể hướng dẫn canvas vẽ bức tranh theo cách khác

Gọi phương thức

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
51 sẽ khiến mọi thứ được vẽ sau nó được thu nhỏ. Phương pháp này có hai tham số, một để đặt tỷ lệ ngang và một để đặt tỷ lệ dọc

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
4

Chia tỷ lệ sẽ khiến mọi thứ về hình ảnh được vẽ, bao gồm cả độ rộng của đường kẻ, được kéo dài ra hoặc ép lại với nhau như đã chỉ định. Chia tỷ lệ theo số tiền âm sẽ lật hình ảnh xung quanh. Việc lật xảy ra xung quanh điểm (0,0), có nghĩa là nó cũng sẽ đảo hướng của hệ tọa độ. Khi tỷ lệ ngang -1 được áp dụng, một hình được vẽ ở vị trí x 100 sẽ kết thúc ở vị trí từng là -100

Vì vậy, để xoay ngược một bức tranh, chúng ta không thể chỉ cần thêm

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
52 trước lệnh gọi đến
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
02 vì điều đó sẽ di chuyển bức tranh của chúng ta ra ngoài canvas, nơi nó sẽ không hiển thị. Bạn có thể điều chỉnh tọa độ cho
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
02 để bù cho điều này bằng cách vẽ hình ảnh ở vị trí x -50 thay vì 0. Một giải pháp khác, không yêu cầu mã thực hiện bản vẽ để biết về thay đổi tỷ lệ, là điều chỉnh trục xung quanh nơi xảy ra tỷ lệ

Có một số phương pháp khác ngoài

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
51 ảnh hưởng đến hệ tọa độ cho canvas. Bạn có thể xoay các hình được vẽ sau đó bằng phương pháp
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
56 và di chuyển chúng bằng phương pháp
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
57. Điều thú vị—và khó hiểu—là các phép biến đổi này xếp chồng lên nhau, nghĩa là mỗi phép biến đổi xảy ra liên quan đến các phép biến đổi trước đó

Vì vậy, nếu chúng ta dịch 10 pixel ngang hai lần, mọi thứ sẽ được vẽ 20 pixel ở bên phải. Nếu đầu tiên chúng ta di chuyển tâm của hệ tọa độ đến (50,50) và sau đó xoay 20 độ (khoảng 0. 1π radian), phép quay đó sẽ xảy ra quanh điểm (50,50)

Nhưng nếu trước tiên chúng ta xoay 20 độ và sau đó dịch chuyển theo (50,50), thì quá trình dịch chuyển sẽ xảy ra trong hệ tọa độ đã quay và do đó tạo ra một hướng khác. Thứ tự áp dụng các phép biến đổi

Để lật một bức tranh quanh đường thẳng đứng tại một vị trí x cho trước, ta có thể làm như sau

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
5

Chúng tôi di chuyển trục y đến nơi chúng tôi muốn gương của mình, áp dụng phản chiếu và cuối cùng di chuyển trục y trở lại vị trí thích hợp của nó trong vũ trụ được nhân đôi. Hình ảnh sau đây giải thích tại sao điều này hoạt động

Điều này cho thấy các hệ tọa độ trước và sau khi phản chiếu qua đường trung tâm. Các hình tam giác được đánh số để minh họa từng bước. Nếu chúng ta vẽ một hình tam giác ở vị trí x dương, theo mặc định, nó sẽ ở vị trí của tam giác 1. Một cuộc gọi đến

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
58 trước tiên thực hiện dịch sang phải, đưa chúng ta đến tam giác 2. Sau đó, nó chia tỷ lệ, lật hình tam giác sang vị trí 3. Đây không phải là nơi nó nên ở, nếu nó được nhân đôi trong dòng đã cho. Lệnh gọi
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
57 thứ hai sửa lỗi này—nó “hủy bỏ” bản dịch ban đầu và làm cho tam giác 4 ​​xuất hiện chính xác ở vị trí cần

Bây giờ chúng ta có thể vẽ một nhân vật được nhân đôi ở vị trí (100,0) bằng cách lật thế giới xung quanh tâm dọc của nhân vật

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
6

Lưu trữ và xóa các biến đổi

Chuyển đổi dính xung quanh. Mọi thứ khác mà chúng ta vẽ sau khi vẽ nhân vật được nhân đôi đó cũng sẽ được nhân đôi. Điều đó có thể bất tiện

Có thể lưu chuyển đổi hiện tại, thực hiện một số thao tác vẽ và chuyển đổi, sau đó khôi phục lại chuyển đổi cũ. Đây thường là điều thích hợp để làm đối với một chức năng cần tạm thời chuyển đổi hệ tọa độ. Đầu tiên, chúng tôi lưu bất kỳ phép biến đổi nào mà mã được gọi là hàm đang sử dụng. Sau đó, hàm thực hiện công việc của nó, thêm nhiều phép biến đổi lên trên phép biến đổi hiện tại. Cuối cùng, chúng tôi trở lại quá trình chuyển đổi mà chúng tôi đã bắt đầu với

Các phương thức

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
00 và
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
01 trên bối cảnh canvas 2D thực hiện việc quản lý chuyển đổi này. Về mặt khái niệm, họ giữ một chồng các trạng thái biến đổi. Khi bạn gọi
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
00, trạng thái hiện tại được đẩy lên ngăn xếp và khi bạn gọi
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
01, trạng thái trên cùng của ngăn xếp sẽ được tắt và được sử dụng làm phép chuyển đổi ngữ cảnh hiện tại. Bạn cũng có thể gọi
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
04 để đặt lại hoàn toàn quá trình chuyển đổi

Hàm

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
05 trong ví dụ sau minh họa những gì bạn có thể làm với một hàm thay đổi phép biến đổi và sau đó gọi một hàm (trong trường hợp này là chính nó), hàm này tiếp tục vẽ với phép biến đổi đã cho

Hàm này vẽ một hình dạng giống cái cây bằng cách vẽ một đường, di chuyển tâm của hệ tọa độ đến cuối đường và gọi chính nó hai lần—đầu tiên xoay sang trái rồi xoay sang phải. Mỗi cuộc gọi làm giảm độ dài của nhánh được vẽ và đệ quy dừng khi độ dài giảm xuống dưới 8

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
7

Nếu các cuộc gọi đến

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
00 và
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
01 không có ở đó, cuộc gọi đệ quy thứ hai đến
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
05 sẽ kết thúc với vị trí và vòng quay được tạo bởi cuộc gọi đầu tiên. Nó sẽ không được kết nối với nhánh hiện tại mà là với nhánh trong cùng, ngoài cùng bên phải được vẽ bởi lệnh gọi đầu tiên. Hình dạng kết quả cũng có thể thú vị, nhưng nó chắc chắn không phải là một cái cây

Trở lại cuộc chơi

Bây giờ chúng ta đã biết đủ về vẽ canvas để bắt đầu làm việc trên hệ thống hiển thị dựa trên canvas cho trò chơi từ chương trước. Màn hình mới sẽ không còn chỉ hiển thị các hộp màu. Thay vào đó, chúng tôi sẽ sử dụng

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
02 để vẽ những bức tranh đại diện cho các yếu tố của trò chơi

Chúng tôi định nghĩa một loại đối tượng hiển thị khác có tên là

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
10, hỗ trợ cùng một giao diện như
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
11 từ Chương 16, cụ thể là các phương thức
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
12 và
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
13

Đối tượng này giữ nhiều thông tin hơn một chút so với

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
11. Thay vì sử dụng vị trí cuộn của phần tử DOM, nó theo dõi chế độ xem của chính nó, cho chúng tôi biết phần nào của cấp độ mà chúng tôi hiện đang xem. Cuối cùng, nó giữ một thuộc tính
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
15 để ngay cả khi người chơi đang đứng yên, nó vẫn quay mặt về hướng mà nó di chuyển lần cuối

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
8

Phương pháp

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
12 trước tiên tính toán một khung nhìn mới và sau đó vẽ cảnh trò chơi ở vị trí thích hợp

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
9

Trái ngược với

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
11, kiểu hiển thị này phải vẽ lại nền trên mỗi lần cập nhật. Vì các hình trên khung vẽ chỉ là các pixel nên sau khi chúng ta vẽ chúng, không có cách nào tốt để di chuyển chúng (hoặc xóa chúng). Cách duy nhất để cập nhật màn hình canvas là xóa nó và vẽ lại cảnh. Chúng tôi cũng có thể đã cuộn, điều này yêu cầu nền phải ở một vị trí khác

Phương pháp

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
18 tương tự như phương pháp
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
20 của
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
11. Nó kiểm tra xem trình phát có ở quá gần mép màn hình hay không và di chuyển khung nhìn khi trường hợp này xảy ra

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
60

Các cuộc gọi đến

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
21 và
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
22 đảm bảo rằng chế độ xem không hiển thị không gian bên ngoài cấp độ.
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
23 đảm bảo số kết quả không nhỏ hơn 0.
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
22 tương tự đảm bảo rằng một giá trị nằm dưới một giới hạn nhất định

Khi xóa màn hình, chúng tôi sẽ sử dụng màu hơi khác tùy thuộc vào trò chơi thắng (sáng hơn) hay thua (tối hơn)

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
61

Để vẽ nền, chúng ta chạy qua các ô có thể nhìn thấy trong chế độ xem hiện tại, sử dụng thủ thuật tương tự được sử dụng trong phương pháp

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
25 từ chương trước

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
62

Các ô không trống được vẽ bằng

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
02. Hình ảnh
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
27 chứa các hình ảnh được sử dụng cho các yếu tố khác ngoài trình phát. Nó bao gồm, từ trái sang phải, gạch ốp tường, gạch dung nham và sprite cho một đồng xu

Bạn có thể vẽ hình trong HTML không?

Các ô nền có kích thước 20 x 20 pixel vì chúng tôi sẽ sử dụng cùng tỷ lệ mà chúng tôi đã sử dụng trong

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
11. Do đó, phần bù cho gạch nham thạch là 20 (giá trị của ràng buộc
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
51) và phần bù cho tường là 0

Chúng tôi không bận tâm chờ tải hình ảnh sprite. Gọi

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
02 với một hình ảnh chưa được tải sẽ không làm gì cả. Do đó, chúng tôi có thể không vẽ trò chơi đúng cách trong một vài khung hình đầu tiên, trong khi hình ảnh vẫn đang tải, nhưng đó không phải là vấn đề nghiêm trọng. Vì chúng tôi liên tục cập nhật màn hình nên cảnh chính xác sẽ xuất hiện ngay khi quá trình tải kết thúc

Nhân vật đi bộ được hiển thị trước đó sẽ được sử dụng để đại diện cho người chơi. Mã vẽ nó cần chọn đúng sprite và hướng dựa trên chuyển động hiện tại của người chơi. Tám họa tiết đầu tiên chứa hoạt ảnh đi bộ. Khi người chơi di chuyển dọc theo một tầng, chúng tôi sẽ lướt qua chúng dựa trên thời gian hiện tại. Chúng tôi muốn chuyển đổi khung hình cứ sau 60 mili giây, vì vậy trước tiên thời gian được chia cho 60. Khi người chơi đứng yên, chúng tôi vẽ hình ảnh thứ chín. Trong các lần nhảy, được nhận ra bởi thực tế là tốc độ thẳng đứng không bằng 0, chúng tôi sử dụng sprite thứ mười, ngoài cùng bên phải

Bởi vì các họa tiết rộng hơn một chút so với đối tượng người chơi—24 thay vì 16 pixel để có một số khoảng trống cho bàn chân và cánh tay—phương pháp này phải điều chỉnh tọa độ x và chiều rộng theo một lượng nhất định (

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
31)

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
63

Phương thức

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
32 được gọi bởi
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
33, có nhiệm vụ vẽ tất cả các diễn viên trong trò chơi

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
64

Khi vẽ một cái gì đó không phải là trình phát, chúng tôi nhìn vào loại của nó để tìm phần bù của sprite chính xác. Gạch dung nham được tìm thấy ở offset 20 và hình đồng xu được tìm thấy ở 40 (hai lần

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
51)

Chúng ta phải trừ vị trí của khung nhìn khi tính toán vị trí của diễn viên vì (0,0) trên khung vẽ của chúng ta tương ứng với phía trên bên trái của khung nhìn, không phải phía trên bên trái của mức. Chúng tôi cũng có thể đã sử dụng

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
57 cho việc này. Dù bằng cách nào cũng hoạt động

Tài liệu này cắm màn hình mới vào

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
36

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
65

Lựa chọn giao diện đồ họa

Vì vậy, khi bạn cần tạo đồ họa trong trình duyệt, bạn có thể chọn giữa HTML, SVG và canvas thuần túy. Không có cách tiếp cận tốt nhất duy nhất hoạt động trong mọi tình huống. Mỗi lựa chọn đều có điểm mạnh và điểm yếu

HTML đơn giản có ưu điểm là đơn giản. Nó cũng tích hợp tốt với văn bản. Cả SVG và canvas đều cho phép bạn vẽ văn bản, nhưng chúng sẽ không giúp bạn định vị hoặc ngắt dòng văn bản đó khi văn bản đó chiếm nhiều hơn một dòng. Trong một hình ảnh dựa trên HTML, việc bao gồm các khối văn bản sẽ dễ dàng hơn nhiều

SVG có thể được sử dụng để tạo ra đồ họa sắc nét trông đẹp mắt ở mọi mức thu phóng. Không giống như HTML, nó được thiết kế để vẽ và do đó phù hợp hơn cho mục đích đó

Cả SVG và HTML đều xây dựng cấu trúc dữ liệu (DOM) đại diện cho ảnh của bạn. Điều này cho phép sửa đổi các phần tử sau khi chúng được vẽ. Nếu bạn cần thay đổi liên tục một phần nhỏ của bức tranh lớn để đáp ứng với những gì người dùng đang làm hoặc như một phần của hoạt ảnh, thì thực hiện việc đó trong canvas có thể tốn kém không cần thiết. DOM cũng cho phép chúng tôi đăng ký trình xử lý sự kiện chuột trên mọi thành phần trong ảnh (ngay cả trên các hình được vẽ bằng SVG). Bạn không thể làm điều đó với canvas

Nhưng cách tiếp cận theo hướng pixel của canvas có thể là một lợi thế khi vẽ một số lượng lớn các phần tử nhỏ. Thực tế là nó không xây dựng cấu trúc dữ liệu mà chỉ vẽ lặp đi lặp lại trên cùng một bề mặt pixel giúp canvas có chi phí cho mỗi hình dạng thấp hơn

Ngoài ra còn có các hiệu ứng, chẳng hạn như hiển thị từng pixel một cảnh (ví dụ: sử dụng công cụ dò tia) hoặc xử lý hậu kỳ hình ảnh bằng JavaScript (làm mờ hoặc bóp méo hình ảnh), chỉ có thể được xử lý thực tế bằng cách tiếp cận dựa trên pixel

Trong một số trường hợp, bạn có thể muốn kết hợp một số kỹ thuật này. Ví dụ: bạn có thể vẽ biểu đồ bằng SVG hoặc canvas nhưng hiển thị thông tin văn bản bằng cách định vị phần tử HTML ở trên cùng của hình ảnh

Đối với các ứng dụng không yêu cầu cao, việc bạn chọn giao diện nào thực sự không quan trọng lắm. Màn hình mà chúng tôi xây dựng cho trò chơi của mình trong chương này có thể đã được triển khai bằng cách sử dụng bất kỳ công nghệ đồ họa nào trong số ba công nghệ đồ họa này vì nó không cần vẽ văn bản, xử lý tương tác chuột hoặc làm việc với một số lượng lớn các thành phần

Tóm lược

Trong chương này, chúng ta đã thảo luận về các kỹ thuật vẽ đồ họa trong trình duyệt, tập trung vào phần tử

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
6

Nút canvas đại diện cho một khu vực trong tài liệu mà chương trình của chúng tôi có thể vẽ trên đó. Bản vẽ này được thực hiện thông qua một đối tượng ngữ cảnh vẽ, được tạo bằng phương thức

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
22

Giao diện vẽ 2D cho phép chúng ta tô và kẻ các hình dạng khác nhau. Thuộc tính

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
26 của bối cảnh xác định cách các hình dạng được lấp đầy. Các thuộc tính
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
27 và
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
28 kiểm soát cách vẽ các đường

Hình chữ nhật và các đoạn văn bản có thể được vẽ bằng một lệnh gọi phương thức duy nhất. Các phương thức

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
24 và
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
25 vẽ hình chữ nhật và các phương thức
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
56 và
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
57 vẽ văn bản. Để tạo các hình dạng tùy chỉnh, trước tiên chúng ta phải xây dựng một đường dẫn

Gọi

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
46 bắt đầu một đường dẫn mới. Một số phương pháp khác thêm các đường thẳng và đường cong vào đường dẫn hiện tại. Ví dụ,
<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
62 có thể thêm một đường thẳng. Khi một đường dẫn kết thúc, nó có thể được lấp đầy bằng phương pháp
<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
65 hoặc vuốt bằng phương pháp
<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
61

Việc di chuyển pixel từ một hình ảnh hoặc canvas khác lên canvas của chúng tôi được thực hiện bằng phương thức

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
02. Theo mặc định, phương pháp này vẽ toàn bộ hình ảnh nguồn, nhưng bằng cách cung cấp cho nó nhiều tham số hơn, bạn có thể sao chép một khu vực cụ thể của hình ảnh. Chúng tôi đã sử dụng tính năng này cho trò chơi của mình bằng cách sao chép các tư thế riêng lẻ của nhân vật trong trò chơi ra khỏi một hình ảnh chứa nhiều tư thế như vậy

Các phép biến đổi cho phép bạn vẽ một hình dạng theo nhiều hướng. Bối cảnh bản vẽ 2D có một phép biến đổi hiện tại có thể được thay đổi bằng các phương thức

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
57,
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
51 và
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
56. Những điều này sẽ ảnh hưởng đến tất cả các hoạt động vẽ tiếp theo. Trạng thái chuyển đổi có thể được lưu bằng phương pháp
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
00 và được khôi phục bằng phương pháp
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
01

Khi hiển thị hoạt ảnh trên khung vẽ, phương thức

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
08 có thể được sử dụng để xóa một phần khung vẽ trước khi vẽ lại

bài tập

Hình dạng

Viết chương trình vẽ các hình sau trên canvas

  1. Hình thang (hình chữ nhật có một cạnh rộng hơn)

  2. Một viên kim cương màu đỏ (một hình chữ nhật xoay 45 độ hoặc ¼π radian)

  3. Một đường ngoằn ngoèo

  4. Một hình xoắn ốc được tạo thành từ 100 đoạn thẳng

  5. một ngôi sao màu vàng

Bạn có thể vẽ hình trong HTML không?

Khi vẽ hai cái cuối cùng, bạn có thể tham khảo phần giải thích về

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
57 và
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
58 trong Chương 14, mô tả cách lấy tọa độ trên một đường tròn bằng các hàm này

Tôi khuyên bạn nên tạo một chức năng cho từng hình dạng. Chuyển vị trí và các thuộc tính tùy chọn khác như kích thước hoặc số điểm, làm tham số. Giải pháp thay thế, đó là mã số cứng trên tất cả mã của bạn, có xu hướng làm cho mã khó đọc và sửa đổi một cách không cần thiết

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
66

Hình thang (1) dễ vẽ nhất bằng đường dẫn. Chọn tọa độ trung tâm phù hợp và thêm từng góc trong bốn góc xung quanh trung tâm

Viên kim cương (2) có thể được vẽ theo cách đơn giản, với một đường dẫn hoặc cách thú vị, với phép biến đổi

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
56. Để sử dụng phép quay, bạn sẽ phải áp dụng một thủ thuật tương tự như những gì chúng ta đã làm trong hàm
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
58. Vì bạn muốn xoay quanh tâm hình chữ nhật của mình chứ không phải quanh điểm (0,0), trước tiên bạn phải
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
57 đến đó, sau đó xoay, rồi dịch ngược lại

Đảm bảo rằng bạn đặt lại phép biến đổi sau khi vẽ bất kỳ hình dạng nào tạo ra một

Đối với đường ngoằn ngoèo (3), việc viết lệnh gọi mới tới

<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
62 cho mỗi đoạn đường trở nên không thực tế. Thay vào đó, bạn nên sử dụng một vòng lặp. Bạn có thể yêu cầu mỗi lần lặp vẽ hai đoạn thẳng (phải rồi lại trái) hoặc một đoạn, trong trường hợp đó, bạn phải sử dụng độ đều (_______763) của chỉ số vòng lặp để xác định đi sang trái hay sang phải

Bạn cũng sẽ cần một vòng cho hình xoắn ốc (4). Nếu bạn vẽ một loạt các điểm, với mỗi điểm di chuyển xa hơn dọc theo một vòng tròn xung quanh tâm của hình xoắn ốc, bạn sẽ có một vòng tròn. Nếu trong vòng lặp, bạn thay đổi bán kính của vòng tròn mà bạn đang đặt điểm hiện tại và đi vòng quanh nhiều lần, thì kết quả là một hình xoắn ốc

Ngôi sao (5) được mô tả được xây dựng từ

<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
68 dòng. Bạn cũng có thể vẽ một cái bằng các đường thẳng. Chia một vòng tròn thành tám phần cho một ngôi sao có tám điểm, hoặc bao nhiêu phần bạn muốn. Vẽ các đường giữa các điểm này, làm cho chúng cong về phía tâm của ngôi sao. Với
<p>Normal HTML here.</p>
<svg xmlns="http://www.w3.org/2000/svg">
  <circle r="50" cx="50" cy="50" fill="red"/>
  <rect x="120" y="5" width="90" height="90"
        stroke="blue" fill="none"/>
</svg>
68, bạn có thể sử dụng trung tâm làm điểm kiểm soát

Biểu đồ hình tròn

Trước đó trong chương này, chúng ta đã xem một chương trình ví dụ vẽ biểu đồ hình tròn. Sửa đổi chương trình này để tên của mỗi danh mục được hiển thị bên cạnh lát đại diện cho nó. Cố gắng tìm một cách dễ nhìn để tự động định vị văn bản này cũng sẽ hoạt động cho các tập dữ liệu khác. Bạn có thể cho rằng các danh mục đủ lớn để có nhiều chỗ cho nhãn của chúng

Bạn có thể cần lại

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
58 và
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
57, được mô tả trong Chương 14

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
67

Bạn sẽ cần gọi

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
56 và thiết lập các thuộc tính
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
75 và
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
78 của ngữ cảnh sao cho văn bản kết thúc ở nơi bạn muốn

Một cách hợp lý để định vị các nhãn là đặt văn bản trên đường đi từ tâm của chiếc bánh đến giữa lát. Bạn không muốn đặt văn bản trực tiếp vào mặt bên của chiếc bánh mà muốn di chuyển văn bản ra bên cạnh chiếc bánh theo một số pixel nhất định

Góc của đường này là

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
71. Đoạn mã sau tìm một vị trí trên dòng này cách trung tâm 120 pixel

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
68

Đối với

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
78, giá trị
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
00 có thể phù hợp khi sử dụng phương pháp này. Việc sử dụng cho
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
75 phụ thuộc vào việc chúng ta đang ở phía nào của vòng tròn. Ở bên trái, nó phải là
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
75 và ở bên phải, nó phải là
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
76, để văn bản được đặt cách xa chiếc bánh

Nếu bạn không chắc làm thế nào để tìm ra một góc cho trước nằm ở phía nào của đường tròn, hãy xem phần giải thích về

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
57 trong Chương 14. Côsin của một góc cho chúng ta biết nó tương ứng với tọa độ x nào, từ đó cho chúng ta biết chính xác chúng ta đang ở phía nào của vòng tròn

Một quả bóng nảy

Sử dụng kỹ thuật

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
78 mà chúng ta đã thấy trong Chương 14 và Chương 16 để vẽ một chiếc hộp có một quả bóng đang nảy trong đó. Quả bóng di chuyển với tốc độ không đổi và bật ra khỏi các mặt của hộp khi nó chạm vào chúng

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
69

Một hộp rất dễ vẽ với

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
25. Xác định một ràng buộc giữ kích thước của nó hoặc xác định hai ràng buộc nếu chiều rộng và chiều cao của hộp của bạn khác nhau. Để tạo một quả bóng tròn, hãy bắt đầu một đường dẫn và gọi
let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
80, lệnh này sẽ tạo ra một vòng cung đi từ 0 đến hơn một vòng tròn. Sau đó điền vào đường dẫn

Để lập mô hình vị trí và tốc độ của quả bóng, bạn có thể sử dụng lớp

let circle = document.querySelector("circle");
circle.setAttribute("fill", "cyan");
81 từ Chương 16 (có sẵn trên trang này). Cung cấp cho nó một tốc độ bắt đầu, tốt nhất là tốc độ không hoàn toàn theo chiều dọc hoặc chiều ngang và đối với mỗi khung hình, hãy nhân tốc độ đó với lượng thời gian đã trôi qua. Khi quả bóng đến quá gần bức tường thẳng đứng, hãy đảo ngược thành phần x theo tốc độ của nó. Tương tự như vậy, đảo ngược thành phần y khi nó chạm vào bức tường nằm ngang

Sau khi tìm thấy vị trí và tốc độ mới của quả bóng, hãy sử dụng

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
08 để xóa cảnh và vẽ lại bằng vị trí mới

Phản chiếu được tính toán trước

Một điều đáng tiếc về các phép biến đổi là chúng làm chậm quá trình vẽ ảnh bitmap. Vị trí và kích thước của mỗi pixel phải được chuyển đổi và mặc dù có thể các trình duyệt sẽ trở nên thông minh hơn trong việc chuyển đổi trong tương lai, nhưng chúng hiện đang gây ra sự gia tăng có thể đo lường được về thời gian cần thiết để vẽ một bitmap

Trong một trò chơi như trò chơi của chúng tôi, nơi chúng tôi chỉ vẽ một nhân vật được biến đổi duy nhất, đây không phải là vấn đề. Nhưng hãy tưởng tượng rằng chúng ta cần vẽ hàng trăm ký tự hoặc hàng nghìn hạt đang quay từ một vụ nổ

Hãy nghĩ ra một cách cho phép chúng tôi vẽ một ký tự đảo ngược mà không cần tải các tệp hình ảnh bổ sung và không phải thực hiện các cuộc gọi

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
02 đã chuyển đổi trong mỗi khung hình

Chìa khóa của giải pháp là chúng ta có thể sử dụng phần tử canvas làm hình ảnh nguồn khi sử dụng

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
02. Có thể tạo thêm một phần tử
<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
6 mà không cần thêm phần tử đó vào tài liệu và vẽ các họa tiết đảo ngược của chúng ta vào phần tử đó, một lần. Khi vẽ một khung thực tế, chúng ta chỉ cần sao chép các họa tiết đã được đảo ngược vào khung vẽ chính

Cần phải cẩn thận vì hình ảnh không tải ngay lập tức. Chúng tôi chỉ thực hiện bản vẽ đảo ngược một lần và nếu chúng tôi thực hiện trước khi tải hình ảnh, nó sẽ không vẽ được gì. Trình xử lý

<canvas></canvas>
<script>
  let cx = document.querySelector("canvas").getContext("2d");
  cx.strokeStyle = "blue";
  cx.strokeRect(5, 5, 50, 50);
  cx.lineWidth = 5;
  cx.strokeRect(135, 5, 50, 50);
</script>
05 trên hình ảnh có thể được sử dụng để vẽ các hình ảnh đảo ngược vào khung vẽ bổ sung. Khung vẽ này có thể được sử dụng làm nguồn vẽ ngay lập tức (nó sẽ trống cho đến khi chúng ta vẽ ký tự lên đó)

Chúng ta có thể vẽ hình trong HTML không?

Bạn có thể vẽ các hình dạng như hình tròn, hình chữ nhật, đường thẳng, v.v. bằng cách sử dụng SVG trong HTML5 một cách dễ dàng . Hãy xem một ví dụ để vẽ một hình chữ nhật bằng SVG.

Làm cách nào để vẽ hình chữ nhật trong HTML?

Tổng quan .
fillRect(x, y, chiều rộng, chiều cao). Vẽ một hình chữ nhật đầy
strokeRect(x, y, chiều rộng, chiều cao). Vẽ đường viền hình chữ nhật
ClearRect(x, y, chiều rộng, chiều cao). Xóa vùng hình chữ nhật được chỉ định, làm cho nó hoàn toàn trong suốt

Làm cách nào để vẽ đồ họa trong HTML?

Phần tử HTML . Phần tử

CSS có thể sử dụng hình dạng không?

CSS có khả năng tạo mọi loại hình . Hình vuông và hình chữ nhật rất dễ dàng vì chúng là hình dạng tự nhiên của web. Thêm chiều rộng và chiều cao và bạn có hình chữ nhật kích thước chính xác mà bạn cần. Thêm bán kính đường viền và bạn có thể làm tròn hình dạng đó và đủ để bạn có thể biến các hình chữ nhật đó thành hình tròn và hình bầu dục.