Skip to content

Latest commit

 

History

History
 
 

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

README.md

在Github上看到了wesbos的一个Javascript30天挑战的repo,旨在使用纯Js来进行练习,不允许使用任何其他的库和框架,该挑战共30天,我会在这里记录下自己练习的过程和遇到的问题。

Day01 - JavaScript Drum Kit

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

效果如下

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

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

页面基础布局

页面的Html布局大致如下:

<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>

页面里通过data-key将页面展示的内容和audio关联起来。

data-key属性是无法在网上搜索到的,这是一个自定义属性,如果没有合适的属性可以定义该数据,就可以通过data-*的方式自定义页面的数据。参考文档

页面主要CSS设置

.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;
}

主要属性有以下几个:

  • 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代码如下:

function playAudio(e){
    const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
    if(!audio) return;
    const key = document.querySelector(`div[data-key="${e.keyCode}"]`);
    if(!key) return;
    audio.currentTime = 0;
    audio.play();
    key.classList.add('playing');
}
window.addEventListener('keydown',playAudio);
  • 监听页面的keydown事件,触发playAudio函数。

  • 那么怎样检测我们按了哪个按键呢?每一个按键都有自己的KeyCode,可以通过按键的keyCode来判断。如果不清楚每一个按键的keyCode是多少,这个网站可以帮到你。

  • 在这里我们用到了ES6的模板字符串,${e.keyCode},可以动态的将按键的Keycode传过去,以使audio动态的获取每一个按键绑定的audio。需要注意的是模板字符串一定要使用"`"(Esc下面那个键)包裹,而不是双引号。

  • 我们注意到audio.play();前面一行是audio.currentTime = 0;,这是因为,如果没有在播放音效前将该音乐重置,会发生以下情况,当我连续点击某一按键的时候,只有第一次点击会响,第二次第三次连续的点击可能没声音。所以在每一次点击之前重置音效是很有必要的。

  • key.classList.add('playing');可以在按键点击的同时为该元素添加playing类,展示小动画。

  • if(!audio) return; if(!key) return;因为并不是每一个按键都有音效,当用户点击了非绑定音效按键,及时退出函数是很好的习惯。

动画结束后移除动画

关键js代码如下:

function stopTransition(e){
    if(e.propertyName !== 'transform') return;
    console.log(this === e.target); //true
    e.target.classList.remove('playing');
  }
const keys = Array.from(document.querySelectorAll('.key'));
keys.forEach(key => key.addEventListener('transitionend',stopTransition));
  • 监听每一个按键元素的transitionend事件,当按键元素的动画结束后会触发stopTransition函数。

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

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

总结

至此为止就可以实现该效果了,查看效果

整体的代码如下:

function playAudio(e){
    const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
    if(!audio) return;
    const key = document.querySelector(`div[data-key="${e.keyCode}"]`);
    if(!key) return;
    audio.currentTime = 0;
    audio.play();
    key.classList.add('playing');
  }
function stopTransition(e){
    if(e.propertyName !== 'transform') return;
    console.log(this === e.target); //true
    e.target.classList.remove('playing');
}
window.addEventListener('keydown',playAudio);
const keys = Array.from(document.querySelectorAll('.key'));
keys.forEach(key => key.addEventListener('transitionend',stopTransition));

当然要实现该效果绝对不止这一种方法,还比如这样:

function stopTransition(e){
    if (e.propertyName !== 'transform') return;
    e.target.classList.remove('playing');
  }
function playAudio(e){
    const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`); //注意:这里的querySelector里面是`号,不是单引号
    const key = document.querySelector(`div[data-key="${e.keyCode}"]`);
    if(!key) return;
    if (!audio) return;
    key.addEventListener('transitionend', stopTransition);
    key.classList.add('playing');
    audio.currentTime = 0;
    audio.play();
}
window.addEventListener('keydown', playAudio);

如果您有新的方法,也欢迎您和我一起交流。