简易网页钢琴(一)

2020-09-07  本文已影响0人  kevin5979

先看效果图

效果图.png

网站预览或源码下载
方式一:http://kevin5979.3vfree.net/share/javaScript/piano/index.html
方式二: http://kevin5979.3vfree.net

Ok,接下来详细讲解我书写代码的过程
先构建思路

第一步:建立工程目录,编写html文件和css文件基本布局,完成基本的样式
第二步:获取所有需要的DOM元素对象,设计数据格式,并思考如何存储曲目的数据
第三步:实现键盘按钮事件,onkeydown,拿到每个琴键的key值
第四步:实现鼠标的点击事件,onclick,也是拿到每个DOM元素的对应key
第五步:实现钢琴点击/按键的动态效果
第六步:实现功能按钮的点击事件 onclick
第七步:处理用户交互信息
第八步:列表渲染
第九步:接入声音类,将key转化成对应的琴音
第十步:保存的曲目播放
第十一步:优化代码,解决bug

第一步 编写html文件和css文件基本布局,完成基本的样式

这里就不用细讲了,直接上代码

目录结构

HTML

<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>piano</title>
    <link rel="stylesheet" href="./css/index.css">
  </head>

  <body>
    <div class="piano-box">
      <ul class="key-list">
        <li class="item" data-key="q"><span class="bottom">1</span></li>
        <li class="item" data-key="w"><span class="bottom">2</span></li>
        <li class="item" data-key="e"><span class="bottom">3</span></li>
        <li class="item" data-key="r"><span class="bottom">4</span></li>
        <li class="item" data-key="t"><span class="bottom">5</span></li>
        <li class="item" data-key="y"><span class="bottom">6</span></li>
        <li class="item" data-key="u"><span class="bottom">7</span></li>
        <li class="item" data-key="a"><span>1</span></li>
        <li class="item" data-key="s"><span>2</span></li>
        <li class="item" data-key="d"><span>3</span></li>
        <li class="item" data-key="f"><span>4</span></li>
        <li class="item" data-key="g"><span>5</span></li>
        <li class="item" data-key="h"><span>6</span></li>
        <li class="item" data-key="j"><span>7</span></li>
        <li class="item" data-key="z"><span class="top">1</span></li>
        <li class="item" data-key="x"><span class="top">2</span></li>
        <li class="item" data-key="c"><span class="top">3</span></li>
        <li class="item" data-key="v"><span class="top">4</span></li>
        <li class="item" data-key="b"><span class="top">5</span></li>
        <li class="item" data-key="n"><span class="top">6</span></li>
        <li class="item" data-key="m"><span class="top">7</span></li>
      </ul>
      <ul class="btn-list">
        <li><button class="record">录制</button></li>
        <li><button class="end">结束录制</button></li>
      </ul>
      <h4>曲目单</h4>
      <ul class="music-list">
      </ul>
    </div>
    <script src="./js/index.js" type="module"></script>
  </body>

</html>

CSS

@import "//at.alicdn.com/t/font_1724264_3u1pq0ga9u6.css";

* {
  margin: 0;
  padding: 0;
}

body,
html {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: rgba(82, 82, 82, 0.2);
}

.key-list {
  list-style: none;
  margin: 50px auto 30px auto;
  width: 92%;
  display: flex;
  justify-self: start;
  cursor: pointer;
}

.item {
  width: 4.3%;
  height: 250px;
  background: #fff;
  text-align: center;
  margin-right: 3px;
  user-select: none;
  position: relative;
}

.item span {
  position: absolute;
  left: 50%;
  bottom: 15px;
  transform: translateX(-50%);
  display: block;
}

.item .bottom::after {
  content: ".";
  font-size: 20px;
  font-weight: bold;
  display: block;
  position: absolute;
  top: 3px;
  left: 50%;
  transform: translateX(-50%);
}

.item .top::before {
  content: ".";
  font-size: 20px;
  font-weight: bold;
  display: block;
  position: absolute;
  top: -20px;
  left: 50%;
  transform: translateX(-50%);
}

.active {
  animation: press 0.5s;
}

@keyframes press {
  0% {
    transform: scale(1);
    box-shadow: none;
  }

  50% {
    transform: scale(1.05);
    box-shadow: 2px 2px 2px 2px rgba(0, 0, 0, 0.2);
  }

  100% {
    transform: scale(1);
    box-shadow: none;
  }
}

.btn-list {
  width: 90%;
  margin: 0 auto;
  list-style: none;
  display: flex;
  justify-content: start;
}

