65.9K
CodeProject 正在变化。 阅读更多。
Home

HTML5/CSS3入门指南,第七课:Canvas编码

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2014年4月27日

CPOL

8分钟阅读

viewsIcon

15116

downloadIcon

1

通过创建一个简单的游戏介绍 HTML5 Canvas

引言


本文向读者介绍HTML5的canvas元素。它建立对核心canvas使用概念的基础理解;canvas创建、坐标、渐变、文本以及在canvas上绘图。然后,通过一个简单的游戏制作过程,展示HTML5中canvas的强大功能,在此基础上进一步扩展这些核心概念。

什么是Canvas?

简单来说,顾名思义,canvas就是一个绘制或涂写东西的地方。官方定义大致是,“canvas是一个与分辨率相关的位图画布,用于实时渲染图表、游戏图形或其他视觉图像”。

创建Canvas

通过使用canvas标签并指定宽度和高度属性,可以在HTML页面上创建一个矩形canvas区域。

<canvas id=”myCanvas” width=”800” height=”600”></canvas>

就像其他HTML元素一样,canvas可以应用样式。例如,创建一个80x60像素、边框宽度为1像素的黑色边框canvas

<canvas id="myCanvas" width="80" height="60"
style="border:1px solid #000000;">
</canvas>

为canvas元素提供id属性至关重要。JavaScript将使用该ID来引用canvas。

  • Canvas坐标、路径、文本、渐变、图像

既然我们知道如何创建canvas,在开始在其上绘制之前,我们需要了解它的一些布局。

canvas的左上角是原点,坐标为(0,0)。X值向右增加,Y值向下增加。因此,根据我们上面的80x60像素canvas示例,布局如下:

路径

HTML5中的canvas路径由一个或多个子路径组成。子路径被定义为canvas上两点之间的直线或曲线。可以使用以下方法连接两个canvas点形成子路径;lineTo()、arcTo()、quadraticCurveTo()和bezierCurveTo()。

需要注意的是,这些方法实际上并不会在canvas上绘制子路径。它们将新的子路径终点添加到整个路径中,而stroke()方法实际上将路径绘制到canvas上。

beginPath()方法用于指示要组合/处理为一个路径的多个子路径的开始。

closePath()可用于通过绘制一条从最后一个子路径的终点到第一个子路径的起点的线来“闭合”路径。

路径方法摘要

方法

描述

beginPath()

开始一条路径

closePath()

从当前点返回到起点创建一条路径

moveTo()

将路径移动到canvas中的指定点

lineTo()

添加一个新点,并从该点到canvas中最后指定的点创建一条线

quadraticCurveTo()

创建一条二次曲线

bezierCurveTo()

创建一条三次贝塞尔曲线

arc()

创建弧形/曲线

arcTo()

在两条切线之间创建弧形/曲线

stroke()

实际绘制您定义的路径

fill()

填充当前绘制路径

clip()

从原始canvas中裁剪任意形状和大小的区域

isPointInPath()

如果指定点在当前路径中,则返回true,否则返回false

以下示例使用路径和曲线方法创建了一个草书大写字母“B”

    <canvas id="myCanvas" width="300" height="200" style="border:1px solid #000000"></canvas>

    <script>

      var canvas = document.getElementById('myCanvas');

      var context = canvas.getContext('2d');


      context.beginPath();

      context.moveTo(20, 180);


      // line 1

      context.lineTo(25, 10);


      // bezier curve

      context.bezierCurveTo(190, -40, 200, 100, 40, 80);


      // bezier curve

      context.bezierCurveTo(190, 40, 250, 250, 45, 180);


      // quadratic curve

      context.quadraticCurveTo(230, 200, 250, 120);


      context.lineWidth = 5;

      context.strokeStyle = 'green';

      context.stroke();


    </script>

结果:

文本

HTML5 Canvas文本允许您使用各种字体、大小和颜色在canvas上绘制文本。文本可以使用fillText()(实心)或strokeText()(描边)绘制。上下文的font和fillStyle属性可用于控制文本字体、字号和颜色。

例如

var canvas  = document.getElementById("myCanvas");

var context = canvas.getContext("2d");


context.font      = "normal 24px Verdana";

context.fillStyle = "#FF0000";

context.fillText("Red Verdana Filled Text", 50, 40);


context.font        = "normal 36px Arial";

context.strokeStyle = "#0000FF";

context.strokeText("Blue Arial Stroke Text", 50, 80);


context.font        = "normal 24px Courier";

context.fillStyle = "#00FF21";

context.fillText("Green Courier Filled Text", 50, 120);

结果

渐变

使用以下方法创建线性渐变

Context.createLinearGradient(x0,y0,x1,y1);

其中

x0是渐变起点的x坐标

y0是渐变起点的y坐标

x1是渐变终点的x坐标

y1是渐变终点的y坐标

例如

var canvas=document.getElementById("myCanvas");

var context=canvas.getContext("2d");


