1.第一个Canvas程序

看的是HTML5移动开发即学即用这本书,首先学习Canvas基础,废话不多说,直接看第一个例子。

效果图为:

代码如下:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
</body>
<script type="text/javascript">
//canvas对象的取得
var canvas=document.getElementById("c1");
//取得绘图用的上下文对象
var ctx=canvas.getContext("2d");
//绘图处理
ctx.fillStyle="rgb(255,0,0)";
ctx.fillRect(50,50,200,200);
ctx.fillStyle="rgba(0,0,255,0.5)";
ctx.fillRect(100,100,200,200);
<!--alert("hello");-->
</script>
</html>

知识点:

Canvas 的基本用法

1)取得Canvas对象

2)从Canvas对象中获取绘图用的上下文

3)使用上下文中的方法与属性进行绘图

颜色的指定方法

1)ctx.fillStyle="#FF0000";

2)ctx.fillStyle="rgb(255,0,0)";

3)ctx.fillStyle="rgba(0,0,255,0.5)"; 最后这个指透明度的。。。

2.路径

绘制一个简单的三角形,效果:

代码如下:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
</body>
<script type="text/javascript">
//canvas对象的取得
var canvas=document.getElementById("c1");
//取得绘图用的上下文对象
var ctx=canvas.getContext("2d");
//路径绘制开始
ctx.beginPath();
//路径的绘制
ctx.moveTo(0,0);
ctx.lineTo(0,290);
ctx.lineTo(290,290);
//路径绘制结束
ctx.closePath();
//进行绘图处理
ctx.fillStyle="rgb(200,0,0)"
ctx.fill();
<!--alert("hello");-->
</script>
</html>

知识点:

控制路径时使用的方法:
  1) beginPath() 重置路径的开始

2) closePath() 关闭到现在为止的路径

3) moveTo() 指定绘图开始时的基点(x,y)

4) lineTo() 绘制从前一次绘图位置到(x,y)的直线

绘制路径时使用的方法:

1)stroke() 绘制路径

2)fill()填充路径

指定绘图样式时使用的属性

1)fillStyle 指定填充时使用的颜色与样式

2)strokeStyle 指定路径的线颜色与样式

3)lineWidth 指定路径线的粗细

下面制作一个当用户触摸屏幕时在触摸位置绘制三角形的实例程序 (书上的是用户触摸屏幕时绘制,现在改一下,鼠标移动时在移动的位置绘制三角形)效果:

代码如下:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=320,user-scalable=no" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
</body> <script type="text/javascript"> function getPointOnCanvas(canvas, x, y) {
var bbox = canvas.getBoundingClientRect();
return { x: x - bbox.left * (canvas.width / bbox.width),
y: y - bbox.top * (canvas.height / bbox.height)};
}
//canvas对象的取得
var canvas=document.getElementById("c1");
//取得绘图用的上下文对象
var ctx=canvas.getContext("2d");
//设置Canvas的onmouse事件
canvas.onmousemove=function(event)
{
//取得鼠标移动处的坐标
var x=event.pageX;
var y=event.pageY;
var canvas=event.target;
var loc=getPointOnCanvas(canvas,x,y);
console.log("mouse down at point(x:"+loc.x+",y:"+loc.y+")"); var r=Math.random()*10+25;
//路径指定 ctx.beginPath();
ctx.moveTo(loc.x,loc.y);
ctx.lineTo(loc.x,loc.y+r);
ctx.lineTo(loc.x+r,loc.y+r);
ctx.lineTo(loc.x,loc.y); //绘图
ctx.strokeStyle="red";
ctx.stroke();
};
</script>
</html>

遇到的问题,刚开始取不到鼠标移动处的坐标,借鉴了http://www.jb51.net/html5/89807.html 这里面的方法,把效果做出来了,注意console.log()的运用,看下代码运行时的效果:

3.颜色定义

这一小节感觉书上分得不太合理,我实现以下这个程序是为了熟练下JS代码

效果:

代码如下:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=320,user-scalable=no" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style>
<script>
(function(){
window.addEventListener("load",function(){
var ctx=document.getElementById("c1").getContext("2d");
//圆1
ctx.beginPath();
ctx.arc(150,45,35,0,Math.PI*2,false);
ctx.fillStyle='rgba(192,80,77,0.7)';
ctx.fill();
ctx.strokeStyle='rgba(192,80,77,1)';
ctx.stroke(); //圆2
ctx.beginPath();
ctx.arc(125,95,35,0,Math.PI*2,false);
ctx.fillStyle='rgba(155,187,89,0.7)';
ctx.fill();
ctx.strokeStyle='rgba(155,187,89,1)';
ctx.stroke(); //圆3
ctx.beginPath();
ctx.arc(175,95,35,0,Math.PI*2,false);
ctx.fillStyle='rgba(128,100,162,0.7)';
ctx.fill();
ctx.strokeStyle='rgba(128,100,162,1)';
ctx.stroke();}, false);
})();
</script>
</head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="150" ></canvas>
</body>
</html>

知识点:

1)描绘轮廓线

ctx.strokeStyle="#ff0000";

2)填充轮廓

ctx.fillStyle="#0000ff";

我自己从中练习的知识点应该是

1)匿名函数 (function(){})();的使用

2)window.addEventListener("load",function(){},false);

4.绘制方法的介绍

1) 绘制圆弧的arc()方法

arc()方法的语法如下:context.arc(x,y,半径,开始角度,结束角度,是否逆时针旋转);

从指定的开始角度开始至结束角度为止,按指定方向进行圆弧绘制。最后的参数为ture时,将按逆时针旋转。角度不是“度”,而是“弧度”。

效果:

