javascript map 排序_1Keys仅用1 kb的JavaScript制作钢琴
传说中的JavaScript竞赛JS1k已经结束,但是它以JS1024的形式从灰烬中崛起!这种制作微型JavaScript程序的竞赛已经进行了10年,现在将以新名称继续进行。感谢组织者和其他参与者,您一定要检查其他条目。
弄清楚如何用这么少量的代码使某些东西变得很酷是很大的挑战,但这不是我的第一个牛仔竞技表演。今年,我提交了两个程序,所以请查看我的其他条目Digit Dilemma Plus,这是一个具有程序级别的逻辑益智游戏。竞赛为canvas和WebGL程序提供了“垫片”和html入门代码,但我的参赛作品都没有使用它们。
JavaScript提供了一个功能强大但简单的音频库,即Web Audio API。我在开发微型声音效果系统ZzFX时首先进行了探索。一段时间以来,我一直渴望制作一些乐器。该演示与ZzFX的工作原理大不相同,主要是使用振荡器和增益节点。
在这篇文章中,我将介绍1 KB钢琴的每一行,并解释一些棘手的部分是如何工作的。整个事情在GitHub上都是开源的,并且自从我提交JS1024以来有了一些改进。因此,请继续阅读,让我们一起创作优美的音乐!
GitHub官方JS1024条目上的源代码 https://github.com/KilledByAPixel/1Keys
初始化
该程序几乎完全是JavaScript,但是需要一些HTML来进行设置。通常,可以使用“ document.body”访问主体,但是为了节省空间,我们将主体ID设置为B。主体样式是在JavaScript代码中设置的,因此可以与其他所有内容一起压缩。在最终版本中,所有代码都移至主体的onload,而不是使用script标签挤出更多的字节。
所有变量和函数都是单个字母,但只有几个。为了帮助组织,全局变量使用大写字母,而局部变量使用小写字母。这不是我通常的代码编写风格,而是我用于极小的程序的一种简写形式。
音频上下文已创建并存储在C中,它将用于所有音频呼叫。D函数获取给定索引的钢琴键html元素。每个密钥将使用唯一的ID K + i创建,因此可以通过这种方式访问它们。代替调用document.getElementById(),我们可以使用eval将“ K3”之类的ID转换为变量K3。
<body id=B><script>
// body style
B.style = `background:#112;color:#fff;user-select:none;text-align:center`;
C = new AudioContext; // audio context
D = i=> eval(`K` + i); // get piano key div
创建仪器单选按钮
该程序的所有HTML内容都是动态构建的,以节省空间。首先,我们为四种不同的乐器添加单选按钮。我使用表情符号是因为它们比单词小!
为了创建按钮,我们将表情符号字符串转换为数组,并使用map遍历图标,为每个图标添加单选按钮。输入的onmousedown事件会在更改乐器时进行设置。
所有乐器按钮均设置为选中状态,但由于它们是单选按钮,因此最后的一个按钮最终将被选中,这是默认乐器。节省空间的另一招。
// instrument select
[...`∿???`].map((i,j)=> // instrument icons
B.innerHTML += i + // icon
``);
);
创建钢琴键Div
像乐器按钮一样,键盘本身也是动态创建的,主要由div组成。钢琴共有3行,每行12个键。为了节省空间,在此还初始化了乐器I和有源声音阵列A。
公式24 + i%12 –(i / 12 | 0)* 12可以对键进行重新排序,使下部键位于底部。结果存储在变量k中,可以再使用几次。
钢琴键的CSS代码占用了大量空间。必须将显示设置为内嵌式,才能使用高度和设置。使用边距设置将键分开,但它们的轮廓看起来更好一些,例如“ outline:3px solid#000”。
// piano keysfor( I = i = 0; i < 36; A = []) // 36 keys & init `B.innerHTML += ${i%12 ? `` : `` // new row }
id=K${ // create key
k = 24 + i%12 - (i/12|0)*12 // reorder } style=display:inline-block;margin:2;background:${ // style
要检查按键是黑色还是白色,我们在对应于黑色按键的列表中查找按键索引。使用左空白设置将黑键移至正确位置。
为了允许鼠标输入,我们为触发弹奏功能P()或取消功能X()的每个钢琴键连接了鼠标事件。除了在onmouseover中是否按下按钮外,这些回调中的每个回调都非常相似。
`02579`.indexOf(i++%12 - 1) < 0 ? // b or w `#fff;color:#000;width:60;height:180` : // w `#000;position:absolute;margin-left:-17;width:33;height:99` // b
由于我们将附加到正文的innerHTML,因此div将自动关闭,因此无需添加结束标记。
} onmouseover=event.buttons&&P(${ k // mouse over }) onmousedown=P(${ k // mouse down }) onmouseup=X(${ k // mouse up }) onmouseout=X(${ k // mouse out })>`, // end key div
播放音符
播放音符功能是该程序的最大组成部分,因此我将其分为几部分。首先,我们检查它是否是尚未被演奏的有效音符。按住某个键时,onkeydown会反复触发,因此我们需要防止在后续通话中播放该音符。
每个仪器都由谐波正弦波组成,这些谐波正弦波的基频是一串数字定义的倍数。通过这个简单的系统,我们可以产生各种有趣的声音。例如,风琴是根音,其上方的三个八度音阶使音色更饱满。其他乐器的工作方式相同,请尝试使用数字,看看可以发出什么样的声音。
为了创建重叠的波,我们将仪器的倍频器字符串转换为数组,并迭代通过活动的仪器I。
// play note
P = i=> i < 0 || A[i] || // is valid and note not playing
(
A[i] = [ // instruments
[...`1248`], // ? organ
[...`3579`], // ? brass
[...`321`], // ? strings
[...`3`], // ∿ sine
][ I ].map(j=>( // for each harmonic
从上到下的波形:风琴,萨克斯管,弦和波形。
最后是一些音频代码。在这里,我们创建正弦波振荡器并设置其音量,也称为增益。
其中的某些功能(例如createOscillator)不带参数,因此为了节省空间,将其他无关的东西也塞进去了。这项技术减少了对多余内容的需要,否则这些内容是必需的。
要获得与键对应的div,我们只需使用前面定义的D函数。这里有一个技巧可以重置过渡。D(i).style.transition需要取消设置,但仅此一项是无法做到的。您需要访问元素中的任何特定变量之一,该变量会触发“ DOM重排”并重置CSS转换。在这种情况下,我们使用innerHTML。
有一个简单的公式可以将半音补偿转换为振荡器的频率。该公式使用的气质标度为A1或55 hz的均方根。根频乘以每个乐器的谐波,以产生重叠的声音。我的系统音乐是建立在数学之上的,这对我来说总是令人惊讶!
振荡器和增益连接调用串在一起,因为连接函数返回传递的值以允许这种类型的链接。
o = C.createOscillator( // create oscillator
D(i).style.transition =
D(i).innerHTML), // reset transition
o.connect( // oscillator to gain
o.g = C.createGain( // create gain node
o.frequency.value = // set frequency
j * 55 * 2**((i+3)/12))) // A 55 root note
.connect(C.destination), // gain to destination
乐器的每个谐波都有一个按比例缩放的音量,以平衡低调音符消耗较少的能量,使它们听起来比高音调更安静。另外,为防止削波,我们将音量按0.2缩放,因为最多有4个重叠的声音,这限制了组合振幅超过1。
一切设置完成后,可以通过调用start播放声音。最后一部分返回o作为map函数的结果,以创建存储在A [i]中的振荡器数组。
o.g.gain.value = .2/(1+Math.log2(j)), // set gain o.start(), // start audio o) // return sound
为了提供一些正在播放音符的视觉反馈,将按键颜色设置为红色。但是首先我们必须将原始颜色保存在名为b的数据成员中,以便在释放后可以将其设置回原始颜色。
D(i).b = D(i).style.background, // save original color D(i).style.background = `#f00` // set key color red);
取消备注
我们可以简单地立即叫停,但这会导致弹出。为了防止这种情况的发生,我们可以使用linearRampToValueAtTime逐渐减弱声音。
减少音符而不是立即停止。
每个音符由多个振荡器组成,需要通过在A [i]上进行迭代来停止。按键的背景也以.5s的过渡时间重新设置为原始颜色,因此会逐渐设置动画效果,以实现更好的演示效果。
实际的停止事件使用setTimeout延迟了350毫秒(.35秒),以覆盖斜坡关闭时间。
// cancel noteX = i=> A[i] && // is already playing? A[i].map(o=> // for each oscilator setTimeout(i=>o.stop(), 350, // stop sound after delay o.g.gain.linearRampToValueAtTime( // set gain start ramp o.g.gain.value, C.currentTime), // set gain o.g.gain.linearRampToValueAtTime( // ramp off gain A[i] = 0, C.currentTime + .3), // clear note D(i).style.transition = `.5s`, // set transition D(i).style.background = D(i).b // reset original color ));
键盘控制
钢琴可以用鼠标或键盘来弹奏,尽管键盘需要更多的代码,但它可以带来更多细微的控制。这对功能触发onkeydown和onkeyup事件的播放和取消声音功能。
要将键盘琴键映射到钢琴琴键,我们使用一个表示键盘到钢琴琴键映射的字符串,并查找小写琴键。可能会更小一些,但是会有些重复,因为这样可以更好地压缩。在大多数情况下,为获得最佳压缩效果,如果精确匹配,则最好复制并粘贴代码,尽管这可能会很难使用。
// keyboard to piano key mappingK = `zsxdcvgbhnjm,l.;/q2w3er5t6y7ui9o0p[=]`;// play note on key downonkeydown = i=> P( K.indexOf(i.key.toLowerCase()) // map key to note - 5 * (K.indexOf(i.key.toLowerCase()) > 16) // overlap 2nd row);// release note on key uponkeyup = i=> X( K.indexOf(i.key.toLowerCase()) // map key to note - 5 * (K.indexOf(i.key.toLowerCase()) > 16) // overlap 2nd row);
在模糊
除了一个小的错误修复,几乎所有内容。如果用户在弹奏音符时单击该程序,它将被卡住,因为onkeyup事件将永远不会发生!为了解决这个问题,我们可以连接onblur事件来取消所有活动音符。
我们还需要一个结束脚本标签,尽管正如我前面提到的,最终版本将使用onload事件。
// stop all sounds if focus lost
onblur = e=> A.map((e,i)=> X(i));
缩小
该代码已被最小化,但是还有一些技巧可以打破1 KB的障碍。首先,我们需要删除所有空白,我在网上使用了xem的terser。您可能没有注意到,但是代码中的每个字符串都使用模板字符串`字符。这很重要,因为我们将需要嵌套所有3种字符串类型。当然,我们可以使用转义符,但这需要更多的空间。不幸的是,如果不使用模板功能,大多数JavaScript压缩程序都会将`替换为。因此,我们需要对所有“替换为”进行替换。
现在,我们需要使用称为JSCrush的巧妙算法来压缩代码。它的工作原理类似于zip,可创建毫不妥协的JavaScript。我使用siorki出色的regpack工具,该工具具有出色的功能。为防止使用全局B主体变量,请将B添加到“重新分配变量名称...…变量除外”输入中。使用默认设置打包代码,并验证其是否仍然有效。
Regpack将重复的字符串替换为未使用的字符。通常,这很好,但是“”是替换字符,我们需要使用它。因此用Q或其他未使用的字符替换“”。
最终结果1020字节
看一下最终代码,它漂亮吗?好吧,也许不是每个人都这样,但是他们说,情人眼里是情人!
"for(='onWtiW~keyQo.!${ZutYin;marg_^=>FerE;width:Ddown.map((e,o)F.cWnect(.dexOf(;height:;color:#12(Ke.Q.toLowECase())-5*>16)=C.create[…],3tTimestylealue$(e)..transi~=),A[e] =eF,C.curren(Zk})!g.ga..nEHTML Wmouse,l_earRampToVA(backgroundfor(B.=:#1fff;usE-select:nWe;text-align:centE,C=new AudioCWtext,$=iFeval(K+i∿???]B+=e+<_py type="radio" name="I" checked> I=i=0;i<36;A=[])B+=Zi%?`:},Pe<0|| ||( =[4857921]][I]nF(oOscillator(o!gGa_(!frequency.v=55*n*2**((e+3)/)))C.dest_a~v=.2/(1+Math.log2(n)!start(o).b=,=#f00);X && oFseoY(eF!stop(350v) =0+.3.5s¨C12Czsxdcvgbhnjm,l.;/q2w3E5t6y7ui9o0p[=]`,WQPWQupX)';G=/[-D-F^_YZ!Q~W]/.exec();)with(.split(G))=join(shift());eval(_)"id=B>
javascript map 排序_1Keys仅用1 kb的JavaScript制作钢琴相关推荐
- javascript map 排序_数据结构-JavaScript字典结构的编程实现
字典是一类特殊的数据元素的集合,字典的基本单元为数对,所谓数对是指类似(key,value)形式的数据.每一个数对结构包括了关键字key与该元素对应的value值,在一个普通字典所组成的数对集合中,关 ...
- 算法笔记(JavaScript版)——排序
算法笔记(JavaScript版)--排序 本文内容根据Rebert Sedgewick和Kevin Wayne的<算法(第四版)>整理,原代码为java语言,自己修改为JavaScrip ...
- Javascript .map文件-JavaScript源地图
本文翻译自:Javascript .map files - javascript source maps Recently I have seen files with .js.map extensi ...
- javascript排序_鸡尾酒在JavaScript中排序
javascript排序 Just want the code? Scroll all the way down for two versions of the code: 只需要代码? 一直向下滚动 ...
- Map排序(Java)
Java中的Map排序问题 在Java编写程序的过程中,常常会碰到使用map(key,value)来记录数据的情况,有些时候我们需要根据实际需要来对map中的数据进行排序.以下就是个人总结的map排序 ...
- 腾讯最新开源IoT操作系统登上GitHub热榜,最小体积仅1.8 KB,标星1200+
乾明 发自 凹非寺 量子位 报道 | 公众号 QbitAI 鹅厂又开源,这次是一个操作系统,面向物联网场景,名为TencentOS-tiny. 项目如其名,核心亮点就是tiny--最小体积仅1.8 ...
- JavaScript Map 和 Set
结论 Map:存放键值对,区别于 Object,键可以是任何值. Set:存放不重复的值 Map 存储键值对,读取时与插入顺序一致. var map = new Map([[1, "1&qu ...
- 根据数组中的某个键值大小进行排序,仅支持二维数组
/** * 根据数组中的某个键值大小进行排序,仅支持二维数组 * * @param array $array 排序数组 * @param string $key 键值 ...
- 使用lambda去重、map排序、按任意时间间隔(小时、半小时、分钟)进行结果统计
1.lambda去重 public static <T> Predicate<T> distinctByKey(Function<? super T, Object> ...
最新文章
- html将变量打印到屏幕_用可视化过程解释代码运行过程和变量作用空间
- 网络推广费用之你的文章标题优化“合格”了吗?
- python 中五种常用的数据类型
- 优化的ms sql server分页sql语句
- 图像处理常用边缘检测算子总结
- Xcode 卸载方法
- AT4120-[ARC096D]Sweet Alchemy【贪心,背包】
- MySQL(6)视图
- java.util.zip.zipexception_Java 压缩zip异常,java.util.zip.ZipException: duplicate entry: 问题...
- 史上最全的键盘快捷键
- 原码,反码,补码的表示范围总结
- 狸窝音频剪辑软件_干货资源之精选音频剪辑软件工具
- Git命令行和Puttygen生成公钥私钥的方法和区别
- win32 指令大全
- 1.1 第一课:操作示范 [Ps教程]
- flutter学习之基础组件(一)
- C++中的delete与delete[]
- 弦截法 解高次方程 C语言/C++
- 短视频热度还能持续多久
- 怎样给CSDN博客添加微信二维码
热门文章
- postman电脑版无法安装_CPU使用率高达100%?试试安装Win10 2004版,旧电脑也能运行如飞...
- g++链接boost库
- golang module实践
- matlab中 s 函数简记
- Hadoop 生态系列之 HDFS
- anaconda管理不同版本Python
- [深度学习-TF2实践]应用Tensorflow2.x训练ResNet,SeNet和Inception模型在cifar10,测试集上准确率88.6%
- linux创建sftp服务器,Linux Centos 6.6搭建SFTP服务器
- Iverson Bracket. 艾弗森括号
- 多频法如何解包裹相位?