//green to white upward gradient

var grd=context.createLinearGradient(0,50,0,0);

grd.addColorStop(0,"green");

grd.addColorStop(1,"white");

context.fillStyle=grd;

context.fillRect(10,10,90,90);


//red to white left to right gradient

grd=context.createLinearGradient(100,10,190,10);

grd.addColorStop(0,"red");

grd.addColorStop(1,"white");

context.fillStyle=grd;

context.fillRect(100,10,90,90);


//black to blue diagonal downward gradient

var grd=context.createLinearGradient(100,100,190,150);

grd.addColorStop(0,"black");

grd.addColorStop(1,"blue");

context.fillStyle=grd;

context.fillRect(100,100,90,90);


//RGB gradient right to left

var grd=context.createLinearGradient(100,10,10,10);

grd.addColorStop(0,"red");

grd.addColorStop(0.5,"green");

grd.addColorStop(1,"blue");

context.fillStyle=grd;

context.fillRect(10,100,90,90);

结果:

注意示例中使用了Gradient.addColorStop(stop,color)。stop是介于0和1之间的值,表示渐变中从起点到终点的过渡。例如,在示例的最后RGB部分,我们使用0、0.5和1的stop值,以便在从右向左过渡时获得均匀的渐变分布。

可以使用context.createRadialGradient(x0,y0,r0,x1,y1,r1)创建圆形或径向渐变图案,其中

x0是起始圆的x坐标

y0是起始圆的y坐标

r0是起始圆的半径

x1是结束圆的x坐标

y1是结束圆的y坐标

r1是结束圆的半径

与线性渐变一样,我们可以使用addColorStop()方法来设置渐变的过渡颜色。

例如

var canvas=document.getElementById("myCanvas");

var context=canvas.getContext("2d");

var grd=context.createRadialGradient(100,100,5,100,100,100);

grd.addColorStop(0,"red");

grd.addColorStop(0.5,"green");

grd.addColorStop(1,"blue");

context.fillStyle=grd;

context.fillRect(10,10,180,180);

结果:

使用JavaScript在Canvas上绘图

我们已经学习了几种使用JavaScript在canvas上绘图的方法,包括路径、文本和渐变。我们真的能在canvas上绘图吗?

到目前为止,我们一直在绘制或涂写元素到canvas上。在计算机世界之外,将物品绘制或涂写到canvas上被称为艺术(尽管在某些情况下“艺术”一词值得商榷)。本文的目的是通过开发一个简单的游戏来向读者介绍HTML5 Canvas的强大功能。为了实现这一点,我们需要让我们的艺术具有交互性。也就是说,用户输入需要影响屏幕上绘制的内容。

那么,我们能用用户输入在canvas上真正绘图吗?是的,借助事件处理程序,我们可以接受用户的鼠标输入。

canvas.addEventListener("mousemove", onMouseMove, false);

使用鼠标处理程序代码片段,我们可以创建一个非常简单的HTML5 Canvas绘画程序,用户可以使用鼠标在屏幕上绘画。

<!DOCTYPE html>

<html>

<body>

<canvas id="myCanvas" width="600" height="300" style="border:1px solid #d3d3d3;">

Your browser does not support the HTML5 canvas tag.</canvas>


<script>

function onMouseMove(event)

{

     var c = document.getElementById("myCanvas");

     var ctx = c.getContext("2d");

     var boundingRect = c.getBoundingClientRect();

     var x = event.clientX - boundingRect.left;

    var y = event.clientY - boundingRect.top;

     ctx.fillRect(x,y,5,5);

}


var canvas = document.getElementById("myCanvas");

var context = canvas.getContext("2d");

context.fillStyle = "#000000";

canvas.addEventListener("mousemove", onMouseMove, false);

</script>


</body>

</html>

我将把它留给读者作为练习,但我们可以轻松地看到如何将canvas绘图程序的功能扩展到其他方面。例如,鼠标单击即可更改画笔颜色,或者插入路径元素等。

Canvas示例

现在我们知道了如何在canvas上绘制各种元素,并且知道如何使用事件处理程序与用户输入交互并更改屏幕上的图形外观,我们就可以开始着手制作游戏了。

我有一个模板,作为我游戏的起点。对于那些曾经制作过XNA游戏的人来说,这个模板应该有些熟悉。因为它有一个主游戏循环,其中包含绘制和更新游戏内容的部分。

<!DOCTYPE HTML>

<html>

<head>

<style>

body {

margin: 0px;

padding 0px;

}

#myCanvas {

border: 1px solid #9c9898;

}

</style>

<script>

window.requestAnimFrame = (function(callback) {

return window.requestAnimationFrame ||

window.webkitRequestAnimationFrame ||

window.mozRequestAnimationFrame ||

window.oRequestAnimationFrame ||

window.msRequestAnimationFrame ||

function(callback) {

     window.setTimeout(callback, 1000 / 60);

     };

     })();

    

Global = {};       // universal stuff we want to access across functions


function initialize(){


}


function draw(context){

// draw object logic goes here, or calls to other draw functions


}


function update() {

// update position of objects


}


function animate() {

// main game loop

var canvas = document.getElementById('myCanvas');

var contex = canvas.getContext('2d');


//clear

context.clearRect(0,0,canvas.width, canvas.height);


//update position of objects

for(i=0; i<Global.{whatever}.length-1; i++){

     update(Global.{whatever}[i]);

}


// request new frame

requestAnimFrame(function() {

animate();

});

}


window.onload = function() {

     initialize();

     animate();

     }

    

     </script>

     </head>

     <body>

     <canvas id="myCanvas" width="800" height="600"></canvas>

     </body>

     </html>

这种方法与XNA的一个关键区别在于,XNA通过游戏计时器处理刷新。对于JavaScript,帧率是通过这段小代码中的回调函数处理的。

window.requestAnimFrame = (function(callback) {

return window.requestAnimationFrame ||

window.webkitRequestAnimationFrame ||

window.mozRequestAnimationFrame ||

window.oRequestAnimationFrame ||

window.msRequestAnimationFrame ||

function(callback) {

     window.setTimeout(callback, 1000 / 60);

     };

     })();

上面代码片段的关键一行是window.setTimeout(callback, 1000 / 60);这基本上意味着将1000毫秒(或1秒)分成60份,并在每次执行我们的回调。这提供了每秒60次屏幕刷新的帧率。

既然我们知道了游戏代码的基本布局以及游戏如何刷新屏幕,那么就开始工作吧。由于我缺乏原创性,而且我没有驻场的美术师,我将创建一个名为“Block Invaders”(方块入侵者)的游戏。你们都知道那个游戏,邪恶的外星方块以完美的矩形阵列模式向下飞行攻击。与此同时,我们通过躲在漂浮的盾牌后面并向它们射击来保护地球。

由于本文的重点是HTML5 canvas,我们将不对游戏逻辑进行深入回顾。然而,我们将指出游戏代码中使用了本文前面讨论过的canvas方法的区域。

可以想象,每个游戏元素都有一个相关的函数将其渲染到屏幕上。这些函数是从主动画游戏循环中调用的。例如,绘制坏人或外星人的函数。

function drawBadGuy(alien, context){

context.beginPath();

context.rect(alien.x, alien.y, alien.width, alien.height);

context.fillStyle = alien.fillStyle;

context.fill();

context.lineWidth = alien.borderthickness;

context.strokeStyle = 'black';

context.stroke();

}

主动画循环遍历存储敌对外星人的数组的所有元素,并调用drawBadGuy方法将每个外星人渲染到屏幕上。使用canvas的rect、fillStyle和lineWidth方法,我们可以轻松地创建绿色的方块入侵者。

基地或方块入侵者发射的子弹是使用canvas的arc方法创建的。

function drawBullet(b, context) {

context.beginPath();

context.arc(b.x,b.y,5,0,2*Math.PI);

context.fillStyle = 'yellow';

context.fill();

context.stroke();

}

保护玩家射击入侵者的屏障或护盾是使用canvas矩形数组创建的。这样做是为了在游戏中可以逐步被子弹摧毁护盾。我们使用径向渐变方法和addColorStop来表示护盾被子弹造成的局部损伤。

function drawBarrier(b, context) {

if(b.impactX == 0) {

      context.beginPath();

      context.rect(b.x, b.y, b.width, b.height);

      context.fillStyle = 'blue';

      context.fill();

}

else {

var grd=context.createRadialGradient(b.impactX, b.impactY,5,b.impactX,b.impactY,10);

      grd.addColorStop(0,"white");

      grd.addColorStop(1,"blue");

      context.fillStyle = grd;

      context.fillRect(b.x, b.y, b.width, b.height);

}

}

我们在游戏中使用的最后一个canvas方法是Text方法。Text方法在几个地方被使用。首先是通过在canvas左上角显示玩家分数来提供反馈,使用蓝色描边文本。

context.font = "normal 24px Arial";

context.strokeStyle = "#0000FF";

context.strokeText("Score: " + Global.Score, 20, 40);

文本用于的第二个地方是通知用户游戏已结束。这是使用红色描边文本canvas方法以72磅的大字体完成的。这标志着玩家未能保卫地球,方块外星人已成功入侵。

context.font = "normal 72px Arial";

context.strokeStyle = "#FF0000";

context.strokeText("GAME", 375, 300);

context.strokeText("OVER", 375, 400);

Block Invaders游戏展示了使用HTML5 Canvas创建游戏的力量和便捷性。大约400行代码,我们就能够创建一个可玩但并非完全打磨的游戏。

如果这项技术在80年代初就存在,我们可以想象我们节省了多少零花钱。

要玩游戏,只需下载BlockInvaders.html文件,并在您喜欢的HTML5兼容浏览器中打开它。

© . All rights reserved.