From b1a71607276e8a65af00553f51523b5e93eba912 Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Thu, 19 May 2022 17:00:11 +0200 Subject: [PATCH 01/15] isometric-menu-hover-effect --- index.html | 21 +++++++++++++ style.css | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 index.html create mode 100644 style.css diff --git a/index.html b/index.html new file mode 100644 index 0000000..452e6e3 --- /dev/null +++ b/index.html @@ -0,0 +1,21 @@ + + + + + + + CSS Isometric Menu + + + + + + + \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..e92c1d8 --- /dev/null +++ b/style.css @@ -0,0 +1,87 @@ +@import url('https://fonts.googleapis.com/css?family=Oswald:400,700'); + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Oswald', sans-serif; +} + +body { + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + background: #434750; +} + +ul { + position: relative; + transform: skewY(-15deg); +} + +ul li { + position: relative; + list-style: none; + width: 200px; + background: #3e3f46; + padding: 15px; + z-index: var(--i); + transition: 0.5s; +} + +ul li:hover { + background: #33a3ee; + transform: translateX(-50px); +} + +ul li::before { + content: ''; + position: absolute; + top: 0; + left: -40px; + width: 40px; + height: 100%; + background: #2e3133; + transform-origin: right; + transform: skewY(45deg); + transition: 0.5s; +} + +ul li:hover::before { + background: #1f5378; +} + +ul li::after { + content: ''; + position: absolute; + top: -40px; + left: 0; + height: 40px; + width: 100%; + background: #35383e; + transform-origin: bottom; + transform: skewX(45deg); + transition: 0.5s; +} + +ul li:hover::after { + background: #2982b9; +} + +ul li a { + text-decoration: none; + color: #999; + display: block; + text-transform: uppercase; + letter-spacing: 0.05em; + transition: 0.5s; +} + +ul li:hover a { + color: #fff; +} + +ul li:last-child::after { + box-shadow: -120px 120px 20px rgba(0, 0, 0, 0.25); +} \ No newline at end of file From 52226f3bb1ea20df873dc5dae3702b3a7783d965 Mon Sep 17 00:00:00 2001 From: Filippo Erbisti <80033304+filippoerbisti@users.noreply.github.com> Date: Thu, 19 May 2022 17:43:36 +0200 Subject: [PATCH 02/15] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 85fcb99..31d6357 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# css_effect \ No newline at end of file +# Isometric Menu Hover Effect + +View and try [DEMO](https://codepen.io/filippoerbisti/pen/BaYRMrW) on Codepen. From a71d0b190368f935bd7cf5d0849440501ff226c9 Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Tue, 24 May 2022 10:50:04 +0200 Subject: [PATCH 03/15] arrow-menu-indicator --- index.html | 31 +++++++++++++++----- style.css | 86 +++++++++++++++++++++--------------------------------- 2 files changed, 57 insertions(+), 60 deletions(-) diff --git a/index.html b/index.html index 452e6e3..f7c3f5d 100644 --- a/index.html +++ b/index.html @@ -4,18 +4,35 @@ - CSS Isometric Menu + Arrow Menu Indicator + + \ No newline at end of file diff --git a/style.css b/style.css index e92c1d8..4ab74a9 100644 --- a/style.css +++ b/style.css @@ -1,10 +1,10 @@ -@import url('https://fonts.googleapis.com/css?family=Oswald:400,700'); +@import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;600;700&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; - font-family: 'Oswald', sans-serif; + font-family: 'Ubuntu', sans-serif; } body { @@ -12,76 +12,56 @@ body { justify-content: center; align-items: center; min-height: 100vh; - background: #434750; + background: #333; } ul { position: relative; - transform: skewY(-15deg); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; } ul li { - position: relative; list-style: none; - width: 200px; - background: #3e3f46; - padding: 15px; - z-index: var(--i); - transition: 0.5s; } -ul li:hover { - background: #33a3ee; - transform: translateX(-50px); +ul li a { + position: relative; + font-size: 2em; + color: #fff; + text-decoration: none; + margin: 10px 0; + display: inline-block; } -ul li::before { - content: ''; +#marker { position: absolute; top: 0; - left: -40px; - width: 40px; - height: 100%; - background: #2e3133; - transform-origin: right; - transform: skewY(45deg); + margin-top: -5px; + height: 50px; transition: 0.5s; + display: flex; + justify-content: space-between; + align-items: center; } -ul li:hover::before { - background: #1f5378; -} - -ul li::after { +#marker::before { content: ''; - position: absolute; - top: -40px; - left: 0; - height: 40px; - width: 100%; - background: #35383e; - transform-origin: bottom; - transform: skewX(45deg); - transition: 0.5s; -} - -ul li:hover::after { - background: #2982b9; -} - -ul li a { - text-decoration: none; - color: #999; - display: block; - text-transform: uppercase; - letter-spacing: 0.05em; - transition: 0.5s; + width: 15px; + height: 15px; + border-right: 4px solid #30a3ff; + border-top: 4px solid #30a3ff; + transform: rotate(45deg) translate(-20px, 20px); } -ul li:hover a { - color: #fff; +#marker::after { + content: ''; + width: 15px; + height: 15px; + border-right: 4px solid #30a3ff; + border-top: 4px solid #30a3ff; + transform: rotate(225deg) translate(-20px, 20px); } -ul li:last-child::after { - box-shadow: -120px 120px 20px rgba(0, 0, 0, 0.25); -} \ No newline at end of file From f81db4ab385e8e08021a64384c17dd41710ea112 Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Tue, 24 May 2022 10:52:03 +0200 Subject: [PATCH 04/15] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 31d6357..e26f44b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Isometric Menu Hover Effect +# Arrow Menu Indicator -View and try [DEMO](https://codepen.io/filippoerbisti/pen/BaYRMrW) on Codepen. +View and try [DEMO](https://codepen.io/filippoerbisti/pen/KKQXqrB)) on Codepen. From ea6ed478f6c665d7cc4af2fa4e57f3919302ca86 Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Tue, 24 May 2022 11:42:22 +0200 Subject: [PATCH 05/15] ed-text-cube-animation --- index.html | 53 ++++++++++++++++---------------- style.css | 88 ++++++++++++++++++++++++++++++------------------------ 2 files changed, 76 insertions(+), 65 deletions(-) diff --git a/index.html b/index.html index f7c3f5d..4479ab5 100644 --- a/index.html +++ b/index.html @@ -4,35 +4,36 @@ - Arrow Menu Indicator + 3D Text Cube Animation Effects - - - +
+
+ 2 + 3 + 4 + 5 +
+
+ 0 + 1 + 2 + 3 +
+
+ 2 + 3 + 4 + 5 +
+
+ 2 + 3 + 4 + 5 +
+
\ No newline at end of file diff --git a/style.css b/style.css index 4ab74a9..5173b0f 100644 --- a/style.css +++ b/style.css @@ -1,10 +1,10 @@ -@import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;600;700&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@500;600;700&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; - font-family: 'Ubuntu', sans-serif; + font-family: 'Oswald', sans-serif; } body { @@ -12,56 +12,66 @@ body { justify-content: center; align-items: center; min-height: 100vh; - background: #333; + background: #3d3d3d; } -ul { - position: relative; +.container { display: flex; - flex-direction: column; - align-items: center; - justify-content: center; + transform-style: preserve-3d; + gap: 10px; + transform: rotateY(30deg) rotateX(10deg); } -ul li { - list-style: none; +.container .text { + position: relative; + width: 100px; + height: 100px; + transform-style: preserve-3d; + transition: 2.5s ease-in-out; + transition-delay: calc(0.25s * var(--j)); } -ul li a { - position: relative; - font-size: 2em; - color: #fff; - text-decoration: none; - margin: 10px 0; - display: inline-block; +.container:hover .text { + transform: rotateX(360deg); + + } -#marker { - position: absolute; - top: 0; - margin-top: -5px; - height: 50px; - transition: 0.5s; - display: flex; - justify-content: space-between; - align-items: center; +.container:hover .text:last-child { + transform: rotateX(630deg); } -#marker::before { +.container .text::before { content: ''; - width: 15px; - height: 15px; - border-right: 4px solid #30a3ff; - border-top: 4px solid #30a3ff; - transform: rotate(45deg) translate(-20px, 20px); + position: absolute; + width: 100%; + height: 100%; + background: #373737; + transform-origin: left; + transform: rotateY(90deg) translateX(-50px); } -#marker::after { - content: ''; - width: 15px; - height: 15px; - border-right: 4px solid #30a3ff; - border-top: 4px solid #30a3ff; - transform: rotate(225deg) translate(-20px, 20px); +.container .text:last-child:before { + background: #29ab3c; } +.container .text span { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(#434343, #535353); + display: flex; + align-items: center; + justify-content: center; + font-size: 4em; + color: #fff; + transform-style: preserve-3d; + transform: rotateX(calc(90deg * var(--i))) translateZ(50px); +} + +.container .text:last-child span { + background: linear-gradient(#29c040, #32ed4e); + color: #333; +} From 79e9d6ec08d9444320e048355929d62deeb5f464 Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Tue, 24 May 2022 11:43:43 +0200 Subject: [PATCH 06/15] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e26f44b..45a812f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Arrow Menu Indicator +# 3D Text Cube Animation Effects -View and try [DEMO](https://codepen.io/filippoerbisti/pen/KKQXqrB)) on Codepen. +View and try [DEMO](https://codepen.io/filippoerbisti/pen/PoQJKyd) on Codepen. From 6704f72c55507cf14a89952c35386a08e35c3077 Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Tue, 24 May 2022 14:48:17 +0200 Subject: [PATCH 07/15] wavy-text-animation --- index.html | 38 ++++++++----------------- style.css | 83 ++++++++++++++++-------------------------------------- 2 files changed, 36 insertions(+), 85 deletions(-) diff --git a/index.html b/index.html index 4479ab5..9ecf886 100644 --- a/index.html +++ b/index.html @@ -4,35 +4,21 @@ - 3D Text Cube Animation Effects + Wavy Text Animation Effects -
-
- 2 - 3 - 4 - 5 -
-
- 0 - 1 - 2 - 3 -
-
- 2 - 3 - 4 - 5 -
-
- 2 - 3 - 4 - 5 -
+
+ L + o + a + d + i + n + g + . + . + .
diff --git a/style.css b/style.css index 5173b0f..1047a29 100644 --- a/style.css +++ b/style.css @@ -1,10 +1,9 @@ -@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@500;600;700&display=swap'); +@import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700,800,900&display=swap'); * { margin: 0; padding: 0; - box-sizing: border-box; - font-family: 'Oswald', sans-serif; + font-family: consolas; } body { @@ -12,66 +11,32 @@ body { justify-content: center; align-items: center; min-height: 100vh; - background: #3d3d3d; + background: #000; } -.container { - display: flex; - transform-style: preserve-3d; - gap: 10px; - transform: rotateY(30deg) rotateX(10deg); -} - -.container .text { +.wavy { position: relative; - width: 100px; - height: 100px; - transform-style: preserve-3d; - transition: 2.5s ease-in-out; - transition-delay: calc(0.25s * var(--j)); -} - -.container:hover .text { - transform: rotateX(360deg); - - + -webkit-box-reflect: below -12px linear-gradient(transparent, rgba(0,0,0,0.2)); } -.container:hover .text:last-child { - transform: rotateX(630deg); -} - -.container .text::before { - content: ''; - position: absolute; - width: 100%; - height: 100%; - background: #373737; - transform-origin: left; - transform: rotateY(90deg) translateX(-50px); -} - -.container .text:last-child:before { - background: #29ab3c; -} - -.container .text span { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient(#434343, #535353); - display: flex; - align-items: center; - justify-content: center; - font-size: 4em; +.wavy span { + position: relative; + display: inline-block; color: #fff; - transform-style: preserve-3d; - transform: rotateX(calc(90deg * var(--i))) translateZ(50px); -} - -.container .text:last-child span { - background: linear-gradient(#29c040, #32ed4e); - color: #333; + font-size: 2em; + text-transform: uppercase; + animation: animate 1s ease-in-out infinite; + animation-delay: calc(0.1s * var(--i)); +} + +@keyframes animate { + 0% { + transform: translateY(0px); + } + 20% { + transform: translateY(-20px); + } + 40%, 100% { + transform: translateY(0px); + } } From 4891d678d123ccf8e7468a31fe46952e73dee883 Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Tue, 24 May 2022 14:50:09 +0200 Subject: [PATCH 08/15] wavy-text-animation --- style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style.css b/style.css index 1047a29..14e53b2 100644 --- a/style.css +++ b/style.css @@ -16,7 +16,7 @@ body { .wavy { position: relative; - -webkit-box-reflect: below -12px linear-gradient(transparent, rgba(0,0,0,0.2)); + -webkit-box-reflect: below -12px linear-gradient(transparent, rgba(0,0,0,0.2)); /* This effect not work on Firefox */ } .wavy span { From af031d3d4263a7694ec88c341906d93d39adef0c Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Tue, 24 May 2022 14:50:25 +0200 Subject: [PATCH 09/15] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 45a812f..45135f7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# 3D Text Cube Animation Effects +# Wavy Text Animation Effects -View and try [DEMO](https://codepen.io/filippoerbisti/pen/PoQJKyd) on Codepen. +View and try [DEMO](https://codepen.io/filippoerbisti/pen/YzereVJ) on Codepen. From e1a6e4590581e79a65ba8e841c1110186e55070c Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Tue, 24 May 2022 15:07:52 +0200 Subject: [PATCH 10/15] tower-block-game --- game.js | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 33 +++--- style.css | 144 ++++++++++++++++++++------ 3 files changed, 431 insertions(+), 45 deletions(-) create mode 100644 game.js diff --git a/game.js b/game.js new file mode 100644 index 0000000..c9df8ce --- /dev/null +++ b/game.js @@ -0,0 +1,299 @@ +"use strict"; + +console.clear(); + +class Stage { + constructor() { + // container + this.render = function () { + this.renderer.render(this.scene, this.camera); + }; + this.add = function (elem) { + this.scene.add(elem); + }; + this.remove = function (elem) { + this.scene.remove(elem); + }; + this.container = document.getElementById('game'); + // renderer + this.renderer = new THREE.WebGLRenderer({ + antialias: true, + alpha: false + }); + this.renderer.setSize(window.innerWidth, window.innerHeight); + this.renderer.setClearColor('#D0CBC7', 1); + this.container.appendChild(this.renderer.domElement); + // scene + this.scene = new THREE.Scene(); + // camera + let aspect = window.innerWidth / window.innerHeight; + let d = 20; + this.camera = new THREE.OrthographicCamera(-d * aspect, d * aspect, d, -d, -100, 1000); + this.camera.position.x = 2; + this.camera.position.y = 2; + this.camera.position.z = 2; + this.camera.lookAt(new THREE.Vector3(0, 0, 0)); + //light + this.light = new THREE.DirectionalLight(0xffffff, 0.5); + this.light.position.set(0, 499, 0); + this.scene.add(this.light); + this.softLight = new THREE.AmbientLight(0xffffff, 0.4); + this.scene.add(this.softLight); + window.addEventListener('resize', () => this.onResize()); + this.onResize(); + } + setCamera(y, speed = 0.3) { + TweenLite.to(this.camera.position, speed, { y: y + 4, ease: Power1.easeInOut }); + TweenLite.to(this.camera.lookAt, speed, { y: y, ease: Power1.easeInOut }); + } + onResize() { + let viewSize = 30; + this.renderer.setSize(window.innerWidth, window.innerHeight); + this.camera.left = window.innerWidth / -viewSize; + this.camera.right = window.innerWidth / viewSize; + this.camera.top = window.innerHeight / viewSize; + this.camera.bottom = window.innerHeight / -viewSize; + this.camera.updateProjectionMatrix(); + } +} +class Block { + constructor(block) { + // set size and position + this.STATES = { ACTIVE: 'active', STOPPED: 'stopped', MISSED: 'missed' }; + this.MOVE_AMOUNT = 12; + this.dimension = { width: 0, height: 0, depth: 0 }; + this.position = { x: 0, y: 0, z: 0 }; + this.targetBlock = block; + this.index = (this.targetBlock ? this.targetBlock.index : 0) + 1; + this.workingPlane = this.index % 2 ? 'x' : 'z'; + this.workingDimension = this.index % 2 ? 'width' : 'depth'; + // set the dimensions from the target block, or defaults. + this.dimension.width = this.targetBlock ? this.targetBlock.dimension.width : 10; + this.dimension.height = this.targetBlock ? this.targetBlock.dimension.height : 2; + this.dimension.depth = this.targetBlock ? this.targetBlock.dimension.depth : 10; + this.position.x = this.targetBlock ? this.targetBlock.position.x : 0; + this.position.y = this.dimension.height * this.index; + this.position.z = this.targetBlock ? this.targetBlock.position.z : 0; + this.colorOffset = this.targetBlock ? this.targetBlock.colorOffset : Math.round(Math.random() * 100); + // set color + if (!this.targetBlock) { + this.color = 0x333344; + } + else { + let offset = this.index + this.colorOffset; + var r = Math.sin(0.3 * offset) * 55 + 200; + var g = Math.sin(0.3 * offset + 2) * 55 + 200; + var b = Math.sin(0.3 * offset + 4) * 55 + 200; + this.color = new THREE.Color(r / 255, g / 255, b / 255); + } + // state + this.state = this.index > 1 ? this.STATES.ACTIVE : this.STATES.STOPPED; + // set direction + this.speed = -0.1 - (this.index * 0.005); + if (this.speed < -4) + this.speed = -4; + this.direction = this.speed; + // create block + let geometry = new THREE.BoxGeometry(this.dimension.width, this.dimension.height, this.dimension.depth); + geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(this.dimension.width / 2, this.dimension.height / 2, this.dimension.depth / 2)); + this.material = new THREE.MeshToonMaterial({ color: this.color, shading: THREE.FlatShading }); + this.mesh = new THREE.Mesh(geometry, this.material); + this.mesh.position.set(this.position.x, this.position.y + (this.state == this.STATES.ACTIVE ? 0 : 0), this.position.z); + if (this.state == this.STATES.ACTIVE) { + this.position[this.workingPlane] = Math.random() > 0.5 ? -this.MOVE_AMOUNT : this.MOVE_AMOUNT; + } + } + reverseDirection() { + this.direction = this.direction > 0 ? this.speed : Math.abs(this.speed); + } + place() { + this.state = this.STATES.STOPPED; + let overlap = this.targetBlock.dimension[this.workingDimension] - Math.abs(this.position[this.workingPlane] - this.targetBlock.position[this.workingPlane]); + let blocksToReturn = { + plane: this.workingPlane, + direction: this.direction + }; + if (this.dimension[this.workingDimension] - overlap < 0.3) { + overlap = this.dimension[this.workingDimension]; + blocksToReturn.bonus = true; + this.position.x = this.targetBlock.position.x; + this.position.z = this.targetBlock.position.z; + this.dimension.width = this.targetBlock.dimension.width; + this.dimension.depth = this.targetBlock.dimension.depth; + } + if (overlap > 0) { + let choppedDimensions = { width: this.dimension.width, height: this.dimension.height, depth: this.dimension.depth }; + choppedDimensions[this.workingDimension] -= overlap; + this.dimension[this.workingDimension] = overlap; + let placedGeometry = new THREE.BoxGeometry(this.dimension.width, this.dimension.height, this.dimension.depth); + placedGeometry.applyMatrix4(new THREE.Matrix4().makeTranslation(this.dimension.width / 2, this.dimension.height / 2, this.dimension.depth / 2)); + let placedMesh = new THREE.Mesh(placedGeometry, this.material); + let choppedGeometry = new THREE.BoxGeometry(choppedDimensions.width, choppedDimensions.height, choppedDimensions.depth); + choppedGeometry.applyMatrix4(new THREE.Matrix4().makeTranslation(choppedDimensions.width / 2, choppedDimensions.height / 2, choppedDimensions.depth / 2)); + let choppedMesh = new THREE.Mesh(choppedGeometry, this.material); + let choppedPosition = { + x: this.position.x, + y: this.position.y, + z: this.position.z + }; + if (this.position[this.workingPlane] < this.targetBlock.position[this.workingPlane]) { + this.position[this.workingPlane] = this.targetBlock.position[this.workingPlane]; + } + else { + choppedPosition[this.workingPlane] += overlap; + } + placedMesh.position.set(this.position.x, this.position.y, this.position.z); + choppedMesh.position.set(choppedPosition.x, choppedPosition.y, choppedPosition.z); + blocksToReturn.placed = placedMesh; + if (!blocksToReturn.bonus) + blocksToReturn.chopped = choppedMesh; + } + else { + this.state = this.STATES.MISSED; + } + this.dimension[this.workingDimension] = overlap; + return blocksToReturn; + } + tick() { + if (this.state == this.STATES.ACTIVE) { + let value = this.position[this.workingPlane]; + if (value > this.MOVE_AMOUNT || value < -this.MOVE_AMOUNT) + this.reverseDirection(); + this.position[this.workingPlane] += this.direction; + this.mesh.position[this.workingPlane] = this.position[this.workingPlane]; + } + } +} +class Game { + constructor() { + this.STATES = { + 'LOADING': 'loading', + 'PLAYING': 'playing', + 'READY': 'ready', + 'ENDED': 'ended', + 'RESETTING': 'resetting' + }; + this.blocks = []; + this.state = this.STATES.LOADING; + this.stage = new Stage(); + this.mainContainer = document.getElementById('container'); + this.scoreContainer = document.getElementById('score'); + this.startButton = document.getElementById('start-button'); + this.instructions = document.getElementById('instructions'); + this.scoreContainer.innerHTML = '0'; + this.newBlocks = new THREE.Group(); + this.placedBlocks = new THREE.Group(); + this.choppedBlocks = new THREE.Group(); + this.stage.add(this.newBlocks); + this.stage.add(this.placedBlocks); + this.stage.add(this.choppedBlocks); + this.addBlock(); + this.tick(); + this.updateState(this.STATES.READY); + document.addEventListener('keydown', e => { + if (e.keyCode == 32) + this.onAction(); + }); + document.addEventListener('click', e => { + this.onAction(); + }); + document.addEventListener('touchstart', e => { + e.preventDefault(); + // this.onAction(); + // ☝️ this triggers after click on android so you insta-lose. + }); + } + updateState(newState) { + for (let key in this.STATES) + this.mainContainer.classList.remove(this.STATES[key]); + this.mainContainer.classList.add(newState); + this.state = newState; + } + onAction() { + switch (this.state) { + case this.STATES.READY: + this.startGame(); + break; + case this.STATES.PLAYING: + this.placeBlock(); + break; + case this.STATES.ENDED: + this.restartGame(); + break; + } + } + startGame() { + if (this.state != this.STATES.PLAYING) { + this.scoreContainer.innerHTML = '0'; + this.updateState(this.STATES.PLAYING); + this.addBlock(); + } + } + restartGame() { + this.updateState(this.STATES.RESETTING); + let oldBlocks = this.placedBlocks.children; + let removeSpeed = 0.2; + let delayAmount = 0.02; + for (let i = 0; i < oldBlocks.length; i++) { + TweenLite.to(oldBlocks[i].scale, removeSpeed, { x: 0, y: 0, z: 0, delay: (oldBlocks.length - i) * delayAmount, ease: Power1.easeIn, onComplete: () => this.placedBlocks.remove(oldBlocks[i]) }); + TweenLite.to(oldBlocks[i].rotation, removeSpeed, { y: 0.5, delay: (oldBlocks.length - i) * delayAmount, ease: Power1.easeIn }); + } + let cameraMoveSpeed = removeSpeed * 2 + (oldBlocks.length * delayAmount); + this.stage.setCamera(2, cameraMoveSpeed); + let countdown = { value: this.blocks.length - 1 }; + TweenLite.to(countdown, cameraMoveSpeed, { value: 0, onUpdate: () => { this.scoreContainer.innerHTML = String(Math.round(countdown.value)); } }); + this.blocks = this.blocks.slice(0, 1); + setTimeout(() => { + this.startGame(); + }, cameraMoveSpeed * 1000); + } + placeBlock() { + let currentBlock = this.blocks[this.blocks.length - 1]; + let newBlocks = currentBlock.place(); + this.newBlocks.remove(currentBlock.mesh); + if (newBlocks.placed) + this.placedBlocks.add(newBlocks.placed); + if (newBlocks.chopped) { + this.choppedBlocks.add(newBlocks.chopped); + let positionParams = { y: '-=30', ease: Power1.easeIn, onComplete: () => this.choppedBlocks.remove(newBlocks.chopped) }; + let rotateRandomness = 10; + let rotationParams = { + delay: 0.05, + x: newBlocks.plane == 'z' ? ((Math.random() * rotateRandomness) - (rotateRandomness / 2)) : 0.1, + z: newBlocks.plane == 'x' ? ((Math.random() * rotateRandomness) - (rotateRandomness / 2)) : 0.1, + y: Math.random() * 0.1, + }; + if (newBlocks.chopped.position[newBlocks.plane] > newBlocks.placed.position[newBlocks.plane]) { + positionParams[newBlocks.plane] = '+=' + (40 * Math.abs(newBlocks.direction)); + } + else { + positionParams[newBlocks.plane] = '-=' + (40 * Math.abs(newBlocks.direction)); + } + TweenLite.to(newBlocks.chopped.position, 1, positionParams); + TweenLite.to(newBlocks.chopped.rotation, 1, rotationParams); + } + this.addBlock(); + } + addBlock() { + let lastBlock = this.blocks[this.blocks.length - 1]; + if (lastBlock && lastBlock.state == lastBlock.STATES.MISSED) { + return this.endGame(); + } + this.scoreContainer.innerHTML = String(this.blocks.length - 1); + let newKidOnTheBlock = new Block(lastBlock); + this.newBlocks.add(newKidOnTheBlock.mesh); + this.blocks.push(newKidOnTheBlock); + this.stage.setCamera(this.blocks.length * 2); + if (this.blocks.length >= 5) + this.instructions.classList.add('hide'); + } + endGame() { + this.updateState(this.STATES.ENDED); + } + tick() { + this.blocks[this.blocks.length - 1].tick(); + this.stage.render(); + requestAnimationFrame(() => { this.tick(); }); + } +} +let game = new Game(); diff --git a/index.html b/index.html index 9ecf886..ed8ac09 100644 --- a/index.html +++ b/index.html @@ -4,22 +4,29 @@ - Wavy Text Animation Effects + Tower Block Game + + + -
- L - o - a - d - i - n - g - . - . - . -
+
+
+
0
+
Click (or press the spacebar) to place the block
+
+

Game Over

+

You did great, you're the best.

+

Click or spacebar to start again

+
+
+
Start
+
+
+
+ + \ No newline at end of file diff --git a/style.css b/style.css index 14e53b2..7894ede 100644 --- a/style.css +++ b/style.css @@ -1,42 +1,122 @@ -@import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700,800,900&display=swap'); +@import url("https://fonts.googleapis.com/css?family=Comfortaa"); -* { - margin: 0; - padding: 0; - font-family: consolas; +html, body { + margin: 0; + overflow: hidden; + height: 100%; + width: 100%; + position: relative; + font-family: "Comfortaa", cursive; } -body { - display: flex; - justify-content: center; - align-items: center; - min-height: 100vh; - background: #000; +#container { + width: 100%; + height: 100%; } -.wavy { - position: relative; - -webkit-box-reflect: below -12px linear-gradient(transparent, rgba(0,0,0,0.2)); /* This effect not work on Firefox */ +#container #score { + position: absolute; + top: 20px; + width: 100%; + text-align: center; + font-size: 10vh; + transition: transform 0.5s ease; + color: #333344; + transform: translatey(-200px) scale(1); } -.wavy span { - position: relative; - display: inline-block; - color: #fff; - font-size: 2em; - text-transform: uppercase; - animation: animate 1s ease-in-out infinite; - animation-delay: calc(0.1s * var(--i)); +#container #game { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; } -@keyframes animate { - 0% { - transform: translateY(0px); - } - 20% { - transform: translateY(-20px); - } - 40%, 100% { - transform: translateY(0px); - } +#container .game-over { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 85%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; } + +#container .game-over * { + transition: opacity 0.5s ease, transform 0.5s ease; + opacity: 0; + transform: translatey(-50px); + color: #333344; +} + +#container .game-over h2 { + margin: 0; + padding: 0; + font-size: 40px; +} + +#container .game-ready { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-around; +} + +#container .game-ready #start-button { + transition: opacity 0.5s ease, transform 0.5s ease; + opacity: 0; + transform: translatey(-50px); + border: 3px solid #333344; + padding: 10px 20px; + background-color: transparent; + color: #333344; + font-size: 30px; +} + +#container #instructions { + position: absolute; + width: 100%; + top: 16vh; + left: 0; + text-align: center; + transition: opacity 0.5s ease, transform 0.5s ease; + opacity: 0; +} + +#container #instructions.hide { + opacity: 0 !important; +} + +#container.playing #score, #container.resetting #score { + transform: translatey(0px) scale(1); +} + +#container.playing #instructions { + opacity: 1; +} + +#container.ready .game-ready #start-button { + opacity: 1; + transform: translatey(0); +} + +#container.ended #score { + transform: translatey(6vh) scale(1.5); +} + +#container.ended .game-over * { + opacity: 1; + transform: translatey(0); +} + +#container.ended .game-over p { + transition-delay: 0.3s; +} \ No newline at end of file From 936cade6ce88492d2eb004991beaad01959e7779 Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Tue, 24 May 2022 15:08:01 +0200 Subject: [PATCH 11/15] tower-block-game --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index ed8ac09..190bb2a 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - Tower Block Game + Tower Block Game CSS JS From b89279d578413773db8937aefefec4ca03be04b7 Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Tue, 24 May 2022 15:10:02 +0200 Subject: [PATCH 12/15] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 45135f7..e3dedb6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Wavy Text Animation Effects +# Tower Block Game CSS JS -View and try [DEMO](https://codepen.io/filippoerbisti/pen/YzereVJ) on Codepen. +View and try [DEMO](https://codepen.io/filippoerbisti/pen/GRQMQzK) on Codepen. From 64517bf60becf4f4981075753b558cfe7fdf9ed8 Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Tue, 24 May 2022 15:21:36 +0200 Subject: [PATCH 13/15] memory-game --- game.js | 474 ++++++++++++++++++++--------------------------------- index.html | 41 ++--- style.css | 365 +++++++++++++++++++++++++++-------------- 3 files changed, 442 insertions(+), 438 deletions(-) diff --git a/game.js b/game.js index c9df8ce..2e79bdb 100644 --- a/game.js +++ b/game.js @@ -1,299 +1,179 @@ -"use strict"; +(function(){ + + var Memory = { -console.clear(); + init: function(cards){ + this.$game = $(".game"); + this.$modal = $(".modal"); + this.$overlay = $(".modal-overlay"); + this.$restartButton = $("button.restart"); + this.cardsArray = $.merge(cards, cards); + this.shuffleCards(this.cardsArray); + this.setup(); + }, -class Stage { - constructor() { - // container - this.render = function () { - this.renderer.render(this.scene, this.camera); - }; - this.add = function (elem) { - this.scene.add(elem); - }; - this.remove = function (elem) { - this.scene.remove(elem); - }; - this.container = document.getElementById('game'); - // renderer - this.renderer = new THREE.WebGLRenderer({ - antialias: true, - alpha: false - }); - this.renderer.setSize(window.innerWidth, window.innerHeight); - this.renderer.setClearColor('#D0CBC7', 1); - this.container.appendChild(this.renderer.domElement); - // scene - this.scene = new THREE.Scene(); - // camera - let aspect = window.innerWidth / window.innerHeight; - let d = 20; - this.camera = new THREE.OrthographicCamera(-d * aspect, d * aspect, d, -d, -100, 1000); - this.camera.position.x = 2; - this.camera.position.y = 2; - this.camera.position.z = 2; - this.camera.lookAt(new THREE.Vector3(0, 0, 0)); - //light - this.light = new THREE.DirectionalLight(0xffffff, 0.5); - this.light.position.set(0, 499, 0); - this.scene.add(this.light); - this.softLight = new THREE.AmbientLight(0xffffff, 0.4); - this.scene.add(this.softLight); - window.addEventListener('resize', () => this.onResize()); - this.onResize(); - } - setCamera(y, speed = 0.3) { - TweenLite.to(this.camera.position, speed, { y: y + 4, ease: Power1.easeInOut }); - TweenLite.to(this.camera.lookAt, speed, { y: y, ease: Power1.easeInOut }); - } - onResize() { - let viewSize = 30; - this.renderer.setSize(window.innerWidth, window.innerHeight); - this.camera.left = window.innerWidth / -viewSize; - this.camera.right = window.innerWidth / viewSize; - this.camera.top = window.innerHeight / viewSize; - this.camera.bottom = window.innerHeight / -viewSize; - this.camera.updateProjectionMatrix(); - } -} -class Block { - constructor(block) { - // set size and position - this.STATES = { ACTIVE: 'active', STOPPED: 'stopped', MISSED: 'missed' }; - this.MOVE_AMOUNT = 12; - this.dimension = { width: 0, height: 0, depth: 0 }; - this.position = { x: 0, y: 0, z: 0 }; - this.targetBlock = block; - this.index = (this.targetBlock ? this.targetBlock.index : 0) + 1; - this.workingPlane = this.index % 2 ? 'x' : 'z'; - this.workingDimension = this.index % 2 ? 'width' : 'depth'; - // set the dimensions from the target block, or defaults. - this.dimension.width = this.targetBlock ? this.targetBlock.dimension.width : 10; - this.dimension.height = this.targetBlock ? this.targetBlock.dimension.height : 2; - this.dimension.depth = this.targetBlock ? this.targetBlock.dimension.depth : 10; - this.position.x = this.targetBlock ? this.targetBlock.position.x : 0; - this.position.y = this.dimension.height * this.index; - this.position.z = this.targetBlock ? this.targetBlock.position.z : 0; - this.colorOffset = this.targetBlock ? this.targetBlock.colorOffset : Math.round(Math.random() * 100); - // set color - if (!this.targetBlock) { - this.color = 0x333344; - } - else { - let offset = this.index + this.colorOffset; - var r = Math.sin(0.3 * offset) * 55 + 200; - var g = Math.sin(0.3 * offset + 2) * 55 + 200; - var b = Math.sin(0.3 * offset + 4) * 55 + 200; - this.color = new THREE.Color(r / 255, g / 255, b / 255); - } - // state - this.state = this.index > 1 ? this.STATES.ACTIVE : this.STATES.STOPPED; - // set direction - this.speed = -0.1 - (this.index * 0.005); - if (this.speed < -4) - this.speed = -4; - this.direction = this.speed; - // create block - let geometry = new THREE.BoxGeometry(this.dimension.width, this.dimension.height, this.dimension.depth); - geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(this.dimension.width / 2, this.dimension.height / 2, this.dimension.depth / 2)); - this.material = new THREE.MeshToonMaterial({ color: this.color, shading: THREE.FlatShading }); - this.mesh = new THREE.Mesh(geometry, this.material); - this.mesh.position.set(this.position.x, this.position.y + (this.state == this.STATES.ACTIVE ? 0 : 0), this.position.z); - if (this.state == this.STATES.ACTIVE) { - this.position[this.workingPlane] = Math.random() > 0.5 ? -this.MOVE_AMOUNT : this.MOVE_AMOUNT; - } - } - reverseDirection() { - this.direction = this.direction > 0 ? this.speed : Math.abs(this.speed); - } - place() { - this.state = this.STATES.STOPPED; - let overlap = this.targetBlock.dimension[this.workingDimension] - Math.abs(this.position[this.workingPlane] - this.targetBlock.position[this.workingPlane]); - let blocksToReturn = { - plane: this.workingPlane, - direction: this.direction - }; - if (this.dimension[this.workingDimension] - overlap < 0.3) { - overlap = this.dimension[this.workingDimension]; - blocksToReturn.bonus = true; - this.position.x = this.targetBlock.position.x; - this.position.z = this.targetBlock.position.z; - this.dimension.width = this.targetBlock.dimension.width; - this.dimension.depth = this.targetBlock.dimension.depth; - } - if (overlap > 0) { - let choppedDimensions = { width: this.dimension.width, height: this.dimension.height, depth: this.dimension.depth }; - choppedDimensions[this.workingDimension] -= overlap; - this.dimension[this.workingDimension] = overlap; - let placedGeometry = new THREE.BoxGeometry(this.dimension.width, this.dimension.height, this.dimension.depth); - placedGeometry.applyMatrix4(new THREE.Matrix4().makeTranslation(this.dimension.width / 2, this.dimension.height / 2, this.dimension.depth / 2)); - let placedMesh = new THREE.Mesh(placedGeometry, this.material); - let choppedGeometry = new THREE.BoxGeometry(choppedDimensions.width, choppedDimensions.height, choppedDimensions.depth); - choppedGeometry.applyMatrix4(new THREE.Matrix4().makeTranslation(choppedDimensions.width / 2, choppedDimensions.height / 2, choppedDimensions.depth / 2)); - let choppedMesh = new THREE.Mesh(choppedGeometry, this.material); - let choppedPosition = { - x: this.position.x, - y: this.position.y, - z: this.position.z - }; - if (this.position[this.workingPlane] < this.targetBlock.position[this.workingPlane]) { - this.position[this.workingPlane] = this.targetBlock.position[this.workingPlane]; - } - else { - choppedPosition[this.workingPlane] += overlap; - } - placedMesh.position.set(this.position.x, this.position.y, this.position.z); - choppedMesh.position.set(choppedPosition.x, choppedPosition.y, choppedPosition.z); - blocksToReturn.placed = placedMesh; - if (!blocksToReturn.bonus) - blocksToReturn.chopped = choppedMesh; - } - else { - this.state = this.STATES.MISSED; - } - this.dimension[this.workingDimension] = overlap; - return blocksToReturn; - } - tick() { - if (this.state == this.STATES.ACTIVE) { - let value = this.position[this.workingPlane]; - if (value > this.MOVE_AMOUNT || value < -this.MOVE_AMOUNT) - this.reverseDirection(); - this.position[this.workingPlane] += this.direction; - this.mesh.position[this.workingPlane] = this.position[this.workingPlane]; - } - } -} -class Game { - constructor() { - this.STATES = { - 'LOADING': 'loading', - 'PLAYING': 'playing', - 'READY': 'ready', - 'ENDED': 'ended', - 'RESETTING': 'resetting' - }; - this.blocks = []; - this.state = this.STATES.LOADING; - this.stage = new Stage(); - this.mainContainer = document.getElementById('container'); - this.scoreContainer = document.getElementById('score'); - this.startButton = document.getElementById('start-button'); - this.instructions = document.getElementById('instructions'); - this.scoreContainer.innerHTML = '0'; - this.newBlocks = new THREE.Group(); - this.placedBlocks = new THREE.Group(); - this.choppedBlocks = new THREE.Group(); - this.stage.add(this.newBlocks); - this.stage.add(this.placedBlocks); - this.stage.add(this.choppedBlocks); - this.addBlock(); - this.tick(); - this.updateState(this.STATES.READY); - document.addEventListener('keydown', e => { - if (e.keyCode == 32) - this.onAction(); - }); - document.addEventListener('click', e => { - this.onAction(); - }); - document.addEventListener('touchstart', e => { - e.preventDefault(); - // this.onAction(); - // ☝️ this triggers after click on android so you insta-lose. - }); - } - updateState(newState) { - for (let key in this.STATES) - this.mainContainer.classList.remove(this.STATES[key]); - this.mainContainer.classList.add(newState); - this.state = newState; - } - onAction() { - switch (this.state) { - case this.STATES.READY: - this.startGame(); - break; - case this.STATES.PLAYING: - this.placeBlock(); - break; - case this.STATES.ENDED: - this.restartGame(); - break; - } - } - startGame() { - if (this.state != this.STATES.PLAYING) { - this.scoreContainer.innerHTML = '0'; - this.updateState(this.STATES.PLAYING); - this.addBlock(); - } - } - restartGame() { - this.updateState(this.STATES.RESETTING); - let oldBlocks = this.placedBlocks.children; - let removeSpeed = 0.2; - let delayAmount = 0.02; - for (let i = 0; i < oldBlocks.length; i++) { - TweenLite.to(oldBlocks[i].scale, removeSpeed, { x: 0, y: 0, z: 0, delay: (oldBlocks.length - i) * delayAmount, ease: Power1.easeIn, onComplete: () => this.placedBlocks.remove(oldBlocks[i]) }); - TweenLite.to(oldBlocks[i].rotation, removeSpeed, { y: 0.5, delay: (oldBlocks.length - i) * delayAmount, ease: Power1.easeIn }); - } - let cameraMoveSpeed = removeSpeed * 2 + (oldBlocks.length * delayAmount); - this.stage.setCamera(2, cameraMoveSpeed); - let countdown = { value: this.blocks.length - 1 }; - TweenLite.to(countdown, cameraMoveSpeed, { value: 0, onUpdate: () => { this.scoreContainer.innerHTML = String(Math.round(countdown.value)); } }); - this.blocks = this.blocks.slice(0, 1); - setTimeout(() => { - this.startGame(); - }, cameraMoveSpeed * 1000); - } - placeBlock() { - let currentBlock = this.blocks[this.blocks.length - 1]; - let newBlocks = currentBlock.place(); - this.newBlocks.remove(currentBlock.mesh); - if (newBlocks.placed) - this.placedBlocks.add(newBlocks.placed); - if (newBlocks.chopped) { - this.choppedBlocks.add(newBlocks.chopped); - let positionParams = { y: '-=30', ease: Power1.easeIn, onComplete: () => this.choppedBlocks.remove(newBlocks.chopped) }; - let rotateRandomness = 10; - let rotationParams = { - delay: 0.05, - x: newBlocks.plane == 'z' ? ((Math.random() * rotateRandomness) - (rotateRandomness / 2)) : 0.1, - z: newBlocks.plane == 'x' ? ((Math.random() * rotateRandomness) - (rotateRandomness / 2)) : 0.1, - y: Math.random() * 0.1, - }; - if (newBlocks.chopped.position[newBlocks.plane] > newBlocks.placed.position[newBlocks.plane]) { - positionParams[newBlocks.plane] = '+=' + (40 * Math.abs(newBlocks.direction)); - } - else { - positionParams[newBlocks.plane] = '-=' + (40 * Math.abs(newBlocks.direction)); - } - TweenLite.to(newBlocks.chopped.position, 1, positionParams); - TweenLite.to(newBlocks.chopped.rotation, 1, rotationParams); - } - this.addBlock(); - } - addBlock() { - let lastBlock = this.blocks[this.blocks.length - 1]; - if (lastBlock && lastBlock.state == lastBlock.STATES.MISSED) { - return this.endGame(); - } - this.scoreContainer.innerHTML = String(this.blocks.length - 1); - let newKidOnTheBlock = new Block(lastBlock); - this.newBlocks.add(newKidOnTheBlock.mesh); - this.blocks.push(newKidOnTheBlock); - this.stage.setCamera(this.blocks.length * 2); - if (this.blocks.length >= 5) - this.instructions.classList.add('hide'); - } - endGame() { - this.updateState(this.STATES.ENDED); - } - tick() { - this.blocks[this.blocks.length - 1].tick(); - this.stage.render(); - requestAnimationFrame(() => { this.tick(); }); - } -} -let game = new Game(); + shuffleCards: function(cardsArray){ + this.$cards = $(this.shuffle(this.cardsArray)); + }, + + setup: function(){ + this.html = this.buildHTML(); + this.$game.html(this.html); + this.$memoryCards = $(".card"); + this.paused = false; + this.guess = null; + this.binding(); + }, + + binding: function(){ + this.$memoryCards.on("click", this.cardClicked); + this.$restartButton.on("click", $.proxy(this.reset, this)); + }, + // kinda messy but hey + cardClicked: function(){ + var _ = Memory; + var $card = $(this); + if(!_.paused && !$card.find(".inside").hasClass("matched") && !$card.find(".inside").hasClass("picked")){ + $card.find(".inside").addClass("picked"); + if(!_.guess){ + _.guess = $(this).attr("data-id"); + } else if(_.guess == $(this).attr("data-id") && !$(this).hasClass("picked")){ + $(".picked").addClass("matched"); + _.guess = null; + } else { + _.guess = null; + _.paused = true; + setTimeout(function(){ + $(".picked").removeClass("picked"); + Memory.paused = false; + }, 600); + } + if($(".matched").length == $(".card").length){ + _.win(); + } + } + }, + + win: function(){ + this.paused = true; + setTimeout(function(){ + Memory.showModal(); + Memory.$game.fadeOut(); + }, 1000); + }, + + showModal: function(){ + this.$overlay.show(); + this.$modal.fadeIn("slow"); + }, + + hideModal: function(){ + this.$overlay.hide(); + this.$modal.hide(); + }, + + reset: function(){ + this.hideModal(); + this.shuffleCards(this.cardsArray); + this.setup(); + this.$game.show("slow"); + }, + + // Fisher--Yates Algorithm -- https://bost.ocks.org/mike/shuffle/ + shuffle: function(array){ + var counter = array.length, temp, index; + // While there are elements in the array + while (counter > 0) { + // Pick a random index + index = Math.floor(Math.random() * counter); + // Decrease counter by 1 + counter--; + // And swap the last element with it + temp = array[counter]; + array[counter] = array[index]; + array[index] = temp; + } + return array; + }, + + buildHTML: function(){ + var frag = ''; + this.$cards.each(function(k, v){ + frag += '
\ +
'+ v.name +'
\ +
Codepen
\ +
'; + }); + return frag; + } + }; + + var cards = [ + { + name: "php", + img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/php-logo_1.png", + id: 1, + }, + { + name: "css3", + img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/css3-logo.png", + id: 2 + }, + { + name: "html5", + img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/html5-logo.png", + id: 3 + }, + { + name: "jquery", + img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/jquery-logo.png", + id: 4 + }, + { + name: "javascript", + img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/js-logo.png", + id: 5 + }, + { + name: "node", + img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/nodejs-logo.png", + id: 6 + }, + { + name: "photoshop", + img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/photoshop-logo.png", + id: 7 + }, + { + name: "python", + img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/python-logo.png", + id: 8 + }, + { + name: "rails", + img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/rails-logo.png", + id: 9 + }, + { + name: "sass", + img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/sass-logo.png", + id: 10 + }, + { + name: "sublime", + img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/sublime-logo.png", + id: 11 + }, + { + name: "wordpress", + img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/wordpress-logo.png", + id: 12 + }, + ]; + + Memory.init(cards); + + +})(); \ No newline at end of file diff --git a/index.html b/index.html index 190bb2a..6b6f7ee 100644 --- a/index.html +++ b/index.html @@ -4,29 +4,32 @@ - Tower Block Game CSS JS + Memory Game CSS JS - - - + -
-
-
0
-
Click (or press the spacebar) to place the block
-
-

Game Over

-

You did great, you're the best.

-

Click or spacebar to start again

-
-
-
Start
-
+
+
+ + -
+
+

All logos are property of their respective owners, No Copyright infringement intended.

+
+
+ + - - \ No newline at end of file diff --git a/style.css b/style.css index 7894ede..fc00b64 100644 --- a/style.css +++ b/style.css @@ -1,122 +1,243 @@ -@import url("https://fonts.googleapis.com/css?family=Comfortaa"); - -html, body { - margin: 0; - overflow: hidden; - height: 100%; - width: 100%; - position: relative; - font-family: "Comfortaa", cursive; -} - -#container { - width: 100%; - height: 100%; -} - -#container #score { - position: absolute; - top: 20px; - width: 100%; - text-align: center; - font-size: 10vh; - transition: transform 0.5s ease; - color: #333344; - transform: translatey(-200px) scale(1); -} - -#container #game { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; -} - -#container .game-over { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 85%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -#container .game-over * { - transition: opacity 0.5s ease, transform 0.5s ease; - opacity: 0; - transform: translatey(-50px); - color: #333344; -} - -#container .game-over h2 { - margin: 0; - padding: 0; - font-size: 40px; -} - -#container .game-ready { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: space-around; -} - -#container .game-ready #start-button { - transition: opacity 0.5s ease, transform 0.5s ease; - opacity: 0; - transform: translatey(-50px); - border: 3px solid #333344; - padding: 10px 20px; - background-color: transparent; - color: #333344; - font-size: 30px; -} - -#container #instructions { - position: absolute; - width: 100%; - top: 16vh; - left: 0; - text-align: center; - transition: opacity 0.5s ease, transform 0.5s ease; - opacity: 0; -} - -#container #instructions.hide { - opacity: 0 !important; -} - -#container.playing #score, #container.resetting #score { - transform: translatey(0px) scale(1); -} - -#container.playing #instructions { - opacity: 1; -} - -#container.ready .game-ready #start-button { - opacity: 1; - transform: translatey(0); -} - -#container.ended #score { - transform: translatey(6vh) scale(1.5); -} - -#container.ended .game-over * { - opacity: 1; - transform: translatey(0); -} - -#container.ended .game-over p { - transition-delay: 0.3s; -} \ No newline at end of file +* { + box-sizing: border-box; + } + + html, body { + height: 100%; + } + + body { + background: black; + min-height: 100%; + font-family: "Arial", sans-serif; + } + + .wrap { + position: relative; + height: 100%; + min-height: 500px; + padding-bottom: 20px; + } + + .game { + transform-style: preserve-3d; + perspective: 500px; + min-height: 100%; + height: 100%; + } + + @-webkit-keyframes matchAnim { + 0% { + background: #bcffcc; + } + 100% { + background: white; + } + } + + @keyframes matchAnim { + 0% { + background: #bcffcc; + } + 100% { + background: white; + } + } + .card { + float: left; + width: 16.66666%; + height: 25%; + padding: 5px; + text-align: center; + display: block; + perspective: 500px; + position: relative; + cursor: pointer; + z-index: 50; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + } + @media (max-width: 800px) { + .card { + width: 25%; + height: 16.666%; + } + } + .card .inside { + width: 100%; + height: 100%; + display: block; + transform-style: preserve-3d; + transition: 0.4s ease-in-out; + background: white; + } + .card .inside.picked, .card .inside.matched { + transform: rotateY(180deg); + } + .card .inside.matched { + -webkit-animation: 1s matchAnim ease-in-out; + animation: 1s matchAnim ease-in-out; + -webkit-animation-delay: 0.4s; + animation-delay: 0.4s; + } + .card .front, .card .back { + border: 1px solid black; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 20px; + } + .card .front img, .card .back img { + max-width: 100%; + display: block; + margin: 0 auto; + max-height: 100%; + } + .card .front { + transform: rotateY(-180deg); + } + @media (max-width: 800px) { + .card .front { + padding: 5px; + } + } + .card .back { + transform: rotateX(0); + } + @media (max-width: 800px) { + .card .back { + padding: 10px; + } + } + + .modal-overlay { + display: none; + background: rgba(0, 0, 0, 0.8); + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + } + + .modal { + display: none; + position: relative; + width: 500px; + height: 400px; + max-height: 90%; + max-width: 90%; + min-height: 380px; + margin: 0 auto; + background: white; + top: 50%; + transform: translateY(-50%); + padding: 30px 10px; + } + .modal .winner { + font-size: 80px; + text-align: center; + font-family: "Anton", sans-serif; + color: #4d4d4d; + text-shadow: 0px 3px 0 black; + } + @media (max-width: 480px) { + .modal .winner { + font-size: 60px; + } + } + .modal .restart { + font-family: "Anton", sans-serif; + margin: 30px auto; + padding: 20px 30px; + display: block; + font-size: 30px; + border: none; + background: #4d4d4d; + background: linear-gradient(#4d4d4d, #222); + border: 1px solid #222; + border-radius: 5px; + color: white; + text-shadow: 0px 1px 0 black; + cursor: pointer; + } + .modal .restart:hover { + background: linear-gradient(#222, black); + } + .modal .message { + text-align: center; + } + .modal .message a { + text-decoration: none; + color: #28afe6; + font-weight: bold; + } + .modal .message a:hover { + color: #56c0eb; + border-bottom: 1px dotted #56c0eb; + } + .modal .share-text { + text-align: center; + margin: 10px auto; + } + .modal .social { + margin: 20px auto; + text-align: center; + } + .modal .social li { + display: inline-block; + height: 50px; + width: 50px; + margin-right: 10px; + } + .modal .social li:last-child { + margin-right: 0; + } + .modal .social li a { + display: block; + line-height: 50px; + font-size: 20px; + color: white; + text-decoration: none; + border-radius: 5px; + } + .modal .social li a.facebook { + background: #3b5998; + } + .modal .social li a.facebook:hover { + background: #4c70ba; + } + .modal .social li a.google { + background: #D34836; + } + .modal .social li a.google:hover { + background: #dc6e60; + } + .modal .social li a.twitter { + background: #4099FF; + } + .modal .social li a.twitter:hover { + background: #73b4ff; + } + + footer { + height: 20px; + position: absolute; + bottom: 0; + width: 100%; + z-index: 0; + } + footer .disclaimer { + line-height: 20px; + font-size: 12px; + color: #727272; + text-align: center; + } + @media (max-width: 767px) { + footer .disclaimer { + font-size: 8px; + } + } \ No newline at end of file From 092698b4859b3aacb06dfa0425009055526462dc Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Tue, 24 May 2022 15:24:29 +0200 Subject: [PATCH 14/15] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e3dedb6..81a7dc9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Tower Block Game CSS JS +# Memory Game CSS JS -View and try [DEMO](https://codepen.io/filippoerbisti/pen/GRQMQzK) on Codepen. +View and try [DEMO](https://codepen.io/filippoerbisti/pen/WNMZzQp) on Codepen. From bc51a5d68dd5b1d4f58212df9b0e3afca186814d Mon Sep 17 00:00:00 2001 From: Filippo Erbisti Date: Tue, 24 May 2022 15:35:24 +0200 Subject: [PATCH 15/15] update README.md --- README.md | 4 +- game.js | 536 ++++++++++++++++++++++++++++++++++++----------------- index.html | 23 +-- style.css | 246 +----------------------- 4 files changed, 368 insertions(+), 441 deletions(-) diff --git a/README.md b/README.md index 81a7dc9..f0d92d1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Memory Game CSS JS +# Ping Pong Game JS -View and try [DEMO](https://codepen.io/filippoerbisti/pen/WNMZzQp) on Codepen. +View and try [DEMO](https://codepen.io/filippoerbisti/pen/ExQwEXG) on Codepen. diff --git a/game.js b/game.js index 2e79bdb..a77daa0 100644 --- a/game.js +++ b/game.js @@ -1,179 +1,365 @@ -(function(){ - - var Memory = { - - init: function(cards){ - this.$game = $(".game"); - this.$modal = $(".modal"); - this.$overlay = $(".modal-overlay"); - this.$restartButton = $("button.restart"); - this.cardsArray = $.merge(cards, cards); - this.shuffleCards(this.cardsArray); - this.setup(); - }, - - shuffleCards: function(cardsArray){ - this.$cards = $(this.shuffle(this.cardsArray)); - }, - - setup: function(){ - this.html = this.buildHTML(); - this.$game.html(this.html); - this.$memoryCards = $(".card"); - this.paused = false; - this.guess = null; - this.binding(); - }, - - binding: function(){ - this.$memoryCards.on("click", this.cardClicked); - this.$restartButton.on("click", $.proxy(this.reset, this)); - }, - // kinda messy but hey - cardClicked: function(){ - var _ = Memory; - var $card = $(this); - if(!_.paused && !$card.find(".inside").hasClass("matched") && !$card.find(".inside").hasClass("picked")){ - $card.find(".inside").addClass("picked"); - if(!_.guess){ - _.guess = $(this).attr("data-id"); - } else if(_.guess == $(this).attr("data-id") && !$(this).hasClass("picked")){ - $(".picked").addClass("matched"); - _.guess = null; - } else { - _.guess = null; - _.paused = true; - setTimeout(function(){ - $(".picked").removeClass("picked"); - Memory.paused = false; - }, 600); +// Global Variables +var DIRECTION = { + IDLE: 0, + UP: 1, + DOWN: 2, + LEFT: 3, + RIGHT: 4 +}; + +var rounds = [5, 5, 3, 3, 2]; +var colors = ['#1abc9c', '#2ecc71', '#3498db', '#e74c3c', '#9b59b6']; + +// The ball object (The cube that bounces back and forth) +var Ball = { + new: function (incrementedSpeed) { + return { + width: 18, + height: 18, + x: (this.canvas.width / 2) - 9, + y: (this.canvas.height / 2) - 9, + moveX: DIRECTION.IDLE, + moveY: DIRECTION.IDLE, + speed: incrementedSpeed || 9 + }; + } +}; + +// The paddle object (The two lines that move up and down) +var Paddle = { + new: function (side) { + return { + width: 18, + height: 70, + x: side === 'left' ? 150 : this.canvas.width - 150, + y: (this.canvas.height / 2) - 35, + score: 0, + move: DIRECTION.IDLE, + speed: 10 + }; + } +}; + +var Game = { + initialize: function () { + this.canvas = document.querySelector('canvas'); + this.context = this.canvas.getContext('2d'); + + this.canvas.width = 1400; + this.canvas.height = 1000; + + this.canvas.style.width = (this.canvas.width / 2) + 'px'; + this.canvas.style.height = (this.canvas.height / 2) + 'px'; + + this.player = Paddle.new.call(this, 'left'); + this.paddle = Paddle.new.call(this, 'right'); + this.ball = Ball.new.call(this); + + this.paddle.speed = 8; + this.running = this.over = false; + this.turn = this.paddle; + this.timer = this.round = 0; + this.color = '#2c3e50'; + + Pong.menu(); + Pong.listen(); + }, + + endGameMenu: function (text) { + // Change the canvas font size and color + Pong.context.font = '50px Courier New'; + Pong.context.fillStyle = this.color; + + // Draw the rectangle behind the 'Press any key to begin' text. + Pong.context.fillRect( + Pong.canvas.width / 2 - 350, + Pong.canvas.height / 2 - 48, + 700, + 100 + ); + + // Change the canvas color; + Pong.context.fillStyle = '#ffffff'; + + // Draw the end game menu text ('Game Over' and 'Winner') + Pong.context.fillText(text, + Pong.canvas.width / 2, + Pong.canvas.height / 2 + 15 + ); + + setTimeout(function () { + Pong = Object.assign({}, Game); + Pong.initialize(); + }, 3000); + }, + + menu: function () { + // Draw all the Pong objects in their current state + Pong.draw(); + + // Change the canvas font size and color + this.context.font = '50px Courier New'; + this.context.fillStyle = this.color; + + // Draw the rectangle behind the 'Press any key to begin' text. + this.context.fillRect( + this.canvas.width / 2 - 350, + this.canvas.height / 2 - 48, + 700, + 100 + ); + + // Change the canvas color; + this.context.fillStyle = '#ffffff'; + + // Draw the 'press any key to begin' text + this.context.fillText('Press any key to begin', + this.canvas.width / 2, + this.canvas.height / 2 + 15 + ); + }, + + // Update all objects (move the player, paddle, ball, increment the score, etc.) + update: function () { + if (!this.over) { + // If the ball collides with the bound limits - correct the x and y coords. + if (this.ball.x <= 0) Pong._resetTurn.call(this, this.paddle, this.player); + if (this.ball.x >= this.canvas.width - this.ball.width) Pong._resetTurn.call(this, this.player, this.paddle); + if (this.ball.y <= 0) this.ball.moveY = DIRECTION.DOWN; + if (this.ball.y >= this.canvas.height - this.ball.height) this.ball.moveY = DIRECTION.UP; + + // Move player if they player.move value was updated by a keyboard event + if (this.player.move === DIRECTION.UP) this.player.y -= this.player.speed; + else if (this.player.move === DIRECTION.DOWN) this.player.y += this.player.speed; + + // On new serve (start of each turn) move the ball to the correct side + // and randomize the direction to add some challenge. + if (Pong._turnDelayIsOver.call(this) && this.turn) { + this.ball.moveX = this.turn === this.player ? DIRECTION.LEFT : DIRECTION.RIGHT; + this.ball.moveY = [DIRECTION.UP, DIRECTION.DOWN][Math.round(Math.random())]; + this.ball.y = Math.floor(Math.random() * this.canvas.height - 200) + 200; + this.turn = null; + } + + // If the player collides with the bound limits, update the x and y coords. + if (this.player.y <= 0) this.player.y = 0; + else if (this.player.y >= (this.canvas.height - this.player.height)) this.player.y = (this.canvas.height - this.player.height); + + // Move ball in intended direction based on moveY and moveX values + if (this.ball.moveY === DIRECTION.UP) this.ball.y -= (this.ball.speed / 1.5); + else if (this.ball.moveY === DIRECTION.DOWN) this.ball.y += (this.ball.speed / 1.5); + if (this.ball.moveX === DIRECTION.LEFT) this.ball.x -= this.ball.speed; + else if (this.ball.moveX === DIRECTION.RIGHT) this.ball.x += this.ball.speed; + + // Handle paddle (AI) UP and DOWN movement + if (this.paddle.y > this.ball.y - (this.paddle.height / 2)) { + if (this.ball.moveX === DIRECTION.RIGHT) this.paddle.y -= this.paddle.speed / 1.5; + else this.paddle.y -= this.paddle.speed / 4; + } + if (this.paddle.y < this.ball.y - (this.paddle.height / 2)) { + if (this.ball.moveX === DIRECTION.RIGHT) this.paddle.y += this.paddle.speed / 1.5; + else this.paddle.y += this.paddle.speed / 4; + } + + // Handle paddle (AI) wall collision + if (this.paddle.y >= this.canvas.height - this.paddle.height) this.paddle.y = this.canvas.height - this.paddle.height; + else if (this.paddle.y <= 0) this.paddle.y = 0; + + // Handle Player-Ball collisions + if (this.ball.x - this.ball.width <= this.player.x && this.ball.x >= this.player.x - this.player.width) { + if (this.ball.y <= this.player.y + this.player.height && this.ball.y + this.ball.height >= this.player.y) { + this.ball.x = (this.player.x + this.ball.width); + this.ball.moveX = DIRECTION.RIGHT; + + //beep1.play(); } - if($(".matched").length == $(".card").length){ - _.win(); + } + + // Handle paddle-ball collision + if (this.ball.x - this.ball.width <= this.paddle.x && this.ball.x >= this.paddle.x - this.paddle.width) { + if (this.ball.y <= this.paddle.y + this.paddle.height && this.ball.y + this.ball.height >= this.paddle.y) { + this.ball.x = (this.paddle.x - this.ball.width); + this.ball.moveX = DIRECTION.LEFT; + + //beep1.play(); } } - }, - - win: function(){ - this.paused = true; - setTimeout(function(){ - Memory.showModal(); - Memory.$game.fadeOut(); - }, 1000); - }, - - showModal: function(){ - this.$overlay.show(); - this.$modal.fadeIn("slow"); - }, - - hideModal: function(){ - this.$overlay.hide(); - this.$modal.hide(); - }, - - reset: function(){ - this.hideModal(); - this.shuffleCards(this.cardsArray); - this.setup(); - this.$game.show("slow"); - }, - - // Fisher--Yates Algorithm -- https://bost.ocks.org/mike/shuffle/ - shuffle: function(array){ - var counter = array.length, temp, index; - // While there are elements in the array - while (counter > 0) { - // Pick a random index - index = Math.floor(Math.random() * counter); - // Decrease counter by 1 - counter--; - // And swap the last element with it - temp = array[counter]; - array[counter] = array[index]; - array[index] = temp; - } - return array; - }, - - buildHTML: function(){ - var frag = ''; - this.$cards.each(function(k, v){ - frag += '
\ -
'+ v.name +'
\ -
Codepen
\ -
'; - }); - return frag; } - }; - - var cards = [ - { - name: "php", - img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/php-logo_1.png", - id: 1, - }, - { - name: "css3", - img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/css3-logo.png", - id: 2 - }, - { - name: "html5", - img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/html5-logo.png", - id: 3 - }, - { - name: "jquery", - img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/jquery-logo.png", - id: 4 - }, - { - name: "javascript", - img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/js-logo.png", - id: 5 - }, - { - name: "node", - img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/nodejs-logo.png", - id: 6 - }, - { - name: "photoshop", - img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/photoshop-logo.png", - id: 7 - }, - { - name: "python", - img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/python-logo.png", - id: 8 - }, - { - name: "rails", - img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/rails-logo.png", - id: 9 - }, - { - name: "sass", - img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/sass-logo.png", - id: 10 - }, - { - name: "sublime", - img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/sublime-logo.png", - id: 11 - }, - { - name: "wordpress", - img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/wordpress-logo.png", - id: 12 - }, - ]; - - Memory.init(cards); - - -})(); \ No newline at end of file + + // Handle the end of round transition + // Check to see if the player won the round. + if (this.player.score === rounds[this.round]) { + // Check to see if there are any more rounds/levels left and display the victory screen if + // there are not. + if (!rounds[this.round + 1]) { + this.over = true; + setTimeout(function () { Pong.endGameMenu('Winner!'); }, 1000); + } else { + // If there is another round, reset all the values and increment the round number. + this.color = this._generateRoundColor(); + this.player.score = this.paddle.score = 0; + this.player.speed += 0.5; + this.paddle.speed += 1; + this.ball.speed += 1; + this.round += 1; + + //beep3.play(); + } + } + // Check to see if the paddle/AI has won the round. + else if (this.paddle.score === rounds[this.round]) { + this.over = true; + setTimeout(function () { Pong.endGameMenu('Game Over!'); }, 1000); + } + }, + + // Draw the objects to the canvas element + draw: function () { + // Clear the Canvas + this.context.clearRect( + 0, + 0, + this.canvas.width, + this.canvas.height + ); + + // Set the fill style to black + this.context.fillStyle = this.color; + + // Draw the background + this.context.fillRect( + 0, + 0, + this.canvas.width, + this.canvas.height + ); + + // Set the fill style to white (For the paddles and the ball) + this.context.fillStyle = '#ffffff'; + + // Draw the Player + this.context.fillRect( + this.player.x, + this.player.y, + this.player.width, + this.player.height + ); + + // Draw the Paddle + this.context.fillRect( + this.paddle.x, + this.paddle.y, + this.paddle.width, + this.paddle.height + ); + + // Draw the Ball + if (Pong._turnDelayIsOver.call(this)) { + this.context.fillRect( + this.ball.x, + this.ball.y, + this.ball.width, + this.ball.height + ); + } + + // Draw the net (Line in the middle) + this.context.beginPath(); + this.context.setLineDash([7, 15]); + this.context.moveTo((this.canvas.width / 2), this.canvas.height - 140); + this.context.lineTo((this.canvas.width / 2), 140); + this.context.lineWidth = 10; + this.context.strokeStyle = '#ffffff'; + this.context.stroke(); + + // Set the default canvas font and align it to the center + this.context.font = '100px Courier New'; + this.context.textAlign = 'center'; + + // Draw the players score (left) + this.context.fillText( + this.player.score.toString(), + (this.canvas.width / 2) - 300, + 200 + ); + + // Draw the paddles score (right) + this.context.fillText( + this.paddle.score.toString(), + (this.canvas.width / 2) + 300, + 200 + ); + + // Change the font size for the center score text + this.context.font = '30px Courier New'; + + // Draw the winning score (center) + this.context.fillText( + 'Round ' + (Pong.round + 1), + (this.canvas.width / 2), + 35 + ); + + // Change the font size for the center score value + this.context.font = '40px Courier'; + + // Draw the current round number + this.context.fillText( + rounds[Pong.round] ? rounds[Pong.round] : rounds[Pong.round - 1], + (this.canvas.width / 2), + 100 + ); + }, + + loop: function () { + Pong.update(); + Pong.draw(); + + // If the game is not over, draw the next frame. + if (!Pong.over) requestAnimationFrame(Pong.loop); + }, + + listen: function () { + document.addEventListener('keydown', function (key) { + // Handle the 'Press any key to begin' function and start the game. + if (Pong.running === false) { + Pong.running = true; + window.requestAnimationFrame(Pong.loop); + } + + // Handle up arrow and w key events + if (key.keyCode === 38 || key.keyCode === 87) Pong.player.move = DIRECTION.UP; + + // Handle down arrow and s key events + if (key.keyCode === 40 || key.keyCode === 83) Pong.player.move = DIRECTION.DOWN; + }); + + // Stop the player from moving when there are no keys being pressed. + document.addEventListener('keyup', function (key) { Pong.player.move = DIRECTION.IDLE; }); + }, + + // Reset the ball location, the player turns and set a delay before the next round begins. + _resetTurn: function(victor, loser) { + this.ball = Ball.new.call(this, this.ball.speed); + this.turn = loser; + this.timer = (new Date()).getTime(); + + victor.score++; + //beep2.play(); + }, + + // Wait for a delay to have passed after each turn. + _turnDelayIsOver: function() { + return ((new Date()).getTime() - this.timer >= 1000); + }, + + // Select a random color as the background of each level/round. + _generateRoundColor: function () { + var newColor = colors[Math.floor(Math.random() * colors.length)]; + if (newColor === this.color) return Pong._generateRoundColor(); + return newColor; + } +}; + +var Pong = Object.assign({}, Game); +Pong.initialize(); \ No newline at end of file diff --git a/index.html b/index.html index 6b6f7ee..223e12f 100644 --- a/index.html +++ b/index.html @@ -4,31 +4,12 @@ - Memory Game CSS JS + Ping Pong Game JS -
-
- - -
-

All logos are property of their respective owners, No Copyright infringement intended.

-
-
- + diff --git a/style.css b/style.css index fc00b64..23d3398 100644 --- a/style.css +++ b/style.css @@ -1,243 +1,3 @@ -* { - box-sizing: border-box; - } - - html, body { - height: 100%; - } - - body { - background: black; - min-height: 100%; - font-family: "Arial", sans-serif; - } - - .wrap { - position: relative; - height: 100%; - min-height: 500px; - padding-bottom: 20px; - } - - .game { - transform-style: preserve-3d; - perspective: 500px; - min-height: 100%; - height: 100%; - } - - @-webkit-keyframes matchAnim { - 0% { - background: #bcffcc; - } - 100% { - background: white; - } - } - - @keyframes matchAnim { - 0% { - background: #bcffcc; - } - 100% { - background: white; - } - } - .card { - float: left; - width: 16.66666%; - height: 25%; - padding: 5px; - text-align: center; - display: block; - perspective: 500px; - position: relative; - cursor: pointer; - z-index: 50; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - } - @media (max-width: 800px) { - .card { - width: 25%; - height: 16.666%; - } - } - .card .inside { - width: 100%; - height: 100%; - display: block; - transform-style: preserve-3d; - transition: 0.4s ease-in-out; - background: white; - } - .card .inside.picked, .card .inside.matched { - transform: rotateY(180deg); - } - .card .inside.matched { - -webkit-animation: 1s matchAnim ease-in-out; - animation: 1s matchAnim ease-in-out; - -webkit-animation-delay: 0.4s; - animation-delay: 0.4s; - } - .card .front, .card .back { - border: 1px solid black; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - padding: 20px; - } - .card .front img, .card .back img { - max-width: 100%; - display: block; - margin: 0 auto; - max-height: 100%; - } - .card .front { - transform: rotateY(-180deg); - } - @media (max-width: 800px) { - .card .front { - padding: 5px; - } - } - .card .back { - transform: rotateX(0); - } - @media (max-width: 800px) { - .card .back { - padding: 10px; - } - } - - .modal-overlay { - display: none; - background: rgba(0, 0, 0, 0.8); - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - } - - .modal { - display: none; - position: relative; - width: 500px; - height: 400px; - max-height: 90%; - max-width: 90%; - min-height: 380px; - margin: 0 auto; - background: white; - top: 50%; - transform: translateY(-50%); - padding: 30px 10px; - } - .modal .winner { - font-size: 80px; - text-align: center; - font-family: "Anton", sans-serif; - color: #4d4d4d; - text-shadow: 0px 3px 0 black; - } - @media (max-width: 480px) { - .modal .winner { - font-size: 60px; - } - } - .modal .restart { - font-family: "Anton", sans-serif; - margin: 30px auto; - padding: 20px 30px; - display: block; - font-size: 30px; - border: none; - background: #4d4d4d; - background: linear-gradient(#4d4d4d, #222); - border: 1px solid #222; - border-radius: 5px; - color: white; - text-shadow: 0px 1px 0 black; - cursor: pointer; - } - .modal .restart:hover { - background: linear-gradient(#222, black); - } - .modal .message { - text-align: center; - } - .modal .message a { - text-decoration: none; - color: #28afe6; - font-weight: bold; - } - .modal .message a:hover { - color: #56c0eb; - border-bottom: 1px dotted #56c0eb; - } - .modal .share-text { - text-align: center; - margin: 10px auto; - } - .modal .social { - margin: 20px auto; - text-align: center; - } - .modal .social li { - display: inline-block; - height: 50px; - width: 50px; - margin-right: 10px; - } - .modal .social li:last-child { - margin-right: 0; - } - .modal .social li a { - display: block; - line-height: 50px; - font-size: 20px; - color: white; - text-decoration: none; - border-radius: 5px; - } - .modal .social li a.facebook { - background: #3b5998; - } - .modal .social li a.facebook:hover { - background: #4c70ba; - } - .modal .social li a.google { - background: #D34836; - } - .modal .social li a.google:hover { - background: #dc6e60; - } - .modal .social li a.twitter { - background: #4099FF; - } - .modal .social li a.twitter:hover { - background: #73b4ff; - } - - footer { - height: 20px; - position: absolute; - bottom: 0; - width: 100%; - z-index: 0; - } - footer .disclaimer { - line-height: 20px; - font-size: 12px; - color: #727272; - text-align: center; - } - @media (max-width: 767px) { - footer .disclaimer { - font-size: 8px; - } - } \ No newline at end of file +body { + text-align: center; +} \ No newline at end of file