diff --git a/01 - JavaScript Drum Kit/index-START.html b/01 - JavaScript Drum Kit/index-START.html index 4070d32767..ff418081a6 100644 --- a/01 - JavaScript Drum Kit/index-START.html +++ b/01 - JavaScript Drum Kit/index-START.html @@ -58,7 +58,23 @@ diff --git a/01 - JavaScript Drum Kit/index.html b/01 - JavaScript Drum Kit/index.html new file mode 100644 index 0000000000..a365cfcd40 --- /dev/null +++ b/01 - JavaScript Drum Kit/index.html @@ -0,0 +1,65 @@ + + + + + + Wes Bos Soundboard + + + + +

Wes Bos Soundboard

+
+
+ A + Boo +
+
+ S + Open Close +
+
+ D + Scrolling +
+
+ F + Trumpet +
+
+ G + Woo +
+
+ H + Awesome +
+
+ J + I Like That +
+
+ K + Embarassing +
+
+ + L + Turtle +
+
+ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/01 - JavaScript Drum Kit/main.css b/01 - JavaScript Drum Kit/main.css new file mode 100644 index 0000000000..dc1a282a98 --- /dev/null +++ b/01 - JavaScript Drum Kit/main.css @@ -0,0 +1,143 @@ +html { + font-size: 10px; + background: url(https://i.imgur.com/b9r5sEL.jpg) bottom center; + background-size: cover; +} + +body,html { + margin: 0; + padding: 0; + font-family: sans-serif; +} + +h1 { + font-size: 7vw; + background: #000; + color: #fff; + padding: 1vh; + margin: 1vh 0 1vh; +} + +.keys { + /* display: flex; */ + /* flex: 1; */ + min-height: 100vh; + align-items: center; + justify-content: center; +} + +.key { + float: left; + border: .4rem solid black; + border-radius: .5rem; + margin: 1rem; + font-size: 1.5rem; + padding: 1rem .5rem; + transition: all .07s ease; + width: 10rem; + text-align: center; + color: white; + background: rgba(0,0,0,0.8); + text-shadow: 0 0 .5rem black; +} + +.playing { + transform: scale(1.1); + border-color: #ffc600; + box-shadow: 0 0 1rem #ffc600; + background: rgba(0,0,0,0.7); +} + +kbd { + display: block; + font-size: 4rem; +} + +.sound { + font-size: 1.2rem; + text-transform: uppercase; + letter-spacing: .1rem; + color: #ffc600; +} + +img.turtle { + display: none; + margin: 0 auto; + width: 5rem; +} + +img.turtle.shake { + animation: 0.5s shake 1.6s linear 2; +} + +@keyframes shake { + + 10%, + 90% { + transform: translate3d(-1px, 0, 0); + } + + 20%, + 80% { + transform: translate3d(2px, 0, 0); + } + + 30%, + 50%, + 70% { + transform: translate3d(-4px, 0, 0); + } + + 40%, + 60% { + transform: translate3d(4px, 0, 0); + } +} + +/* Portrait */ +@media only screen + and (min-device-width: 375px) + and (max-device-width: 667px) + and (-webkit-min-device-pixel-ratio: 2) + and (orientation: portrait) { +.key { + width: 33vw; + margin: 5vw 5vw 5vw 8vw; + } + + kbd { + font-size: 5vh; + } + + .sound { + font-size: 2vh; + } + + img.turtle { + width: 23vw; + } +} + +/* Landscape */ +@media only screen + and (min-device-width: 375px) + and (max-device-width: 667px) + and (-webkit-min-device-pixel-ratio: 2) + and (orientation: landscape) { + .key { + width: 18vw; + margin: 2vw 2vw 2vw 3vw; + } + + kbd { + font-size: 8vh; + } + + .sound { + font-size: 4vh; + } + + img.turtle { + width: 10vw; + } +} \ No newline at end of file diff --git a/01 - JavaScript Drum Kit/sounds/awesome.mp3 b/01 - JavaScript Drum Kit/sounds/awesome.mp3 new file mode 100644 index 0000000000..b38b457897 Binary files /dev/null and b/01 - JavaScript Drum Kit/sounds/awesome.mp3 differ diff --git a/01 - JavaScript Drum Kit/sounds/boo.mp3 b/01 - JavaScript Drum Kit/sounds/boo.mp3 new file mode 100644 index 0000000000..4276dfdcdf Binary files /dev/null and b/01 - JavaScript Drum Kit/sounds/boo.mp3 differ diff --git a/01 - JavaScript Drum Kit/sounds/i-like-that.mp3 b/01 - JavaScript Drum Kit/sounds/i-like-that.mp3 new file mode 100644 index 0000000000..6b0559c06b Binary files /dev/null and b/01 - JavaScript Drum Kit/sounds/i-like-that.mp3 differ diff --git a/01 - JavaScript Drum Kit/sounds/look-at-this-turtle.mp3 b/01 - JavaScript Drum Kit/sounds/look-at-this-turtle.mp3 new file mode 100644 index 0000000000..63b2bc9c6f Binary files /dev/null and b/01 - JavaScript Drum Kit/sounds/look-at-this-turtle.mp3 differ diff --git a/01 - JavaScript Drum Kit/sounds/open-close.mp3 b/01 - JavaScript Drum Kit/sounds/open-close.mp3 new file mode 100644 index 0000000000..d9f54f9d75 Binary files /dev/null and b/01 - JavaScript Drum Kit/sounds/open-close.mp3 differ diff --git a/01 - JavaScript Drum Kit/sounds/scrolling.mp3 b/01 - JavaScript Drum Kit/sounds/scrolling.mp3 new file mode 100644 index 0000000000..953b6f06e8 Binary files /dev/null and b/01 - JavaScript Drum Kit/sounds/scrolling.mp3 differ diff --git a/01 - JavaScript Drum Kit/sounds/thats-embarassing.mp3 b/01 - JavaScript Drum Kit/sounds/thats-embarassing.mp3 new file mode 100644 index 0000000000..aa471bfe24 Binary files /dev/null and b/01 - JavaScript Drum Kit/sounds/thats-embarassing.mp3 differ diff --git a/01 - JavaScript Drum Kit/sounds/trumpet.mp3 b/01 - JavaScript Drum Kit/sounds/trumpet.mp3 new file mode 100644 index 0000000000..f376335af4 Binary files /dev/null and b/01 - JavaScript Drum Kit/sounds/trumpet.mp3 differ diff --git a/01 - JavaScript Drum Kit/sounds/woo.mp3 b/01 - JavaScript Drum Kit/sounds/woo.mp3 new file mode 100644 index 0000000000..d69259cb5a Binary files /dev/null and b/01 - JavaScript Drum Kit/sounds/woo.mp3 differ diff --git a/01 - JavaScript Drum Kit/turtle.png b/01 - JavaScript Drum Kit/turtle.png new file mode 100644 index 0000000000..7b53615974 Binary files /dev/null and b/01 - JavaScript Drum Kit/turtle.png differ diff --git a/01 - JavaScript Drum Kit/wes-sound-board.js b/01 - JavaScript Drum Kit/wes-sound-board.js new file mode 100644 index 0000000000..29ac15094b --- /dev/null +++ b/01 - JavaScript Drum Kit/wes-sound-board.js @@ -0,0 +1,76 @@ +console.log('This soundboard is a tribute to Wes Bos for his entertaining and accessible videos.'); + +function playSound(soundKey) { + const audio = document.querySelector(`audio[data-key="${soundKey}"]`); + const key = document.querySelector(`div[data-key="${soundKey}"]`); + if (!audio) return; + + // Match animation to the duration + key.setAttribute('style', `transition-duration: ${audio.duration}s`); + + key.classList.add('playing'); // Highlight the currently playing sound + audio.currentTime = 0; + audio.play(); + + countdown(key, audio.duration); // Display a countdown + + // Remove highlight after the effect finishes playing + audio.addEventListener('ended', function() { + key.classList.remove('playing'); + }); +} + +// Displays a countdown while the audio clip plays +function countdown(key, duration) { + let kbdTag = key.querySelector('kbd'); + + if (key.dataset.key != "76") { // Insert duration for non-turtle + kbdTag.innerHTML = duration.toFixed(1); // sound clips + } + + timer = duration.toFixed(1); + + function updateTimeLeft() { + timer -= 0.1; + if (key.dataset.key === "76") { + // When the turtle clip is played, display the turtle + kbdTag.style.display = 'none'; + turtle(); + } else { + kbdTag.innerHTML = timer.toFixed(1); + } + + if (timer < 0) { + clearInterval(intervalId); // Stop updateTimeLeft() from running + if (key.dataset.key === "76") { + document.querySelector('.turtle').style.display = 'none'; + kbdTag.style.display = 'block'; + } + kbdTag.innerHTML = key.dataset.letter; + } + } + + let intervalId = window.setInterval(updateTimeLeft, 100); +} + +function turtle() { + let turtle = document.querySelector('.turtle'); + turtle.style.display = 'inline-block'; + turtle.classList.add('shake'); +} + +function handleKey(e) { + let soundKey = e.keyCode; + playSound(soundKey); +} + +function handleClick(e) { + let soundKey = e.target.closest('div.key').dataset.key; + playSound(soundKey); +} + +const keys = Array.from(document.querySelectorAll('.key')); + +// Handle keys and clicks +window.addEventListener('keydown', handleKey); +keys.forEach(key => key.addEventListener('click', handleClick)); \ No newline at end of file diff --git a/02 - JS and CSS Clock/index.html b/02 - JS and CSS Clock/index.html new file mode 100644 index 0000000000..ad2cfc70a8 --- /dev/null +++ b/02 - JS and CSS Clock/index.html @@ -0,0 +1,251 @@ + + + + + + JS + CSS Clock + + + + +
+
+
+
+
+ +
+
+ + + + + + + + + + \ No newline at end of file diff --git a/03 - CSS Variables/index-START.html b/03 - CSS Variables/index-START.html index 6b9b539c09..624a6f980f 100644 --- a/03 - CSS Variables/index-START.html +++ b/03 - CSS Variables/index-START.html @@ -1,9 +1,11 @@ + Scoped CSS Variables and JS +

Update CSS Variables with JS

@@ -21,6 +23,17 @@

Update CSS Variables with JS

- + + \ No newline at end of file diff --git a/05 - Flex Panel Gallery/index-START.html b/05 - Flex Panel Gallery/index-START.html index 71d1c26f00..081aee6995 100644 --- a/05 - Flex Panel Gallery/index-START.html +++ b/05 - Flex Panel Gallery/index-START.html @@ -1,10 +1,12 @@ + Flex Panels 💪 + @@ -105,10 +149,26 @@ - + + \ No newline at end of file diff --git a/06 - Type Ahead/bouncemarker.js b/06 - Type Ahead/bouncemarker.js new file mode 100755 index 0000000000..b2ce7fd894 --- /dev/null +++ b/06 - Type Ahead/bouncemarker.js @@ -0,0 +1,185 @@ +/** + * Copyright (C) 2013 Maxime Hadjinlian + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +(function() { + // Retain the value of the original onAdd and onRemove functions + let originalOnAdd = L.Marker.prototype.onAdd; + let originalOnRemove = L.Marker.prototype.onRemove; + + // Add bounceonAdd options + L.Marker.mergeOptions({ + bounceOnAdd: false, + bounceOnAddOptions: { + duration: 1000, + height: -1, + loop: 1, + }, + bounceOnAddCallback: function() {}, + }); + + L.Marker.include({ + + _toPoint: function(latlng) { + return this._map.latLngToContainerPoint(latlng); + }, + + _toLatLng: function(point) { + return this._map.containerPointToLatLng(point); + }, + + _motionStep: function(opts) { + let self = this; + let timePassed = new Date() - opts.start; + let progress = timePassed / opts.duration; + + if (progress > 1) { + progress = 1; + } + + let delta = self._easeOutBounce(progress); + opts.step(delta); + + if (progress === 1) { + opts.start = new Date(); + progress = 0; + if (opts.loop > 0) opts.loop = opts.loop - 1; + if (opts.loop === 0) { + opts.end(); + return; + } + } + + self._animationId = L.Util.requestAnimFrame(function(timestamp) { + self._motionStep(opts); + }); + }, + + _bounceMotion: function(opts, callback) { + let original = L.latLng(this._origLatlng); + let start_y = this._dropPoint.y; + let start_x = this._dropPoint.x; + let distance = this._point.y - start_y; + let self = this; + + self._animationId = L.Util.requestAnimFrame(function() { + self._motionStep({ + duration: opts.duration || 1000, // 1 sec by default + loop: opts.loop || 1, + start: new Date(), + step: function(delta) { + self._dropPoint.y = + start_y + + (distance * delta) + - (self._map.project(self._map.getCenter()).y - self._origMapCenter.y); + self._dropPoint.x = + start_x + - (self._map.project(self._map.getCenter()).x - self._origMapCenter.x); + self.setLatLng(self._toLatLng(self._dropPoint)); + }, + end: function() { + self.setLatLng(original); + if (typeof callback === 'function') callback(); + }, + }); + }); + }, + + // Many thanks to Robert Penner for this function + _easeOutBounce: function(pos) { + if ((pos) < (1 / 2.75)) { + return (7.5625 * pos * pos); + } else if (pos < (2 / 2.75)) { + return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); + } else if (pos < (2.5 / 2.75)) { + return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); + } else { + return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); + } + }, + + // Bounce : if options.height in pixels is not specified, drop from top. + // If options.duration is not specified animation is 1s long. + bounce: function(options, endCallback) { + if (typeof options === 'function') { + endCallback = options; + options = null; + } + options = options || {duration: 1000, height: -1, loop: 1}; + + // backward compatibility + if (typeof options === 'number') { + options.duration = arguments[0]; + options.height = arguments[1]; + } + + // Keep original map center + this._origMapCenter = this._map.project(this._map.getCenter()); + this._dropPoint = this._getDropPoint(options.height); + this._bounceMotion(options, endCallback); + }, + + stopBounce: function(){ + // We may have modified the marker; so we need to place it where it + // belongs so next time its coordinates are not changed. + this.setLatLng(this._origLatlng); + L.Util.cancelAnimFrame(this._animationId); + }, + + // This will get you a drop point given a height. + // If no height is given, the top y will be used. + _getDropPoint: function(height) { + // Get current coordidates in pixel + this._point = this._toPoint(this._origLatlng); + let top_y; + if (height === undefined || height < 0) { + top_y = this._toPoint(this._map.getBounds()._northEast).y; + } else { + top_y = this._point.y - height; + } + return new L.Point(this._point.x, top_y); + }, + + onAdd: function(map) { + this._map = map; + + // Call leaflet original method to add the Marker to the map. + originalOnAdd.call(this, map); + + // Keep original latitude and longitude + this._origLatlng = this.getLatLng(); + + if (this.options.bounceOnAdd === true) { + this.bounce(this.options.bounceOnAddOptions, this.options.bounceOnAddCallback); + } + }, + + onRemove: function(map) { + this.stopBounce(); + originalOnRemove.call(this, map); + }, + }); +})(); diff --git a/06 - Type Ahead/index-START.html b/06 - Type Ahead/index-START.html deleted file mode 100644 index 109c90fb36..0000000000 --- a/06 - Type Ahead/index-START.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Type Ahead 👀 - - - - -
- - -
- - - diff --git a/06 - Type Ahead/index.html b/06 - Type Ahead/index.html new file mode 100644 index 0000000000..45775feea4 --- /dev/null +++ b/06 - Type Ahead/index.html @@ -0,0 +1,183 @@ + + + + + + Boss Headquarters' Map 👀 + + + + + + + +
+ +
+ + +
+ + + + \ No newline at end of file diff --git a/06 - Type Ahead/marker-bos-icon-2x.png b/06 - Type Ahead/marker-bos-icon-2x.png new file mode 100644 index 0000000000..d77120ae6c Binary files /dev/null and b/06 - Type Ahead/marker-bos-icon-2x.png differ diff --git a/06 - Type Ahead/marker-bos-icon.png b/06 - Type Ahead/marker-bos-icon.png new file mode 100644 index 0000000000..8dae13c2e6 Binary files /dev/null and b/06 - Type Ahead/marker-bos-icon.png differ diff --git a/06 - Type Ahead/marker-user-location-2x.png b/06 - Type Ahead/marker-user-location-2x.png new file mode 100644 index 0000000000..1e9a3b5c5b Binary files /dev/null and b/06 - Type Ahead/marker-user-location-2x.png differ diff --git a/06 - Type Ahead/marker-user-location.png b/06 - Type Ahead/marker-user-location.png new file mode 100644 index 0000000000..2a3737cbef Binary files /dev/null and b/06 - Type Ahead/marker-user-location.png differ diff --git a/06 - Type Ahead/style.css b/06 - Type Ahead/style.css index 0c8c74e01b..4a736f665a 100644 --- a/06 - Type Ahead/style.css +++ b/06 - Type Ahead/style.css @@ -65,6 +65,11 @@ input.search { background: linear-gradient(to top, #ffffff 0%,#EFEFEF 100%); } +.suggestions li.highlight { +background: #C9CCD2; +font-weight: 400; +} + span.population { font-size: 15px; } @@ -72,3 +77,10 @@ span.population { .hl { background: #ffc600; } + +#mapid { + height: 50vh; + width: 95vw; + margin: 0 auto; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.24), inset 0 0 2px rgba(0, 0, 0, 0.38) +} \ No newline at end of file diff --git a/33 - Etch-a-Sketch/etch-a-sketch.js b/33 - Etch-a-Sketch/etch-a-sketch.js new file mode 100644 index 0000000000..5c5bd6071b --- /dev/null +++ b/33 - Etch-a-Sketch/etch-a-sketch.js @@ -0,0 +1,251 @@ +// Select the elements on the page - canvas, shake button +const canvas = document.querySelector('#etch-a-sketch'); +const ctx = canvas.getContext('2d'); +const shakebutton = document.querySelector('#shake'); +const MOVE_AMOUNT = 10; +const slider = document.querySelector('.slider'); +const background = document.querySelector('.wes-a-sketch'); +const rainbowButton = document.querySelector('.rainbow'); +let rainbowMode = false; +const CANVAS_BACKGROUND = '212, 217, 221'; +const DEFAULT_STROKE_COLOR = '#515054'; +const knobs = document.querySelectorAll('.knob'); + +// Setup our canvas for drawing +// Make variables called height and width from the same properties on our canavas +const { width, height } = canvas; + +drawWes(); // Draw Wes Bos + +// Create random x and y starting points on the canvas +let x = Math.floor(Math.random() * width); +let y = Math.floor(Math.random() * height); + +ctx.lineJoin = 'round'; +ctx.lineCap = 'round'; +ctx.lineWidth = MOVE_AMOUNT; + +let hue = 0; +ctx.strokeStyle = `${DEFAULT_STROKE_COLOR}`; +ctx.beginPath(); // Start the drawing +ctx.moveTo(x, y); +ctx.lineTo(x, y); +ctx.stroke(); + +// Draw on the canvas +function draw({ key }) { + setStrokeColor(); + + // Start the path + ctx.beginPath(); + ctx.moveTo(x, y); + // Move our x and y values depending on what the user did + switch (key) { + case 'ArrowUp': + y -= MOVE_AMOUNT; + rotateKnob('left', 'up'); + break; + case 'ArrowDown': + y += MOVE_AMOUNT; + rotateKnob('left', 'down'); + break; + case 'ArrowLeft': + x -= MOVE_AMOUNT; + rotateKnob('right', 'left'); + break; + case 'ArrowRight': + x += MOVE_AMOUNT; + rotateKnob('right', 'right'); + break; + default: + break; + } + + // Ensure line remains on the canvas + if (x > width) { + x = width; + } + else if (x < 0) { + x = 0; + } + if (y > height) { + y = height; + } + else if (y < 0) { + y = 0; + } + ctx.lineTo(x, y); + ctx.stroke(); +} + +// Draw on the canvas with the mouse cursor +function drawByCursor({x, y}) { + setStrokeColor(); + + // Start the path + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(x, y); + ctx.closePath(); + ctx.stroke(); +} + +function setStrokeColor() { + if (rainbowMode) { + // Increment the hue + hue += 10; + ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`; + } + else { + ctx.strokeStyle = DEFAULT_STROKE_COLOR; + } +} + +// Used to shrink the line dot after enlarging it with the slider +function vigorousStroke() { + ctx.stroke();ctx.stroke();ctx.stroke();ctx.stroke();ctx.stroke();ctx.stroke();ctx.stroke();ctx.stroke(); +} + +function handleSlider() { + let tmpStrokeColor = ctx.strokeStyle; + + if (slider.value < ctx.lineWidth) { + ctx.strokeStyle = `rgb(${CANVAS_BACKGROUND})`; + vigorousStroke(); + ctx.lineWidth = slider.value; + ctx.strokeStyle = tmpStrokeColor; + ctx.stroke(); + } + else { + ctx.lineWidth = slider.value; + ctx.stroke(); + } +} + +function handleRainbowButton() { + if (rainbowMode === false) { + rainbowMode = true; + rainbowButton.classList.add('on'); + } + else { + rainbowMode = false; + rainbowButton.classList.remove('on'); + } + +} + +// Handler for the keys +function handleKey(e) { + if (e.key.includes('Arrow')) { + e.preventDefault(); + draw({ key: e.key }); + } +} + +// Clear / Shake function +function clearCanvas() { + canvas.classList.add('shake'); + background.classList.add('shake'); + shakebutton.classList.add('shake'); + rainbowButton.classList.add('shake'); + slider.classList.add('shake'); + knobs.forEach(knob => knob.classList.add('shake')); + + // Gradually shake away the drawing. Takes 3-4 shakes. + ctx.fillStyle = `rgba(${CANVAS_BACKGROUND}, 0.8)` + ctx.rect(0, 0, width, height); + ctx.fill(); + + ctx.beginPath(); // Removes ctx.rect above from the path + + canvas.addEventListener( + 'animationend', + function() { + canvas.classList.remove('shake'); + background.classList.remove('shake'); + shakebutton.classList.remove('shake'); + rainbowButton.classList.remove('shake'); + slider.classList.remove('shake'); + knobs.forEach(knob => knob.classList.remove('shake')); + }, + { once: true } + ); +} + +// Rotate the knobs while drawing +function rotateKnob(side, direction) { + switch(direction) { + case 'right': + direction = 'rotateRight'; + break; + case 'left': + direction = 'rotateLeft'; + break; + case 'up': + direction = 'rotateRight'; + break; + case 'down': + direction = 'rotateLeft'; + break; + default: + break; + } + + if (side === 'left') { + side = document.querySelector('.left-knob'); + } + else { + side = document.querySelector('.right-knob'); + } + + side.classList.add(direction); + + side.addEventListener( + 'animationend', + function() { + side.classList.remove(direction); + }, + { once: true } + ); +} + +function drawWes() { + const image = new Image(800, 800); // Stretching a 400x file out + image.src = 'wes-a-sketch.png'; // to 800x800 + + const dx = width / 2 - image.width / 2; + const dy = height / 2 - image.height / 2; + + // Draw when image has loaded + image.onload = () => ctx.drawImage(image, dx, dy, image.width, image.height); +} + +function getMousePos(canvas, e) { + var rect = canvas.getBoundingClientRect(), // abs. size of element + scaleX = canvas.width / rect.width, // relationship bitmap vs. element for X + scaleY = canvas.height / rect.height; // relationship bitmap vs. element for Y + + return { + x: (e.clientX - rect.left) * scaleX, // scale mouse coordinates after they have + y: (e.clientY - rect.top) * scaleY // been adjusted to be relative to element + } +} + +function handleMouseMovement(e) { + var mousePos = getMousePos(canvas, e); + drawByCursor(mousePos); +} + + +// Listen for arrow keys +window.addEventListener('keydown', handleKey); +shakebutton.addEventListener('click', clearCanvas); + +// Listen for changes to the line width slider +slider.addEventListener('input', handleSlider); + +// Listen for click on rainbow button +rainbowButton.addEventListener('click', handleRainbowButton); + +// Listen for mouse movement on canvas +canvas.addEventListener('mousemove', handleMouseMovement); \ No newline at end of file diff --git a/33 - Etch-a-Sketch/favicon.png b/33 - Etch-a-Sketch/favicon.png new file mode 100644 index 0000000000..13db046968 Binary files /dev/null and b/33 - Etch-a-Sketch/favicon.png differ diff --git a/33 - Etch-a-Sketch/index.html b/33 - Etch-a-Sketch/index.html new file mode 100644 index 0000000000..6df699b21b --- /dev/null +++ b/33 - Etch-a-Sketch/index.html @@ -0,0 +1,35 @@ + + + + + + + Wes-a-Sketch + + + + + +
+

Magic Wes-a-Sketch Screen

+ + +
+
+
+
+ + + +
+
+
+ +

Modified by Aaron Stroud. Original design + from + Wes Bos' Beginner JavaScript + course.

+ + + + \ No newline at end of file diff --git a/33 - Etch-a-Sketch/main.css b/33 - Etch-a-Sketch/main.css new file mode 100644 index 0000000000..3d7ab624ba --- /dev/null +++ b/33 - Etch-a-Sketch/main.css @@ -0,0 +1,276 @@ +/* normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */button,hr,input{overflow:visible}progress,sub,sup{vertical-align:baseline}[type=checkbox],[type=radio],legend{box-sizing:border-box;padding:0}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}details,main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{color:inherit;display:table;max-width:100%;white-space:normal}textarea{overflow:auto}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}[hidden],template{display:none} + +/* Variables */ +html { + --grey: #e7e7e7; + --gray: var(--grey); + --blue: #0072B9; + --pink: #D60087; + --yellow: #ffc600; + --black: #2e2e2e; + --red: #c73737; + --green: #61e846; + --text-shadow: 2px 2px 0 rgba(0,0,0,0.2); + --box-shadow: 0 0 5px 5px rgba(0,0,0,0.2); + font-size: 62.5%; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + box-sizing: border-box; +} + +*, *:before, *:after { + box-sizing: inherit; +} + +body { + font-size: 2rem; + line-height: 1.5; + background-color: var(--blue); + background-image: url("data:image/svg+xml,%3Csvg width='20' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 21.184c.13.357.264.72.402 1.088l.661 1.768C4.653 33.64 6 39.647 6 50c0 10.271-1.222 15.362-4.928 24.629-.383.955-.74 1.869-1.072 2.75v6.225c.73-2.51 1.691-5.139 2.928-8.233C6.722 65.888 8 60.562 8 50c0-10.626-1.397-16.855-5.063-26.66l-.662-1.767C1.352 19.098.601 16.913 0 14.85v6.335zm20 0C17.108 13.258 16 8.077 16 0h2c0 5.744.574 9.951 2 14.85v6.334zm0 56.195c-2.966 7.86-4 13.123-4 22.621h2c0-6.842.542-11.386 2-16.396v-6.225zM6 0c0 8.44 1.21 13.718 4.402 22.272l.661 1.768C14.653 33.64 16 39.647 16 50c0 10.271-1.222 15.362-4.928 24.629C7.278 84.112 6 89.438 6 100h2c0-10.271 1.222-15.362 4.928-24.629C16.722 65.888 18 60.562 18 50c0-10.626-1.397-16.855-5.063-26.66l-.662-1.767C9.16 13.223 8 8.163 8 0H6z' fill='%23fff' fill-rule='nonzero' fill-opacity='.1' opacity='.349'/%3E%3C/svg%3E%0A"); + background-size: 15px; +} + +a.button, button, input[type="button"] { + color: white; + background: #CF9F35; + padding: 0.5rem; + margin-right: 1rem; + border: 0; + border: 2px solid transparent; + border-radius: 5px; + text-decoration: none; + font-weight: 600; + font-size:2rem; +} + +/* End of base.css */ + +body { + min-height: 100vh; + display: grid; + align-items: center; + justify-items: center; +} + +h1 { + color: #CF9F35; + text-align: center; + font-size: 3vw; + text-shadow: 2px 2px #333; + padding-top: 0.2em; + margin-top: 0; +} + +h1 strong { + font-size: 4vw; + padding-left: 1em; + padding-right: 1em; +} + +p.credits { + color: #111; + text-align: center; + margin: 0 5% 0; +} + +p.credits a { + color: #111; + text-decoration-color: #A6C695; + text-decoration-style: wavy; +} + + +div.wes-a-sketch { + display: block; + margin: 3% 10% 5% 10%; + max-height: 90vh; + max-width: 800px; + background-color: #CF000F; + border-radius: 45px; + border: 1px #000 inset; +} + +div.buttons { + max-width: 40%; + padding-top: 3%; + padding-bottom: 2%; + margin: 0 auto; +} + +.on { + background: #cf9e35b5; +} + +canvas { + width: 90%; + max-height: 60vh; + margin: 0 5%; + background: rgb(212, 217, 221); + border-radius: 10px; + border: 3px #ccc ridge; + +} + +canvas.shake, +div.wes-a-sketch.shake, +div.knob.shake, +input.shake, +button.shake { + animation: shake 0.5s linear 1; +} + +@keyframes shake { + + 10%, + 90% { + transform: translate3d(-1px, 0, 0); + } + + 20%, + 80% { + transform: translate3d(2px, 0, 0); + } + + 30%, + 50%, + 70% { + transform: translate3d(-4px, 0, 0); + } + + 40%, + 60% { + transform: translate3d(4px, 0, 0); + } +} + +input:not([type="checkbox"]):not([type="radio"]), +textarea, +select { + padding: 0; + margin: 2rem; +} + +.slider { + /* Override default CSS styles */ + -webkit-appearance: none; + appearance: none; + float: right; + height: 7px; + background: #d3d3d3; + border-radius: 5px; + outline: none; + opacity: 0.7; + -webkit-transition: .2s; + transition: opacity .2s; +} + +.slider:hover { + opacity: 1; +} + +/* The slider handle (use -webkit- (Chrome, Opera, Safari, Edge) and -moz- (Firefox) to override default look) */ +.slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 25px; + height: 25px; + border-radius: 50%; + background: #CF9F35; + cursor: pointer; +} + +.slider::-moz-range-thumb { + width: 25px; + height: 25px; + border-radius: 50%; + background: #CF9F35; + cursor: pointer; +} + +.knob { + width: 90px; + height: 90px; + background-color: #fcfcfc; + border-radius: 90%; + border: 3px #AAA dotted; +} + +.left-knob { + float: left; + margin-left: -5px; +} + +.right-knob { + float: right; + margin-right: -5px; +} + +.rotateLeft { + animation: rotateLeft 100ms 1 linear; +} + +.rotateRight { + animation: rotateRight 100ms 1 linear; +} + +@keyframes rotateLeft { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(-5deg); + } +} + +@keyframes rotateRight { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(5deg); + } +} + +@media screen and (min-width: 1000px) { + h1 { + font-size: 1.5em; + } + h1 strong { + font-size: 1.6em; + } +} + +@media screen and (max-width: 400px) { + div.buttons { + max-width: 27%; + padding-top: 1%; + padding-bottom: 1%; + margin: 0 auto; + } + + a.button, button, input[type="button"] { + color: white; + background: #CF9F35; + padding: 0.1rem; + margin-right: 0; + font-weight: 300; + font-size:1.5rem; + } + + .slider { + height: 5px; + width: 50%; + } + + /* The slider handle (use -webkit- (Chrome, Opera, Safari, Edge) and -moz- (Firefox) to override default look) */ + .slider::-webkit-slider-thumb { + width: 5px; + height: 15px; + } + + .slider::-moz-range-thumb { + width: 5px; + height: 15px; + } +} \ No newline at end of file diff --git a/33 - Etch-a-Sketch/wes-a-sketch.png b/33 - Etch-a-Sketch/wes-a-sketch.png new file mode 100644 index 0000000000..f01454752e Binary files /dev/null and b/33 - Etch-a-Sketch/wes-a-sketch.png differ diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000000..c741881743 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-slate \ No newline at end of file diff --git a/readme.md b/readme.md index 70d76d28df..ad3bb3ab8d 100644 --- a/readme.md +++ b/readme.md @@ -1,73 +1,11 @@ -![](https://javascript30.com/images/JS3-social-share.png) +Wes-a-Sketch [code] - An interactive Etch-a-Sketch from Wes Bos' Beginner JavaScript course -# JavaScript30 +Wes Bos Soundboard [code] - A modified soundboard using Wes Bos sound clips. Let Wes bring a smile to your face 😄 -Starter Files + Completed solutions for the JavaScript 30 Day Challenge. +Boss Headquarters' Map 👀 [code] - Project 6 - Type Ahead 👀 + Leaflet.js map. +


-Grab the course at [https://JavaScript30.com](https://JavaScript30.com) +![](https://javascript30.com/images/JS3-social-share.png) -## Community #JavaScript30 Content - -Feel free to submit a PR adding a link to your own recaps, guides or reviews! - -* [Arjun Khode’s blog](http://thesagittariusme.blogspot.com/search/label/JS30) about summaries for each day, including fixed glitches, bugs and extra features -* [Nitish Dayal's Text Guides](https://github.com/nitishdayal/JavaScript30) are great for those who like reading over watching -* [Meredith Underell's](http://meredithunderell.com/tag/javascript30/) Quick Lessons Learned -* [Rowan Weismiller's](http://rowanweismiller.com/blog/javascript-30/) Recaps + Lessons Learned -* [Thorsten Frommen](https://tfrommen.de/tag/javascript-30/) shares how he solved the exercises before viewing the answers -* [Soyaine 写的中文指南](https://github.com/soyaine/JavaScript30)包含了过程记录和难点解释 -* [Ayo Isaiah's](https://freshman.tech/archive/#javascript30) Recaps and Lessons Learned -* [Adriana Rios](https://stpcollabr8nlstn.github.io/JavaScript30/) shares her alternative solutions -* [Michael Einsohn](http://30daysofjs.michaeleinsohn.com) publishes each challenge after watching the video once -* [Mike Ekkel](https://medium.com/@mike_ekkel/javascript-30-a-30-day-vanilla-js-challenge-6a733fc9f62c#.9frjtaje9) -* [Yusef Habib](https://github.com/yhabib/JavaScript30) lessons and tricks learned, and a [gh-page](https://yhabib.github.io/JavaScript30/) to see working all the mini-projects. -* [Amelie Yeh](https://github.com/amelieyeh/JS30) 30 lessons notes with things I've learned, and those important recaps. and directly view my demos [here](https://amelieyeh.github.io/JS30/) 🇹🇼😄 -* [Winar](https://github.com/winar-jin/JavaScript30-Challenge)的JavaScript30天挑战,记录练习过程,重难点和其他的解决方案。🎨 -* [Rayhatron](https://rayhatron.github.io/blog/) - walkthroughs, recaps and lessons learned. -* [Andrei Dobra](https://github.com/andreidbr/JS30) Full repo with lessons learned and a [gh-page](https://andreidbr.github.io/JS30/) with all the exercises. -* [从零到壹全栈部落](https://github.com/liyuechun/JavaScript30-liyuechun),春哥发起的从零到壹全栈部落,旨在带领大家一起学习,一起输出,文档化,代码化,中文视频化,全栈部落口号:输出是最好的学习方式。 -* [Usmaan Ali's](https://github.com/usyyy/javascript/blob/master/JavaScript30/analysis.md) summary of the technical skills learned from each project. He's also posting them as separate blog posts [here](https://medium.com/@usyyy) -* [Axel](https://github.com/afuh/js30)'s lessons learned and a [showcase](https://afuh.github.io/js30/) with the projects. -* [Chris](https://github.com/dwatow/JavaScript30) 中文實戰,目標描述、過程紀錄。 -* [Muhammad D. Ramadhan's](https://miayam.github.io) blog. He shamlesly mixed his personal life with 30 day JavaScript challenge so as to increase his learning retention. He also summarised the challenge on [one single page](https://miayam.github.io/js30). Do not read his blog! -* [Lee Keitel's Blog](https://blog.keitel.xyz/categories/javascript30/) includes summaries of each lesson, what I learned from each one, and my thoughts about the topic taught and using them in the future. -* [Dustin Hsiao](https://github.com/dustinhsiao21/Javascript30-dustin) 包含了各篇介紹、 效果Demo、各篇詳解及記錄過程,附上部分延伸閱讀及[gh-page](https://dustinhsiao21.github.io/Javascript30-dustin/)。 -* [GuaHsu](https://github.com/guahsu/JavaScript30) - 紀錄各篇練習過程與心得,並嘗試擴充部分練習,也做了一個包含全部練習的[介紹站](http://guahsu.io/JavaScript30/)🇹🇼 -* [Daniela](https://github.com/misslild)'s completed challenges on [GitHub Pages](https://misslild.github.io/WesBos-30day-Coding-challenge/) and [Codepen](https://codepen.io/collection/DapZeP/) :raised_hands: :muscle: :+1: -* [Dmitrii Pashutskii's](https://github.com/guar47) code of all challenges on [GitHub with Pages](https://github.com/guar47/javascript30Summary) and review [blog posts](https://blog.dpashutskii.com/tag/javascript30/) -* [Abid Hasan's](https://github.com/sabidhasan/javascript-30) completion of all challenges. This was awesome!! Learned so much! :+1: -* [Yusong Notes](https://sky172839465.github.io/course/js30) Records Yusong JS 30 days note and demo :star2: -* [Ding's Implementation](https://github.com/Ding-Fan/javascript30) code and online demo -* [Herminio Torres](https://github.com/herminiotorres/JavaScript30) lessons and tricks learned, and a [gh-page](https://herminiotorres.github.io/JavaScript30/) to see working all the mini-projects. -* [Dmytro Borysovskyi](https://github.com/dimabory) says many thanks to for the course to Wes 🤝 It was incredible challenge 👌 The full repository with code available [here](https://github.com/dimabory/dimabory.github.io/tree/gh-pages/src/components/JavaScript30Days) and demos can be reached by the link to [gh-pages](https://dimabory.github.io/#/js30days) 👍👍👍 -* [Kizito](https://github.com/akhilome/)'s follow along [repo](https://github.com/akhilome/js30) with [completed challenges](https://akhilome.github.io/js30) and [notes](https://akhilome.github.io/js30/notes). -* [VannTile](https://github.com/vanntile)'s [repository](https://github.com/vanntile/JavaScript30) and [GitHub Pages showcase](https://vanntile.github.io/JavaScript30/). Thank you for a great ⌨️ experience. -* [Alex Kim](https://github.com/Alex-K1m/js30-challenge) completed all the challenges. You can check them out at [github pages](https://alex-k1m.github.io/js30-challenge/). -* [Mikhail Thomas](https://github.com/seckela) created [JS30++](https://github.com/seckela/js30plusplus) to add another level of challenge ontop of this already great course. -* [Ramon Morcillo](https://github.com/reymon359/JavaScript30) finished this awesome challenge!. You can see the showcase of his implementations on [this link](https://reymon359.github.io/JavaScript30/). -* [Santiago Escobar](https://github.com/sescobar99)'s [repository](https://github.com/sescobar99/javascript30-challenge) and [GitHub Pages showcase](https://sescobar99.github.io/javascript30-challenge/). -* [Harry Xie](https://github.com/a90100/JavaScript30) 紀錄 30 天的練習筆記在 [此連結](https://github.com/a90100/JavaScript30). -* [ Van Ribeiro's ](https://vanribeiro-30daysofjavascript.netlify.app/) about demos and recaps. On [GitHub Repo](https://github.com/vanribeiro/30days-Of-JavaScript) there's a summary about what was learned and researched. -* [Mugilan](https://github.com/Mugilan-Codes) is currently doing this challenge. Check out his [Repo](https://github.com/Mugilan-Codes/javascript-30) and the [Live Demo](https://mugilan-codes.github.io/javascript-30/). -* [Eshan Vohra] (https://github.com/eshanvohra) is currently doing this challenge. Check out my repo at(https://github.com/eshanvohra/JavaScript30) - - -## Alternative Implementations -Here are some links of people who have done the tutorials, but in a different language or framework: - -* [Thomas Mattacchione](https://github.com/tkjone/clojurescript-30) JavaScript 30 written in ClojureScript -* [Dave Follett's](https://github.com/davefollett) blog series, [A New Vue on #JavaScript30](https://davefollett.io/categories/a-new-vue-on-javascript30/), where he explores re-implementing #JavaScript30 projects using [Vue](https://vuejs.org). -* [Akinjide Bankole](https://github.com/akinjide/JS30days) used Node.js with [Jade](http://jadelang.net) to solve the exercises -* [Adrien Poly](https://github.com/adrienpoly/javascript30-stimulus) a modest attempt to convert Drum Kit, Video Player, Local Tapas, TypeHead to [Stimulus JS](https://stimulusjs.org/) framework in a Rails App. -* [Bogdan Lazar](https://github.com/tricinel/TypeScript30) all the JavaScript 30 written in [TypeScript](https://www.typescriptlang.org/) -* [Will Wager](https://github.com/wwags33/JavaScript30) another [TypeScript](https://www.typescriptlang.org/) implementation! -* [marcoSven](https://github.com/marcoSven) solution suggestion for [10 - Hold Shift and Check Checkboxes](https://github.com/marcoSven/JavaScript30/blob/master/10%20-%20Hold%20Shift%20and%20Check%20Checkboxes/index-FINISHED.html) -* [ALMaclaine](https://github.com/almaclaine) Javascript 30 written in [Dart 2.0](https://github.com/ALMaclaine/Dart30). - -## A note on Pull Requests - -These are meant to be 1:1 copies of what is done in the video. If you found a better / different way to do things, great, but I will be keeping them the same as the videos. - -The starter files + solutions will be updated if/when the videos are updated. - -Thanks! +Grab the course at [https://JavaScript30.com](https://JavaScript30.com) \ No newline at end of file