button {
  margin: 0 20px;
  width: 70px;
  height: 70px;
  background-color: aqua;
  cursor: pointer;
}

h4 {
  margin-left: 6%;
  margin-top: 20px;
}

.music-list {
  width: 30%;
  height: 200px;
  margin-left: 6%;
  margin-top: 10px;
  list-style: none;
  background-color: #fff;
  overflow: scroll;
}

.music-list li {
  padding: 5px 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 16px;
  user-select: none;
}

.music-list li:nth-child(2n) {
  background: rgb(216, 216, 216);
}

.icon-bofang {
  color: #333;
  font-size: 20px;
  cursor: pointer;
}

.music-list .name {
  color: #333;
  margin-right: 20px;
}

.music-list .time {
  color: #999;
  font-size: 14px;
}

注意点

  1. 由于js代码中需要用到模块化,所以在html书写script标签时需要添加 type="module"
  2. 由于css中导入了阿里图标库iconfont,所以必须联网访问,而且需要在编译器中打开代码,直接打开会报错

第二步:获取所有需要的DOM元素对象,设计数据格式,并思考如何存储曲目的数据
image.png
window.onload = function () {
  const keysUl = document.querySelector(".key-list")
  const keysList = keysUl.querySelectorAll("li")
  const btnList = document.querySelector(".btn-list")
  const record = btnList.querySelector(".record")
  const end = btnList.querySelector(".end")
  let musicList = document.querySelector('.music-list')
}
let resources = {
    songs: [],
    stamps: [],
    startStamps: [],
    endStamps: [],
    names: [],
    songTime: []
  }

// 定义其他需要的变量,后续随着代码变多,这里的变量可增加
// 定义所有的key值
const keys = "qwertyuasdfghjzxcvbnm"


第三步:实现键盘按钮事件,onkeydown,拿到每个琴键的key值
window.onkeydown = function (e) {
  console.log(e.key)  // e.key : a,b,c ...
  showEffect(e.key)   // showEffect为控制动画方法,后面有写
}

第四步:实现鼠标的点击事件,onclick,也是拿到每个DOM元素的对应key
keysUl.onclick = function (e) {
  console.log(e.target.dataset.key)  // a,b,c ...
  key && onViews.showEffect(key)     // showEffect为控制动画方法,后面有写
}

第五步:实现钢琴点击/按键的动态效果
/**
 * 控制钢琴动画效果
 * @param {*} key 按钮对应的key
 */
function showEffect (key) {
  const index = keys.indexOf(key) // 得到下标
  if (index !== -1) {
    // 添加类名,用于动画展示
    keysList[index].classList.add("active")
    // 300ms后移除类
    setTimeout(() => {
      keysList[index].classList.remove('active')
    }, 300)
  }
}
第六步:实现功能按钮的点击事件 onclick
  let isStart = false
  let isEnd = true
  let startstamp = null // 开始录制时间戳
  let endstamp = null   // 结束录制事件戳
  let tempSong = []  // 记录当前录制曲目的按键信息
  let tempStamp = [] // 记录当前录制每个按键之间的时间差
// 开始录制
record.onclick = function () {
  if (isEnd) {
    startstamp = new Date().getTime()
    tempSong = []
    tempStamp = []
    isStart = true
    isEnd = false
  } else {
    const isOver = confirm("当前正在录制,是否结束录制?")
    isOver && end.onclick()
  }
}
// 结束录制
end.onclick = function () {
if (isStart) {
    isStart = false
    isEnd = true
    endstamp = new Date().getTime()
    const isSave = confirm("是否保存曲目?")
    if (isSave) {
      let name = prompt("输入曲目名")
      if (!resources.names.includes(name) && name !== null) {
        // 到这里说明用户完成了保存操作,我们需要将数据整理并保存到本地或全局变量中
        resources.endStamps.push(endstamp)
        resources.startStamps.push(startstamp)
        resources.songs.push(tempSong)
        resources.stamps.push(tempStamp)
        resources.names.push(name)
        resources.songTime.push(endstamp - startstamp)
        localStorage.setItem("resources", JSON.stringify(resources))
       renderList({ name, time: endstamp - startstamp })
      } else {
        alert("保存失败,输入为空或者该名字已存在")
      }
    }
 } else {
    alert("未开始录制")
  }
}

为避免文章过长,本章先讲解第1~6步,后续会继续写完这个小功能,欢迎点赞,评论
END
上一篇 下一篇

猜你喜欢

热点阅读