绘制雨滴时,目前大多数的程序中,雨滴的原型有属性update。对于雨滴的移动move进行了原型外的function操作。并使用数组下标的方式对雨滴实例进行定位实现move。
那么,倘若move是固定在每一个雨滴的程序,是否意味着,可以将move也写为雨滴原型中的属性。下面进行了实验:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>雨滴下落1</title>
<style>
body{margin:0;}
#rain{display:block;
background-color:black;}
</style>
</head>
<body>
<canvas id="rain"></canvas>
<script>
/*
1.获取
1.1 设置canvas宽高
1.2 获取窗口宽高
1.3 当窗口大小变化时,改变canvas宽高
2.绘制雨滴
3.多个雨滴
3.雨滴降落
*/
//1.设置canvas大小
var can=document.getElementById("rain");//为can元素设置宽高,而不是画布getcontext
//var can=document.getContext(canva);
var w=window.innerWidth;//使用全局变量保存宽高,减少访问,便于调用
var h=window.innerHeight;//属性名字为inner-width/height
can.width=w;
can.height=h;
window.οnresize=function(){
w=window.innerWidth;
h=window.innerHeight;//需要重新为w,h赋值
can.width=w;
can.height=h;
}
//2.绘制雨滴
//绘制一滴
/*
function random(min,max){
return Math.random()*(max-min)+min;
}
var x=random(0,w);
var y=0;//起点
var canContent=can.getContext("2d");//画布
//初始位置绘制
canContent.fillStyle="rgb(0,255,255)";
canContent.fillRect(x,y,5,20);//雨滴
//移动--动画
function draw(){
canContent.fillStyle="rgba(0,0,0,0.01)";
canContent.fillRect(0,0,w,h);//毛玻璃
canContent.fillStyle="rgb(0,255,255)";
canContent.fillRect(x,y++,5,20);//雨滴位置变化
}
setInterval(draw,1000/60);
*/
function random(min,max){
return Math.random()*(max-min)+min;
}
function Rain(){};
var canContent=can.getContext("2d");
Rain.prototype={
init:function(){
//参数
this.x=random(0,w);
this.y=0;//起点
this.l=random(0.8*h,0.9*h);//下落高度
this.v=random(4,5);//下落速度
this.r=1;//水花的初始半径
this.vr=random(1,2);//半径扩大的速度
this.rmax=random(50,60);//开始变透明的半径
this.a=1;//初始透明度
this.va=0.98;//透明度的变化
this.amin=0.2;
},
draw:function(){
//绘制
//.1绘制下落
if(this.y<this.l){
canContent.fillStyle="rgb(0,255,255)";
canContent.fillRect(this.x,this.y,2,20);//雨滴位置变化
}else{
//.2绘制水花
canContent.strokeStyle="rgba(0,255,255,"+this.a+")";//
//"rgba(0,255,255,this.a)";//会出错,读取字符串,无法获得变量变化值
canContent.beginPath();
canContent.arc(this.x,this.y,this.r,0,2*Math.PI,false);
canContent.stroke();
}
},
update:function(){
//更新坐标值,并绘制
//.1下落过程的坐标更新
if(this.y<this.l){
this.y+=this.v;
}else{
//.2水花过程的坐标更新(坐标不变,半径变化,透明度变化)
//更新后绘制雨滴:
/*
透明度的变化取决于半径大小
this.r+=this.vr;
if(this.r<this.rmax){
this.a*=this.va;
}else{
this.init();
}
}
*/
//透明度的变化取决于半径大小,半径大小取决于透明度大小
if(this.a>this.amin){
this.r+=this.vr;
if(this.r>this.rmax){
this.a*=this.va;//乘更自然
}
}
else{
this.init();//透明度变了的话,回到天上
}
}
this.draw();
console.log(this);
},
move:function(){//将move添加在原型属性中
//移动显示:覆盖不透明度不同的层数,并不断在新的位置绘制雨滴,所形成的
canContent.fillStyle="rgba(0,0,0,0.05)";
canContent.fillRect(0,0,w,h);//毛玻璃
console.log("move"+this);
this.update();
setInterval(this.move,1000/60);//this的指向
//console.log("thisismove");
}
}
//创建30个
for(var i=0;i<30;i++){
setTimeout(createRain,300*i);//隔300*i ms执行一次createRain
}//同时创建30个setTimeout,但每个setTimeout开始执行的时间不同
function createRain(){
var rain=new Rain();
rain.init();
rain.draw();
rain.move();
}
//不能在函数体内写setInterval
//所有的计算与渲染必须在16ms内完成???
//在第0秒就开始执行
//setInterval(hanshu,10ms);
//每个一段时间就执行setInterval中的函数,插到堆栈中,会打断整个任务的运行
//假设10mm运行一次interval,而若调用的"hanshu"程序19ms才运行完,那么会抛弃第二次的interval,即造成丢帧
//运行主体:window;只有window才能调用setInterval,注意this指向因此不存在this.update
//替代
//请问:如果move是在跟着元素创建中的属性,那么可否在prototype中运行?
</script>
</body>
</html>
运行发现出现错误显示为this.update()不存在
查找资料与视频讲解,了解到:
setInterval函数的特殊性:
setInterval(codeFunc,timeInterval)
①此命令将会,在JS最初运行的时间t=0ms时就开始运行,并在运行栈中插入固定的位置,意味着每间隔timeInterval秒就执行一次。
②要求在时间间隔timeInterval内执行完毕所有的codeFunc
③命令的主体只能是window
在上述程序中,当要求执行setInterval时,对rain实例进行操作,此时时间为1000/60ms,而rain至少在300ms时才被创建,因此此时的命令没有操作的对象。
另外,setInterval有一个缺点是容易造成丢帧。原因是:
//setInterval(rain.move,1000/60);//此时还没有rain//在第0秒就开始执行