2016-11-11 18:52:19 +00:00
|
|
|
/**
|
|
|
|
* Copyright 2016 Google Inc.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
import events from 'events'
|
|
|
|
import 'style/keyboard.css'
|
|
|
|
import 'pepjs'
|
|
|
|
import {Roll} from 'roll/Roll'
|
|
|
|
|
|
|
|
|
|
|
|
const offsets = [0, 0.5, 1, 1.5, 2, 3, 3.5, 4, 4.5, 5, 5.5, 6]
|
|
|
|
|
|
|
|
class KeyboardElement extends events.EventEmitter {
|
|
|
|
|
|
|
|
constructor(container, lowest=36, octaves=4){
|
|
|
|
super()
|
|
|
|
this._container = document.createElement('div')
|
|
|
|
this._container.id = 'keyboard'
|
|
|
|
container.setAttribute('touch-action', 'none')
|
|
|
|
container.addEventListener('pointerup', () => this._mousedown = false)
|
|
|
|
container.appendChild(this._container)
|
|
|
|
|
|
|
|
this._keys = {}
|
|
|
|
|
|
|
|
this._mousedown = false
|
|
|
|
|
|
|
|
this.resize(lowest, octaves)
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The piano roll
|
|
|
|
* @type {Roll}
|
|
|
|
*/
|
|
|
|
this._roll = new Roll(container)
|
|
|
|
}
|
|
|
|
|
|
|
|
resize(lowest, octaves){
|
|
|
|
this._keys = {}
|
|
|
|
// clear the previous ones
|
|
|
|
this._container.innerHTML = ''
|
|
|
|
// each of the keys
|
|
|
|
const keyWidth = (1 / 7) / octaves
|
|
|
|
for (let i = lowest; i < lowest + octaves * 12; i++){
|
|
|
|
let key = document.createElement('div')
|
|
|
|
key.classList.add('key')
|
|
|
|
let isSharp = ([1, 3, 6, 8, 10].indexOf(i % 12) !== -1)
|
|
|
|
key.classList.add(isSharp ? 'black' : 'white')
|
|
|
|
this._container.appendChild(key)
|
|
|
|
// position the element
|
|
|
|
|
|
|
|
let noteOctave = Math.floor(i / 12) - Math.floor(lowest / 12)
|
|
|
|
let offset = offsets[i % 12] + noteOctave * 7
|
|
|
|
key.style.width = `${keyWidth * 100}%`
|
|
|
|
key.style.left = `${offset * keyWidth * 100}%`
|
|
|
|
key.id = i.toString()
|
|
|
|
key.setAttribute('touch-action', 'none')
|
|
|
|
|
|
|
|
const fill = document.createElement('div')
|
|
|
|
fill.id = 'fill'
|
|
|
|
key.appendChild(fill)
|
|
|
|
|
|
|
|
this._bindKeyEvents(key)
|
|
|
|
this._keys[i] = key
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_bindKeyEvents(key){
|
|
|
|
|
|
|
|
key.addEventListener('pointerover', (e) => {
|
|
|
|
if (this._mousedown){
|
|
|
|
const noteNum = parseInt(e.target.id)
|
|
|
|
// this.keyDown(noteNum, false)
|
|
|
|
this.emit('keyDown', noteNum)
|
|
|
|
} else {
|
|
|
|
key.classList.add('hover')
|
|
|
|
}
|
|
|
|
})
|
|
|
|
key.addEventListener('pointerout', (e) => {
|
|
|
|
if (this._mousedown){
|
|
|
|
const noteNum = parseInt(e.target.id)
|
|
|
|
// this.keyUp(noteNum, false)
|
|
|
|
this.emit('keyUp', noteNum)
|
|
|
|
} else {
|
|
|
|
key.classList.remove('hover')
|
|
|
|
}
|
|
|
|
})
|
|
|
|
key.addEventListener('pointerdown', (e) => {
|
|
|
|
e.preventDefault()
|
|
|
|
const noteNum = parseInt(e.target.id)
|
|
|
|
// this.keyDown(noteNum, false)
|
|
|
|
this.emit('keyDown', noteNum)
|
|
|
|
this._mousedown = true
|
|
|
|
})
|
|
|
|
key.addEventListener('pointerup', (e) => {
|
|
|
|
e.preventDefault()
|
|
|
|
const noteNum = parseInt(e.target.id)
|
|
|
|
// this.keyUp(noteNum, false)
|
|
|
|
this.emit('keyUp', noteNum)
|
|
|
|
this._mousedown = false
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
keyDown(noteNum, ai=false){
|
|
|
|
// console.log('down', noteNum, ai)
|
|
|
|
if (this._keys.hasOwnProperty(noteNum)){
|
|
|
|
const key = this._keys[noteNum]
|
|
|
|
key.classList.remove('hover')
|
|
|
|
|
|
|
|
const highlight = document.createElement('div')
|
|
|
|
highlight.classList.add('highlight')
|
|
|
|
highlight.classList.add('active')
|
|
|
|
if (ai){
|
|
|
|
highlight.classList.add('ai')
|
|
|
|
}
|
|
|
|
key.querySelector('#fill').appendChild(highlight)
|
|
|
|
|
|
|
|
this._roll.keyDown(noteNum, this._getNotePosition(noteNum), ai)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
keyUp(noteNum, ai=false){
|
|
|
|
// console.log('up', noteNum, ai)
|
|
|
|
if (this._keys.hasOwnProperty(noteNum)){
|
|
|
|
const query = ai ? '.highlight.active.ai' : '.highlight.active'
|
|
|
|
const highlight = this._keys[noteNum].querySelector(query)
|
|
|
|
if (highlight){
|
|
|
|
highlight.classList.remove('active')
|
|
|
|
setTimeout(() => highlight.remove(), 2000)
|
|
|
|
//and up on the roll
|
|
|
|
} else {
|
2016-11-15 00:35:36 +00:00
|
|
|
//try again
|
2016-11-11 18:52:19 +00:00
|
|
|
this.keyUp(noteNum)
|
|
|
|
}
|
|
|
|
}
|
2016-11-15 00:35:36 +00:00
|
|
|
this._roll.keyUp(noteNum, ai)
|
2016-11-11 18:52:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_getNotePosition(key){
|
|
|
|
if (this._keys.hasOwnProperty(key)){
|
|
|
|
return this._keys[key].querySelector('#fill').getBoundingClientRect()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export {KeyboardElement}
|