七、画布简介

在这一章中,我们将学习 HTML 画布。HTML 画布有助于您绘制,尤其是 HTML 页面上的图形(例如,圆形、正方形、矩形等)。<canvas></canvas>标签通常由 JavaScript 控制。Canvas 可以绘制文本,也可以制作动画。让我们看看我们可以使用 HTML 画布做什么。

实现画布

要在您的 HTML 页面上添加画布,您需要在<canvas></canvas>标签中定义画布的高度和宽度,如下所示:

<html>
  <head>
    <title>Canvas</title>
  </head>
  <body>
  <canvas id="canvasTest" width="200" height="100" style="border:2px solid #000;">

    </canvas>
  </body>
</html>

我们已经将画布 ID 定义为canvasTest,将用来玩画布。我们在画布上使用了内嵌 CSS。2 px 实心边框用于更好地查看画布。

添加 JavaScript

现在,我们将为我们的画布添加几行 JavaScript。我们需要在<script></script>标签中的<canvas></canvas>标签之后添加我们的 JavaScript。

画一个长方形

为了测试我们的画布,让我们通过输入下面的代码在画布中绘制一个矩形:

<script type="text/javascript">
  var canvas = document.getElementById("canvasTest"); //called our canvas by id
  var canvasElement = canvas.getContext("2d"); // made our canvas 2D
  canvasElement.fillStyle = "black"; //Filled the canvas black
  canvasElement.fillRect(10, 10, 50, 50); //created a rectangle
</script>

在脚本中,我们声明了两个 JavaScript 变量。canvas变量用于使用画布标识保存画布的内容,我们在<canvas></canvas>标签中使用了画布标识。canvasElement变量用于保存画布的上下文。我们将black指定给fillstyle,这样我们想要绘制的矩形在填充后会变成黑色。我们使用canvasElement.fillRect(x, y, w, h);作为矩形的形状。其中x是矩形距离 x 轴的距离;y是矩形距离 y 轴的距离;而wh分别是矩形的宽度和高度。

完整的代码如下所示:

<html>
  <head>
    <title>Canvas</title>
  </head>
  <body>
    <canvas id="canvasTest" width="200" height="100" style="border:2px solid #000;">
    </canvas>
    <script type="text/javascript">
      var canvas = document.getElementById("canvasTest"); //called our canvas by id
      var canvasElement = canvas.getContext("2d"); // made our canvas 2D
      canvasElement.fillStyle = "black"; //Filled the canvas black
      canvasElement.fillRect(10, 10, 50, 50); //created a rectangle
    </script>
  </body>
</html>

代码的输出如下:

Drawing a rectangle

画线

要在画布上绘制一条线,您需要在您的<script></script>标签中插入以下代码:

<script type="text/javascript">
  var c = document.getElementById("canvasTest");
  var canvasElement = c.getContext("2d");
  canvasElement.moveTo(0,0);
  canvasElement.lineTo(100,100);
  canvasElement.stroke();
</script>

这里,canvasElement.moveTo(0,0);用来让我们的线从画布的(0,0)坐标开始。canvasElement.lineTo(100,100);语句用于使线对角化。canvasElement.stroke();语句用于使线条可见。我建议您更改canvasElement.lineTo(100,100);canvasElement.moveTo(0,0);中的数字,并查看画布上绘制的线条的变化。

下面是代码的输出:

Drawing a line

快速锻炼

  1. 使用画布和 JavaScript 绘制一条线,该线将平行于画布的 y 轴。
  2. 画一个高 300 像素宽 200 像素的矩形。在同一画布上画一条线,接触矩形。

画一个圆

要在画布中绘制一个圆,需要在<script></script>标签中添加以下代码:

<script type="text/javascript">
  var c = document.getElementById("canvasTest");
  var canvasElement = c.getContext("2d");
  canvasElement.beginPath();
  canvasElement.arc(95,50,40,0,2*Math.PI);
  canvasElement.stroke();
</script>

这里我们用canvasElement.beginPath();开始画圆,canvasElement.arc(95,50,40,0,2*Math.PI);为圆的形状,canvasElement.stroke();设置圆可见。

canvasElement.arc(95,50,40,0,2*Math.PI);语句类似于canvasElement.arc(x, y, r, sA, eA, ac);

其中x是从 x 轴开始的坐标,y是从 y 轴开始的坐标,r是圆的半径,sA是圆的起始角,eA是圆的结束角,ac是圆的方向。这里,ac表示逆时针。

