WEBLEB
Início
Editor
Entrar
Pro
Português
English
Français
Español
Português
Deutsch
Italiano
हिंदी
Jogo de pilha
1750
fastbug170
Abrir no Editor
Publique Seu Código
Recomendado
4 November 2024
Modelo de barra de navegação HTML CSS
16 June 2025
Um código de Mete
13 October 2025
Elementos HTML do contêiner de animação CSS
HTML
Copy
Javascript Stack Game
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
CSS
Copy
@import url("https://fonts.googleapis.com/css?family=Comfortaa"); html, body { margin: 0; overflow: hidden; height: 100vh; 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; }
JS
Copy
"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.applyMatrix(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.applyMatrix(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.applyMatrix(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(); }); } 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();