diff --git a/static/src/ai/Tutorial.js b/static/src/ai/Tutorial.js new file mode 100644 index 0000000..26d211d --- /dev/null +++ b/static/src/ai/Tutorial.js @@ -0,0 +1,130 @@ +/** + * 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 Tone from 'Tone/core/Tone' +import 'style/tutorial.css' + +const beat = 0.4 +const testMelody = [ + { + note : 60, + time : beat * 0, + duration : beat + }, + { + note : 62, + time : beat * 1, + duration : beat + }, + { + note : 64, + time : beat * 2, + duration : beat + }, + { + note : 64, + time : beat * 3, + duration : beat * 0.5 + }, + { + note : 62, + time : beat * 3.6, + duration : beat * 0.5 + }, + { + note : 60, + time : beat * 4, + duration : beat * 0.5 + }, + { + note : 60, + time : beat * 5, + duration : beat + } +] + +export class Tutorial extends events.EventEmitter{ + constructor(container){ + super() + + this._tutorial = document.createElement('div') + this._tutorial.id = 'tutorial' + container.appendChild(this._tutorial) + } + start(){ + if (window.localStorage){ + if (window.localStorage.getItem('showedTutorial') === 'true' && window.location.hash !== '#tutorial'){ + return + } else { + window.localStorage.setItem('showedTutorial', 'true') + } + } + + this._promiseTimeout(400).then(() => { + this._addText('When you play a few notes', 'user', 4200) + return this._promiseTimeout(1000) + }).then(() => { + const now = Tone.now() + testMelody.forEach((event) => { + this.emit('keyDown', event.note, event.time + now) + this.emit('keyUp', event.note, event.time + event.duration * 0.9 + now) + }) + return this._promiseTimeout(3000) + }).then(() => { + this._sendUserMelody() + return this._promiseTimeout(500) + }).then(() => { + this._addText('A neural network will respond to what you play', 'ai', 5000) + }) + } + _promiseTimeout(time){ + return new Promise(done => { + setTimeout(done, time) + }) + } + + _sendUserMelody(){ + const now = Tone.now() + testMelody.forEach((event) => { + this.emit('aiKeyDown', event.note, event.time + now) + this.emit('aiKeyUp', event.note, event.time + event.duration * 0.9 + now) + }) + } + _addText(text, className, time){ + const element = document.createElement('div') + element.classList.add('text') + element.classList.add(className) + element.textContent = text + this._tutorial.appendChild(element) + requestAnimationFrame(() => { + element.classList.add('visible') + }) + if (time){ + setTimeout(() => { + this._removeText(element) + }, time) + } + return element + } + + _removeText(element){ + element.classList.remove('visible') + setTimeout(() => { + element.remove() + }, 500) + } +} \ No newline at end of file diff --git a/static/style/tutorial.css b/static/style/tutorial.css new file mode 100644 index 0000000..0b6e178 --- /dev/null +++ b/static/style/tutorial.css @@ -0,0 +1,43 @@ +@import 'common.scss'; + +#tutorial { + position: absolute; + top: 50%; + left: 50%; + height: 50px; + width: 80%; + min-width: 300px; + transform: translate(-50%, -50%); + color: white; + font-family: $font-family; + text-align: center; + font-size: 20px; + opacity: 1; + transition: opacity 0.3s; + + .text { + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + opacity: 0; + transition: opacity 0.3s; + + &.visible { + opacity: 1; + } + + &.user { + // color: $blue; + } + + &.ai { + // color: $orange; + } + } + + &.disappear { + opacity: 0; + } +} \ No newline at end of file