我们的代码的输出将是如下图像:

Drawing a circle

绘制线性渐变

让我们画一些新的东西。我们将绘制一个矩形并使其颜色逐渐淡化。在您的<script></script>标签中键入以下代码:

<script type="text/javascript">
  var c = document.getElementById("canvasTest");
  var canvasElement = c.getContext("2d");
  // Create the gradient
  var grdient = canvasElement.createLinearGradient(0,0,100,0);
  grdient.addColorStop(0,"blue"); // here we added blue as our primary color
  grdient.addColorStop(1,"white"); //here we used white as our secondary color. 
  // Fill with gradient
  canvasElement.fillStyle = grdient;
  canvasElement.fillRect(10,10,150,80);
</script>

我们添加了canvasElement.createLinearGradient(0,0,100,0);来创建渐变或渐变。我们添加了grdient.addColorStop(0,"blue");grdient.addColorStop(1,"white");来给矩形上色。

代码的输出如下图所示:

Draw linear gradient

快速锻炼

  1. Draw the following smiley face using HTML canvas. (Hint: you will have to draw three full circles and a half circle. The trick is that you can draw the figure by playing with the code of circle for the canvas.):

    A quick exercise

  2. 用颜色渐变画一个圆。

我们做个钟吧!

我们要画一个模拟时钟,让它像真正的时钟一样工作。在 HTML 文档的正文部分,键入以下代码:

<canvas id="myclock" height="500" width="500"></canvas>
In your <script></script> tags, take the following variables:
Var canvas; // the clock canvas
var canvasElement; // canvas's elements

// clock settings
var cX = 0; 
var cY = 0;
var radius = 150;

这里,cXcY是我们时钟的中心坐标。我们把 150 像素作为时钟的半径。您可以增加或减少它。

然后,我们需要初始化变量。在定义了前面的变量之后,创建一个init();函数。

该函数应该类似于以下内容:

function init() {

  canvas = document.getElementById("myclock");
  //Called the element to work on. 
  canvasElement = canvas.getContext("2d");
  //Made the context 2d. 

  cX = canvas.width / 2;
  // we divided by two to get the middle point of X-axis
  cY = canvas.height / 2;
  // we divided by two to get the middle point of Y-axis
  initTime(); //called the initTime() function.
  drawClock(); //Called the drawClock() function to draw the graphics. 

  setInterval("animateClock()", 1000); // Made the animation for each second. Here 1000 is equal to 1 second. 

}

让我们初始化时钟的秒针、分针和时针:

function initTime() {
  date = new Date();
  hours = date.getHours() % 12; // Divided by 12 to make our clock 12 hours. 
  minutes = date.getMinutes(); 
  seconds = date.getSeconds();

}

这里,date.getHours()date.getMinutes()date.getSeconds()会返回你电脑的时间,保存在我们的变量里。

让成为另一个激活我们的时钟的功能:

function animateClock() {
  //This function will help our 'second' hand to move after an interval. 
  clearCanvas(); // This will clear the canvas 
  refreshTime(); // This will refresh time after 1 second. 
  drawClock();   // This will draw the clock. 

}

我们现在写clearCanvas()refreshTime()drawClock():

function clearCanvas() {
  canvasElement.clearRect(0, 0, canvas.width, canvas.height);
}

这里,canvasElement.clearRect(0, 0, canvas.width, canvas.height);会在一定的时间间隔后重置我们的画布。

我们的refreshTime()功能应该如下图所示:

function refreshTime() {
  seconds += 1;
  if (Math.floor((seconds / 60)) != 0) { //we divide seconds by 60 until second is equal to zero. 
    minutes += 1; // If 60 second is passed we increment minute by 1\. 
    seconds %= 60; 
  }
  if (Math.floor((minutes / 60)) != 0) { 
    hours += 1; //We increment hour by 1 after 60 minutes. 
    minutes %= 60; 
  }
}

我们在refreshTime()函数中增加了seconds变量。因此,每当调用这个函数时,我们的变量将增加1。然后,我们为我们的hoursminutes做了两个条件运算。

现在,让我们画出时钟:

function drawClock() {
  drawClockBackground(); //This draws clock background. 
  drawSecondsHand(); //This draws clock's second hand. 
  drawMinutesHand(); //This draws clock's minute hand. 
  drawHoursHand(); //This draws clock's hour hand.
}

