小试 Canvas

相对于离线应用,Web SQL Database 等特性,如 Canvas,Video 等媒体应用类的 HTML5 特性我用得比较少,于是今年年初的时候就尝试用这些新特性做点练习,感受一下 HTML5 在媒体应用方面的能力,今天重新翻了一下这些 Demo,整理了两个网页时钟 —— 一个主要用 Canvas 实现,一个主要用 CSS3 实现,为什么是“主要”?因为无论那种方式,都需要 JavaScript 和 HTML 的配合。

首先给出两个 Demo

Canvas 时钟

CSS3 时钟

因为是很简单的内容,所以很难说用那种方式实现会更快,但也不难从这个过程中感受到 Canvas 的魅力,JavaScript 操纵,简单易用的 API ,使到前端开发者用很少的学习成本便能掌握一种 Web 图像处理的方式,对于 HTML5 在图像处理、动画和文本渲染方面的能力也有很大的提升。

接下来简单介绍一下 Canvas 几个常用的 API ,而在介绍这些 API 之前首先贴出上面那个例子的 JavaScript 源码,

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() 方法。

例如上面例子中的:

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 ,效果如图:

2013-3-29 16-46-55

完整的 JavaScript 如下:

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没怎么研究。

  • 评论者头像
    回复

    不是很懂。学习中。。。 :-P

回复

你正在以游客身份访问网站,请输入你的昵称和 E-mail