.infos.fadeIn.animated
  h2 Project 4.
  h1 Memory<br>Blocks
  h3.status -
.blocks.fadeIn.animated
  .row
    .block.block1(onclick="game.userSendInput('1')") 1 
    .block.block2(onclick="game.userSendInput('2')") 2 
  .row
    .block.block3(onclick="game.userSendInput('3')") 3 
    .block.block4(onclick="game.userSendInput('4')") 4
  .row
    .inputStatus
View Compiled
$colorRed: #FF5353
$colorYellow: #FFC429
$colorBlue: #5980C1
$colorWhite: #FBE9B7
@mixin size ($w,$h:$w)
  width: $w
  height: $h
@import url('https://fonts.googleapis.com/css?family=Roboto:700')
html,body
  font-family: "roboto"
  background-color: #232526
  display: flex
  justify-content: center
  align-items: center
  flex-direction: column
  +size(100%)
  margin: 0
  padding: 0  
  border: solid 5px rgba(white,0.05)
  transition: 0.5s
  box-sizing: border-box
  &:active
    transition: 0s
    border: solid 5px rgba(white,0.1)

.infos
  color: white
  position: absolute
  left: 40px
  top: 40px
  z-index: 100
  h1
    margin-top: 0
    margin-bottom: 0
    font-size: 40px
    letter-spacing: 1px
    line-height: 1.2
  h3
    font-weight: 500
    color: $colorRed
.blocks

  .row
    display: flex
  .block
    animation: comeOut 1s
    width: 100px
    height: 100px
    border: solid 1px rgba(white,0.3)
    display: flex
    justify-content: center
    align-items: center
    transition-duration: 0.6s
    tranisiton-delay: 0.2s
    cursor: pointer
    margin: 12px
    color: white
    font-weight: 100
    
    @mixin setBlockColor($color)
      box-shadow: 0px 0px 35px rgba($color,0.2) inset, 0px 0px 30px rgba($color,0.2)
      background-color: transparent
      border: solid 1px lighten($color,5)
      color: transparent
      &:hover
        background-color: rgba($color,0.3)
        box-shadow: 0px 0px 35px rgba($color,0.2)
      &:active , &.active
        background-color: $color
        box-shadow: 0px 0px 35px $color
        // color: white
        // transform: scale(0.95)
    &:active , &.active
      background-color: red
      transition-duration: 0s
      
    &.block1
      +setBlockColor($colorRed)
    &.block2
      +setBlockColor($colorYellow)
    &.block3
      +setBlockColor($colorBlue)
    &.block4
      +setBlockColor($colorWhite)
      
.inputStatus
  margin-top: 10px
  .circle
    +size(6px)
    display: inline-block
    opacity: 0.3
    background-color: white
    border-radius: 100%
    margin: 5px
    
    &.correct,.wrong
      opacity: 1
  &.wrong .circle
    background-color: $colorRed
    
  &.correct .circle
    background-color: $colorBlue

  
View Compiled
//--------------------------------
//          方塊物件
//--------------------------------
var Blocks = function(blockAssign,setAssign){
  this.allOn=false
  this.blocks=blockAssign.map((d,i)=>
    ({
      name: d.name,
      el: $(d.selector),
      audio: this.getAudioObject(d.pitch) 
    })
  )
  this.soundSets = setAssign.map((d,i)=>
    ({
      name: d.name,
      sets: d.sets.map((pitch)=>this.getAudioObject(pitch))
    })
  )
}
//閃爍單一方塊+聲音(方塊名)
Blocks.prototype.flash=function(note){
  let block = this.blocks.find(d=>d.name==note)
  if (block){
    block.audio.currentTime=0
    block.audio.play()
    block.el.addClass("active")
    setTimeout(()=>{
      if (this.allOn==false){
        block.el.removeClass("active")
      }
    },100)
  }
}
//點亮所有方塊
Blocks.prototype.turnOnAll=function(note){
  this.allOn=true
  this.blocks.forEach(d=>{
    d.el.addClass("active")
  })
}
//關掉所有方塊
Blocks.prototype.turnOffAll=function(note){
  this.allOn=false
  this.blocks.forEach(d=>{
    d.el.removeClass("active")
  })
}
//取得聲音物件
Blocks.prototype.getAudioObject=function(pitch){
  var audio =  new Audio("https://awiclass.monoame.com/pianosound/set/"+ pitch+".wav")
  audio.setAttribute("preload","auto")
  return audio
}
//播放序列聲音(成功/失敗...)
Blocks.prototype.playSet = function(type){
  this.soundSets
    .find(set => set.name==type).sets
    .forEach(o=>{
      o.currentTime=0
      o.play()
    })
}

