<h1>ピアノの鍵盤</h1>
<div class="piano" id="pf">
<div class="absoslm">
<div class="basetone"><input type="text" v-model="basehz" @change="bshzChange"/></div>
<div class="keyname">{{tonename}}</div>
</div>
<div v-for="range in ranges" v-bind:class="range.clsnm">
<button v-for="key in range.keys" v-on:mousedown="keydown" v-on:mouseup="keyup" v-bind:hz="key.hz" v-bind:rangerate="range.rangerate" v-bind:range="range.range" v-bind:tone="key.tone" v-bind:class="key.clsnm"></button>
</div>
</div>
@charset "utf-8";
/*
各種色
黒鍵:#1a1c1b
黒鍵影:#121413
白鍵:#ffffff
白鍵影:#b2b2b2
背景黒:#060707
*/
:root{
--white_key_width:30px;
--black_key_width:20px;
--black_key_pad:5px;
}
.piano{
background:#060707;
height:250px;
display:flex;
padding:50px;
box-sizing:border-box;
}
.piano .scale{
display:flex;
height:100%;
position:relative;
}
#pf{
position:relative;
}
#pf .absoslm{
position:absolute;
left:10px; top:10px;
color:#fff;
display:flex;
}
/* 重なりが順番になるように */
.scale.lowlow{ z-index:80; }
.scale.low{ z-index:70; }
.scale.midi1{ z-index:60; }
.scale.midi2{ z-index:50; }
.scale.hi{ z-index:40; }
.scale.hihi{ z-index:30; }
.scale.hihihi{ z-index:20; }
.scale.hihihihi{ z-index:10; }
.piano .key{
width:var(--white_key_width);
border:none;
border-radius:3px;
background:#b2b2b2;
height:100%;
border:#b2b2b2 solid 1px;
padding:0;
transition:all 0.2s ease-out;
}
.piano .key:before{
content:"";
height:calc(100% - 10px);
width:100%;
position:relative;
top:-5px;
display:block;
background:#fff;
}
.piano .key.bl:before{
background:#1a1c1b;
}
.piano .key.bl{
width:var(--black_key_width);
background:#121413;
height:50%;
position:absolute;
z-index:10
}
.piano .key.bl.aSh{ left:calc(var(--white_key_width) / 2 + var(--black_key_pad));}
.piano .key.bl.cSh{ left:calc(var(--white_key_width) *2 + var(--white_key_width) / 2 + var(--black_key_pad)); }
.piano .key.bl.dSh{ left:calc(var(--white_key_width) *3 + var(--white_key_width) / 2 + var(--black_key_pad)); }
.piano .key.bl.fSh{ left:calc(var(--white_key_width) *5 + var(--white_key_width) / 2 + var(--black_key_pad)); }
.piano .key.bl.gSh{ left:calc(var(--white_key_width) *6 + var(--white_key_width) / 2 + var(--black_key_pad)); }
.piano .key:active:before{
height:calc(100% - 2px);
top:1px;
}
.piano .key.wh:active:before{
background:#ffafba;
}
.piano .key.bl:active:before{
background:#7f4b63;
}
window.onload = function(){
//vueを使ったピアノ
//web Audio API
var actx = new AudioContext();
var osciillatorNode = null;
var bairitu = Math.pow(2,1/12); //半音の差をあらかじめ計算しておく
var _baseHz = 440; //A(ラ)の音が基準
//音程の名前を配列であらかじめ定義する
var scale_names = {
'0':'a', '1':'aSh', '2':'b', '3':'c', '4':'cSh', '5':'d', '6':'dSh',
'7':'e', '8':'f', '9':'fSh', '10':'g', '11': 'gSh'
}
var scale = [];
//var scale_num = Object.keys(scale_names).length; //音程の種類数
scale = makeScale(scale_names, _baseHz, bairitu);
/*
var scale = [
{'tone':'a', 'clsnm':'key a wh', 'hz':baseHz},
{'tone':'aSh', 'clsnm':'key aSh bl', 'hz':baseHz * bairitu},
{'tone':'b', 'clsnm':'key b wh', 'hz':baseHz * Math.pow(bairitu, 2)},
{'tone':'c', 'clsnm':'key c wh', 'hz':baseHz * Math.pow(bairitu, 3)},
{'tone':'cSh', 'clsnm':'key cSh bl', 'hz':baseHz * Math.pow(bairitu, 4)},
{'tone':'d', 'clsnm':'key d wh', 'hz':baseHz * Math.pow(bairitu, 5)},
{'tone':'dSh', 'clsnm':'key dSh bl', 'hz':baseHz * Math.pow(bairitu, 6)},
{'tone':'e', 'clsnm':'key e wh', 'hz':baseHz * Math.pow(bairitu, 7)},
{'tone':'f', 'clsnm':'key f wh', 'hz':baseHz * Math.pow(bairitu, 8)},
{'tone':'fSh', 'clsnm':'key fSh bl', 'hz':baseHz * Math.pow(bairitu, 9)},
{'tone':'g', 'clsnm':'key g wh', 'hz':baseHz * Math.pow(bairitu, 10)},
{'tone':'gSh', 'clsnm':'key gSh bl', 'hz':baseHz * Math.pow(bairitu, 11)},
];
*/
//音程を定義する 本当はもう少したくさんあるけどとりあえずこんだけ
var range = [
{'range':'low', 'clsnm':'scale low', 'keys':scale, 'rangerate':0.5},
{'range':'midi1', 'clsnm':'scale midi1', 'keys':scale, 'rangerate':1},
{'range':'midi2', 'clsnm':'scale midi2', 'keys':scale, 'rangerate':2},
{'range':'hi', 'clsnm':'scale hi', 'keys':scale, 'rangerate':4},
];
// {'range':'hihi', 'clsnm':'scale hihi', 'keys':scale, 'rangerate':8}
var piano = new Vue({
el:'#pf',
data:{
'tonename':'',
'ranges':range,
'basehz':_baseHz
},
methods:{
'bshzChange':function(e){
console.log('change base');
var ojbk = Object.keys(this.ranges);
var l = ojbk.length;
for(var k = 0; k < l; k++){
var range_key = ojbk[k];
this.ranges[range_key]['keys'] = makeScale(scale_names, this.basehz, bairitu);
/*var keyskeys = Object.key(range[range_key]['keys']);
var kl = keyskeys.length;
for(var kk = 0; kk < kl; kk++){
range[range_key]['keys']
}*/
}
},
'keydown':function(e){
var nm = '';
var hz = e.target.getAttribute('hz') * e.target.getAttribute('rangerate');
nm += e.target.getAttribute('range');
nm += ' ';
nm += e.target.getAttribute('tone');
nm += ' : ';
nm += hz
this.tonename = nm;
//発音処理
osciillatorNode = actx.createOscillator();
osciillatorNode.frequency.value = hz; //発音周波数を指定する
osciillatorNode.connect(actx.destination);
osciillatorNode.start(); //実際に音を鳴らす
},
'keyup':function(e){
osciillatorNode.stop(); //音をストップする
//this.tonename = ''; //音程名をリセットする
}
}
});
}
/*
引数の説明
scl_nms:音階の名前
bhz :基準の周波数(通常440)
brt :半音の倍率(12√2)
*/
function makeScale(scl_nms, bhz, brt){
var scl = [];
var scl_num = Object.keys(scl_nms).length; //音程の種類数
//キーボードを描画するためのオブジェクトを動的に生成する
for(var i = 0; i < scl_num; i++){
//割り振られた音の周波数を計算する
var xhz = bhz;
if(i > 1){
xhz = bhz * Math.pow(brt, i);
} else if(i == 1){
xhz = bhz * brt;
}
//表示する鍵盤ボタンのクラス名を生成する
var keycls = 'key';
keycls += ' ';
keycls += scl_nms[i];
keycls += ' ';
if(scl_nms[i].match(/Sh$/)){ //末尾が「Sh」の場合は黒鍵にする
keycls += 'bl';
} else {
keycls += 'wh';
}
scl.push({'tone':scl_nms[i], 'clsnm':keycls, 'hz':xhz});
}
return scl;
}
This Pen doesn't use any external CSS resources.