用Vue.js和Webpack开发Web在线钢琴
缘起
由于童心未泯,之前在手机上玩过钢琴模拟App,但是手机屏幕太小,始终觉得不过瘾。其实对于我这个连基本乐理都不懂的“乐盲”来说,就算给我一台真正的钢琴,我也玩不转。不过是图个新鲜、权当娱乐罢了。最近刚好入手一台带触摸屏的Lenovo Yoga 4 Pro,这倒给了我新的想象空间:大屏幕玩起来是不是更带感?在Win10应用商店里搜了下,还真有各种模拟钢琴的应用,随便选了一款安装。结果非常令人失望,音效惨不忍听,还各种闪退。这里顺便吐槽下win10的应用商店,里面的很多应用不是经常安装失败,就是经常闪退,简直没法用啊。作为一名前端开发和坚定的Web支持者,客户端不好用果断转向Web啊。本着尽量不重造轮子的原则,先在网上搜了一下。百度的搜索结果几乎都是那一个例子,也不知道是哪位哥们写的,被到处引用。就那么几个键,怎么玩?Google的结果也不尽如人意,不是打不开就是加载半天。算了,还是自己动手吧。
准备
我们知道,HTML5有音频接口,播放声音自然不在话下。这模拟钢琴自然需要各种音阶的音频文件吧,于是在网上搜了一通,找齐了88键钢琴的音频文件。为什么钢琴有88个键?别问我,我是乐盲。看看这张钢琴示意图就知道了:

开工
最近一直在用Vue.js开发项目,配合Webpack神器构建打包,开发前端项目从来没有如此方便。在此要特别感谢Vue.js的作者Evan You尤雨溪(知乎), 给我们贡献了这么好用的框架。
新建一个Vue.js项目非常简单,可以用官方推荐的脚手架命令行工具vue-cli创建新工程。首先安装这个工具:
npm install -g vue-cli
安装好后执行命令生成工程模板:
vue init webpack piano
这里我们用webpack作为构建工具,你也可以使用browserify。
就这么简单,一个Vue.js project诞生了,而且Webpack已经配置好。接下来执行命令安装相关的node模块:
npm install
如果一切顺利的话,项目就可以跑起来了:
npm run dev
访问http://localhost:8080就可以看到默认的欢迎界面。至此,项目的搭建算是完成了。
界面
现在开始写界面。虽然是乐盲,钢琴键盘上有哪些键还是要搞清楚的。对于标准的88键钢琴,总共有88个键,其中52个白色键,36个黑色键。分为低音区、中音区和高音区,每个区有三组。对于我们画界面来说,重要的是找出其中的规律。最两端的两组先不管,其他的分组看上去都是一样的:三白夹两黑跟着四白夹三黑。