我们将编写drawClockBackground()drawSecondsHand()drawMinutesHand()drawHoursHand()功能:

function drawClockBackground() {
  //this function will draw the background of our clock. We are declaring few variables for mathematical purposes. 
  var correction = 1/300;
  var shift_unit = 1/170;
  var shift_factor = 1/30;
  var angle_initial_position = 2;
  var angle_current_position_begin = 0;
  var angle_current_position_end = 0;
  var repeat = 60;
  var lineWidth = 10;

  for (var i=0; i < repeat; i+=1) {
  //These lines are written for making our clock error free with the angle of the hands (hands' positions)
  angle_current_position_begin = angle_initial_position - (i * shift_factor) - correction;
  angle_current_position_end = angle_current_position_begin + shift_unit;

  if (i % 5 === 0) 
  lineWidth = 20;
  else 
  lineWidth = 10;

  drawArcAtPosition(cX, cY, radius, angle_current_position_begin*Math.PI, angle_current_position_end*Math.PI, false, lineWidth);
  }
  drawLittleCircle(cX, cY);
}

我们在这个函数中做了一些数学上的事情,并在我们的时钟中心写了一个小圆的drawLittleCircle(cX, cY)函数。

该函数应该类似于以下内容:

function drawLittleCircle(cX, cY) {
  drawArcAtPosition(cX, cY, 4, 0*Math.PI, 2*Math.PI, false, 4);
}

drawSecondsHand()功能。该函数将绘制秒针,如下所示:

function drawSecondsHand() {
  /* Simple mathematics to find the co ordinate of the second hand; 
    You may know this: x = rcos(theta), y = rsin(theta). We used these here.
    We divided the values n=by 30 because after 5 seconds the second hand moves 30 degree. 
  */ 
  endX = cX + radius*Math.sin(seconds*Math.PI / 30);
  endY = cY - radius*Math.cos(seconds*Math.PI / 30);
  drawHand(cX, cY, endX, endY);
}

我们的drawMinutesHand()功能应该如下图所示。该功能将绘制我们时钟的分针,如下所示:

function drawMinutesHand() {
  var rotationUnit = minutes + seconds / 60;
  var rotationFactor = Math.PI / 30;
  var rotation = rotationUnit*rotationFactor;
  var handLength = 0.8*radius;
  endX = cX + handLength*Math.sin(rotation);
  endY = cY - handLength*Math.cos(rotation);
  drawHand(cX, cY, endX, endY);
}

现在,让我们看看我们的drawHoursHand();功能。该功能将绘制时针:

function drawHoursHand() {
  var rotationUnit = 5 * hours + minutes / 12;
  var rotationFactor = Math.PI / 30;
  var rotation = rotationUnit*rotationFactor;
  var handLength = 0.4*radius;

  endX = cX + handLength*Math.sin(rotation);
  endY = cY - handLength*Math.cos(rotation);
  drawHand(cX, cY, endX, endY);
}

我们在前面的函数中使用了drawHand();函数。让我们编写函数,如下所示:

function drawHand(beginX, beginY, endX, endY) {
  canvasElement.beginPath();
  canvasElement.moveTo(beginX, beginY);
  canvasElement.lineTo(endX, endY);
  canvasElement.stroke();
  canvasElement.closePath();
}

现在,我们将为我们的时钟编写最后一个函数,如下面的代码片段所示:

function drawArcAtPosition(cX, cY, radius, start_angle, end_angle, counterclockwise, lineWidth) {
  canvasElement.beginPath();
  canvasElement.arc(cX, cY, radius, start_angle, end_angle, counterclockwise);
  canvasElement.lineWidth = lineWidth;
  canvasElement.strokeStyle = "black";
  canvasElement.stroke();
  canvasElement.closePath();
}

我们时钟的完整代码应该类似于下面的代码:

