Cyberpunk 2077, reboot animation using GPU enhanced motion techniques.
Created
August 25, 2022 07:13
-
-
Save zux0x3a/e44280caa32595942b9eac13d49ba06e to your computer and use it in GitHub Desktop.
hydra
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <div class="terminal glitch"> | |
| <div class="scanline"></div> | |
| <p class="spinner">[/]</p> | |
| <div class="hydra"> | |
| <div class="hydra_rebooting"> | |
| <p>< SYSTEM REBOOTING ></p> | |
| <p class="text--sm">HYDRA VER 2.1 SYS RECOVERY</p> | |
| <p class="text--sm">PROCESS: <span class="process-amount">0</span>%</p> | |
| <p class="loading-bar"></p> | |
| </div> | |
| <div class="hydra_reboot_success hidden"> | |
| <p>REBOOTING SUCCESSFUL</p> | |
| </div> | |
| </div> | |
| </div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const terminal = document.querySelector(".terminal"); | |
| const hydra = document.querySelector(".hydra"); | |
| const rebootSuccessText = document.querySelector(".hydra_reboot_success"); | |
| const maxCharacters = 24; | |
| const unloadedCharacter = "."; | |
| const loadedCharacter = "#"; | |
| const spinnerFrames = ["/", "-", "\\", "|"]; | |
| // Clone the element and give the glitch classes | |
| (glitchElement => { | |
| const glitch = glitchElement.cloneNode(true); | |
| const glitchReverse = glitchElement.cloneNode(true); | |
| glitch.classList.add("glitch--clone", "glitch--bottom"); | |
| glitchReverse.classList.add("glitch--clone", "glitch--top"); | |
| glitch.setAttribute("aria-hidden", "true"); | |
| glitchReverse.setAttribute("aria-hidden", "true"); | |
| glitchElement.insertAdjacentElement("afterend", glitch); | |
| glitchElement.insertAdjacentElement("afterend", glitchReverse); | |
| })(terminal); | |
| // Get all the loading bars | |
| const loadingBars = document.querySelectorAll(".loading-bar"); | |
| const processAmounts = document.querySelectorAll(".process-amount"); | |
| const spinners = document.querySelectorAll(".spinner"); | |
| const rebootingText = document.querySelectorAll(".hydra_rebooting"); | |
| const glitches = document.querySelectorAll(".glitch--clone"); | |
| // Helper for random number | |
| const RandomNumber = (min, max) => Math.floor(Math.random() * max) + min; | |
| const Delay = (time) => { | |
| return new Promise((resolve) => setTimeout(resolve, time)) | |
| }; | |
| const HideAll = elements => | |
| elements.forEach(glitchGroup => | |
| glitchGroup.forEach(element => element.classList.add("hidden")) ); | |
| const ShowAll = elements => | |
| elements.forEach(glitchGroup => | |
| glitchGroup.forEach(element => element.classList.remove("hidden")) ); | |
| // Render the bar to HTML | |
| const RenderBar = ( values ) => { | |
| const currentLoaded = values.lastIndexOf(loadedCharacter) + 1; | |
| const loaded = values.slice(0, currentLoaded).join(""); | |
| const unloaded = values.slice(currentLoaded).join(""); | |
| // Update all the loading bars | |
| loadingBars.forEach(loadingBar => { | |
| loadingBar.innerHTML = `(${loaded}<span class="loading-bar--unloaded">${unloaded}</span>)`; | |
| }); | |
| // Update all the percentages | |
| loadingPercent = Math.floor(currentLoaded / maxCharacters * 100); | |
| processAmounts.forEach(processAmount => { | |
| processAmount.innerText = loadingPercent; | |
| }); | |
| }; | |
| // Update the loaded value and render it to HTML | |
| const DrawLoadingBar = ( values ) => { | |
| return new Promise((resolve) => { | |
| const loadingBarAnimation = setInterval(() => { | |
| if (!values.includes(unloadedCharacter)) { | |
| clearInterval(loadingBarAnimation); | |
| resolve(); | |
| } | |
| values.pop(unloadedCharacter); | |
| values.unshift(loadedCharacter); | |
| RenderBar(values); | |
| }, RandomNumber(50, 300)); | |
| }); | |
| }; | |
| const DrawSpinner = (spinnerFrame = 0) => { | |
| return setInterval(() => { | |
| spinnerFrame += 1; | |
| spinners.forEach( | |
| spinner => | |
| (spinner.innerText = `[${ | |
| spinnerFrames[spinnerFrame % spinnerFrames.length] | |
| }]`) | |
| ); | |
| }, RandomNumber(50, 300)); | |
| }; | |
| const AnimateBox = () => { | |
| const first = hydra.getBoundingClientRect(); | |
| HideAll([spinners, glitches, rebootingText]); | |
| rebootSuccessText.classList.remove("hidden"); | |
| rebootSuccessText.style.visibility = "hidden"; | |
| const last = hydra.getBoundingClientRect(); | |
| const hydraAnimation = hydra.animate([ | |
| { transform: `scale(${first.width / last.width}, ${first.height / last.height})` }, | |
| { transform: `scale(${first.width / last.width}, 1.2)` }, | |
| { transform: `none` } | |
| ],{ | |
| duration: 600, | |
| easing: 'cubic-bezier(0,0,0.32,1)', | |
| }); | |
| hydraAnimation.addEventListener('finish', () => { | |
| rebootSuccessText.removeAttribute("style"); | |
| hydra.removeAttribute("style"); | |
| }); | |
| }; | |
| const PlayHydra = async() => { | |
| terminal.classList.add("glitch"); | |
| rebootSuccessText.classList.add("hidden"); | |
| ShowAll([spinners, glitches, rebootingText]); | |
| const loadingBar = new Array(maxCharacters).fill(unloadedCharacter); | |
| const spinnerInterval = DrawSpinner(); | |
| // Play the loading bar | |
| await DrawLoadingBar(loadingBar); | |
| // Loading is complete on the next frame, hide spinner and glitch | |
| requestAnimationFrame(()=> { | |
| clearInterval(spinnerInterval); | |
| terminal.classList.remove("glitch"); | |
| AnimateBox(); | |
| setTimeout(PlayHydra, 5000); | |
| }); | |
| }; | |
| PlayHydra(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| html { | |
| font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, | |
| monospace; | |
| font-size: 3vw; | |
| font-weight: 100; | |
| text-shadow: 0 0 5px red; | |
| background-color: #0f0000; | |
| background-image: url(http://api.thumbr.it/whitenoise-200x200.png?background=00000000&noise=626262&density=15&opacity=15); | |
| background-size: 100px; | |
| color: #ff6666; | |
| height: 100%; | |
| text-align: center; | |
| } | |
| body { | |
| margin: 0; | |
| height: 100%; | |
| display: grid; | |
| overflow: hidden; | |
| } | |
| .terminal { | |
| grid-row: 1; | |
| grid-column: 1; | |
| display: grid; | |
| grid-gap: 3vw; | |
| padding: 3vw; | |
| grid-template-rows: 1fr auto 1fr; | |
| grid-template-columns: 1fr auto 1fr; | |
| } | |
| .glitch { | |
| animation: glitch 1.5s linear infinite; | |
| } | |
| .glitch--clone { | |
| opacity: 0.2; | |
| } | |
| .glitch--clone .hydra { | |
| filter: blur(2px); | |
| opacity: 0.8; | |
| } | |
| .glitch--top { | |
| animation: glitch--top 1s linear infinite; | |
| } | |
| .glitch--top .hydra { | |
| transform: translate(4vw, 4vw); | |
| } | |
| .glitch--bottom { | |
| animation: glitch--bottom 0.75s linear infinite; | |
| } | |
| .glitch--bottom .hydra { | |
| transform: translate(-1vw, -1vw); | |
| } | |
| .hydra { | |
| box-shadow: 0 0 2px red, inset 0 0 2px red; | |
| padding: 2vw; | |
| border: 0.2vw solid #ff6666; | |
| grid-row: 2/2; | |
| grid-column: 2/2; | |
| } | |
| .scanline { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: rgba(255, 255, 255, 0.03); | |
| animation: scanline 6s linear infinite; | |
| } | |
| .loading-bar--unloaded { | |
| color: #fff; | |
| text-shadow: 0 0 5px #fff; | |
| } | |
| .hidden { | |
| display: none; | |
| } | |
| p { | |
| font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, | |
| monospace; | |
| font-size: inherit; | |
| line-height: 1.5; | |
| margin: 0; | |
| } | |
| .text--sm { | |
| font-size: 1.75vw; | |
| } | |
| * + .text--sm { | |
| padding-top: 0.5vw; | |
| } | |
| * + .loading-bar { | |
| padding-top: 1vw; | |
| } | |
| @keyframes scanline { | |
| 0.01% { | |
| transform: translatey(-100%); | |
| } | |
| 99.99% { | |
| transform: translatey(0); | |
| } | |
| 100% { | |
| transform: translatey(-100%); | |
| } | |
| } | |
| @keyframes glitch { | |
| 2%, | |
| 64% { | |
| transform: translate(2px, 0) skew(0deg); | |
| } | |
| 4%, | |
| 60% { | |
| transform: translate(-2px, 0) skew(0deg); | |
| } | |
| 62% { | |
| transform: translate(0, 0) skew(5deg); | |
| } | |
| } | |
| @keyframes glitch--top { | |
| 2%, | |
| 64% { | |
| transform: translate(2px, -2px); | |
| } | |
| 4%, | |
| 60% { | |
| transform: translate(-2px, 2px); | |
| } | |
| 62% { | |
| transform: translate(13px, -1px) skew(-13deg); | |
| } | |
| } | |
| @keyframes glitch--bottom { | |
| 2%, | |
| 64% { | |
| transform: translate(-2px, 0); | |
| } | |
| 4%, | |
| 60% { | |
| transform: translate(-2px, 0); | |
| } | |
| 62% { | |
| transform: translate(-22px, 5px) skew(21deg); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment