【温故而知新-Javascript】使用canvas元素(第一部分)

1. 开始使用 canvas 元素

canvas 元素非常简单,这是指它所有的功能都体现在一个JavaScript对象上,因此该元素本身只有两个属性:width 和 height。

canvas 元素里的内容会在浏览器不支持此元素时作为备用内容显示。下面例子展示了canvas 元素和一些简单的备用内容。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用带有基本备用内容的canvas元素</title>
    <style>
        canvas {border: medium double black;margin: 4px;}
    </style>
</head>
<body>
<canvas width="500" height="200">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
</body>
</html>

其显示效果如下:

 

2. 获取画布上的上下文

为了在canvas元素上绘图,需要获得一个上下文对象,这个对象会开放针对特定图形样式的绘图函数。

通过在DOM里代表canvas元素的对象获得上下文。下表介绍了这个对象:HTMLCanvasElement。

其中关键的方法是getContext。为了获得二维上下文对象,需要给这个方法传递参数2d。一旦得到这个上下文,就可以开始绘图了。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>为画布获取二维上下文对象</title>
    <style>
        canvas {border: medium double black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="200">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<script>
    var ctx = document.getElementById("canvas").getContext("2d");
    ctx.fillRect(10,10,50,50);
</script>
</body>
</html>

此例中,用document 对象找到DOM里代表canvas元素的对象,并使用参数2d调用了getContext方法得到上下文对象。然后调用 fillRect方法,它会在画布上绘制一个实心矩形。

 

3. 绘制图形

这里先从canvas对矩形的支持开始。下表介绍了相关方法,所有的这些方法要用在上下文对象上(而不是画布本身)。

所有这三个方法都要四个参数。前两个(如表格所示的x和y)是从canvas元素左上角算起的偏移量。w和h参数指定了待绘制矩形的宽度和高度。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用fillRect和strokeRect方法</title>
    <style>
        canvas {border: medium double black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="200">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<script>
    var ctx = document.getElementById("canvas").getContext("2d");
    var offset = 10;
    var size = 50;
    var count = 5;
    for(var i=0;i<count;i++){
        ctx.fillRect(i*(offset+size)+offset,offset,size,size);
        ctx.strokeRect(i*(offset+size)+offset,(2*offset)+size,size,size);
    }
</script>
</body>
</html>

此例中的脚本用fillRect 和strokeRect 方法来创建一系列实现和空心的矩形。

用这种方式编写脚本是为了突出canvas 元素的编程本质。使用JavaScript的for循环绘制这些矩形。其实本可以使用10条独立的语句,每一条都带有特定的坐标参数,但是canvas的一大乐趣就是可以不必这么做。

clearRect 方法会清楚指定矩形里已绘制的所有内容。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用clearRect方法</title>
    <style>
        canvas {border: medium double black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="200">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<script>
    var ctx = document.getElementById("canvas").getContext("2d");
    var offset = 10;
    var size = 50;
    var count = 5;
    for(var i=0;i<count;i++){
        ctx.fillRect(i*(offset+size)+offset,offset,size,size);
        ctx.strokeRect(i*(offset+size)+offset,(2*offset)+size,size,size);
        ctx.clearRect(i*(offset+size)+offset,offset+5,size,size);
    }
</script>
</body>
</html>

此例用clearRect方法清楚了之前被fillRect方法绘制过的一片画布区域。从下图可以看到效果:

 

4. 设置画布绘制状态

绘制操作有绘制状态(drawing state)加以配置。后者是一组属性,指定了从线条宽度到填充色的所有参数。当绘制一个图形时,就会用到绘制状态的当前设置。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>在执行操作前设置绘制状态</title>
    <style>
        canvas {border: medium double black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="200">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<script>
    var ctx = document.getElementById("canvas").getContext("2d");
    ctx.lineWidth = 2;
    ctx.strokeRect(10,10,50,50);
    ctx.lineWidth = 4;
    ctx.strokeRect(70,10,50,50);
    ctx.lineWidth = 6;
    ctx.strokeRect(130,10,50,50);
    ctx.strokeRect(190,10,50,50);
</script>
</body>
</html>

此例使用了lineWidth属性,此属性是绘制状态的一部分,负责设置用于图形(比如strokeRect方法生成的那些)的线条宽。当使用strokeRect方法时,lineWidth属性的当前值就会用于绘制矩形。

下表展示了基本的绘制状态属性。

 

4.1 设置线条连接样式

lineJoin 属性决定了相互连接的线条应该如何绘制,它有三个值:round、bevel和 miter,默认值是miter。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>设置lineJoin属性</title>
    <style>
        canvas {border: medium double black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="140">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<script>
    var ctx = document.getElementById("canvas").getContext("2d");
    ctx.lineWidth = 20;

    ctx.lineJoin = "round";
    ctx.strokeRect(20,20,100,100);
    ctx.lineJoin = "bevel";
    ctx.strokeRect(160,20,100,100);
    ctx.lineJoin = "miter";
    ctx.strokeRect(300,20,100,100);
</script>
</body>
</html>

其显示效果如下所示:

 

4.2 设置填充和笔触样式

当用fillStyle 或strokeStyle 属性设置样式时, 可以用CSS颜色值来指定一种颜色,名称或颜色模型都可以。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用fillStyle和strokeStyle属性设置颜色</title>
    <style>
        canvas {border: medium double black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="140">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<script>
    var ctx = document.getElementById("canvas").getContext("2d");
    var offset = 10;
    var size = 50;
    var count = 5;
    ctx.lineWidth = 3;
    var fillColors = ["black","gray","lightgray","red","blue"];
    var strokeColors = ["rgb(0,0,0)","rgb(100,100,100)","rgb(200,200,200)","rgb(255,0,0)","rgb(0,0,255)"];
    for(var i=0;i<count;i++){
        ctx.fillStyle = fillColors[i];
        ctx.strokeStyle = strokeColors[i];
        ctx.fillRect(i*(offset+size)+offset,offset,size,size);
        ctx.strokeRect(i*(offset+size)+offset,(2*offset)+size,size,size);
    }
</script>
</body>
</html>

此例中,用CSS颜色名和 rgb模型定义了两个颜色数组。然后再 for循环中把这些颜色指派给 fillStyle和 strokeStyle属性,并调用了 fillRect和 strokeRect方法。

 

 4.3 使用渐变

除了纯色,还可以把填充和笔触样式设置为渐变色。渐变是两种或更多颜色之间的渐进转变。canvas 元素支持两类渐变:线性和径向。

这两个方法都返回一个CanvasGradient对象,它定义了 addColorStop方法。其中参数描述了渐变使用的线条或圆。

(1) 使用线性渐变

线性渐变(linear gradient)指的是沿着一条线设定要用的若干颜色。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>创建线性渐变</title>
    <style>
        canvas {border: medium double black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="140">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<script>
    var ctx = document.getElementById("canvas").getContext("2d");
    var grad = ctx.createLinearGradient(0,0,500,140);
    grad.addColorStop(0,"red");
    grad.addColorStop(0.5,"white");
    grad.addColorStop(1,"black");
    ctx.fillStyle = grad;
    ctx.fillRect(0,0,500,140);
</script>
</body>
</html>

使用createLinearGradient 方法时,提供的四个值会作为画布上一条线段的开始和结束坐标。在这个例子中,用坐标描述了一条开始于(0,0),结束于(500,140)的线段。这些点分别对应画布的左上角和右下角,如下图所示:

这条线就代表了渐变。现在可以在createLinearGradient 方法返回的CanvasGradient上使用 addColorStop方法,沿着梯度线添加各种颜色了,就像这样:

    grad.addColorStop(0,"red");
    grad.addColorStop(0.5,"white");
    grad.addColorStop(1,"black");

addColorStop方法的第一个参数是想要在线段上应用颜色的位置,颜色则由第二个参数指定。线段的起点(此例中是坐标(0,0))由0这个值代表,1则代表终点。此例中,告诉canvas想让red(红色)处于线段起点,white(白色)处于线段中点,而black(黑色)处于线段终点。然后画布会计算出如何在这些点上逐渐转变这些颜色。想要指定多少个颜色点都可以。

定义渐变并添加颜色之后,就可以用CanvasGradient对象来设置fillStyle或strokeStyle属性了,就像这样:

ctx.fillStyle = grad;

最后,可以绘制一个图形。此例中,绘制了一个实心的矩形:

ctx.fillRect(0,0,500,140);

从下图可以看到这个矩形填满了画布,并展示出完整的渐变。

(2) 在更小的图形里使用线性渐变

在定义梯度线时要相对于画布进行设置,而不是绘制图形。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>在不填满画布的图形中使用渐变</title>
    <style type="text/css">
        canvas {border: thin solid black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="140">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<script>
    var ctx = document.getElementById("canvas").getContext("2d");
    var grad = ctx.createLinearGradient(0,0,500,140);
    grad.addColorStop(0,"red");
    grad.addColorStop(0.5,"white");
    grad.addColorStop(1,"black");
    ctx.fillStyle = grad;
    ctx.fillRect(10,10,50,50);
</script>
</body>
</html>

对此例的改动只是让矩形变小。

这就是所说的梯度线相对于画布的意思。此例在一个纯红色区域里绘制了这个矩形。(事实上,如果能靠的足够近,也许可以觉察颜色略微向白色过渡,但外观整体上是纯色的。)最好的理解方式是:我们绘制图形时其实是让底下的一部分渐变显露了出来。这就意味着必须思考梯度线如何对应将要显露的区域。下面展示了如何针对图形来设置梯度线。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>让梯度线匹配想要的图形</title>
    <style type="text/css">
        canvas {border: thin solid black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="140">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<script type="application/javascript">
    var ctx = document.getElementById("canvas").getContext("2d");
    var grad = ctx.createLinearGradient(10,10,60,60);
    grad.addColorStop(0,"red");
    grad.addColorStop(0.5,"white");
    grad.addColorStop(1,"black");
    ctx.fillStyle = grad;
    ctx.fillRect(0,0,500,140);
</script>
</body>
</html>

此例中,对梯度线进了设置,使它开始和终止于想用小矩形显露出的区域之内。但是,绘制了显露全部渐变的矩形,这样就可以看到改动产生的效果了。

可以看到渐变色如何转移到了将用小矩形显露出的区域内。最后一步是让矩形匹配渐变,如下例所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使图形匹配渐变</title>
    <style type="text/css">
        canvas {border: thin solid black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="140">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<script type="application/javascript">
    var ctx = document.getElementById("canvas").getContext("2d");
    var grad = ctx.createLinearGradient(10,10,60,60);
    grad.addColorStop(0,"red");
    grad.addColorStop(0.5,"white");
    grad.addColorStop(1,"black");
    ctx.fillStyle = grad;
    ctx.fillRect(10,10,50,50);
</script>
</body>
</html>

PS:请注意,在createLinearGradient参数里使用的数值和在fillRect参数里使用的有所不同。createLinearGradient的值代表画布里的一组坐标,而fillRect的值代表了矩形相对与单个坐标点的宽度和高度。如果你发现渐变和图形没有对齐,这可能就是导致问题的原因。

现在,图形和渐变已经完美对齐了,如下图所示:

 

4.4 使用径向渐变

用两个圆定义径向渐变。渐变的起点是由第一个圆定义,终点则是第二个圆,在两者之间添加颜色点。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用径向渐变</title>
    <style type="text/css">
        canvas {border: thin solid black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="140">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<script type="application/javascript">
    var ctx = document.getElementById("canvas").getContext("2d");
    var grad = ctx.createRadialGradient(250,70,20,200,60,100);
    grad.addColorStop(0,"red");
    grad.addColorStop(0.5,"white");
    grad.addColorStop(1,"black");
    ctx.fillStyle = grad;
    ctx.fillRect(0,0,500,140);
</script>
</body>
</html>

createRadialGradient方法的六个参数分部代表:

* 起点圆的圆心坐标(第一个参数和第二个参数)

* 起点圆的半径(第三个参数)

* 终点圆的圆心坐标(第四个参数和第五个参数)

* 终点圆的半径(第六个参数)

下图展示了例子里的值生成的起点圆和终点圆。请注意,可以指定画布之外的渐变(这句话对线性渐变同样适用)。

 此例中起点圆较小,被终点圆所包围。当给这个渐变添加颜色点时,它们会被放置在起点圆边界(该点的值为0.0)和终点圆边界(该点的值为1.0)之间的一条线段上。

因为可以指定两个圆的位置,所以圆边界之间的距离可能会有变化,颜色之间的渐变速度也会不同。

这张图展示了整个渐变,但是渐变与绘制图形之间的对应方式适用于同样的规则。下面的例子创建了一对较小的图形,它们显露出了渐变的子区域。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用较小的图形和径向渐变</title>
    <style type="text/css">
        canvas {border: thin solid black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="140">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<script type="application/javascript">
    var ctx = document.getElementById("canvas").getContext("2d");
    var grad = ctx.createRadialGradient(250,70,20,200,60,100);
    grad.addColorStop(0,"red");
    grad.addColorStop(0.5,"white");
    grad.addColorStop(1,"black");
    ctx.fillStyle = grad;
    ctx.fillRect(150,20,75,50);
    ctx.lineWidth = 8;
    ctx.strokeStyle = grad;
    ctx.strokeRect(250,20,75,50);
</script>
</body>
</html>

请注意可以同时将渐变用于fillStyle和strokeStyle属性,这让我们不仅能将渐变用在实心图形上,还能够用于线条。

 

4.5 使用图案

除了纯色和渐变之外,还可以创建图案(pattern)。具体做法是使用画布上下文对象所定义的createPattern方法。2D绘图上下文定义了对三种图案类型的支持:图像、视频和画布。但是只有图像得以实现。

要将图像作为图案,需要把一个HTMLImageElement对象作为一个参数传递给createPattern方法。第二个参数是重复样式,它的值必须是下表中的一个。

下面例子展示了如何创建和使用图像类型的图案。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用图像类型的图案</title>
    <style type="text/css">
        canvas {border: thin solid black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="140">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<img src="../imgs/apple.png" id="apple" hidden />
<script type="application/javascript">
    var ctx = document.getElementById("canvas").getContext("2d");
    var imageElem = document.getElementById("apple");
    var pattern = ctx.createPattern(imageElem,"repeat");
    ctx.fillStyle = pattern;
    ctx.fillRect(0,0,500,140);
</script>
</body>
</html>

此例里的文档包含一个img元素,它对用户是不可见的,因为应用了hidden属性。在脚本中,用DOM定位了代表img元素的HTMLImageElement对象,并将它作为createPattern方法的第一个参数。第二个参数使用了reapeat这个值,使图像在两个方向上都会重复。最后,将这个图案设为fillStyle属性的值,并使用fillRect方法绘制一个大小与画布相当的实心矩形。

这个图案复制的是 img元素的当前状态,这就意味着即使我们用JavaScript和DOM修改了img元素src属性的值,此图案也不会改变。

和渐变一样,这个图案会应用到整张画布上,由我们来决定图案的哪部分将通过我们绘制的图像显露出来。下例展示了将图案用于较小的填充和笔触图形上。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用较小的图形和图像类型的图案</title>
    <style type="text/css">
        canvas {border: thin solid black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="140">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<img src="../imgs/apple.png" id="apple" hidden />
<script type="application/javascript">
    var ctx = document.getElementById("canvas").getContext("2d");
    var imageElem = document.getElementById("apple");
    var pattern = ctx.createPattern(imageElem,"repeat");

    ctx.fillStyle = pattern;
    ctx.fillRect(50,20,175,100);

    ctx.lineWidth = 8;
    ctx.strokeStyle = pattern;
    ctx.strokeRect(250,20,175,100);
</script>
</body>
</html>

其显示效果如下:

 

5. 保存和恢复绘制状态

可以用下表中介绍的方法保存绘制状态,稍后再返回。

绘制状态保存时会被放在一个后进先出(LIFO)的栈中,意思是我们用save方法最后保存的状态会被restore方法首先进行恢复,下例展示了这些方法的用法:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>保护和恢复状态</title>
    <style type="text/css">
        canvas {border: thin solid black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="140">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<br />
<button>Save</button>
<button>Restore</button>
<script>
    var ctx = document.getElementById("canvas").getContext("2d");
    var grad = ctx.createLinearGradient(500,0,500,140);
    grad.addColorStop(0,"red");
    grad.addColorStop(0.5,"white");
    grad.addColorStop(1,"black");

    var colors = ["black",grad,"red","green","yellow","black","grey"];
    var cIndex = 0;
    ctx.fillStyle = colors[cIndex];
    draw();

    var buttons = document.getElementsByTagName("button");
    for(var i=0;i<buttons.length;i++){
        buttons[i].onclick = handleButtonPress;
    }
    function handleButtonPress(e){
        switch (e.target.innerHTML){
            case "Save":
                ctx.save();
                cIndex = (cIndex + 1)%colors.length;
                ctx.fillStyle = colors[cIndex];
                draw();
                break;
            case "Restore":
                cIndex = Math.max(0,cIndex-1);
                ctx.restore();
                draw();
                break;
        }
    }
    function draw(){
        ctx.fillRect(0,0,500,140);
    }
</script>
</body>
</html>

此例中,定义了一个包含CSS颜色名的数组和一个线性渐变。当Save(保存)按钮被按下就会用save方法保存当前的绘制状态。当Restore(恢复)按钮被按下,之前的绘制状态就会被恢复。无论哪个按钮被按下都会调用draw 函数,它会用 fillRect 方法绘制一个实心矩形。fillStyle属性会在数组里来回移动,并在按钮被点击时进行保存和恢复,因为这个属性是绘制状态的一部分。此例的显示效果如下:

画布里的内容不会被保存和恢复,只有绘制状态的属性值才会,例如lineWidth、fillStyle、strokeStyle等。

 

6. 绘制图像

可以用drawImage方法在画布上绘制图像。这个方法需要三个、五个或九个参数。第一个参数始终是图像的来源,它可以是代表img、video或其他canvas元素的DOM对象。下面的例子使用了个img元素作为来源:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用drawImage方法</title>
    <style type="text/css">
        canvas {border: thin solid black;margin: 4px;}
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="140">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
<img src="../imgs/apple.png" hidden id="apple" />
<script>
    var ctx = document.getElementById("canvas").getContext("2d");
    var imageElement = document.getElementById("apple");
    ctx.drawImage(imageElement,10,10);
    ctx.drawImage(imageElement,120,10,100,120);
    ctx.drawImage(imageElement,20,20,100,50,250,10,100,120);
</script>
</body>
</html>

使用三个参数时,第二个和第三个参数给出了图像应当在画布上绘制的坐标。图像按照它原始的宽度和高度进行绘制。使用五个参数时,额外的参数指定了应当给图像绘制的宽度和高度,以代替原始尺寸。

使用九个参数时:

* 第二个和第三个参数是在源图像内的偏移量;

* 第四个和第五个参数是在源图像所需使用区域的宽度和高度;

* 第六个和第七个参数指定了所选区域的左上角将要在画布上绘制的坐标;

* 第八个和第九个参数指定了所选区域将要绘制的宽度和高度。

此例的显示效果如下:

 

6.1 使用视频图像

我们可以用video元素作为drawImage方法的图像来源。这么做其实是对视频做了截图。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用视频作为drawImage方法的来源</title>
    <style type="text/css">
        canvas {border: thin solid black;margin: 4px;}
    </style>
</head>
<body>
<div>
<video id="vid" src="../multimedia/timessquare.webm" controls preload="auto" width="360" height="240">
    Video cannot be displayed
</video>
<canvas id="canvas" width="360" height="240">
    Your browser doesn't support the <code>canvas</code> element
</canvas>
</div>
<div>
<button id="pressme">Press Me</button>
</div>
<script>
    var ctx = document.getElementById("canvas").getContext("2d");
    var imageElement = document.getElementById("vid");
    document.getElementById("pressme").onclick = function(){
        ctx.drawImage(imageElement,0,0,360,240);
    }
</script>
</body>
</html>

此例中有一个video元素、一个button和一个canvas元素。按下按钮后,当前的视频帧会被drawImage方法用来描绘桌面。此例的显示效果如下:

观看各种HTML5演示时,经常会见到canvas被用来在视频上绘图。这就是此例展示的技巧,再加上一个计时器实现的。下面的例子展示了怎样结合使用它们。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用canvas显示视频并在上面绘图</title>
    <style type="text/css">
        canvas {border: thin solid black;margin: 4px;}
    </style>
</head>
<body>
<div>
    <video id="vid" src="../multimedia/timessquare.webm" controls autoplay preload="auto"
           width="360" height="240" hidden>
        Video cannot be displayed
    </video>
    <canvas id="canvas" width="360" height="240">
        Your browser doesn't support the <code>canvas</code> element
    </canvas>
</div>
<script>
    var ctx = document.getElementById("canvas").getContext("2d");
    var imageElement = document.getElementById("vid");
    var width = 100;
    var height = 10;
    ctx.lineWidth = 5;
    ctx.strokeStyle = "red";
    setInterval(function(){
        ctx.drawImage(imageElement,0,0,360,240);
        ctx.strokeRect(180 - (width/2), 120 - (height/2),width,height);
    },25);
    setInterval(function(){
        width = (width + 1) % 200;
        height = (height + 3) % 200;
    },100)
</script>
</body>
</html>

此例中,给一个video元素应用了hidden元素,使它对用户不可见。这里用了两个计时器:第一个每25毫秒触发一次,它会绘制当前的视频帧并添上一个空心的矩形。第二个计时器没100毫秒触发一次,它会改变矩形所使用的值。实现的效果是矩形会改变大小,并且叠加在视频图像上。此例显示效果如下:

像这样操作video元素时无法使用内置控件。这里为了让示例保持简单而使用了autoplay属性,但更有用的解决方式是实现自定义控件。

 

6.2 使用画布图像

我们可以将一张画布的内容作为另一张里drawImage方法的来源,如下例所示: 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用视频作为drawImage方法的来源</title>
    <style type="text/css">
        canvas {border: thin solid black;margin: 4px;}
    </style>
</head>
<body>
<div>
    <video id="vid" src="../multimedia/timessquare.webm" controls autoplay preload="auto"
           width="360" height="240" hidden>
        Video cannot be displayed
    </video>
    <canvas id="canvas" width="360" height="240">
        Your browser doesn't support the <code>canvas</code> element
    </canvas>
    <canvas id="canvas2" width="360" height="240">
        Your browser doesn't support the <code>canvas</code> element
    </canvas>
</div>
<div>
    <button id="pressme">Press Me</button>
</div>
<script>
    var srcCanvasElement = document.getElementById("canvas");
    var ctx = srcCanvasElement.getContext("2d");
    var ctx2 = document.getElementById("canvas2").getContext("2d");
    var imageElement = document.getElementById("vid");
    document.getElementById("pressme").onclick = takeSnapshot;

    var width = 100;
    var height = 10;
    ctx.lineWidth = 5;
    ctx.strokeStyle = "red";
    ctx2.lineWidth = 30;
    ctx2.strokeStyle = "black";

    setInterval(function(){
        ctx.drawImage(imageElement,0,0,360,240);
        ctx.strokeRect(180 - (width/2), 120 - (height/2),width,height);
    },25);

    setInterval(function(){
        width = (width + 1) % 200;
        height = (height + 3) % 200;
    },100)

    function takeSnapshot(){
        ctx2.drawImage(srcCanvasElement,0,0,360,240);
        ctx2.strokeRect(0,0,360,240);
    }
</script>
</body>
</html>

此例在上个例子的基础上添加了第二个canvas元素和一个button。当按钮被按下时,把代表原canvas的HTMLCanvasElement对象作为第一个参数,用于调用第二个canvas上下文对象上的drawImage方法。本质上,点击按钮会给第一张的画布截图,并将它显示在第二张画布上。它会复制画布上的一切,包括叠加的红色方框。此例的显示效果如下:

 

来源:《HTML5权威指南》(《The Definitive Guide to HTML5》)

posted @ 2016-10-02 20:41  叶超Luka  阅读(548)  评论(0编辑  收藏  举报