代码如下:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
<script type="text/javascript">
var canvas=document.getElementById("c1");
var ctx=canvas.getContext("2d"); //使用颜色填充矩形
ctx.fillStyle="#f00ff0";
ctx.fillRect(0,0,300,300);
//描绘圆弧
//路径开始
ctx.beginPath();
var startAngle=0;
var endAngle=120*Math.PI/180;
ctx.arc(100,100,100,startAngle,endAngle,false); //绘制处理
ctx.strokeStyle="#ff0000";
ctx.lineWidth=3;
ctx.stroke();
</script>
</body>
</html>

写完后对arc()方法了解多一点了。x,y是圆心的坐标,现在可以想象得出是怎样画出来的。。。

2)绘制圆弧的arcTo()方法

arcTo()方法的语法如下:

context.arcTo(x1,y1,x2,y2,半径);

此方法的功能是,从路径的起点和终点分别向坐标(x1,y1)、(x2,y2)绘制直线后,在绘制指定半径的圆弧。

效果:

代码如下:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
<script type="text/javascript">
var canvas=document.getElementById("c1");
var ctx=canvas.getContext("2d"); //使用颜色填充矩形
ctx.fillStyle="#f00ff0";
ctx.fillRect(0,0,300,300);
//描绘圆弧
//路径开始
ctx.beginPath();
ctx.moveTo(20,20);
ctx.arcTo(290,150,100,280,100);
ctx.lineTo(20,280); //绘制处理
ctx.strokeStyle="#ff0000";
ctx.lineWidth=3;
ctx.stroke();
</script>
</body>
</html>

自己改了下坐标,效果加深对这个方法的理解。。。

3)quadraticCurveTo()与bezierCurveTo()方法

① quadraticCurveTo()方法用于绘制二元抛物线,其语法格式如下。

context.quadraticCurveTo(cpx,cpy,x,y);

绘制由最后指定的位置开始至坐标(x,y)的曲线。此时,使用控制点为(cpx,cpy)的二元抛物线进行连接,并将位置(x,y)追加到路径中。

② bezierCurveTo()方法用于绘制三元抛物线,语法格式为:

bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y);

绘制由最后指定路径位置至指定位置的曲线。此时,使用控制点分别为(cp1x,cp1y),(cp2x,cp2y)的三元抛物线进行连接,并将位置(x,y)追加到路径中,具体示意图:(qq上对图片的修饰似乎还不够熟练。。。)

代码如下:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
<script type="text/javascript">
var canvas=document.getElementById("c1");
var ctx=canvas.getContext("2d"); //使用颜色填充矩形
ctx.fillStyle="#f00ff0";
ctx.fillRect(0,0,300,300);
//描绘圆弧
//路径开始
ctx.beginPath();
ctx.moveTo(20,20);
ctx.bezierCurveTo(100,280,180,280,280,20); //绘制处理
ctx.strokeStyle="#ff0000";
ctx.lineWidth=3;
ctx.stroke();
</script>
</body>
</html>

4)绘制矩形的rect()方法

语法格式如下:context.rect(x,y,宽度,高度); x,y为矩形左上角坐标

除此之外,Canvas中还提供了三种特定的矩形绘制方法;

① context.strokeRect(x,y,w,h)    绘制矩形的轮廓

② context.fillRect(x,y,w,h)   填充矩形

③ context.clearRect(x,y,w,h) 清空矩形

这个比较好理解就不做效果演示及代码。

5.绘制渐变效果

线性渐变与圆形渐变

线性渐变就是从左至右(或自上而下)依次显示逐渐变化的颜色。而圆形渐变自圆心向外围逐渐显示变化的颜色。

1)线性渐变

指定线性渐变时使用createLinearGradient()方法,具体语法如下:

//先创建CanvasGradient对象: CanvasGradient=context.createLinearGradient(x1,y1,x2,y2);  表示由位置(x1,y1)至位置(x2,y2)显示渐变效果

//然后追加渐变颜色:CanvasGradient.addColorStop(颜色开始的相对位置,颜色);   指定渐变中使用的颜色,第一个参数(开始相对位置)中指定一个数字,从而决定什么位置使用什么颜色。

举个栗子:

代码为:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
<script type="text/javascript">
var canvas=document.getElementById("c1");
var ctx=canvas.getContext("2d"); //绘图
var g=ctx.createLinearGradient(0,0,300,0);
g.addColorStop(0,"rgb(255,0,0)"); //开始位置设置为红色
g.addColorStop(1,"rgb(255,255,0)"); //黄色
ctx.fillStyle=g;
ctx.fillRect(20,20,260,260);
</script>
</body>
</html>

2)圆形渐变

绘制圆形渐变时,使用createRadialGradient()方法创建对象,同样使用addColorStop()方法追加渐变颜色。具体语法如下

//创建CanvasGradient对象  CanvasGradient=context.createRadialGradient(x1,y1,r1,x2,y2,r2);  通过参数指定以(x1,y1)为圆心,半径为r1的圆到以(x2,y2)为圆心,半径为r2的圆的渐变效果

// 追加渐变颜色   CanvasGradient.addColorStop(颜色开始的相对位置,颜色);

举个栗子

代码为:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
<script type="text/javascript">
var canvas=document.getElementById("c1");
var ctx=canvas.getContext("2d"); //绘图
var g=ctx.createRadialGradient(150,150,50,150,150,100);
g.addColorStop(0.3,"red"); //开始位置设置为红色
g.addColorStop(0.7,"yellow");
g.addColorStop(1.0,"blue"); //黄色
ctx.fillStyle=g;
ctx.fillRect(20,20,260,260);
</script>
</body>
</html>

6.绘制图像

Canvas 中的图像绘制

图像绘制的基本步骤如下:

1)读取图像文件

