AudioContext技术和音乐可视化(2)
Intro
转载请注明来源,可以在测试博客查看完成效果。
本篇讲述如何绘制动态的星空,其实关联到频域数据已经没什么悬念了。
一、使用Canvas绘图
1.1 位置和大小
绘制背景的第一要务便是把canvas元素放置在背景这一层次上,避免遮盖其他元素。
对我而言,个人习惯用css来设置大小和位置,用html来确定渲染顺序而不是z-index。
下面是html代码。
<html> <body> <canvas id="background-canvas"></canvas> <!-- other elements --> </body> </html>
下面是css代码。
#background-canvas { position: fixed; left: 0; top: 0; width: 100vw; height: 100vh; background-color: black; }
fixed
确保拖动页面不会令背景也跟随移动。
其余部分我想应该没什么有疑问的地方。
1.2 CanvasContext2D
对于canvas元素的绘图操作我想很多人应该接触过。
以绘制圆形为例,使用如下代码。
const canvas = document.getElementById("background-canvas"); const ctx = canvas.getContext("2d"); ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(100,100,50,0,Math.PI*2); // 参数分别为坐标x,y,半径,起始弧度,结束弧度 ctx.fill();
这样就画完了一个实心圆。
需要注意,canvas的大小通过css设置可能导致画面被拉伸变形模糊,所以最好的办法是绘制前确定一下canvas的大小。
此外需要注意的是,重置大小会导致画面清空,用这种方式可以替代fillRect
或者clearRect
,有的浏览器平台更快但也有浏览器更慢。可以查阅这篇博文来参考如何提升canvas绘图性能。
fillStyle
可以使用css的颜色代码,也就是说我们可以写下诸如rgba
、hsla
之类的颜色,这给我们编写代码提供了很多方便。
1.3 绘制星星
星空是由星星组成的这显然不用多说了,先来看如何绘制单个星星。
星星的绘制方法很多,贴图虽然便利但显然不够灵活,我们的星星是要随节奏改变亮度和大小的,利用贴图的话就只能在alpha
值和drawImage
缩放来处理了。虽然是一种不错的办法,不过这里我使用了RadialGradient
来控制绘图。
PS:
RadialGradient
的性能比较差,大量使用会导致明显的性能下降,这是一个显著降低绘制效率的地方。
那么,我们先画一个圆(加点细节预警)。
const canvas = document.getElementById("background-canvas"); const ctx = canvas.getContext("2d"); // 确保不会变形 canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; // 参数分别为起始坐标x,y,半径,结束坐标x,y,半径 const gradient = ctx.createRadialGradient(100, 100, 0, 100, 100, 50); gradient.addColorStop(0.025, "#fff"); // 中心的亮白色 gradient.addColorStop(0.1, "rgba(255, 255, 255, 0.9)"); // 核心光点和四周的分界线 gradient.addColorStop(0.25, "hsla(198, 66%, 75%, 0.9)"); // 核心亮点往四周发散的蓝光 gradient.addColorStop(0.75, "hsla(198, 64%, 33%, 0.4)"); // 蓝光边缘 gradient.addColorStop(1, "hsla(198, 64%, 33%, 0)"); // 淡化直至透明 ctx.fillStyle = gradient; ctx.beginPath(); ctx.arc(100, 100, 50, 0, Math.PI * 2); ctx.fill();
可以在codepen查看效果或直接编辑你的星(圈)星(圈)。
看上去还不错?
让我们用代码控制它的亮度和大小。
const canvas = document.getElementById("background-canvas"); const ctx = canvas.getContext("2d"); // 确保不会变形 canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; // 通过energy控制亮度和大小 let energy = 255; let radius = 50; let energyChangeRate = -1; function draw() { requestAnimationFrame(draw); // 定时绘制,requestAnimationFrame比setTimeout更好。 energy += energyChangeRate; // 见过呼吸灯吧?我们让它变亮~再变暗~反复循环~ if (energy <= 0 || energy >= 255) energyChangeRate = -energyChangeRate; // 计算出当前的大小 const r = radius + energy * 0.1; // 清空屏幕 ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); // 参数分别为起始坐标x,y,半径,结束坐标x,y,半径 const gradient = ctx.createRadialGradient(100, 100, 0, 100, 100, r); gradient.addColorStop(0.025, "#fff"); // 中心的亮白色 gradient.addColorStop(0.1, "rgba(255, 255, 255, 0.9)"); // 核心光点和四周的分界线 gradient.addColorStop(0.25, `hsla(198, 66%, ${Math.min(75+energy*0.01,100)}%, 0.9)`); // 核心亮点往四周发散的蓝光 gradient.addColorStop(0.75, `hsla(198, 64%, ${Math.min(33+energy*0.01,100)}%, 0.4)`); // 蓝光边缘 gradient.addColorStop(1, "hsla(198, 64%, 33%, 0)"); // 淡化直至透明 ctx.fillStyle = gradient; ctx.beginPath(); ctx.arc(100, 100, r, 0, Math.PI * 2); ctx.fill(); } draw();
可以在codepen查看并编辑效果。
1.4 封装星星
通常来说粒子系统不大会把单个粒子封装成类,因为函数调用的开销还是蛮大的。。。
不过在这里我们这里就先这样了,方便理解和阅读。渲染的瓶颈解决之前,粒子函数调用这点开销根本不是回事儿。
const canvas = document.getElementById("background-canvas"); const ctx = canvas.getContext("2d"); // 确保不会变形 canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; // 用javascript原生的class而不是prototype class Star { constructor(x, y, radius, lightness) { this.radius = radius; this.x = x; this.y = y; this.lightness; } draw(ctx, energy) { // 计算出当前的大小 const r = this.radius + energy * 0.1; // 参数分别为起始坐标x,y,半径,结束坐标x,y,半径 const gradient = ctx.createRadialGradient( this.x, this.y, 0, this.x, this.y, r ); gradient.addColorStop(0.025, "#fff"); // 中心的亮白色 gradient.addColorStop(0.1, "rgba(255, 255, 255, 0.9)"); // 核心光点和四周的分界线 gradient.addColorStop( 0.25, `hsla(198, 66%, ${Math.min(75 + energy * 0.01, 100)}%, 0.9)` ); // 核心亮点往四周发散的蓝光 gradient.addColorStop( 0.75, `hsla(198, 64%, ${Math.min(33 + energy * 0.01, 100)}%, 0.4)` ); // 蓝光边缘 gradient.addColorStop(1, "hsla(198, 64%, 33%, 0)"); // 淡化直至透明 ctx.fillStyle = gradient; ctx.beginPath(); ctx.arc(this.x, this.y, r, 0, Math.PI * 2); ctx.fill(); } } const star = new Star(100, 100, 50); let energy = 255; let energyChangeRate = -1; // 渲染函数来循环渲染! function render() { requestAnimationFrame(render); energy += energyChangeRate; if (energy <= 0 || energy >= 255) energyChangeRate = -energyChangeRate; // 清空屏幕 ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); star.draw(ctx, energy); } // 开始渲染动画! render();
可以在codepen查看代码效果。
完成!
1.5 银河
绘制银河的核心在于随机分布的星星绕着同一中心点旋转,分为两步来讲,第一步是随机分布,这很简单,用Math.random
就好了。
// star 部分略 class Galaxy { constructor(canvas) { this.stars = []; this.canvas = canvas; this.ctx = canvas.getContext("2d"); this.energy = 255; this.energyChangeRate = -2; } init(num) { for (let i = 0; i < num; i++) { this.stars.push( // 随机生成一定数量的星星,初始化星星位置和大小。 new Star( Math.random() * this.canvas.width, Math.random() * this.canvas.height, Math.random() * 10 + 1, Math.random() * 30 + 33 ) ); } } render() { this.energy += this.energyChangeRate; if (this.energy <= 0 || this.energy >= 255) this.energyChangeRate = -this.energyChangeRate; // 清空屏幕 this.ctx.fillStyle = "black"; this.ctx.fillRect(0, 0, canvas.width, canvas.height); for (const star of this.stars) { star.draw(this.ctx, this.energy); } } } const canvas = document.getElementById("background-canvas"); // 确保不会变形 canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; const galaxy = new Galaxy(canvas); galaxy.init(50); function render() { requestAnimationFrame(render); galaxy.render(); } render();
可以在codepen查看效果和完整代码。
1.6 旋转起来!
【加点细节预警】
接下来我们为星星准备轨道参数,让它们动起来!
首先修改Star
类,加入几个字段。
class Star { constructor(x, y, radius, lightness, orbit, speed, t) { this.radius = radius; this.x = x; this.y = y; this.lightness; this.orbit = orbit; // 轨道 this.speed = speed; // 运动速度 this.t = t; // 三角函数x轴参数,用 sin/cos 组合计算位置 } // 下略 }
修改初始化代码。
// 前略 init(num) { const longerAxis = Math.max(this.canvas.width, this.canvas.height); const diameter = Math.round( Math.sqrt(longerAxis * longerAxis + longerAxis * longerAxis) ); const maxOrbit = diameter / 2; for (let i = 0; i < num; i++) { this.stars.push( // 随机生成一定数量的星星,初始化星星位置和大小。 new Star( Math.random() * this.canvas.width, Math.random() * this.canvas.height, Math.random() * 10 + 1, Math.random() * 30 + 33, Math.random() * maxOrbit, // 随机轨道 Math.random() / 1000, // 随机速度 Math.random() * 100 // 随机位置 ) ); } // 后略
然后在Galaxy
里加入控制移动的代码。
move() { for (const star of this.stars) { console.log(star.orbit) star.x = this.canvas.width/2+ Math.cos(star.t) * star.orbit; star.y = this.canvas.height/2+ Math.sin(star.t) * star.orbit/2; star.t += star.speed; }
然后每一帧进行移动!
function render() { requestAnimationFrame(render); galaxy.render(); galaxy.move(); // 动起来! }
大功告成!
在codepen查看完整源码!
1.7 待续
PS:不保证粘贴的代码都能跑,反正codepen上是都能的。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Redis 分布式锁进化史
按:系统架构经过多年演进,现在越来越多的系统采用微服务架构,而说到微服务架构必然牵涉到分布式,以前单体应用加锁是很简单的,但现在分布式系统下加锁就比较难了,我之前曾简单写过一篇文章,关于分布式锁的实现,但有一次发现实现的分布式锁是有问题的,因为出问题的概率很低,所以当时也没在意,前几天和朋友聊这个问题,想起来看过一篇文章,写的不错,今天特转载过来,希望能让更多的人看到,同时也加深一下记忆。原文链接是:http://tech.dianwoda.com/2018/04/11/redisfen-bu-shi-suo-jin-hua-shi/ 以下为原文: 近两年来微服务变得越来越热门,越来越多的应用部署在分布式环境中,在分布式环境中,数据一致性是一直以来需要关注并且去解决的问题,分布式锁也就成为了一种广泛使用的技术,常用的分布式实现方式为Redis,Zookeeper,其中基于Redis的分布式锁的使用更加广泛。 但是在工作和网络上看到过各个版本的Redis分布式锁实现,每种实现都有一些不严谨的地方,甚至有可能是错误的实现,包括在代码中,如果不能正确的使用分布式锁,可能造成严重的生产环境故障...
- 下一篇
PHP从扩展公钥生成比特币钱包地址
重复使用相同的比特币钱包地址是一个很大的隐私问题。如果你有一个简单的电子商店或要求捐赠的网站,你可能需要考虑为每笔交易生成唯一的地址。 有很多支付系统,如Bitpay,为你完成所有艰苦的工作。缺点是他们需要使用你的私钥。但是,你可以使用来自分级确定性(hierarchically deterministic,简称HD)钱包的扩展公钥(XPUB)来实现你自己的简单解决方案。 整个过程在BIP32中进行了解释。我建议你先阅读它,以便大致了解地址的来源。 在本教程中,我们将使用Electrum,OS X Sierra,Apache 2.4,PHP 7.1和Bit-Wasp/bitcoin-php。 当谈到比特币钱包时,任何HD钱包(如Mycelium)都可以使用。在任何类UNIX系统上,特别是Linux,安装过程应该是相同的。 PHP库及其依赖项需要PHP5.6+。打开终端并检查你当前的版本: php -v 在我的环境下,输出是: PHP 7.1.0 (cli) (built: Jan 2 2017 20:09:35) ( NTS ) Copyright (c) 1997-2016 The ...
相关文章
文章评论
共有0条评论来说两句吧...