#app
  

  .audioplayer(v-for="sound in sounddata")
    //- label {{sound.number}}
    audio(v-bind:data-num="sound.number")
     source(v-bind:src="sound.url" type="audio/ogg")
  
  .center_box
    h2 Vue.js Project: Interactive Piano
    .keyboard
      .pianokey(v-for="key in display_keys")
        .white(v-if="key.type==\"white\""
               v-on:click="addnote(key.num)"
               v-bind:class="get_current_highlight(key.num,key.key)?\"playing\":\"\"")
          .label {{String.fromCharCode(key.key)}}
        .black(v-if="key.type==\"black\""
               v-on:click="addnote(key.num)"
               v-bind:class="get_current_highlight(key.num,key.key)?\"playing\":\"\"")
          .labelb {{String.fromCharCode(key.key)}}
    br
    .controls
      ul.notes_list(v-if="notes.length>0")
        li(v-for="(note, id) in notes"
           v-bind:class="now_note_id-1==id? \"playing\":\"\"") 
          .num {{note.num}}
          .time {{note.time}}
      button(v-on:click="load_sample") load sample
      button(v-if='playing_time<=1'
             v-on:click="startplay") startplay
             i.fa.fa-play
      //- button(v-if='playing_time==1'
      //-        v-on:click="stopplay") clean
      button(v-if='playing_time>1'
             v-on:click="stopplay") stopplay
             i.fa.fa-pause
      button(v-on:click="playnext(1)") play a single note       
      button(v-if='record_time<=0'
             v-on:click="start_record") record
             i.fa.fa-circle
      button(v-if='record_time>=1'
             v-on:click="stop_record") stoprecord
             i.fa.fa-top
             
      button(v-on:click="notes=[]") clear sheet

      h4 Time Record: {{playing_time+record_time}}
      .ps 
        h6 Note: <br>Please open in  
          span.highlight Chrome / FireFox.
          br
          span Loading aduio takes a moment.
      
View Compiled
$key_width: 48px
$key_height: 250px

$color_white: #eee
$color_black: #585858

@mixin size($w,$h)
  width: $w
  height: $h
  
@mixin ab_center
  position: absolute
  left: 50%
  top: 50%
  transform: translate(-50%, -50%)

*
  font-family: helvetica
  vertical-align: top
  color: $color_black
  backface-visibility: hidden

html,body
  width: 100%
  height: 100%
  margin: 0
  padding: 0
  


  
h2
  margin-bottom: 20px
  color: $color_black
  
.notes_list
  margin-bottom: 50px
  li
    display: inline-block
    border-right: solid 1px $color_black
    padding: 2px 10px
    cursor: pointer
    transition: .3s
    
    .time
      font-size: 12px
      color: darken($color_white, 20%)
    &.playing
      background-color: darken($color_white, 10%)
    
    &:hover
      background-color: darken($color_white, 5%)
      

button
  margin: 0px 5px
  padding: 4px 12px
  border: solid 1px darken($color_white, 10)
  border-radius: 5px
  transition: .3s
  font-size: 15px
  cursor: pointer
  background-color: #fff
  
  &:hover
    background-color: #333
    color: white
  i
    transform: scale(.7)
    margin-left: 5px
    margin-right: -5px
    margin-bottom: 1px
    vertical-align: bottom
  
i.fa-circle
  color: darken(red,10)
  
.center_box
  +ab_center
  width: 100%
  text-align: center
  margin-top: 0px
  
  
.keyboard
  box-shadow: 0px 0px 20px -3px rgba(0,0,0,.3)
  display: inline-block
  margin: 40px 0px
  margin-bottom: 60px
 
  
  
.pianokey
  position: relative
  display: inline-block
  cursor: pointer
  
.white.playing
  background-color: $color_white
  transform: translate(0px, 0px)
  
.white
  +size($key_width, $key_height)
  border: solid 1px darken($color_white, 10%)
  transition: .2s
  transform: translate(-1px, -3px)
  &:hover
    transform: translate(0px, 0px)
    background-color: $color_white
  
.black.playing
  background-color: darken($color_black, 10%)
  transform: translate(0px, 0px)
  
.black
  position: absolute
  +size($key_width/1.75, $key_height*0.58)
  background-color: $color_black
  margin-left: -$key_width/3.5
  margin-right: -$key_width/3.5
  z-index: 1
  transition: .2s
  transform: translate(-1px, 0px)
  &:hover
    transform: translate(0px, 0px)
    +size($key_width/1.75, $key_height*0.59)
    background-color: darken($color_black, 10%)
  
.label
  position: absolute
  color: rgba($color_black, .5)
  bottom: -15px
  left: 50%
  transform: translate(-50%)
  font-size: 10px
  
.labelb
  position: absolute
  color: rgba($color_black, .5)
  top: -15px
  left: 50%
  transform: translate(-50%)
  font-size: 10px
  
.ps
  line-height: 15px
  h6,span
    color: rgba(black, .5)
  .highlight
    color: $color_black