2)在Canvas中进行绘制

图像读取前,首先创建Image对象,在Image对象的src属性中指定图像文件所在路径后就可以读取了。读取结束后,触发onload事件,基本语法如下:

var image=new Image();

image.src="图像文件路径";

image.onload=function(){//图像读取时的处理}

使用Canvas上下文中的drawImage()方法将读取后的Image对象绘制在Canvas上,实际上是将Image对象中的图像数据输出到Canvas中。有三种drawImage()方法用于图像的绘制

①直接绘制 context.drawImage(image,dx,dy)

②尺寸修改(resize) context.drawImage(image,dx,dy,dw,dh)

③图像截取 context.drawImage()

第①种方法直接将读取的图像绘制在坐标(dx,dy)处。第②种方法按照新的宽度dw与高度dh将图像绘制在坐标(dx,dy)处。第③种方法是将原图像的一部分截取出后再按指定尺寸绘制在Canvas上,从原图像的坐标(sx,sy)开始截取宽(sw),高(sh)的部分图像,然后绘制在Canvas上的坐标(dx,dy)处,宽度为dw,高度为dh。

像素处理

Canvas与SVG以及Flash稍有不同,绘制的图形/图像并不能作为对象操作。也就是说使用stroke()或者fill()方法绘制的图形,既不能移动它也不能删除它。如果想操作绘制的图形/图像,使用SVG或者Flash实现比使用Canvas要好。

Canvas中绘制的图形/图像作为一个整体的位图保存,因此可以访问各个像素信息。也就是说,可以使用JavaScript处理Canvas上绘制的图像像素信息。这是Canvas的一个特色

1)像素处理的API

imagedata=ctx.getImageData(sx,sy,sw,sh) 返回以(sx,sy)为左上顶点,宽为sw,高为sh的矩形图像-imagedata对象。

ctx.putImageData(imagedata,dx,dy) 将imagedata所表示的图像绘制在顶点坐标为(dx,dy)处。

简述之,使用getImageData()方法取出Canvas上图像的像素数据,通过JavaScript加工过这些像素数据后,使用putImageData方法,重新绘制到Canvas中。

ImageData对象是代表图像像素数据的对象。此对象定义了三种属性:

①imagedata.width  图像数据的宽度

②imagedata.height 图像数据的高度

③imagedata.data  图像数据(CanvasPixelArray类型)

在JavaScript中进行像素数据读取,并进行加工与输出时的具体操作是,从imagedata.data中得到CanvasPixelArray类型的对象。此对象是保存像素信息的一元数组。但是与JavaScript的Array对象不同,不可对其进行与通常数组一样的操作。

举个栗子:(本例中,当用户将桌面上的图像文件拖动到浏览器中后,首先读取图像文件并在浏览器中显示,接着对图像进行黑白变换,在原图的旁边显示变换后的图像)

用户将桌面上的图像文件拖动到浏览器中的界面:

进行黑白变换后的效果:

代码如下:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
body{
font-family:宋体,Arial,Helvetica,sans-serif;
font-size:80%;
}
#dp{
width:200px;
min-height:70px;
border:1px solid #000000;
background-color:#eeeeee;
padding:len;
margin:2em;
}
#dp img{
margin-right:lem;
}
</style>
<script>
(function(){ //拖动区域的div元素
var dp=null;
//FileReader接口对象
var reader=null; //页面导入时的处理
window.addEventListener("load",function(){
//获取拖动区域的div元素
dp=document.getElementById("dp");
//设置dragover事件的事件侦听
dp.addEventListener("dragover",function(evt){
evt.preventDefault();},false);
//设置drop事件的事件侦听
dp.addEventListener("drop",function(evt){
evt.preventDefault();
file_droped(evt);},false);
},false); //文件被拖入时的处理
function file_droped(evt)
{
//清空显示区域
while(dp.firstChild)
{
dp.removeChild(dp.firstChild);
}
//拖动文件的File接口对象
var file=evt.dataTransfer.files[0];
//FileReader接口对象
reader=new FileReader();
//非图像文件画像时报错
if(!/^image/.test(file.type)){alert("请拖入图像文件");}
//导入拖入图像
reader.readAsDataURL(file);
reader.onload=prepare_image;
} //显示拖入图像文件
function prepare_image(evt)
{
//创建img元素,显示拖入的图像
var image=document.createElement("img");
image.setAttribute("src",reader.result);
dp.appendChild(image);
//img元素中导入图像文档后进行后续处理
image.onload=function(){
//获取图像的尺寸
var w=parseInt(image.width);
var h=parseInt(image.height);
//创建canvas对象,导入图像
var canvas=document.createElement("canvas");
canvas.width=w;
canvas.height=h;
var ctx=canvas.getContext("2d");
ctx.drawImage(image,0,0);
//取得canvas像素数据
var imagedata=ctx.getImageData(0,0,w,h); //进行黑白转换
convert_image_to_gray_scale(imagedata.data); //替换canvas中的像素数据
ctx.putImageData(imagedata,0,0); //显示canvas
dp.appendChild(canvas);
}
} //黑白变换函数
function convert_image_to_gray_scale(data)
{
var len=data.length;
var pixels=len/4;
for(var i=0;i<pixels;i++){
//取出R,G,B值
var r=data[i*4];
var g=data[i*4+1];
var b=data[i*4+2]; //进行黑白变换
var t=parseInt((11*r+16*g+5*b)/32);
//将变换后的像素数据设置到原来数组元素中
data[i*4]=t;
data[i*4+1]=t;
data[i*4+2]=t;
}
} })();
</script> </head>
<body>
<div id="dp">
<p>将桌面图像文件拖动到此处。</p>
</div>
</body>
</html>

7.绘制数据图表

