加载中...
  • audio 爵士鼓音乐敲击特效 loading

    简介

    项目效果点击查看

    用JS制作一个爵士鼓的页面,通过敲击键盘上不同的字母,会发出不同的声音,并且页面上会伴随着敲击的动画。

    想要实现以上效果,大致思路和解决方案如下:

    • 检测到键盘上什么键被按下–监听keydown事件
    • 在按键被按下的时候,播放音效–audio.play()
    • 在按键被按下的同时,播放动画–Element.classList.add('className')
    • 在动画结束后,移除动画,不然之后再点击不会有任何效果–Element.classList.remove('className')

    基础语法

    一些 ES6 语法

    1. const :声明一个只读的常量,标识符的值只能赋值一次。

    2. `字符串 ${ 变量、属性名 } `:模板字面量(Template literals)中用于表示模板字符串的标识。特点是字符串首尾用反引号(`),内部的模板部分用 ${ } 括起来表示,具体请看MDN文档。简单例子如下:

    1
    2
    3
    4
    5
    6
    var a = 1;
    var b = 2;
    //不用模板的写法
    console.log("三是" + (a + b) + "不是" + (2 * a + b)); //"三是3不是4"
    //使用模板字符串的写法
    console.log(`三是${a + b}不是${2 * a + b}`); //"三是3不是4"

    forEach 与箭头函数

    使用 document.querySelector 获取一组符合 CSS 选择符的元素快照,类型为 NodeList(此对象是对于文档的实时运行的动态查询),对其进行遍历时可采用 forEach 方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // ES6
    nums.forEach(v => {
    if (v % 5 === 0)
    fives.push(v);
    })

    // ES5
    nums.forEach(function (v) {
    if (v % 5 === 0)
    five.push(v);
    })

    页面基础布局

    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
    <!DOCTYPE html>
    <html lang="en">

    <head>
    <meta charset="UTF-8">
    <title>JS Drum Kit</title>
    <link rel="stylesheet" href="style.css">
    </head>

    <body>


    <div class="keys">
    <div data-key="65" class="key">
    <kbd>A</kbd>
    <span class="sound">clap</span>
    </div>
    <div data-key="83" class="key">
    <kbd>S</kbd>
    <span class="sound">hihat</span>
    </div>
    <div data-key="68" class="key">
    <kbd>D</kbd>
    <span class="sound">kick</span>
    </div>
    <div data-key="70" class="key">
    <kbd>F</kbd>
    <span class="sound">openhat</span>
    </div>
    <div data-key="71" class="key">
    <kbd>G</kbd>
    <span class="sound">boom</span>
    </div>
    <div data-key="72" class="key">
    <kbd>H</kbd>
    <span class="sound">ride</span>
    </div>
    <div data-key="74" class="key">
    <kbd>J</kbd>
    <span class="sound">snare</span>
    </div>
    <div data-key="75" class="key">
    <kbd>K</kbd>
    <span class="sound">tom</span>
    </div>
    <div data-key="76" class="key">
    <kbd>L</kbd>
    <span class="sound">tink</span>
    </div>
    </div>

    <audio data-key="65" src="sounds/clap.wav"></audio>
    <audio data-key="83" src="sounds/hihat.wav"></audio>
    <audio data-key="68" src="sounds/kick.wav"></audio>
    <audio data-key="70" src="sounds/openhat.wav"></audio>
    <audio data-key="71" src="sounds/boom.wav"></audio>
    <audio data-key="72" src="sounds/ride.wav"></audio>
    <audio data-key="74" src="sounds/snare.wav"></audio>
    <audio data-key="75" src="sounds/tom.wav"></audio>
    <audio data-key="76" src="sounds/tink.wav"></audio>

    <script>
    </script>


    </body>

    </html>
    • 标签定义键盘文本

    说到技术概念上的特殊样式时,就要提到 标签。正如你已经猜到的,它用来表示文本是从键盘上键入的。
    浏览器通常用等宽字体来显示该标签中包含的文本。
    标签经常用在于计算机相关的文档和手册中。例如:

    1
    键入 <kbd>quit</kbd> 来退出程序,或者键入 <kbd>menu</kbd> 来返回主菜单。
    • 使用 data-* 属性来嵌入自定义数据

    页面里通过data-key将页面展示的内容和audio关联起来。使用方法如下介绍:

    1
    2
    3
    4
    5
    <ul>
    <li data-animal-type="bird">Owl</li>
    <li data-animal-type="fish">Salmon</li>
    <li data-animal-type="spider">Tarantula</li>
    </ul>

    ① data-* 属性用于存储页面或应用程序的私有自定义数据。
    ② data-* 属性赋予我们在所有 HTML 元素上嵌入自定义 data 属性的能力。
    ③ 属性名不应该包含任何大写字母,并且在前缀 “data-“ 之后必须有至少一个字符
    ④ 属性值可以是任意字符串

    语法:

    1
    <element data-*="somevalue">

    属性值:

    描述
    somevalue 规定属性的值(以字符串)。

    主要CSS代码

    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
    html {
    font-size: 10px;
    background: url('./3.jpg') bottom center;
    background-size: cover;
    }
    body,
    html {
    margin: 0;
    padding: 0;
    font-family: sans-serif;
    }

    .keys {
    display: flex;
    flex: 1;
    min-height: 100vh;
    align-items: center;
    justify-content: center;
    }

    .key {
    border: .4rem solid black;
    border-radius: .5rem;
    margin: 1rem;
    font-size: 1.5rem;
    padding: 1rem .5rem;
    transition: all .07s ease;
    width: 10rem;
    text-align: center;
    color: white;
    background: rgba(0, 0, 0, 0.4);
    text-shadow: 0 0 .5rem black;
    }

    .playing {
    transform: scale(1.1);
    border-color: #ffc600;
    box-shadow: 0 0 1rem #ffc600;
    }

    kbd {
    display: block;
    font-size: 4rem;
    }

    .sound {
    font-size: 1.2rem;
    text-transform: uppercase;
    letter-spacing: .1rem;
    color: #ffc600;
    }

    主要属性有以下几个:

    • html中有一个样式为font-size: 10px;,在本案例中,1rem就是10px,rem是以html中的font-size为参照物,1.2rem就是12px
    • transform: scale(1.1);–该属性在键盘被点击时将该元素缩放至原来的1.1倍。
    • .key{border: .4rem solid black;} .playing{border-color: #ffc600;}–这两条属性在按键点击的时候改变边框颜色。
    • .key{text-shadow: 0 0 .5rem black;} .playing{box-shadow: 0 0 1rem #ffc600;}–这两条属性在按键点击的时候改变阴影的效果
    • transition: all .07s ease;–定义以上动画在0.07秒内完成。
      我们注意到我们定义了.palying类,在按键按下的时侯为该元素添加playing类,在结束后移除playing类。

    JS代码

    按键监听&音效播放&添加动画

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function playSound(e) {
    const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
    const key = document.querySelector(`div[data-key="${e.keyCode}"]`);
    if (!audio) return;

    key.classList.add('playing');
    audio.currentTime = 0;
    audio.play();
    }
    /**
    * 监听页面的keydown事件,触发playAudio函数。
    */
    window.addEventListener('keydown', playSound);
    • 监听页面的keydown事件,触发playAudio函数。
    • 通过KeyCode检测我们按下的键盘按钮是哪个按钮。
      • A -> 65
      • B -> 66
      • C -> 67
      • D -> 68
      • E -> 69
      • F -> 70
      • G -> 71
      • H -> 72
      • I -> 73
      • J -> 74
      • K -> 75
      • L -> 76
      • M -> 77
      • N -> 78
      • O -> 79
      • P -> 80
      • Q -> 81
      • R -> 82
      • S -> 83
      • T -> 84
      • U -> 85
      • V -> 86
      • W -> 87
      • X -> 88
      • Y -> 89
      • Z -> 90
    • 在这里我们用到了ES6的模板字符串,${e.keyCode},可以动态的将按键的Keycode传过去,以使audio动态的获取每一个按键绑定的audio。需要注意的是模板字符串一定要使用”`”(Esc下面那个键)包裹,而不是双引号。
    • 我们注意到audio.play();前面一行是audio.currentTime = 0;,这是因为,如果没有在播放音效前将该音乐重置,会发生以下情况,当我连续点击某一按键的时候,只有第一次点击会响,第二次第三次连续的点击可能没声音。所以在每一次点击之前重置音效是很有必要的。
    • key.classList.add('playing');可以在按键点击的同时为该元素添加playing类,展示小动画。
    • if(!audio) return; if(!key) return;因为并不是每一个按键都有音效,当用户点击了非绑定音效按键,及时退出函数是很好的习惯。

    动画结束后移除动画

    1
    2
    3
    4
    5
    6
    7
    function removeTransition(e) {
    if (e.propertyName !== 'transform') return;
    e.target.classList.remove('playing');
    }

    const keys = Array.from(document.querySelectorAll('.key'));
    keys.forEach(key => key.addEventListener('transitionend',stopTransition));
    • 监听每一个按键元素的transitionend事件,当按键元素的动画结束后会触发removeTransition函数。

    • 首先在removeTransition函数中可以输出事件e的内容,会输出该动画每一步具体的变化,发现其中会有propertyName属性,可以通过判断propertyName等于其中的一个值(例如’transform’),等于该值就移除playing类,也即移除动画。

    • 在定位元素的时候,可以使用this也可以使用e.target,可以简单这么理解,this值的是谁出发了这次事件,也就是key,就等同于事件的目标(e.target).

    解决难点

    如何将键盘按键与页面按钮对应起来?

    连接的帮手是 keydown 事件中的 keyCode 属性,keyCode 属性的值和 ASCII 编码值相同(对应小写字母)。在这个网站可以用按键盘来查看对应的键码。

    我们能获取到的初始页面中,按钮 div 和音频 audio 标签中都添加了一个属性 data-key 用于存储对应的键码,这样做的目的是,添加键盘事件监听后,触发键盘事件时即可获取事件的 keyCode 属性值,以此为线索,操作对应的按钮及音频。

    1
    2
    const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
    const key = document.querySelector(`div[data-key="${e.keyCode}"]`);

    如何保证按键被按住不放时,可以马上响起连续鼓点声?

    每次播放音频之前,设置播放时间戳为 0:

    1
    2
    3
    var audio = document.getElementById("video"); 
    audio.currentTime = 0;
    audio.play();

    如何使页面按钮恢复原状?

    利用一个叫 transitionened 的事件,它在 CSS transition 结束后会被触发。我们就可以利用这个事件,在每次打鼓的效果(尺寸变大、颜色变化)完成之后,去除相应样式。

    在这个页面中,发生 transition 的样式属性不止一个(box-shadow, transform, border-color),所以需要添加一个判断语句,使每发生一次按键事件时,只去除一次样式。

    1
    2
    3
    4
    5
    funciton remove(event) {
    if (event.propertyName !== 'border-left-color') return;
    this.classList.remove('playing');
    // event.target.classList.remove('playing');
    }
    上一篇:
    CSS 变量
    下一篇:
    canvas 实现彩虹画笔绘画板
    本文目录
    本文目录