多个图片元素的动画绘制
步骤:
- 每一帧动画的元素都重新绘制在画板中;
- 将元素创建的对象放入数组中,遍历元素,图片加载onload后绘制;
var imgArr=[obj1, obj2 …]; //对象数组
var count = 0; //遍历对象的计数器
imgArr.forEach( function(img){
count++; //计数器计数
if (count == imgArr.length) { //imgArr中所有元素加载完毕
function draw( ){
绘制每一帧的动作; //tips: 重置画布画纸、画各个元素、递归
window.requestAnimationFrame(draw); //递归:自适应帧率
}
draw(); //第一次调用:推一下,跑起来~
}
})
- 将每个元素的绘制都封装在函数draw中,在draw中写递归(draw外手动调用);
- draw函数开始需要进行重置:清空画布、换纸
- 每个图片、文字或图形的绘制参数可以进行封装;
- 画布进行位移/旋转前进行context的读档和存档,换纸。
案例:地球围绕太阳转
实现效果:
地球绕着太阳转.gif
步骤:
- 创建图片对象并添加到数组,forEach遍历数组,加载图片
- 图片加载后执行函数draw,每次执行draw都会把太阳图片和地球图片重新drawImage一次
代码如下:
<body><canvas height="400" width="600" id="canvas"></canvas></body>
<script>
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var earth = new Image(); //创建元素的对象和数组
var sun = new Image();
sun.src = "./img/Canvas_sun.png";
earth.src = "./img/Canvas_earth.png";
var imgArr = [sun, earth];
var count = 0;
imgArr.forEach(function (img) { //forEach遍历
img.onload = function () {
count++;
if (count == imgArr.length) {
function draw(){ //函数draw
context.save(); //清画板,存档画笔状态
// context.beginPath();
context.drawImage(sun, 0, 0); //画太阳
var now = new Date(); //算草稿纸角度:秒和千秒
var seconds = now.getSeconds() + now.getMilliseconds()/1000 ;
var earthR = seconds/30*Math.PI;
var earthX = sun.width/2 - earth.width; //算地球xy
var earthY = -earth.height/2;
context.translate(sun.width/2, sun.height/2); //挪纸,转角度
context.rotate(earthR);
context.drawImage(earth, earthX, earthY); //画地球
context.restore(); //读档画笔状态(重置状态)
window.requestAnimationFrame(draw); //自适应帧率动画,回调draw
}
draw(); //第一次调用
}
}
})
</script>
案例:机械时钟的绘制
呈现效果:
机械钟.gif
思路:
1.每一帧都重新绘制所有元素,包括表盘和指针。
2.时刻线、指针均用草稿纸旋转完成,每一根指针的绘制都需要重新读档存档。
3.每一帧开始前都需要保证画板的坐标处于原始状态。
时钟在画布上的角度分析.png
代码实现:
<body>
<canvas width="600" height="400" id="canvas" style = "border:1px solid #555"></canvas></body>
</html>
<script>
var canvas= document.getElementById("canvas");
var context = canvas.getContext("2d");
function draw(){ //draw函数
context.clearRect(0,0,600,400); //初始化
context.beginPath();
//画表盘
context.strokeStyle="seagreen";
context.lineWidth = 4;
context.arc(300,200,140,0,2*Math.PI); //画圆
context.stroke();
context.beginPath(); //新纸
//存档ct
context.save(); //存档1:坐标系0, 笔粗4
//循环:转稿纸画线
context.translate(300,200); //坐标系300-200, 笔粗4
for(var i =0; i<12; i++){
context.rotate(Math.PI/6);
context.moveTo(125,0);
context.lineTo(135,0);
}
context.stroke();
context.beginPath(); //新纸
context.lineWidth = 2; //坐标系300-200 笔粗2
for(var i =0; i<60; i++){
context.rotate(Math.PI/30);
context.moveTo(130,0);
context.lineTo(135,0);
}
context.stroke(); //印制,新纸
context.beginPath();
//画时针
var now = new Date(); //获取时间,算角度
var hourR =now.getHours()>12?(now.getHours()-12)*Math.PI/6 : (now.getHours())*Math.PI/6;
//读档,位移,旋转,印制,新纸
context.restore(); //读存档1:坐标系0, 笔粗4
context.save(); //存档1:坐标系0, 笔粗4
context.translate(300,200); //坐标系300-200, 笔粗4
context.rotate(-Math.PI/2); //坐标系300-200,-90° 笔粗4
context.lineWidth=8; //坐标系300-200,-90° 笔粗8
context.lineCap="round";
context.save(); //存档2:坐标系300-200,-90° 笔粗8
context.rotate(hourR);
context.moveTo(-10,0);
context.lineTo(50,0);
context.stroke();
context.beginPath(); //新纸
//画分针
var minuteR = now.getMinutes()/30*Math.PI; //算角度
// console.log(minuteR);
//读档,位移,旋转,印制,新纸
context.restore(); //读存档2:坐标系300-200,-90°
context.save(); //存档2
context.rotate(minuteR);
context.moveTo(-15,0);
context.lineTo(95,0);
context.lineWidth = 6;
context.stroke();
context.beginPath();
//画秒针
var secondR = now.getSeconds()/30*Math.PI; //算角度
//读档,位移,旋转,印制
context.restore(); //读存档2:坐标系300-200,-90°
context.rotate(secondR);
context.moveTo(-25,0);
context.lineTo(115,0);
context.lineWidth = 2;
context.strokeStyle="#53C686";
context.stroke();
//画表芯:新纸,读档,印圆
context.restore(); //读存档1:坐标系0, 笔粗4
context.beginPath();
context.fillStyle = "#53C686";
context.arc(300,200,8,0,2*Math.PI);
context.fill();
window.requestAnimationFrame(draw); //自适应帧率,递归调用
}
draw(); //第一次draw
</script>
2018.1.17
网友评论