1)绘制方格图

绘制方格图是为了最终绘制折线数据图表做准备的,方格将作为折线图的基准线。绘制方格图的逻辑很简单,只要在Canvas上绘制一系列的横线和竖线。

效果:

代码如下:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
<script type="text/javascript">
//获取上下文
var canvas=document.getElementById("c1");
var ctx=canvas.getContext("2d"); //描绘背景
var gradient=ctx.createLinearGradient(0,0,0,300);
gradient.addColorStop(0,"#e0e0e0");
gradient.addColorStop(1,"#ffffff");
ctx.fillStyle=gradient;
ctx.fillRect(0,0,canvas.width,canvas.height); //描绘边框
var grid_cols=10;
var grid_rows=10;
var cell_height=canvas.height/grid_rows;
var cell_width=canvas.width/grid_cols;
ctx.lineWidth=1;
ctx.strokeStyle="#a0a0a0"; ctx.beginPath(); //准备画竖线
for( var col=0;col<=grid_cols;col++)
{
var x=col*cell_width;
ctx.moveTo(x,0);
ctx.lineTo(x,canvas.height);
} //准备画横线
for( var row=0;row<=grid_rows;row++)
{
var y=row*cell_height;
ctx.moveTo(0,y);
ctx.lineTo(canvas.width,y);
} //完成描绘
ctx.stroke();
</script>
</body>
</html>

接着我们在方格图中追加绘制数据图表,此处用到的数据为某种商品12个月内的销售数量,涉及的实例数据如下:

1月 2月 3月 4月 5月 6月 7月 8月 9月 10月 11月 12月
80 92 104 110 68 50 45 90 74 68 98 103

效果图为:

代码如下:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
<script type="text/javascript">
//获取上下文
var canvas=document.getElementById("c1");
var ctx=canvas.getContext("2d"); //定义图表数据
var uriage=[80,92,104,110,68,50,45,90,74,68,98,103]; //描绘背景
var gradient=ctx.createLinearGradient(0,0,0,300);
gradient.addColorStop(0,"#e0e0e0");
gradient.addColorStop(1,"#ffffff");
ctx.fillStyle=gradient;
ctx.fillRect(0,0,canvas.width,canvas.height); //描绘边框
var grid_cols=uriage.length+1;
var grid_rows=4;
var cell_height=canvas.height/grid_rows;
var cell_width=canvas.width/grid_cols; ctx.beginPath(); //准备画竖线
for( var col=0;col<=grid_cols;col++)
{
var x=col*cell_width;
ctx.moveTo(x,0);
ctx.lineTo(x,canvas.height);
} //准备画横线
for( var row=0;row<=grid_rows;row++)
{
var y=row*cell_height;
ctx.moveTo(0,y);
ctx.lineTo(canvas.width,y);
} //完成描绘
ctx.lineWidth=1;
ctx.strokeStyle="#a0a0a0";
ctx.stroke(); //获取数据中最大值
var max_v=0;
for(var i=0;i<uriage.length;i++)
{
if(uriage[i]>max_v)
max_v=uriage[i];
}
//为了能让最大值容纳在图表内,计算坐标
max_v=max_v*1.1;
//将数据换算为坐标
var points=[];
for(var i=0;i<uriage.length;i++)
{
var v=uriage[i];
var px=cell_width*(i+1);
var py=canvas.height-canvas.height*(v/max_v);
points.push({"x":px,"y":py});
}
//描绘折线
ctx.beginPath();
ctx.moveTo(points[0].x,points[0].y);
for(var i=1;i<points.length;i++)
{
ctx.lineTo(points[i].x,points[i].y);
}
ctx.lineWidth=2;
ctx.strokeStyle="#ee0000";
ctx.stroke(); //绘制坐标图形
for(var i in points)
{
var p=points[i];
ctx.beginPath();
ctx.arc(p.x,p.y,6,0,2*Math.PI);
ctx.fillStyle="#ee0000";
ctx.fill();
}
</script>
</body>
</html>
8.旋转与变形
 1)变形方法
  ① 变形方法中的旋转/移动相关方法
  setTransform(m11,m12,m21,m22,dx,dy)    变形矩阵的指定(清空先前的指定)
  Transform(m11,m12,m21,m22,dx,dy)    变形矩阵的指定(可重复指定)
  rotate(angle)    旋转
  scale(x,y)   扩大/缩小   说明:参数x为长度缩放的比例,y为宽度缩放的比例
  translate(x,y)   移动/变形    说明:(x,y)为基点移动后的坐标
  setTransform()与transform()的区别是,是否对已经设置的变形矩阵清空。transform()方法可重复指定不同效果的变形矩阵,而setTransform()方法被调用将清空原先设置的变形矩阵。另外,提供旋转或扩大/缩小功能的方法都支持重叠变形矩阵。
 ② 变形矩阵进行保存/恢复的方法
 save()    记录变形矩阵的状态
 restore()    恢复变形矩阵的状态
 
先看移动,扩大/缩小的栗子:
   在Canvas中实现模拟时钟的程序
 效果:
  
代码:
 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style>
