Created
March 17, 2024 05:54
-
-
Save sojohnnysaid/a144b18d9a2f612bf2a1c7de40ebf85f to your computer and use it in GitHub Desktop.
Animation reciting a quote from Pico Iyer
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
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Quote Animation</title> | |
| <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script> | |
| <style> | |
| body { | |
| margin: 0; | |
| padding: 0; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| height: 100vh; | |
| } | |
| .background { | |
| position: absolute; | |
| top: 0; | |
| right: 0; | |
| bottom: 0; | |
| left: 0; | |
| z-index: 1; | |
| } | |
| .quote-box { | |
| position: relative; | |
| background-color: rgba(255, 255, 255, 0.8); | |
| padding: 30px; | |
| border-radius: 10px; | |
| text-align: center; | |
| max-width: 600px; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| opacity: 0; | |
| transition: opacity 2s ease; | |
| z-index: 2; | |
| } | |
| .quote-box p { | |
| font-size: 24px; | |
| line-height: 1.5; | |
| margin: 0; | |
| color: #333; | |
| } | |
| .quote-box.hide-quote { | |
| display: none; | |
| } | |
| .replay-button-box { | |
| position: absolute; | |
| bottom: 20px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| background-color: white; | |
| padding: 10px; | |
| border-radius: 38px; | |
| opacity: 0; | |
| transition: opacity 0.5s ease; | |
| z-index: 3; | |
| } | |
| .replay-button-box.show { | |
| opacity: 1; | |
| } | |
| .replay-button { | |
| padding: 10px 20px; | |
| font-size: 18px; | |
| background-color: #fff; | |
| color: #333; | |
| border: none; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| } | |
| .credit-text { | |
| font-size: 16px; | |
| line-height: 1.5; | |
| margin: 0; | |
| color: #333; | |
| } | |
| .credit-box { | |
| opacity: 0; | |
| transition: opacity 0.5s ease; | |
| } | |
| .credit-box.show { | |
| opacity: 1; | |
| } | |
| .credit-box.fade-out { | |
| opacity: 0; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="app"> | |
| <div class="background" :style="gradientStyle"></div> | |
| <div class="quote-box" :class="{ 'hide-quote': showReplayButton }" :style="{ opacity: quoteBoxOpacity }"> | |
| <transition name="fade" mode="out-in"> | |
| <p :key="currentIndex" v-if="quotes[currentIndex]" v-html="quotes[currentIndex].text"></p> | |
| </transition> | |
| </div> | |
| <div class="quote-box credit-box" :class="{ show: showReplayButton,'fade-out': fadeOutCredit }"> | |
| <p class="credit-text">― Pico Iyer, The Art of Stillness: Adventures in Going Nowhere</p> | |
| </div> | |
| <div class="replay-button-box" :class="{ show: showReplayButton }"> | |
| <button class="replay-button" @click="replayAnimation">Replay</button> | |
| </div> | |
| </div> | |
| <script> | |
| new Vue({ | |
| el: '#app', | |
| data: { | |
| fadeOutCredit: false, | |
| quotes: [ | |
| { text: 'In an age of speed, I began to think, nothing could be more invigorating than going slow.', color: '#8BC34A' }, | |
| { text: 'In an age of distraction, nothing can feel more luxurious than paying attention.', color: '#4CAF50' }, | |
| { text: 'And in an age of constant movement, nothing is more urgent than sitting still.', color: '#CDDC39' }, | |
| { text: 'You can go on vacation to Paris or Hawaii or New Orleans three months from now, and you\'ll have a tremendous time, I\'m sure.', color: '#FFC107' }, | |
| { text: 'But if you want to come back feeling new - alive and full of fresh hope and in love with the world - I think the place to visit may be Nowhere.', color: '#9C27B0' } | |
| ], | |
| currentIndex: 0, | |
| quoteBoxOpacity: 0, | |
| colorNames: [ | |
| '--magic-rainbow-color-0', | |
| '--magic-rainbow-color-1', | |
| '--magic-rainbow-color-2', | |
| ], | |
| transitionDelay: 1000, | |
| showReplayButton: false, | |
| }, | |
| computed: { | |
| gradientStyle() { | |
| const colorKeys = this.colorNames; | |
| const currentColor = this.quotes[this.currentIndex].color; | |
| return { | |
| [colorKeys[0]]: currentColor, | |
| [colorKeys[1]]: this.lightenColor(currentColor, 20), | |
| [colorKeys[2]]: this.lightenColor(currentColor, 40), | |
| transition: ` | |
| ${colorKeys[0]} ${this.transitionDelay}ms linear, | |
| ${colorKeys[1]} ${this.transitionDelay}ms linear, | |
| ${colorKeys[2]} ${this.transitionDelay}ms linear | |
| `, | |
| background: ` | |
| radial-gradient( | |
| circle at top left, | |
| var(${colorKeys[2]}) 0%, | |
| var(${colorKeys[1]}) 50%, | |
| var(${colorKeys[0]}) 100% | |
| ) | |
| `, | |
| }; | |
| }, | |
| }, | |
| mounted() { | |
| this.registerProperties(); | |
| this.startAnimation(); | |
| }, | |
| methods: { | |
| registerProperties() { | |
| this.colorNames.forEach((name) => { | |
| CSS.registerProperty({ | |
| name, | |
| syntax: '<color>', | |
| inherits: false, | |
| initialValue: 'white', | |
| }); | |
| }); | |
| }, | |
| startAnimation() { | |
| this.showReplayButton = false; | |
| this.quoteBoxOpacity = 0; | |
| setTimeout(() => { | |
| this.quoteBoxOpacity = 1; | |
| setTimeout(() => { | |
| this.quoteBoxOpacity = 0; | |
| setTimeout(() => { | |
| this.currentIndex++; | |
| if (this.currentIndex < this.quotes.length) { | |
| setTimeout(() => { | |
| this.startAnimation(); | |
| }, this.transitionDelay); | |
| } else { | |
| this.currentIndex = 0; | |
| setTimeout(() => { | |
| this.showReplayButton = true; | |
| }, this.transitionDelay); | |
| } | |
| }, 2000); | |
| }, 5000); | |
| }, 100); | |
| }, | |
| replayAnimation() { | |
| this.fadeOutCredit = true; | |
| setTimeout(() => { | |
| this.fadeOutCredit = false; | |
| this.startAnimation(); | |
| }, 500); | |
| }, | |
| lightenColor(color, amount) { | |
| const parseColor = (color) => parseInt(color.slice(1), 16); | |
| const rgbToHex = (r, g, b) => '#' + [r, g, b].map(x => { | |
| const hex = x.toString(16); | |
| return hex.length === 1 ? '0' + hex : hex; | |
| }).join(''); | |
| const r = parseColor(color.slice(0, 3)); | |
| const g = parseColor(color.slice(2, 5)); | |
| const b = parseColor(color.slice(4, 7)); | |
| const lightenChannel = (channel) => Math.min(Math.floor(channel * (1 + amount / 100)), 255); | |
| return rgbToHex(lightenChannel(r), lightenChannel(g), lightenChannel(b)); | |
| }, | |
| }, | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment