diff --git a/01 - JavaScript Drum Kit/index-START.html b/01 - JavaScript Drum Kit/index-START.html index 4070d32767..85de0afbfd 100644 --- a/01 - JavaScript Drum Kit/index-START.html +++ b/01 - JavaScript Drum Kit/index-START.html @@ -57,9 +57,7 @@ - + diff --git a/01 - JavaScript Drum Kit/index.js b/01 - JavaScript Drum Kit/index.js new file mode 100644 index 0000000000..9c2ddde39c --- /dev/null +++ b/01 - JavaScript Drum Kit/index.js @@ -0,0 +1,37 @@ +class Dram { + constructor(key, audio) { + this.key = key; + this.audio = audio; + this.className = 'playing'; + + key.addEventListener('transitionend', (event) => { + if (event.propertyName !== 'transform') { + return; + } + key.classList.remove(this.className); + }); + } + + playSound() { + this.audio.currentTime = 0; + this.audio.play(); + this.key.classList.add(this.className); + } +} + +const keys = Array.from(document.querySelectorAll('.key')); +const dramMap = Array.from(document.querySelectorAll('audio')).reduce((drams, audio) => { + const keyCode = audio.dataset.key; + const pressedKey = keys.find(key => key.dataset.key === keyCode); + if (!pressedKey) { + return drams; + } + return Object.assign(drams, { [keyCode]: new Dram(pressedKey, audio) }); +}, {}); + +addEventListener('keydown', (event) => { + const dram = dramMap[event.keyCode.toString()]; + if (dram) { + dram.playSound(); + } +}); diff --git a/02 - JS + CSS Clock/index-START.html b/02 - JS + CSS Clock/index-START.html index 2712384201..b4f8c91dc6 100644 --- a/02 - JS + CSS Clock/index-START.html +++ b/02 - JS + CSS Clock/index-START.html @@ -61,13 +61,15 @@ background:black; position: absolute; top:50%; + transform-origin: 100%; + transform: rotate(90deg); + transition: all 0.05s; + transition-timing-function: cubic-bezier(0.1, 2.7, 0.58, 1); } - - - diff --git a/02 - JS + CSS Clock/index.js b/02 - JS + CSS Clock/index.js new file mode 100644 index 0000000000..44588de597 --- /dev/null +++ b/02 - JS + CSS Clock/index.js @@ -0,0 +1,27 @@ +const units = [ + { + elem: document.querySelector('.second-hand'), + method: 'getSeconds', + max: 60 + }, + { + elem: document.querySelector('.min-hand'), + method: 'getMinutes', + max: 60 + }, + { + elem: document.querySelector('.hour-hand'), + method: 'getHours', + max: 12 + } +]; + +setInterval(setDate, 1000); + +function setDate() { + units.forEach(unit => { + const now = new Date(); + const deg = (now[unit.method]() / unit.max) * 360 + 90; + unit.elem.style.transform = `rotate(${deg}deg)`; + }); +} diff --git a/06 - Type Ahead/index-START.html b/06 - Type Ahead/index-START.html index 1436886918..94e2a10c11 100644 --- a/06 - Type Ahead/index-START.html +++ b/06 - Type Ahead/index-START.html @@ -1,22 +1,18 @@ - - - Type Ahead 👀 - - - - -
- - -
- + + + Type Ahead 👀 + + + +
+ + +
+ diff --git a/06 - Type Ahead/index.js b/06 - Type Ahead/index.js new file mode 100644 index 0000000000..f4b770b379 --- /dev/null +++ b/06 - Type Ahead/index.js @@ -0,0 +1,80 @@ +class Notifier { + constructor() { + this.handlers = []; + } + + observe(handler) { + this.handlers.push(handler); + } + + fire() { + this.handlers.forEach(handler => handler()); + } +} + +class Model { + constructor() { + this._cities = []; + this._matchedCities = []; + this.matchedCitiesChanged = new Notifier(); + } + + get matchedCities() { + return this._matchedCities; + } + + set matchedCities(cities) { + this._matchedCities = cities; + this.matchedCitiesChanged.fire(); + } + + loadCities() { + const endpoint = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29' + + '/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json'; + fetch(endpoint) + .then(resp => resp.json()) + .then(data => this._cities.push(...data)); + } + + match(wordRegex) { + this.matchedCities = this._cities.filter(place => + wordRegex.test(place.city) || wordRegex.test(place.state) + ); + } +} + +class ViewModel { + constructor(suggestions) { + this.suggestions = suggestions; + this.model = new Model(); + this.matchedCities = this.model.matchedCities; + + // register handler to observer + this.model.matchedCitiesChanged.observe(() => this.matchedCities = this.model.matchedCities); + + // initialize model + this.model.loadCities(); + } + + render(wordToMatch) { + const regex = new RegExp(wordToMatch, 'gi'); + this.model.match(regex); + + const html = this.matchedCities.map(place => { + const highlight = `${wordToMatch}`; + const cityName = place.city.replace(regex, highlight); + const stateName = place.state.replace(regex, highlight); + return `
  • ${cityName}, ${stateName}
  • `; + }).join("\n"); + + this.suggestions.innerHTML = html; + } +} + +// main +const searchInput = document.querySelector('input.search'); +const suggestions = document.querySelector('ul.suggestions'); +const vm = new ViewModel(suggestions); +['change', 'keyup'].forEach(eventType => + searchInput.addEventListener(eventType, () => vm.render(searchInput.value)) +); diff --git a/08 - Fun with HTML5 Canvas/index-START.html b/08 - Fun with HTML5 Canvas/index-START.html index 37c148df07..11b5ffcb7f 100644 --- a/08 - Fun with HTML5 Canvas/index-START.html +++ b/08 - Fun with HTML5 Canvas/index-START.html @@ -6,8 +6,7 @@ - +