<script>
(function(){
//canvas元素对象
var canvas=null;
//canvas的2D上下文
var ctx=null;
//canvas的尺寸
var cw=0;
var ch=0; //页面导入时的事件处理
window.addEventListener("load",function(){
//canvas元素的对象
canvas=document.getElementById("c1");
//canvas的2D上下文
ctx=canvas.getContext("2d");
// 获取canvas的尺寸
cw=parseInt(canvas.width);
ch=parseInt(canvas.height);
//将坐标平面向右下移动
ctx.translate(cw/2,ch/2);
//绘制时钟
draw_watch();
}
,false); function draw_watch()
{
//清空Canvas
ctx.clearRect(-cw/2,-ch/2,cw,ch);
//计算针的最大长度
var len=Math.min(cw,ch)/2;
//绘制刻度盘
var tlen=len*0.85;
ctx.font="14px 'Arial'";
ctx.fillStyle="black";
ctx.textAlign="center";
ctx.textBaseline="middle";
for(var i=0;i<12;i++)
{
var tag1=Math.PI*2*(3-i)/12;
var tx=tlen*Math.cos(tag1);
var ty=-tlen*Math.sin(tag1);
ctx.fillText(i,tx,ty);
} //获取当前时刻的时,分,秒
var d=new Date();
var h=d.getHours();
var m=d.getMinutes();
var s=d.getSeconds();
if(h>12)
{h=h-12;}
//绘制时针,短
var angle1=Math.PI*2*(3-(h+m/60))/12;
var length1=len*0.5;
var width1=5;
var color1="#000000";
drawhand(angle1,length1,width1,color1);
//绘制分针,长
var angle2=Math.PI*2*(15-(m+s/60))/60;
var length2=len*0.5;
var width2=5;
var color2="#000000";
drawhand(angle2,length2,width2,color2);
//绘制秒针
var angle3=Math.PI*2*(15-s)/60;
var length3=len*0.5;
var width3=5;
var color3="#000000";
drawhand(angle3,length3,width3,color3); //设置timer
setTimeout(draw_watch,1000);
} //针绘制函数
function drawhand(angle,len,width,color)
{
//计算针端的坐标
var x=len*Math.cos(angle);
var y=-len*Math.sin(angle); //绘制针
ctx.strokeStyle=color; //设置颜色
ctx.lineWidth=width; //设置线的粗细
ctx.lineCap="round";//将针尖设置为圆形
ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(x,y);
ctx.stroke();
}
})();
</script>
</head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
</body>
</html>
 代码解释:
首先看页面导入时的事件处理代码:
window.addEventListener("load",function()
{
...
//将坐标平面向右下移动
ctx.translate(cw/2,ch/2);
//绘制时钟
draw_watch();
},false);
上述代码中,使用translate()方法将绘图基点移动到Canvas的中心(向右移动一半宽度距离,向下移动一半高度距离)。后面进行绘图处理时,坐标(0,0)就变成Canvas的中心,将大大简化时钟坐标的计算。(巧妙之处)
再看下draw_watch()函数
第一行是清空Canvas区域,因为程序中将每隔1秒钟重新绘制整个模拟时针,以实现动态时针的动画效果,如果不清空,上次的秒针将一直被保留
ctx.clearRect(-cw/2,-ch/2,cw,ch);
clearRect()方法的参数依次为区域左上角x,y坐标,区域宽度,区域高度。因为坐标平面已经移动,所以作为基点的左上角的坐标变成(-cw/2,-ch/2)
然后是,绘制刻度盘的代码
for(var i=0;i<;i++)
{
var tag1=Math.PI*2*(3-i)/12;
var tx=tlen*Math.cos(tag1);
var ty=-tlen*Math.sin(tag1);
ctx.fillText(i,tx,ty);
}
从0时到11时的时刻数字由fillText()完成绘制
这里终点关注坐标的计算,因中心坐标是(0,0),所以只要确定好角度和半径,就可以计算出各个时刻的坐标来(注意在刻度盘数字3的位置的角度是0度)
所以这个计算公式 var angle1=Math.PI*2*(3-(h+m/60))/12;也就好理解了。同理分针和秒针也一样。
然后要完成没过1秒要更新的效果,这时用到定时函数setTimeout(draw_watch,1000);表示每过一秒钟循环调用draw_watch函数自身。

上述代码的解释

2)变形的保存于恢复

① 变形的保存与恢复的方法save()与restore()的语法格式如下

context.save();

context.restore();

这两种方法都是基于同一堆栈(stack)构造。调用save()方法后,将变形结果保存于堆栈顶端,调用restore()方法后,从堆栈顶端取出上次保存的结果用于恢复。

看一个栗子,结合了save(),restore(),及rotate()方法

效果:

代码如下:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
<script type="text/javascript">
//获取上下文
var canvas=document.getElementById("c1");
var ctx=canvas.getContext("2d"); //定义绘制矩形的方法
function drawRect(context,color)
{
context.fillStyle=color;
context.fillRect(0,0,100,30);
} //定义旋转函数
function rorateDeg(context,deg)
{
alert(context);
alert(deg);
var rad=deg*Math.PI/180;
context.rotate(rad);
} //绘制普通的矩形 ---1 画出第一个矩形
drawRect(ctx,"red"); //指定移动、旋转后绘图
ctx.save();
ctx.translate(100,30);//旋转基点
rorateDeg(ctx,45);//旋转45度
drawRect(ctx,"blue"); //---2 画出第二个矩形
ctx.restore();//恢复状态 //指定移动,旋转后绘图
ctx.save();
ctx.translate(200,50);//旋转基点
rorateDeg(ctx,90);//旋转45度
drawRect(ctx,"green");//---3 画出第三个矩形
ctx.restore();//恢复状态
</script>
</body>
</html>

②变形矩阵

与变形矩阵相关的方法有两个,即transform()与setTransform()方法。

语法为:

setTransform(m11,m12,m21,m22,dx,dy)    变形矩阵的指定(清空先前的指定)

  Transform(m11,m12,m21,m22,dx,dy)    变形矩阵的指定(可重复指定)
 其中的参数可变换为如下矩阵,这正是变形矩阵这个名称的由来
 m11   m21   dx
 m12   m22   dy
  0        0       1