怎么实现这个界面布局呢?很简单,黑白键都用button元素表示,设置好宽高、背景色和边框。白色的自然定位并排铺开,黑色的用绝对定位,计算出对应的坐标。这里有个小细节,就是黑白键的DOM元素排列最好跟各音阶的先后顺序对应,这样在计算黑键坐标就比较方便。
既然有七个组的界面是一模一样的,我们就把一组设计成一个组件好了。用Vue.js开发组件真的是太方便了,一个.vue文件包含HTML template、script和style,就构成了一个独立的组件。每组的音阶范围不一样,通过组件的props设定。来看组件的源码文件Group.vue
<template>
<div class="group">
<button :class="{'white': whites.indexOf(n) > -1, 'black': blacks.indexOf(n) > -1}" v-for="n in 12" :style="{ left: calcLeft(n) + '%' }" data-note="{{start+n}}" @click="play(start+n)"><span v-show="n === 0">C</span></button>
</div>
</template>
<script>
import {notes} from '../notes.js';
const prefix = 'data:audio/mpeg;base64,';
const base = 3;
const keys = 12;
export default {
props: {
group: {
type: Number,
default: 0
}
},
data() {
return {
// note: changing this line won't causes changes
// with hot-reload because the reloaded component
// preserves its current state and we are modifying
// its initial state.
blacks: [1, 3, 6, 8, 10],
whites: [0, 2, 4, 5, 7, 9, 11]
}
},
computed: {
start() {
return this.group * keys;
}
},
methods: {
play(index) {
var audio = new Audio(prefix + notes[index + base]);
audio.play();
},
calcLeft(index) {
var unit = 14.29;
var i = this.blacks.indexOf(index);
if(i < 2) {
return unit * (0.75 + i);
}
return unit * (1.75 + i);
},
click(index) {
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.group {
font-size: 0;
position: relative;
display: flex;
flex-grow: 1;
}
button {
width: 14.29%;
flex: 1;
height: 300px;
display: inline-block;
border: 1px solid #ccc;
outline: 0;
padding: 0;
box-sizing: border-box;
}
button > span {
position: absolute;
bottom: 10px;
}
.white:active,
.white.active {
background: #ececec;
}
.white {
background: #fff;
}
.black {
background: #000;
border-color: #000;
height: 150px;
width: 7.15%;
position: absolute;
}
</style>
逻辑并不复杂,关键是处理细节。按键的宽度是用百分比的,高度固定。黑键的坐标计算逻辑在方法calcLeft里,具体看代码好了,code will talk.
你可能有个疑问:音频内容哪来的?继续看。
音频处理
前面提到过,我从网上找到了钢琴的88音阶的音频文件,都是mp3格式的。但是我不想让88个音分散在88个.mp3文件里,不然在弹奏的时候一个个文件下载,可不太好。怎么办呢?我们知道图片可以转成base64的字符串显示在DOM里。其实音频文件也一样,用data:audio/mpeg;base64,XXXXXX就可以了。写了个Node程序,一次性将所有Mp3文件都转成了base64字符串数组备用:
var fs = require('fs');
var file = 'notes.json';
// function to encode file data to base64 encoded string
function base64_encode(file) {
// read binary data
var bitmap = fs.readFileSync(file);
// convert binary data to base64 encoded string
return new Buffer(bitmap).toString('base64');
}
fs.readdir('.', function(error, files) {
var content = "";
files.forEach((f, index) => {
if(/^\d/.test(f)) {
var data = base64_encode(f);
content += `"${data}",\n`;
}
});
fs.writeFileSync(file, content);
});
数组内容放在一个单独的文件里,作为模块引入。数组元素的顺序就是音阶从低到高的顺序。HTML5的Audio对象,支持从构造函数传入base64数据,然后调用play()就可以播放声音了。
没有触摸屏咋玩?还有键盘啊。简单起见,用三排字母按键对应中音区的三个组。监听键盘keydown事件,通过keyCode区分不同的键,播放对应的音频内容就好了。
总结
这个过程并不复杂,就是布局和音频处理需要处理一些细节。代码写得很仓促,有些地方可以重构下。完整的源码可以在我的Github找到。喜欢的欢迎star,有闲工夫也可自己改进。最终效果点击这里:http://kaysonli.github.io/piano/dist/
用Vue.js和Webpack开发Web在线钢琴的更多相关文章
- Vue.js中用webpack合并打包多个组件并实现按需加载
对于现在前端插件的频繁更新,所以多多少少要对组件化有点了解,下面这篇文章主要给大家介绍了在Vue.js中用webpack合并打包多个组件并实现按需加载的相关资料,需要的朋友可以参考下. 前言 随着 ...
- Flask Vue.js全栈开发
Flask Vue.js全栈开发的 最新完整代码 及使用方式 本系列的最新代码及使用方式将持续更新到: http://www.madmalls.com/blog/post/latest-code/ 1 ...
- Vue.js 在 webpack 脚手架中使用 cssnext
Vue.js 的 webpack脚手架默认已经使用了 PostCSS 的 autoprefixer 的功能. 如果想使用下一代 css语法,即cssnext: 1. 安装依赖 npm install ...
- Node.js 从零开发 web server博客项目[express重构博客项目]
web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...
- Node.js 从零开发 web server博客项目[数据存储]
web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...
- Node.js 从零开发 web server博客项目[koa2重构博客项目]
web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...
- Node.js 从零开发 web server博客项目[安全]
web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...
- Node.js 从零开发 web server博客项目[日志]
web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...
- Node.js 从零开发 web server博客项目[登录]
web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...
随机推荐
- sql常识-like
LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式. LIKE 操作符 LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式. SQL LIKE 操作符语法 SELECT colum ...
- php递归方法实现无限分类实例
数组: 代码如下 复制代码 $items = array( array('id' => 1, 'pid' => 0, 'name' => '一级11' ), array('id' ...
- jCallout 实现气泡提示
在提交表单前.焦点转移后或者 keyup 时往往需要对输入的文本就行检验,如果输入内容不符合相关约定则要进行提示或警告,有一个叫 jCallout 的插件可以轻松实现该功能,该插件基于 jQuery ...
- 编程语言中的Namespace
Namespace 1.C struct 2.C++(Pronounced 'see jia-jia' or 'see plus-plus') namespace 3.Python module(s) ...
- Shell 循环读取文件
使用Shell将Windows环境下的文件拷贝到Linux下面的用法. 在linux下,将dos文件格式转换成linux文件格式的用法,vi打开,然后转到命令格式,执行,然后保存,就可以转换成linu ...
- STL Traits编程技法
traits编程技法大量运用于STL实现中.通过它在一定程度上弥补了C++不是强型别语言的遗憾,增强了C++关于型别认证方面的能力. traits编程技法是利用“内嵌型别”的编程技法和编译器的temp ...
- JBoss部署项目log4j配置会造成死锁问题,浏览器访问一直pending状态
今天将项目部署到JBoss服务器上,部署成功后,浏览器访问页面一直在等待响应. 查了很长时间,最后在服务器上通过jstack pid命令查看Java堆栈信息,发现了有两个线程死锁. 看到造成死锁的原因 ...
- json-lib-2.4.jar Bug,json字符串中value为"[value]"结构时,解析为数组,不会解析成字符串
使用json-lib.jar 2.4进行json字符串转换为对象时发现一个bug.贴下测试代码: <dependency> <groupId>net.sf.json-lib&l ...
- CSS3中的变形处理(transform)属性
在CSS3中,可以利用transform功能来实现文字或图像的旋转.扭曲.缩放.位移.矩阵.原点这六种类型的变形处理,下面将详细讲解transform的使用. 变形--旋转 rotate() div. ...
- C# and Redis,安装作为服务
本文主要讲述的是如何使用C#语言来进行Redis分布式缓存的程序编写.首先,需要从github下载最新的32/64位安装(下载地址),解压后根据自己机器的实际情况选择32位或者64位,例如:我机器是6 ...