传说中的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 styleB.style = `background:#112;color:#fff;user-select:none;text-align:center`;C = new AudioContext;  // audio contextD = 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 noteP = 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 lostonblur = 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制作钢琴相关推荐

  1. javascript map 排序_数据结构-JavaScript字典结构的编程实现

    字典是一类特殊的数据元素的集合,字典的基本单元为数对,所谓数对是指类似(key,value)形式的数据.每一个数对结构包括了关键字key与该元素对应的value值,在一个普通字典所组成的数对集合中,关 ...

  2. 算法笔记(JavaScript版)——排序

    算法笔记(JavaScript版)--排序 本文内容根据Rebert Sedgewick和Kevin Wayne的<算法(第四版)>整理,原代码为java语言,自己修改为JavaScrip ...

  3. Javascript .map文件-JavaScript源地图

    本文翻译自:Javascript .map files - javascript source maps Recently I have seen files with .js.map extensi ...

  4. javascript排序_鸡尾酒在JavaScript中排序

    javascript排序 Just want the code? Scroll all the way down for two versions of the code: 只需要代码? 一直向下滚动 ...

  5. Map排序(Java)

    Java中的Map排序问题 在Java编写程序的过程中,常常会碰到使用map(key,value)来记录数据的情况,有些时候我们需要根据实际需要来对map中的数据进行排序.以下就是个人总结的map排序 ...

  6. 腾讯最新开源IoT操作系统登上GitHub热榜,最小体积仅1.8 KB,标星1200+

    乾明 发自 凹非寺  量子位 报道 | 公众号 QbitAI 鹅厂又开源,这次是一个操作系统,面向物联网场景,名为TencentOS-tiny. 项目如其名,核心亮点就是tiny--最小体积仅1.8 ...

  7. JavaScript Map 和 Set

    结论 Map:存放键值对,区别于 Object,键可以是任何值. Set:存放不重复的值 Map 存储键值对,读取时与插入顺序一致. var map = new Map([[1, "1&qu ...

  8. 根据数组中的某个键值大小进行排序,仅支持二维数组

    /**     * 根据数组中的某个键值大小进行排序,仅支持二维数组     *     * @param array $array 排序数组     * @param string $key 键值 ...

  9. 使用lambda去重、map排序、按任意时间间隔(小时、半小时、分钟)进行结果统计

    1.lambda去重 public static <T> Predicate<T> distinctByKey(Function<? super T, Object> ...

最新文章

  1. html将变量打印到屏幕_用可视化过程解释代码运行过程和变量作用空间
  2. 网络推广费用之你的文章标题优化“合格”了吗?
  3. python 中五种常用的数据类型
  4. 优化的ms sql server分页sql语句
  5. 图像处理常用边缘检测算子总结
  6. Xcode 卸载方法
  7. AT4120-[ARC096D]Sweet Alchemy【贪心,背包】
  8. MySQL(6)视图
  9. java.util.zip.zipexception_Java 压缩zip异常,java.util.zip.ZipException: duplicate entry: 问题...
  10. 史上最全的键盘快捷键
  11. 原码,反码,补码的表示范围总结
  12. 狸窝音频剪辑软件_干货资源之精选音频剪辑软件工具
  13. Git命令行和Puttygen生成公钥私钥的方法和区别
  14. win32 指令大全
  15. 1.1 第一课:操作示范 [Ps教程]
  16. flutter学习之基础组件(一)
  17. C++中的delete与delete[]
  18. 弦截法 解高次方程 C语言/C++
  19. 短视频热度还能持续多久
  20. 怎样给CSDN博客添加微信二维码

热门文章

  1. postman电脑版无法安装_CPU使用率高达100%?试试安装Win10 2004版,旧电脑也能运行如飞...
  2. g++链接boost库
  3. golang module实践
  4. matlab中 s 函数简记
  5. Hadoop 生态系列之 HDFS
  6. anaconda管理不同版本Python
  7. [深度学习-TF2实践]应用Tensorflow2.x训练ResNet,SeNet和Inception模型在cifar10,测试集上准确率88.6%
  8. linux创建sftp服务器,Linux Centos 6.6搭建SFTP服务器
  9. Iverson Bracket. 艾弗森括号
  10. 多频法如何解包裹相位?