简单介绍下各个参数:
 1)m11的默认值为1,当此值越大时,以canvas区域的左侧面为基准,向右延伸坐标空间。相反当值比1小时,向左侧收缩坐标空间。为0时,实际坐标空间完全消失。但是当为负数时,坐标空间变成以右侧为基准,向左侧延伸。
2)m12的默认值为1,当此值越大时,以canvas区域的左侧面为基准,坐标空间向右下方倾斜。相反为负数时,坐标空间向右上方倾斜。
3)m21的默认值为1,当此值越大时,以canvas区域的上侧面为基准,坐标空间向右下方倾斜。相反为负数时,坐标空间向右上方倾斜。
4)m21的默认值为1,当此值越大时,以canvas区域的上侧面为基准,向下延伸坐标空间。向上侧收缩坐标空间。为0时,实际坐标空间完全消失。但是当为负数时,坐标空间变成以下侧为基准,向上侧延伸。
5)参数dx的默认值为0.当此值越大时,Canvas的坐标空间向右移动。相反为负数时,向左移动。
6)参数dy的默认值为0.当此值越大时,Canvas的坐标空间向下移动。相反为负数时,向上移动。
 
然后看一个使用setTransform()方法实现图标图像立体旋转的动画效果的栗子(效果正是太赞了,看着效果,我欣慰地笑了好久,哎呦,不错哦。。。)
 效果:
代码:
 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<img src="XiaoKeAi.jpg" width="200" height="200" alt="小可爱" id="xka"/>
<script type="text/javascript">
(function(){
//不支持window.addEventListener的浏览器不执行代码
if(!window.addEventListener)
{
return;
}
//Canvas对象
var canvas=null;
//canvas的上下文对象
var ctx=null;
//动画的帧率
var fps=60;
//图像对象
var image=null;
//旋转角度
var deg=0; //页面导入时的事件处理
window.addEventListener("load",function(){
//img元素
image=document.getElementById("xka");
//图像尺寸
var w=parseInt(image.width);
var h=parseInt(image.height);
//创建canvas对象
canvas=document.createElement("canvas");
canvas.width=w;
canvas.height=h;
//canvas的上下文对象
ctx=canvas.getContext("2d");
//不支持canvas的浏览器返回
if(!ctx)
{
return;
}
//将图像绘制入canvas中
ctx.drawImage(image,0,0);
//用canvas替换img元素
image.parentNode.replaceChild(canvas,image);
//canvas的动画开始
move();
}
,false);
function move()
{
//canvas尺寸
var cw=parseInt(canvas.width);
var ch=parseInt(canvas.height);
//初始化变形矩阵
ctx.setTransform(1,0,0,1,0,0);
//清空canvas
ctx.clearRect(0,0,cw,ch);
//计算变形矩阵
var m11=Math.cos(deg*Math.PI/180);
var dx=(cw/2)-(cw*m11/2);
//变形矩阵设置
ctx.setTransform(m11,0,0,1,dx,0);
//将变形后的图像绘制入canvas中
ctx.drawImage(image,0,0);
//旋转角度递增
deg++;
deg=deg%360;
//使用timer定时绘制下一副图
setTimeout(move,1000/fps);
}
})();
</script>
</body>
</html>
代码解释:
//页面导入时的事件处理
window.addEventListener("load",function(){
...
//将图像绘制入canvas中
ctx.drawImage(image,0,0);
//用canvas替换img元素
image.parentNode.replaceChild(canvas,image);
//canvas的动画开始
move();
}
,false);
首先将显示图标的img元素的根对象作为参数传递给drawImage()方法,将图像粘贴到canvas上,随后调用repalceChild()方法将img元素替换为Canvas元素。此时img元素并没有从文档中消失,img元素的跟对象可以用于绘制各桢,可以无限多次使用。
然后再看下move()函数
//初始化变形矩阵
ctx.setTransform(1,0,0,1,0,0);
//清空canvas
ctx.clearRect(0,0,cw,ch);
首先将变形矩阵恢复为默认值,這是为了下一个清空Canvas区域时便于坐标计算。
接着计算将向setTransform()方法中传入的各参数值。为了Canvas坐标平面看起来像在立体的旋转,只需要变动m11和dx即可。
var m11=Math.cos(deg*Math.PI/180);
var dx=(cw/2)-(cw*m11/2);
然后再设置
ctx.setTransform(0,1,m11,0,dx,0);
最后
//将变形后的图像绘制入canvas中
ctx.drawImage(image,0,0);
就OK啦。。。
需要连续处理所以又借助了setTimeout函数。。这个不解释了。。。

上述代码的解释

9.绘制文本
 绘制文本相关的方法和属性
方法 说明
context.fillText(text,x,y) 描绘文本,参数text是将要描绘的文本,参数x与y指定了描绘位置的坐标。默认情况下以描绘对象文本的左下为基准
context.fillText(text,x,y,maxWidth) 描绘文本,参数maxWidth是可选的,指定数值后,文本将显示在指定的宽度内,可缩小显示。
context.strokeText(text,x,y) 描绘文件的轮廓
context.strokeText(text,x,y,maxWidth) 描绘文件的轮廓
属性 说明
context.font 定义文本的样式。与CSS的font属性设置的内容相同
context.textAlign 定义文本的对齐方式,可指定start,end,left,right,center等,水平方向的基准
context.textBaseline 定义文件的基准线。可指定top,hanging(罗马字上缘),middle,alphabetic(罗马字基线),bottom,ideographic(罗马字下缘)等,垂直方向的基准

举个栗子,绘制一个简单的图表,效果如下:

 
代码:
 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="400" height="300" ></canvas>
<table id="tb1">
<caption>会员数的变化</caption>
<thead>
<tr>
<th>公元</th>
<th>2003年</th>
<th>2004年</th>
<th>2005年</th>
<th>2006年</th>
<th>2007年</th>
<th>2008年</th>
<th>2009年</th>
</tr>
</thead>
<tbody>
<tr>
<th>会员数</th>
<th>230</th>
<th>360</th>
<th>459</th>
<th>654</th>
<th>834</th>
<th>956</th>
<th>1,085</th>
</tr>
</tbody>
</table> <script type="text/javascript">
(function(){
//不支持window.addEventListener的浏览器不执行代码
if(!window.addEventListener)
{
return;
}
//Canvas对象
var canvas=null;
//canvas的上下文对象
var ctx=null; //页面导入时的事件处理
add_event_listener(window,"load",function(){
//取得canvas对象
var canvas=document.getElementById("c1"); //取得table元素的根对象
var mytb=document.getElementById("tb1"); //绘制图表
draw_graph(mytb,canvas);
}
); //图表绘制
function draw_graph(tb,canvas)
{
//获取canvas的上下文
var ctx=canvas.getContext("2d"); //取得横轴的显示文字
var head_cells=tb.tHead.rows[0].cells; var heads=[];
for(var i=1;i<head_cells.length;i++)
{
heads.push(head_cells[i].innerHTML);
}
//取得值,计算最大值
var max=0;
var value_cells=tb.tBodies[0].rows[0].cells;
var values=[];
for( var i=1;i<head_cells.length;i++)
{
var v=value_cells[i].innerHTML;
v=parseInt(v.replace(/[^\d]/g,"")); //正则表达式,将非数字的字符替换为空格
values.push(v);
if(v>max)
{
max=v; }
}
//定义图表原点
var basex=parseInt(canvas.width*0.1);
var basey=parseInt(canvas.height*0.8);
//计算图表的宽度与高度
var gw=parseInt(canvas.width*0.8);
var gh=parseInt(canvas.height*0.7);
//绘制图表区域的背景
ctx.fillStyle="#eeeeee";
ctx.fillRect(basex,basey-gh,gw,gh);
//显示轴
ctx.beginPath();
ctx.moveTo(basex,basey-gh);
ctx.lineTo(basex,basey);
ctx.lineTo(basex+gw,basey);
ctx.strokeStyle="#666666";
ctx.stroke();
//定义文字的字型
ctx.font="12px '黑体'";
//描绘图表
for(var i=0;i<heads.length;i++)
{
//取得图表的值
var v=values[i];
//竖线中心x坐标
var x=basex+(gw/heads.length)*i+((gw/heads.length)/2);
//定义竖线的宽度
var barw=(gw/heads.length)*0.7;
//定义竖线的高度
var barh=gh*0.9*(v/max);
//绘制竖线
ctx.fillStyle="green";
ctx.fillRect(x-barw/2,basey-barh,barw,barh);
//检查fillText()方法是否存在
if(!ctx.fillText)
{
continue;
}
//间隔与值显示宽度的最大值
var tw=(gw/heads.length)*0.9;
//将文本的坐标基准设置为center
ctx.textAlign="center";
//绘制X轴
ctx.textBaseline="top";
ctx.fillStyle="black";
ctx.fillText(heads[i],x,basey+3,tw);
//绘制值
ctx.textBaseline="ideographic";
ctx.fillStyle="black";
ctx.fillText(v,x,basey-barh-3,tw);
}
}
function add_event_listener(elm,type,func)
{
if(!elm)
{
return false;
}
if(elm.addEventListener)
{
elm.addEventListener(type,func,false);
}
else if(elm.attachEvent)
{
elm.attachEvent('on'+type,func);
}
else
{
return false;
}
return true;
}
})();
</script>
</body>
</html>

10.动画效果

介绍两种动画的实现实例

1)圆球跳动的动画

效果:

代码:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
<script type="text/javascript">
//获取上下文
var canvas=document.getElementById("c1");
var ctx=canvas.getContext("2d"); //记录圆球的状态
var ball={x:10,y:100,dir_x:5,dir_y:5};
//动画
setInterval(drawBall,100);//每个100毫秒调用一次drawBall()函数
function drawBall()
{
//绘制背景
ctx.fillStyle="yellow";
ctx.fillRect(0,0,300,300);
//绘制圆球
ctx.beginPath();
ctx.arc(ball.x,ball.y,15,0,2*Math.PI,true);
ctx.fillStyle="red";
ctx.fill();
//让圆球运动起来
ball.x+=ball.dir_x;
ball.y+=ball.dir_y;
if(ball.x<0||ball.x>300)
{
ball.dir_x*=-1;
}
if(ball.y<0||ball.y>300)
{
ball.dir_y*=-1;
}
}
</script>
</body>
</html>

2)待机动画

效果:

代码:

 <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border-width: 5px;
border-style: dashed;
border-color: rgba(20, 126, 239, 0.50)
}
</style> </head>
<body>
hello HTML5!
<canvas id="c1" width="300" height="300" ></canvas>
<script type="text/javascript">
//获取上下文
var canvas=document.getElementById("c1");
var ctx=canvas.getContext("2d"); //动画绘制开始
var ci=0;
anim();
//定义动画函数
function anim()
{
ctx.clearRect(0,0,300,300);//清空Canvas
//循环绘制36根长方形棒棒
for(var i=0;i<36;i++)
{
ctx.save();
//旋转
var r=(i*10)*Math.PI/180;
ctx.translate(150,150);//移动中心点
ctx.rotate(r);
//绘制细长方形
if(i==ci)
{
ctx.globalAlpha=1.0;//半透明效果
}
else
{
ctx.globalAlpha=0.3
}
ctx.beginPath();
ctx.fillStyle="red";
ctx.rect(0,0,50,6);
ctx.fill();
ctx.restore();
}
ci=(ci+1)%36;
setTimeout(anim,20);//每隔20微秒调用anim函数,实现动画效果
}
</script>
</body>
</html>

