昨天没把树叶画上去,还是心痒痒的,所以动手简单装饰了一下这棵树:
https://jerry-the-potato.github.io/Chapter3-demo2-object/
不过灵感跟美感毕竟不太够,还是赶快前进到第四章要紧先
(进度快的话最後还能把树放到游戏中然後优化它)
是时候结合1-3章的内容了,首先我们观察一下几首歌的音量变化:(音量增加图)
出自: 我的Demo
当初没有教大家怎麽画这个,没关系这不难也不是重点,听结论就好
经过观察得知,每当音符/和弦出来的时候,都会有2-4侦的音量急遽增加,随後缓慢降低,因此第一个思路就很单纯,我们希望程序能懂得把连续的音量脉冲,当成完整一次的旋律,并且在第2侦时会接近高峰值,也是音乐正在传入耳朵时,因此将其脉冲大小作为参考,来决定要一次做出多少个动画。
接着看下一首,有钢琴和弦作为开场的音乐:
可以看到,音量的曲线应该接近於Ease-out达到高峰,所以音量的增加才会如图,到达高峰後陆续下降并趋於平缓,这种和弦带来的效果是拉长的听觉效果,先做个笔记,未来有时间可以再新增第二种动画模式,来呈现其绵延的旋律。
刚也有提到音量增加图表示了音量的差值,直接取名为volume,还记得我们用reduce方法的那篇写到:
// 阵列最大长度到 INDEX 为止
(dataIndex.volume > INDEX) ? dataIndex.volume = 0 : dataIndex.volume++;
let volume = dataArray.delta.reduce((a,b) => a+b, 0);
dataArray.volume.splice(dataIndex.volume, 1, volume);
// 计算最大值
let maxVolume = dataArray.volume.reduce((a,b) => Math.max(a,b), 1);
没印象的话可以回头去看这篇 https://ithelp.ithome.com.tw/articles/10268606
当初为了画图已经有做好这个阵列,这边我们就有很简单的方法,拿到最近三次的差值:
let v1 = dataArray.volume[dataIndex.volume - 1];
let v2 = dataArray.volume[dataIndex.volume - 2];
let v3 = dataArray.volume[dataIndex.volume - 3];
// 我们只要连续两次,连续三次以上都不算数,因此判断 v3 <= 0
if(v1 > 0 && v2 > 0 && v3 <= 0){
let times = 100 * Math.max(v1, v2) / maxVolume; // 100乘上一个0~1之间的数
animeList.push(new animeObject(times, 'Falling'));
}
animeObject 是我们第二章操作的落叶物件
animeList 是个把物件们放在一起,用来迭代的阵列
let animeList = new Array();
function animeObject(times, animeName='Falling',
imgNumber=Math.floor(Math.random()*4),
sizeMin=0.03,sizeMax=0.04,
lifeTime=5, timestamp=Date.now()){
this.animeName = animeName;
this.imgNumber = imgNumber;
this.img = pngImg[this.imgNumber];
if(animeName == "Falling" || animeName == "Staring"){
this.beginX = Math.random() * WIDTH;
this.beginY = Math.random() * HEIGHT;
}
this.size = Math.random() * (sizeMax - sizeMin) + sizeMin;
this.timestamp = Date.now();
this.lifeTime = lifeTime;
this.period = 1 + Math.random() * 1;
// 变化属性
this.pointX = this.beginX;
this.pointY = this.beginY;
this.sizeNow = 0;
this.rotateTheta = Math.random() * 360 / 180 * Math.PI;
this.rotateOmega = 60 / 180 * Math.PI;
this.revolveTheta = Math.random() * 360 / 180 * Math.PI;
this.revolveOmega = 60 / 180 * Math.PI;
if(times > 5) animeList.push(new animeObject(Math.pow(times, 0.9), 'Falling'));
// times 的算法可以自行设计,就是把输入的参数转换成迭代的参数
// 我是设计为5-100之间会进行迭代,然後每次开0.9次方根号
// (100共会做十次、40会做八次、20会做六次、12会做四次、7会做两次)
}
开头传入参数时,若无值传入,则预设为Falling模式、图片以4个一组随机抽签etc...
整体跟落叶物件那篇相去不远,主要添加了递回的观念
以及一个随机的周期参数period,使动画物件彼此拥有不同的周期,范围是1~2倍
接着搭配第三章画树时学到的prototype改写方法:
animeObject.prototype.NextFrame = function(){
// 计算下一侦的位置
let dT = (Date.now() - this.timestamp) / 1000;
if(this.animeName == "Floating") this.Floating(dT);
else if(this.animeName == "Falling") this.Falling(dT);
else if(this.animeName == "Staring") this.Staring(dT);
if(dT < this.lifeTime){
// 画出下一侦的位置
if(this.img.complete){
let width = this.sizeNow;
let height = this.sizeNow * this.img.height / this.img.width;
let rotateNow = this.rotateTheta + this.rotateOmega * dT;
context.save();
context.translate(this.pointX, this.pointY);
context.rotate(rotateNow);
context.drawImage(this.img, -width/2, -height/2, width, height);
context.restore();
}
}
else{
// 把动画物件删掉
let index = animeList.indexOf(this);
delete animeList[index];
animeList.splice(index, 1);
}
}
计算下一侦的位置,根据不同种类的动画公式去做计算,以落下为例子:
animeObject.prototype.Falling = function(dT){
let revolveNow = this.revolveTheta + this.revolveOmega * dT;
let A = Math.sin(revolveNow);
let B = Math.cos(revolveNow);
let C = Math.sin(revolveNow * this.period);
let D = Math.cos(revolveNow * this.period);
this.pointX = this.beginX + WIDTH * 0.04 * A;
this.pointY = this.beginY + HEIGHT * 0.01 * C + HEIGHT * 0.05 * dT;
const popSize = 0.2;
let lT = this.lifeTime;
let distanceT = (Math.abs(dT - lT*0.35) + Math.abs(dT - lT*0.65)) / lT;
this.sizeNow = WIDTH * this.size * (popSize + (1 - distanceT));
}
在第二章的动画基础上,添加几个部分:
- 有两种三角函数,一种是基本的、另一种是乘上周期倍速period的变速版本
- 添加popSize,使得动画一开始出现时以只有0.2倍大(从远方渐入的效果)
- distanceT是一个国中学过的线性函式,用来计算数线0-1之间,任一点与0.35和0.65的距离总和,结果就不用解释...了吧?XD
接下来第四章(包括这篇)都是重头戏,先前铺陈那麽久,就是为了能让大家循序渐进好上手,如果前面的章节你都看过,想必这一篇并不会太难吧!也是不断精简过後的内容了,这段code是我重写第三次了,第一次大概是2倍的行数,第二次是1.5倍,现在就如各位看到的。
话说,明天上个流程图吧!
<<: 离职倒数5天:「你是中国人吗」应该是我在日本生活最不自在的瞬间之一
今天这篇文章会介绍CSS文字大小、文字粗细、字体和字型,这些都是有关文字样式的相关属性: 文字大小 ...
托管代码(managed code) 微软特定用语 简单来说 managed code 就是由一个 ...
) 上面这个是今天会提到的内容,如果你已经可以轻松看懂,欢迎直接左转去看我队友们的精彩文章! Ind...
在开始学习机器学习之前,我们得先准备好环境,我们使用python来当我们的程序语言,稍微介绍一下py...
什麽是 LeetCode? 「什麽是 LeetCode?」是整个铁人赛系列文章的第一个主题,你现在...