//--------------------------------
//          遊戲物件
//--------------------------------
var Game = function (){
  //定義關卡
  this.blocks = new Blocks(
    [
      {selector: ".block1", name: "1", pitch: "1"},
      {selector: ".block2", name: "2", pitch: "2"},
      {selector: ".block3", name: "3", pitch: "3"},
      {selector: ".block4", name: "4", pitch: "4"}
    ],
    [
      {name: "correct", sets: [1,3,5,8] },
      {name: "wrong", sets: [2,4,5.5,7] }
    ]
  )
  this.levels = [
    "1234",
    "12324",
    "231234",
    "41233412",
    "41323134132",
    "2342341231231423414232"
  ]
  let _this = this
  //下載關卡
  $.ajax({
    url: "https://2017.awiclass.monoame.com/api/demo/memorygame/leveldata",
    success: function(res){
      _this.levels = res
    }
  })
  //設定現在在的關卡跟播放間隔
  this.currentLevel = 0
  this.playInterval = 400
  this.mode="waiting"
}
//開始關卡
Game.prototype.startLevel = function(){
  // console.log("start Level "+ this.currentLevel)
  this.showMessage("Level "+ this.currentLevel)
  this.startGame(this.levels[this.currentLevel])
}
//顯示訊息
Game.prototype.showMessage = function(message){
  console.log(message)
  $(".status").text(message)
}
//開始遊戲(答案)
Game.prototype.startGame = function(answer){
  this.mode="gamePlay"
  this.answer = answer
  let notes = this.answer.split("")
  let _this = this
  
  this.showStatus("")
  this.timer = setInterval(function(){
    let char = notes.shift()
    if (!notes.length){
      clearInterval(_this.timer)
      _this.startUserInput()
    }
    _this.playNote(char)
  },this.playInterval)
}
//播放音符
Game.prototype.playNote = function(note){
  console.log(note)
  this.blocks.flash(note)
 
}
//開始輸入模式
Game.prototype.startUserInput = function(){
  this.userInput = ""
  this.mode="userInput"
}
//使用者輸入
Game.prototype.userSendInput = function(inputChar){
  if (this.mode=="userInput"){
    let tempString = this.userInput + inputChar
    this.playNote(inputChar)
    this.showStatus(tempString)
    if (this.answer.indexOf(tempString)==0){
      if (this.answer==tempString){
        this.currentLevel+=1
        this.mode=="waiting"
        setTimeout(()=>{
          this.startLevel()
        },1000)
      }
      this.userInput += inputChar
    }else{
      this.currentLevel=0
      this.mode=="reset"
      setTimeout(()=>{
        this.startLevel()
      },1000)
    }
  }
}
//顯示回答狀態
Game.prototype.showStatus = function(tempString){
  $(".inputStatus").html("")
  this.answer.split("").forEach((d,i)=>{
    var circle = $("<div class='circle'></div>")
    if(i<tempString.length){
      circle.addClass("correct")
    }
    $(".inputStatus").append(circle)
  })

  if (tempString == this.answer){
    $(".inputStatus").addClass("correct")
    this.showMessage("Correct!")
    setTimeout(()=>{
      this.blocks.turnOnAll()
      this.blocks.playSet("correct")
    },500)
  }else{
    $(".inputStatus").removeClass("correct")
  }
  if (tempString==""){
    this.blocks.turnOffAll()
  }
  if (this.answer.indexOf(tempString)!=0){
    this.showMessage("Wrong...")
    $(".inputStatus").addClass("wrong")
    this.blocks.turnOnAll()
    this.blocks.playSet("wrong")
  }else{
    $(".inputStatus").removeClass("wrong")
    
  }
}

//--------------------
//     開新遊戲
var game = new Game()
setTimeout(function(){
  game.startLevel()
},1000)

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js