<html>
  <head>
    <script type="text/javascript">
      var canvas; 
      var canvasElement;

      // clock settings
      var cX = 0;

      var cY = 0;
      var radius = 150;

      // time settings
      var date;
      var hours;
      var minutes;
      var seconds;

      function init() {
        canvas = document.getElementById("myclock");
        canvasElement = canvas.getContext("2d");

        cX = canvas.width / 2;
        cY = canvas.height / 2;

        initTime();
        drawClock();
        setInterval("animateClock()", 1000);
      }

      // get your system time
      function initTime() {
        date = new Date();
        hours = date.getHours() % 12;
        minutes = date.getMinutes();
        seconds = date.getSeconds();
      }

      // animate the clock
      function animateClock() {
        clearCanvas();
        refreshTime();
        drawClock();
      }

      // clear the canvas
      function clearCanvas() {
        canvasElement.clearRect(0, 0, canvas.width, canvas.height);
      }

      // refresh time after 1 second
      function refreshTime() {
        seconds += 1;
        if (Math.floor((seconds / 60)) != 0) { minutes += 1; seconds %= 60; }
        if (Math.floor((minutes / 60)) != 0) { hours += 1; minutes %= 60; }
      }

      // draw or redraw Clock after time refresh function is called
      function drawClock() {
        drawClockBackground();
        drawSecondsHand();
        drawMinutesHand();
        drawHoursHand();
      }
      function drawHand(beginX, beginY, endX, endY) {
        canvasElement.beginPath();
        canvasElement.moveTo(beginX, beginY);
        canvasElement.lineTo(endX, endY);
        canvasElement.stroke();
        canvasElement.closePath();
      }

      // draw Hand for seconds
      function drawSecondsHand() {
        endX = cX + radius*Math.sin(seconds*Math.PI / 30);
        endY = cY - radius*Math.cos(seconds*Math.PI / 30);
        drawHand(cX, cY, endX, endY);
      }

      // draw Hand for minutes
      function drawMinutesHand() {
        var rotationUnit = minutes + seconds / 60;
        var rotationFactor = Math.PI / 30;
        var rotation = rotationUnit*rotationFactor;
        var handLength = 0.8*radius;

        endX = cX + handLength*Math.sin(rotation);
        endY = cY - handLength*Math.cos(rotation);
        drawHand(cX, cY, endX, endY);
      }

      // draw Hand for hours
      function drawHoursHand() {
        var rotationUnit = 5 * hours + minutes / 12;
        var rotationFactor = Math.PI / 30;
        var rotation = rotationUnit*rotationFactor;
        var handLength = 0.4*radius;

        endX = cX + handLength*Math.sin(rotation);
        endY = cY - handLength*Math.cos(rotation);
        drawHand(cX, cY, endX, endY);
      }

      function drawClockBackground() {
        var correction = 1/300;
        var shift_unit = 1/170;
        var shift_factor = 1/30;
        var angle_initial_position = 2;
        var angle_current_position_begin = 0;
        var angle_current_position_end = 0;
        var repeat = 60;
        var lineWidth = 10;

        for (var i=0; i < repeat; i+=1) {
          angle_current_position_begin = angle_initial_position - (i * shift_factor) - correction;
          angle_current_position_end = angle_current_position_begin + shift_unit;

          if (i % 5 == 0) lineWidth = 20;
          else lineWidth = 10;

          drawArcAtPosition(cX, cY, radius, angle_current_position_begin*Math.PI, angle_current_position_end*Math.PI, false, lineWidth);
        }
        drawLittleCircle(cX, cY);
      }

      function drawArcAtPosition(cX, cY, radius, start_angle, end_angle, counterclockwise, lineWidth) {
        canvasElement.beginPath();
        canvasElement.arc(cX, cY, radius, start_angle, end_angle, counterclockwise);
        canvasElement.lineWidth = lineWidth;
        canvasElement.strokeStyle = "black";
        canvasElement.stroke();
        canvasElement.closePath();
      }
      function drawLittleCircle(cX, cY) {
        drawArcAtPosition(cX, cY, 4, 0*Math.PI, 2*Math.PI, false, 4);
      }

    </script>
  </head>
  <body onload="init()">
    <canvas id="myclock" height="500" width="500"></canvas>
  </body>
</html>

如果你可以看到你的代码输出如下图,那么恭喜你!您使用画布成功创建了您的 HTML 时钟:

Let's make a clock!

总结

在本章中,我们学习了 HTML 画布的基础知识。我希望您现在可以使用 HTML 画布绘制任何东西。你可能玩过网络游戏;其中大部分的组件都是使用 HTML 画布绘制的。因此,如果你想开发自己的网络应用或游戏,你需要学习画布。您可以使用 JavaScript 轻松地编写代码来绘制形状并制作动画。

在下一章中,我们将使用 HTML 画布开发一款名为鼠人的游戏。在开始第八章建造鼠人之前!,希望你通过这一章学到了很多关于 HTML 画布的知识。如果你有,让我们现在开发我们的游戏。