结语:终于将Canvas这章知识点学完,HTML5很好,很强大,很好玩~~

HTML5移动开发学习笔记之Canvas基础的更多相关文章

  1. HTML5移动开发学习笔记之CSS3基础学习

    CSS回顾 在学CSS3之前首先巩固下CSS的基础知识. 1.CSS框模型 举例子: #box { width: 70px; margin: 10px; padding: 5px; } 这个代码将出现 ...

  2. 【整理】HTML5游戏开发学习笔记(1)- 骰子游戏

    <HTML5游戏开发>,该书出版于2011年,似乎有些老,可对于我这样没有开发过游戏的人来说,却比较有吸引力,选择自己感兴趣的方向来学习html5,css3,相信会事半功倍.不过值得注意的 ...

  3. 【整理】HTML5游戏开发学习笔记(5)- 猜谜游戏

    距上次学习笔记已有一个多月过去了,期间由于新项目赶进度,以致该学习计划给打断,十分惭愧.书本中的第六章的例子相对比较简单.所以很快就完成. 1.预备知识html5中video标签的熟悉 2.实现思路对 ...

  4. ios开发学习笔记001-C语言基础知识

    先来学习一下C语言基础知识,总结如下: 在xcode下编写代码. 1.编写代码 2.编译:cc –c 文件名.c 编译成功会生成一个 .o的目标文件 3.链接:把目标文件.o和系统自带的库合并在一起, ...

  5. HTML5移动开发学习笔记

    最近做webapp项目过程中,发现坑还是挺多的,以下是网络收集一些开发中的常见问题及知识汇总,以便查阅,慢慢更新:). meta 基础知识 H5页面窗口自动调整到设备宽度,并禁止用户缩放页面 < ...

  6. 【整理】HTML5游戏开发学习笔记(2)- 弹跳球

    1.预备知识(1)在画布上绘制外部图片资源(2)梯度(gradient):HTML5中的对象类型,包括线性梯度和径向梯度.如createLinearGradient,绘制梯度需要颜色组http://w ...

  7. 【整理】HTML5游戏开发学习笔记(4)- 记忆力游戏

    1.预备知识(1)Canvas绘制多边形(2)Canvas绘制文字 2.实现思路涉及的对象  (1)场景Scene  场景代表了画布上的一块区域,场景里的每个物体都是场景里的一个元素,其绘制统一由场景 ...

  8. 【整理】HTML5游戏开发学习笔记(3)- 抛物线运动

    1.预备知识(1)Canvas旋转的实现过程 window.onload = function(){ var ctx = document.getElementById('canvas1').getC ...

  9. Rest API 开发 学习笔记(转)

    Rest API 开发 学习笔记 概述 REST 从资源的角度来观察整个网络,分布在各处的资源由URI确定,而客户端的应用通过URI来获取资源的表示方式.获得这些表徵致使这些应用程序转变了其状态.随着 ...

随机推荐

  1. SQL Server技术内幕笔记合集

    SQL Server技术内幕笔记合集 发这一篇文章主要是方便大家找到我的笔记入口,方便大家o(∩_∩)o Microsoft SQL Server 6.5 技术内幕 笔记http://www.cnbl ...

  2. 数据库的快照隔离级别(Snapshot Isolation)

    隔离级别定义事务处理数据读取操作的隔离程度,在SQL Server中,隔离级别只会影响读操作申请的共享锁(Shared Lock),而不会影响写操作申请的互斥锁(Exclusive Lock),隔离级 ...

  3. [版本控制之道] Git 常用的命令总结(欢迎收藏备用)

    坚持每天学习,坚持每天复习,技术永远学不完,自己永远要前进 总结日常开发生产中常用的Git版本控制命令 ------------------------------main-------------- ...

  4. MySQL数据库和InnoDB存储引擎文件

    参数文件 当MySQL示例启动时,数据库会先去读一个配置参数文件,用来寻找数据库的各种文件所在位置以及指定某些初始化参数,这些参数通常定义了某种内存结构有多大等.在默认情况下,MySQL实例会按照一定 ...

  5. JdbcTemplate+PageImpl实现多表分页查询

    一.基础实体 @MappedSuperclass public abstract class AbsIdEntity implements Serializable { private static ...

  6. Android注解使用之通过annotationProcessor注解生成代码实现自己的ButterKnife框架

    前言: Annotation注解在Android的开发中的使用越来越普遍,例如EventBus.ButterKnife.Dagger2等,之前使用注解的时候需要利用反射机制势必影响到运行效率及性能,直 ...

  7. [C#] C# 知识回顾 - 委托 delegate

    C# 知识回顾 - 委托 delegate [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6031892.html 目录 What's 委托 委托的属性 ...

  8. javascript运动系列第一篇——匀速运动

    × 目录 [1]简单运动 [2]定时器管理 [3]分享到效果[4]移入移出[5]运动函数[6]透明度[7]多值[8]多物体[9]回调[10]函数完善[11]最终函数 前面的话 除了拖拽以外,运动也是j ...

  9. .Net语言 APP开发平台——Smobiler学习日志:手机应用的TextTabBar快速实现方式

    参考页面: http://www.yuanjiaocheng.net/webapi/create-crud-api-1-put.html http://www.yuanjiaocheng.net/we ...

  10. Android中Activity的四大启动模式实验简述

    作为Android四大组件之一,Activity可以说是最基本也是最常见的组件,它提供了一个显示界面,从而实现与用户的交互,作为初学者,必须熟练掌握.今天我们就来通过实验演示,来帮助大家理解Activ ...