相对于离线应用,Web SQL Database 等特性,如 Canvas,Video 等媒体应用类的 HTML5 特性我用得比较少,于是今年年初的时候就尝试用这些新特性做点练习,感受一下 HTML5 在媒体应用方面的能力,今天重新翻了一下这些 Demo,整理了两个网页时钟 —— 一个主要用 Canvas 实现,一个主要用 CSS3 实现,为什么是“主要”?因为无论那种方式,都需要 JavaScript 和 HTML 的配合。
首先给出两个 Demo
因为是很简单的内容,所以很难说用那种方式实现会更快,但也不难从这个过程中感受到 Canvas 的魅力,JavaScript 操纵,简单易用的 API ,使到前端开发者用很少的学习成本便能掌握一种 Web 图像处理的方式,对于 HTML5 在图像处理、动画和文本渲染方面的能力也有很大的提升。
接下来简单介绍一下 Canvas 几个常用的 API ,而在介绍这些 API 之前首先贴出上面那个例子的 JavaScript 源码,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | var canvas = document.getElementById( 'clock' ); // 获取 Canvas 对象 var clock = canvas.getContext( '2d' ); // 获取 Canvas 上下文 var canvas2 = document.getElementById( 'digital' ); // 获取 Canvas 对象 var digital = canvas2.getContext( '2d' ); // 获取 Canvas 上下文 // 重新定义(0, 0)点 clock.translate(canvas.width / 2, canvas.height / 2); // 数字时钟 function theDigital(n, x, y){ var img = new Image(); img.src = 'numbers.png' ; switch (n){ case ':' : digital.drawImage(img, 183, 0, 15, 30, x, y, 15, 30); break ; case '1' : digital.drawImage(img, 8, 0, 22, 30, x, y, 22, 30); break ; case '2' : digital.drawImage(img, 43, 0, 22, 30, x, y, 22, 30); break ; case '3' : digital.drawImage(img, 78, 0, 22, 30, x, y, 22, 30); break ; case '4' : digital.drawImage(img, 113, 0, 22, 30, x, y, 22, 30); break ; case '5' : digital.drawImage(img, 148, 0, 22, 30, x, y, 22, 30); break ; case '6' : digital.drawImage(img, 8, 30, 22, 30, x, y, 22, 30); break ; case '7' : digital.drawImage(img, 43, 30, 22, 30, x, y, 22, 30); break ; case '8' : digital.drawImage(img, 78, 30, 22, 30, x, y, 22, 30); break ; case '9' : digital.drawImage(img, 113, 30, 22, 30, x, y, 22, 30); break ; case '0' : digital.drawImage(img, 148, 30, 22, 30, x, y, 22, 30); break ; default : break ; } } // 刻画表针 function theClock(){ clock.beginPath(); clock.fillStyle= '#efefef' ; clock.arc(0, 0, 140, 0, 2 * Math.PI, false ); clock.fill(); digital.clearRect(90, 17, 220, 40); var time = new Date(), s = time.getSeconds(), m = time.getMinutes(), h = time.getHours(); var dh = h < 10 ? "0" + h : h, dm = m < 10 ? "0" + m : m, ds = s < 10 ? "0" + s : s; dh = new String(dh); dm = new String(dm); ds = new String(ds); h = h > 12 ? (h - 12) + m / 60 : h + m / 60; // 时针 clock.rotate(Math.PI / 6 * h); clock.beginPath(); clock.moveTo(0, 7); clock.lineTo(0, -63); clock.lineWidth = 4; clock.strokeStyle = '#000' ; clock.stroke(); clock.rotate(-Math.PI / 6 * h); // 分针 clock.rotate(Math.PI / 30 * m); clock.beginPath(); clock.moveTo(0, 11); clock.lineTo(0, -99); clock.lineWidth = 4; clock.strokeStyle = '#000' ; clock.stroke(); clock.rotate(-Math.PI / 30 * m); // 秒针 clock.rotate(Math.PI / 30 * s); clock.beginPath(); clock.moveTo(0, 14); clock.lineTo(0, -126); clock.lineWidth = 2; clock.strokeStyle = '#000' ; clock.stroke(); clock.rotate(-Math.PI / 30 * s); // 表中心 clock.beginPath(); clock.fillStyle = '#000' ; clock.arc(0, 0, 5, 0, 2 * Math.PI, false ); clock.fill(); theDigital(dh.substr(0,1), 95, 17); theDigital(dh.substr(1,1), 114, 17); theDigital( ":" , 133, 17); theDigital(dm.substr(0,1), 142, 17); theDigital(dm.substr(1,1), 160, 17); theDigital( ":" , 178, 17); theDigital(ds.substr(0,1), 188, 17); theDigital(ds.substr(1,1), 207, 17); } // 刻画表盘 function dial(){ clock.beginPath(); clock.lineWidth = 10; clock.strokeStyle = '#000' ; clock.fillStyle = '#efefef' ; clock.arc(0, 0, 155, 0, 2 * Math.PI, false ); clock.fill(); clock.stroke(); setInterval(theClock, 1000); } window.onload = dial; |
1.getContext()
这是最基本的 Canvas 方法,可以获取一个 Canvas 元素的上下文,并返回一个对象,该对象提供了用于在画布上绘图的方法和属性,因此要想操纵 Canvas ,首先需要调用 getContext() 方法。
例如上面例子中的:
1 2 | var canvas = document.getElementById( 'clock' ); // 获取 Canvas 对象 var clock = canvas.getContext( '2d' ); // 获取 Canvas 上下文 |
获取上下文后,即可使用 clock 对象中的方法和属性绘制图像。
2.translate(x,y)
定义原点(0,0),Canvas 默认使用的坐标系是以左上角为原点,在平面中以右侧为 x 轴正方向,下侧为 y 轴正方向,以 translate 则可以改变这个默认原点。
3.drawImage(img,sx,sy,swidth,sheight,x,y,width,height)
drawImage 可以在画布上绘制图像、画布或视频。最常用的功能是复制图片的某一个部分到画布上。关于这个方法的属性比较多,具体可以参考
http://www.w3school.com.cn/html5/canvas_drawimage.asp
在上例中,Kayo 对时钟的数字部分用到的 0-9 共10个数字和“:”符号使用了雅黑字体,并制作成图片,然后使用 drawImage 把需要用到的部分逐个引用出来,具体这个过程是这样的:
(1)把 10 个数字和“:”做成自己喜欢的样式,并合并到一张图片上
(2)输出时间时判断每个符号是什么
(3)根据判断使用 drawImage 引用(1)中准备好的图片上相应的区域
这个做法的实用意义在于,你可以使用很个性的字体来做这些数字和符号,这样就能在网页上以非常个性的样式显示内容了,英文网站中比较流行的 Canvas 个性文章标题也是使用这个方法制作。
4.绘制路径
beginPath() 表示开始绘制一条路径,或重置当前路径
lineWidth 设置或返回当前绘制线条的宽度
strokeStyle 设置或返回用于笔触的颜色、渐变或模式
fillStyle 设置或返回用于填充绘画的颜色、渐变或模式
arc(x,y,r,sAngle,eAngle,counterclockwise) 创建弧/曲线(用于创建圆或部分圆)
arc() 方法的参数比较复杂,详细参数请参考:http://www.w3school.com.cn/html5/canvas_arc.asp
fill() 填充当前绘图(路径)
stroke() 绘制已定义的路径
5.save() 与 restore()
之所以把 save() 方法和 restore() 方法单独列出来是为了明确一点,这里的 save() 方法并不是用于保存上面的绘制,而 restore() 方法也不是用于撤销绘图,Canvas 绘制的图像会立即显示出来,并不需要保存才显示,这个 save() 的作用是保存当前环境的状态,即平移、放缩、旋转、错切、裁剪等操作的值,而 restore() 则是回到上一次保存的状态,具体来说,这里的“环境状态”指的就是下面的方法和属性的值:
globalAlpha, strokeStyle, fillStyle, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation
Canvas 是使用 Immediate-mode (立即模式)工作,即图像绘制完成后,所有相关的绘图结构会立即从内存中丢弃,这点与 DOM 和 SVG 在绘图后仍把绘图结构保存在内存中并不相同。因此开发者想要把状态保存起来就需要利用 save() 方法,每调用一次 save() 方法就会保存一个状态,而 restore() 则可以恢复一次状态。
下面给一个单独的例子说明 save() 和 restore() 的作用:
连续绘制三个逐渐减小的圆,第一个填充颜色为 #efefef ,第二个为 #afafaf ,第三个为 #efefef ,效果如图:
完整的 JavaScript 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | var canvas = document.getElementById( 'round' ); // 获取 Canvas 对象 var round = canvas.getContext( '2d' ); // 获取 Canvas 上下文 // 重新定义(0, 0)点 round.translate(canvas.width / 2, canvas.height / 2); // 绘制圆 function init(){ round.beginPath(); round.fillStyle = '#efefef' ; round.arc(0, 0, 155, 0, 2 * Math.PI, false ); round.fill(); round.save(); // 保存状态 round.closePath(); round.beginPath(); round.arc(0, 0, 125, 0, 2 * Math.PI, false ); round.fillStyle = '#afafaf' ; round.fill(); round.closePath(); round.beginPath(); round.arc(0, 0, 95, 0, 2 * Math.PI, false ); round.restore(); // 恢复状态 round.fill(); round.closePath(); } window.onload = init; |
可以看到,在绘制第一个圆后保存了 Canvas 的状态,即把上面列出的 12 个方法和属性的值保存起来,在例子中只使用了 fillStyle ,因此保存的就是 fillStyle 的值。所以,在绘制第三个圆前恢复了状态,即相当于把 fillStyle 的值重新设置为 '#efefef',绘值第三个圆时就会自动使用这个值。
本文由 Kayo Lee 发表,本文链接:https://kayosite.com/the-experience-of-canvas.html
评论列表
html5基本上就是一堆JS。。
@大发 这样更好,不必额外学习一种方式操纵它!
之前不是报道HTML5耗资源嘛,Canvas,Video此类应用看了就很耗,不过功能还是很牛的
@airoschou drawImage、clearRect等等对画布有重绘操作的方法应该会消耗比较多资源,所以编写时最好优化算法
绘图等方法,里面涉及都一些算法,因此好资源。就好比矢量图每条线条都是一个算法进行勾勒一样。记得貌似是这样子的,不过对html5没怎么研究。
不是很懂。学习中。。。