前言 继写个福字送给新年的自己 后,为了让新春氛围更加浓厚,必不可少的就是烟花和 BGM 了,这篇文章我们就利用 p5.js 实现一个根据音乐来绽放烟花,为新春增色添彩~
效果预览 
什么是 p5.js 
官网:p5.js 是个 JavaScript 创意编程程式库,其专注在让编程更易于使用及更加广泛的包容艺术家、设计师、教育家、初学者以及任何其他人!p5.js 是个免费及开源的软件因为我们相信所有人都应该能自由使用软件及用于学习软件的工具。p5.js 使用绘图的比喻并有一副完整的绘画功能。除此之外,您也不单限于您的绘图画布。您可以将您整个浏览器页面当作您的绘图,这包括了 HTML5 物件如文字、输入框、视屏、摄像头及音频。
 
简单来说,p5.js 可以利用 canvas 实现许许多多炫酷的浏览器效果,以及在浏览器中进行绘图。
实现 首先我们先引用 p5 的 cdn,因为我们要利用音频相关功能,所以再引用它的扩展包 p5.sound
1 2 <script  src ="https://cdn.bootcdn.net/ajax/libs/p5.js/1.4.0/p5.min.js" > </script > <script  src ="https://cdn.bootcdn.net/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js" > </script > 
接下来我们首先实现烟花的部分,然后再将音乐与烟花结合起来
创建画布 p5 的写法与一般情况不同,js 中直接定义 setup 函数即可实现初始化,draw 函数则用来对画布进行绘制:
1 2 3 4 5 6 function  setup (createCanvas (window .innerWidth , window .innerHeight )function  draw (background (0 , 0 , 20 )
在 vue 中如果想要定义 setup 函数则需要使用另一种声明方式:
1 2 3 4 5 6 7 8 const  s  = (sketch ) => {setup  = () =>  {createCanvas (window .innerWidth , window .innerHeight )draw  = () =>  {background (0 , 0 , 20 )
在本文中我们直接在 html 中编写代码所以采用第一种方式。
创建烟花 这里我们模拟的烟花由一个中心点和围绕在周围的随机点构成。 具体代码作用和含义在注释中标注
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 function  Fireworks (radius ) {var  num = 64  var  cp = new  p5.Vector (random (0 , width),random (height / 2 , height / 5 ),random (-100 , 100 )var  velocity = new  p5.Vector (0 , -15 , 0 )var  accel = new  p5.Vector (0 , 0.4 , 0 )var  imgvar  fp = []var  cosThetavar  sinThetavar  phivar  colorChange = random (0 , 5 )for  (var  i = 0 ; i < num; i++) {random (0 , 1 ) * 2  - 1 sqrt (1  - cosTheta * cosTheta)random (0 , 1 ) * 2  * PI new  p5.Vector (cos (phi),sin (phi),Vector .mult (fp[i], 1.01 )var  side = 64 var  center = side / 2.0 createImage (side, side)loadPixels ()const  r = random (0.5 , 0.8 )const  g = random (0.5 , 0.8 )const  b = random (0.5 , 0.8 )for  (var  y = 0 ; y < side; y++) {for  (var  x = 0 ; x < side; x++) {var  distance = (sq (center - x) + sq (center - y)) / 10.0 var  r = int ((255  * r) / distance)var  g = int ((255  * g) / distance)var  b = int ((255  * b) / distance)set (y, x, color (r, g, b))updatePixels ()this .display  = function  (for  (var  i = 0 ; i < num; i++) {push ()translate (cp.x , cp.y , cp.z )translate (fp[i].x , fp[i].y , fp[i].z )blendMode (ADD ) image (img, 0 , 0 )pop ()Vector .mult (fp[i], 1.015 ) this .update  = function  (dist (0 ,0 ,0 ,0 ].x ,0 ].y ,0 ].z add (velocity)add (accel)this .needRemove  = function  (if  (centerPosition.y  - radius > height) {return  true else  {return  false 
现在基本的烟花类已经被定义好了,我们可以给画布绑定一个键盘事件,并修改 draw 函数中相关逻辑来看一下效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function  keyPressed (push (new  Fireworks (20 )) function  draw (background (0 , 0 , 20 )for  (var  i = 0 ; i < fireworks.length ; i++) {display ()update ()if  (fireworks[i].needRemove ()) {splice (i, 1 )
相关 API 
这里将上述用到的相关 api 和类 单列出来方便查看
API 
描述 
 
 
createCanvas 
创建画布 
 
background 
设置背景色 
 
p5.Vector 
创建包含幅度和方向的新 p5 向量 
 
random 
返回在作为参数给出的范围之间的随机浮点数 
 
sqrt 
获取任何输入数字的平方根 
 
createImage 
创建图片 
 
translate 
指定在显示窗口内放置对象的数量 x 参数用于指定左/右平移 y 参数用于指定上/下平移 
 
blendMode 
根据给定的混合模式混合两个像素 
 
dist 
测量计算在 2D 或 3D 中两点之间的距离 
 
获取音频参数 首先我们需要添加一个audio标签来存放我们的音乐
1 <audio  src ="./1.mp3"  autoplay  controls > 您的浏览器不支持 audio 标签。</audio > 
接着改造我们的 setup 函数,让它能够获取到我们的音频资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var  fireworks = []var  highMap = [2 , 5 , 10 , 15 ]const  ORI_NUM  = 32 function  setup (createCanvas (window .innerWidth , window .innerHeight )frameRate (60 ) imageMode (CENTER )getAudioContext ()let  el = document .querySelector ('audio' )const  source = audioContext_.createMediaElementSource (el)connect (p5.soundOut )new  p5.FFT (0.8 , ORI_NUM )setInput (source)
这里我们获取了音频资源的频谱,未来用来控制我们烟花的大小以及高度。当然 p5.sound 还可以获取音频的音量等等,以实现更炫酷的可视化效果。
接下来我们改造 draw 绘制函数,解析本地音频的频谱并利用它来搞事情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 function  draw (resume ()background (0 , 0 , 20 )let  spectrum = fft.analyze ()forEach ((item, j ) =>  {if  (item !== 0  && j % 2 ) {let  a = highMap[0 ]if  (item >= 64  && item < 128 ) {1 ]else  if  (item >= 128  && item < 192 ) {2 ]else  {3 ]const  fire = new  Fireworks (item, a)push (fire)if  (fireworks.length  > 64 ) {shift ()for  (var  i = 0 ; i < fireworks.length ; i++) {display ()update ()if  (fireworks[i].needRemove ()) {splice (i, 1 )
注意audioContext_.resume()是必须要添加的,因为谷歌浏览器默认不会自动播放音频。
 
spectrum 就是我们分析得到的频谱数值组成的数组,ORI_NUM 作为频谱的个数,最少需要 32 个。我们理论上可以放更多的烟花,但是由于本人电脑性能一般,所以直接取 16 个来放烟花。
代码中我们可以看到,Fireworks 类我们传了两个参数:频谱的值和我们定义的高度值,现在就可以对烟花类改造,以实现根据音频来控制烟花的爆炸半径和发射高度。
1 2 3 4 5 6 7 8 9 10 11 12 13 function  Fireworks (radius, h ) {var  num = 64  var  cp = new  p5.Vector (random (0 , width),random (height / 2 , height / 5 ),random (-100 , 100 )var  velocity = new  p5.Vector (0 , -h, 0 )var  accel = new  p5.Vector (0 , 0.4 , 0 )
为了效果更真实,我们优化一下烟花的随机颜色,让它更亮
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 if  (colorChange >= 3.5 ) {makeColor (0.9 , random (0.2 , 0.5 ), random (0.2 , 0.5 ))else  if  (colorChange > 3.2 ) {makeColor (random (0.2 , 0.5 ), 0.9 , random (0.2 , 0.5 ))else  if  (colorChange > 2 ) {makeColor (random (0.2 , 0.5 ), random (0.2 , 0.5 ), 0.9 )else  {makeColor (random (0.5 , 0.8 ), random (0.5 , 0.8 ), random (0.5 , 0.8 ))function  makeColor (rr, gg, bb ) {var  side = 64 var  center = side / 2.0 var  img = createImage (side, side)loadPixels ()for  (var  y = 0 ; y < side; y++) {for  (var  x = 0 ; x < side; x++) {var  distance = (sq (center - x) + sq (center - y)) / 10.0 var  r = int ((255  * rr) / distance)var  g = int ((255  * gg) / distance)var  b = int ((255  * bb) / distance)set (y, x, color (r, g, b))updatePixels ()return  img
效果就如文章开头那样绚烂多彩。
最后 至此音乐烟花就实现了,而 p5.js 也是我不经意间发现的一个库,发现用它可以做出各种强大的动画。本以为我会是第一个用 p5,js 来实现效果的人,而就在我把这个 demo 写出来的第二天,活动的第一天,就读到了北京哥的一场烟花盛宴 🎇,祝你新年快乐 🎉 ,实现思路不同,效果我觉得北京哥的也更美观真实~,心痛哈哈哈哈做不了第一那做个……第n也不错。