View Compiled
var soundpack=[];
var soundpack_index=[1,1.5,2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7,8,8.5,9,9.5,10,11,11.5,12,12.5,13,13.5,14,15];


for(var i=0; i<soundpack_index.length; i++){
  // soundpack[i]="https://awiclass.monoame.com/pianosound/set/"+soundpack_index[i]+".wav"
  // console.log(soundpack[i]);
  soundpack.push({
    number: soundpack_index[i],
    url: "https://awiclass.monoame.com/pianosound/set/"+soundpack_index[i]+".wav"
  });
};



var vm= new Vue({
  el: "#app",
  data: {
    sounddata: soundpack,
    notes:[],
    now_note_id: 0,
    next_note_id: 0,
    playing_time: 0,
    record_time: 0,
    now_press_key: -1,
    pressingorplaying: -1,
    player: null,
    recorder: null,
    display_keys:[
      {num: 1, key: 90 ,type:'white'},
      {num: 1.5, key: 83 ,type:'black'},
      {num: 2, key: 88 ,type:'white'},
      {num: 2.5, key: 68 ,type:'black'},
      {num: 3, key: 67 ,type:'white'},
      {num: 4, key: 86 ,type:'white'},
      {num: 4.5, key: 71 ,type:'black'},
      {num: 5, key: 66 ,type:'white'},
      {num: 5.5, key: 72 ,type:'black'},
      {num: 6, key: 78 ,type:'white'},
      {num: 6.5, key: 74 ,type:'black'},
      {num: 7, key: 77 ,type:'white'},
      {num: 8, key: 81 ,type:'white'},
      {num: 8.5, key: 50 ,type:'black'},
      {num: 9, key: 87 ,type:'white'},
      {num: 9.5, key: 51, type:'black'},
      {num: 10, key: 69 ,type:'white'},
      {num: 11, key: 82 ,type:'white'},
      {num: 11.5, key: 53 ,type:'black'},
      {num: 12, key: 84 ,type:'white'},
      {num: 12.5, key: 54 ,type:'black'},
      {num: 13, key: 89 ,type:'white'},
      {num: 13.5, key: 55 ,type:'black'},
      {num: 14, key: 85 ,type:'white'},
      {num: 15, key: 73 ,type:'white'}
    ],
  },
  methods: {
    playnote: function(id, volume){
      this.pressingorplaying= 1;
      if (id>0){
        var audio_obj=$("audio[data-num='"+id+"']")[0];
        audio_obj.volume=volume;
        audio_obj.currentTime=0;
        audio_obj.play();
      }
    },
    playnext: function(volume){
      var play_note=this.notes[this.now_note_id].num;
      this.playnote(play_note,volume);
      this.now_note_id+=1;
      this.pressingorplaying= 1;
      if(this.now_note_id>=this.notes.length){
        this.stopplay();
        clearInterval(this.player);
        this.now_note_id=0;
        this.next_note_id=0;
        this.playing_time=0;
        this.pressingorplaying= -1;
      }
    },
    start_record: function(){
      this.record_time=0;
      this.recorder=setInterval(function(){
        vm.record_time++;
      });
    },
    stop_record: function(){
      clearInterval(this.recorder);
      this.record_time=0;
      
    },
    startplay: function(){
      this.now_note_id=0;
      this.next_note_id=0;
      this.playing_time=0;
      this.pressingorplaying= 1;
      var vobj=this;
      this.player=setInterval(function(){
        if(vobj.playing_time>=vobj.notes[vobj.next_note_id].time){
          vobj.playnext(1);
          vobj.next_note_id++;
        };
        vobj.playing_time++;
      }, 1);
    },
    stopplay: function(){
      clearInterval(this.player);
      this.now_note_id=0;
      this.next_note_id=0;
      this.playing_time=0;
      this.pressingorplaying= -1;
    },
    get_current_highlight: function(id, skey){
      if(this.now_press_key==skey)
        {return true;}
      if(this.notes.length==0)
        {return false;}
      var cur_id=this.now_note_id-1;
      if(cur_id<0)
        {cur_id=0;}
      var num=this.notes[cur_id].num;
      if(num == id)
        {return true;}
      return false;
    },
    addnote: function(id){
      if(this.record_time>0){
        this.notes.push({num:id, time: this.record_time});
      }
      this.playnote(id,1);
    },
    load_sample: function(){
      var vobj=this;
      $.ajax({
        url: "https://awiclass.monoame.com/api/command.php?type=get&name=music_dodoro",
        success: function(res){
          vobj.notes=JSON.parse(res);
        }
      });
    }
  }
});



$(window).keydown(function(e){
  var key = e.which;
  vm.now_press_key=key;
  for(var j=0; j<vm.display_keys.length; j++){
    if(key==vm.display_keys[j].key){
      vm.addnote(vm.display_keys[j].num);
    }
  }
});

$(window).keyup(function(){
  vm.now_press_key=-1;
});

External CSS

  1. https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js