canvas
是 HTML5 新增的元素,可使用JavaScript脚本来绘制图形。基于渲染上下文的不同,WebGL使用canvas元素绘制三维图形,而本文主要围绕二维图形,也就是canvas.getContext('2d')
。
<canvas id="canvas" width="500" height="500"></canvas>
|
当没有设置宽度和高度的时候,canvas会初始化宽度为300像素和高度为150像素。
基础图形绘制
直线
var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d');
ctx.strokeStyle='red'; ctx.moveTo(100,100); ctx.lineTo(200,100); ctx.stroke();
|
矩形
var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d');
ctx.storkeStyle='blue'; ctx.fillStyle='red';
ctx.fillRect(0,0,100,100); ctx.strokeRect(100,100,150,150);
ctx.rect(250,250,100,50); ctx.fill();
ctx.clearRect(25,25,50,50);
|
圆弧
arc(x, y, radius, startAngle, endAngle, anticlockwise)
画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。
该方法有六个参数:x,y为绘制圆弧所在圆上的圆心坐标。radius为半径。startAngle以及endAngle参数用弧度定义了开始以及结束的弧度。这些都是以x轴为基准。参数anticlockwise 为一个布尔值。为true时,是逆时针方向,否则顺时针方向。
注意:arc()函数中的角度单位是弧度,不是度数。角度与弧度的js表达式:radians=(Math.PI/180)*degrees。
var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d');
ctx.beginPath(); ctx.arc(50, 50, 50, 0, Math.PI * 2, true); ctx.stroke();
ctx.beginPath(); ctx.arc(150, 50, 50, 0, Math.PI * 2, true); ctx.fill();
ctx.beginPath(); ctx.arc(250, 50, 50, 0, Math.PI * 0.5, true); ctx.stroke();
ctx.beginPath(); ctx.arc(350, 50, 50, 0, Math.PI * 1.5, false); ctx.stroke();
|
贝塞尔曲线
一次以及二次贝塞尔曲线都十分有用,一般用来绘制复杂有规律的图形。
quadraticCurveTo(cp1x, cp1y, x, y)
绘制贝塞尔曲线,cp1x,cp1y为控制点,x,y为结束点。
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
绘制二次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。
上图能够很好的描述两者的关系,贝塞尔曲线有一个开始、结束点(蓝色)以及一个控制点(红色),而二次贝塞尔曲线使用两个控制点。
参数x、y在这两个方法中都是结束点坐标。cp1x,cp1y为坐标中的第一个控制点,cp2x,cp2y为坐标中的第二个控制点。
使用一次以及二次贝塞尔曲线是有一定的难度的,因为不同于像Adobe Illustrators这样的矢量软件,我们所绘制的曲线没有直接的视觉反馈给我们。这让绘制复杂的图形十分的困难。
一次贝塞尔曲线
这个例子使用多个贝塞尔曲线来渲染对话气泡。
var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d');
ctx.beginPath(); ctx.moveTo(75, 25); ctx.quadraticCurveTo(25, 25, 25, 62.5); ctx.quadraticCurveTo(25, 100, 50, 100); ctx.quadraticCurveTo(50, 120, 30, 125); ctx.quadraticCurveTo(60, 120, 65, 100); ctx.quadraticCurveTo(125, 100, 125, 62.5); ctx.quadraticCurveTo(125, 25, 75, 25); ctx.stroke();
|
二次贝塞尔曲线
这个例子使用贝塞尔曲线绘制心形。
var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d');
ctx.beginPath(); ctx.moveTo(75, 40); ctx.bezierCurveTo(75, 37, 70, 25, 50, 25); ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5); ctx.bezierCurveTo(20, 80, 40, 102, 75, 120); ctx.bezierCurveTo(110, 102, 130, 80, 130, 62.5); ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25); ctx.bezierCurveTo(85, 25, 75, 37, 75, 40); ctx.fill();
|
动画
canvas动画是逐帧动画,元素的视觉变化,主要依靠不停的清除画布和绘制该元素的新变化。不停的重绘是相当费时的,性能依赖于电脑的速度。
动画的基本步骤
你需要进行以下一些步骤来画出一帧:
清空 canvas
除非接下来要画的内容会完全充满 canvas (例如背景图),否则你需要清空所有。最简单的做法就是用 clearRect 方法。
保存 canvas 状态
如果你要改变一些会改变 canvas 状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,你需要先保存一下。
绘制动画图形(animated shapes)
这一步才是重绘动画帧。
恢复 canvas 状态
如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧。
操控动画
上面的步骤只是绘制了动画的一帧,为了实现动画,我们需要一些可以定时执行重绘的方法。有两种方法可以实现这样的动画操控。一是通过 setInterval 或 setTimeout 方法来控制定时绘制。二是,通过window.requestAnimationFrame()来定期执行一个绘制函数。现代浏览器(IE10及以上)基本都支持该方法,以下例子均采用window.requestAnimationFrame()。
一个简单示例
var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var raf;
function fill() { ctx.fillStyle = 'rgba(255,255,255,1)' ctx.fillRect(0, 0, canvas.width, canvas.height) }
var x = 0, y = 0; function draw() { fill() ctx.save() ctx.beginPath() ctx.strokeStyle = 'red' ctx.arc(x, y, 10, 0, Math.PI * 2) ctx.stroke() ctx.restore() x += 10 y += 10 if (x > 500) { cancelAnimationFrame(raf) return; } raf = requestAnimationFrame(draw) }
draw()
|
较复杂的例子
var sun = new Image(); var moon = new Image(); var earth = new Image();
function init() { sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png'; moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png'; earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png'; window.requestAnimationFrame(draw); }
function draw() { var ctx = document.getElementById('canvas').getContext('2d'); ctx.globalCompositeOperation = 'destination-over'; ctx.clearRect(0, 0, 300, 300); ctx.fillStyle = 'rgba(0,0,0,0.4)'; ctx.strokeStyle = 'rgba(0,153,255,0.4)'; ctx.save(); ctx.translate(150, 150); var time = new Date(); ctx.rotate(((2 * Math.PI) / 60) * time.getSeconds() + ((2 * Math.PI) / 60000) * time.getMilliseconds()); ctx.translate(105, 0); ctx.fillRect(0, -12, 50, 24); ctx.drawImage(earth, -12, -12); ctx.save(); ctx.rotate(((2 * Math.PI) / 6) * time.getSeconds() + ((2 * Math.PI) / 6000) * time.getMilliseconds()); ctx.translate(0, 28.5); ctx.drawImage(moon, -3.5, -3.5); ctx.restore(); ctx.restore(); ctx.beginPath(); ctx.arc(150, 150, 105, 0, Math.PI * 2, false); ctx.stroke(); ctx.drawImage(sun, 0, 0, 300, 300); window.requestAnimationFrame(draw); }
init();
|
本文完
参考文档