习题

形状

编写一个程序,在画布上画出下面的图形。

  1. 一个梯形(一个在一边比较长的矩形)

  2. 一个红色的钻石(一个矩形旋转45度角)

  3. 一个锯齿线

  4. 一个由 100 条直线线段构成的螺旋

  5. 一个黄色的星星

习题 - 图1

当绘制最后两个图形时,你可以参考第 14 章中的Math.cosMath.sin的解释,它描述了如何使用这两个函数获得圆上的坐标。

建议你为每一个图形创建一个方法,传入坐标信息,以及其他的一些参数,比如大小或者点的数量。另一种方法,可以在你的代码中硬编码,会使得你的代码变得难以阅读和修改。

  1. <canvas width="600" height="200"></canvas>
  2. <script>
  3. let cx = document.querySelector("canvas").getContext("2d");
  4. // Your code here.
  5. </script>

饼状图

在本章的前部分,我们看到一个绘制饼状图的样例程序。修改这个程序,使得每个部分的名字可以被显示在相应的切片旁边。试着找到一个合适的方法来自动放置这些文字,同时也可以适用于其他数据。你可以假设分类大到足以为标签留出空间。

你可能还会需要Math.sinMath.cos方法,像第 14 章描述的一样。

  1. <canvas width="600" height="300"></canvas>
  2. <script>
  3. let cx = document.querySelector("canvas").getContext("2d");
  4. let total = results
  5. .reduce((sum, {count}) => sum + count, 0);
  6. let currentAngle = -0.5 * Math.PI;
  7. let centerX = 300, centerY = 150;
  8. // Add code to draw the slice labels in this loop.
  9. results.forEach(function(result) {
  10. for (let result of results) {
  11. let sliceAngle = (result.count / total) * 2 * Math.PI;
  12. cx.arc(centerX, centerY, 100,
  13. currentAngle, currentAngle + sliceAngle);
  14. currentAngle += sliceAngle;
  15. cx.lineTo(centerX, centerY);
  16. cx.fillStyle = result.color;
  17. cx.fill();
  18. }
  19. </script>

弹力球

使用在第 14 章和第 16 章出现的requestAnimationFrame方法画出一个装有弹力球的盒子。这个球匀速运动并且当撞到盒子的边缘的时候反弹。

  1. <canvas width="400" height="400"></canvas>
  2. <script>
  3. let cx = document.querySelector("canvas").getContext("2d");
  4. let lastTime = null;
  5. function frame(time) {
  6. if (lastTime != null) {
  7. updateAnimation(Math.min(100, time - lastTime) / 1000);
  8. }
  9. lastTime = time;
  10. requestAnimationFrame(frame);
  11. }
  12. requestAnimationFrame(frame);
  13. function updateAnimation(step) {
  14. // Your code here.
  15. }
  16. </script>

预处理镜像

当进行图形变换时,绘制位图图像会很慢。每个像素的位置和大小都必须进行变换,尽管将来浏览器可能会更加聪明,但这会导致绘制位图所需的时间显着增加。

在一个像我们这样的只绘制一个简单的子画面图像变换的游戏中,这个不是问题。但是如果我们需要绘制成百上千的角色或者爆炸产生的旋转粒子时,这将会成为一个问题。

思考一种方法来允许我们不需要加载更多的图片文件就可以画出一个倒置的角色,并且不需要在每一帧调用drawImage方法。