having a bit of fun with GSAP and SVG.
The rainbow color scheme being used here I first saw on https://githubuniverse.com
A Pen by Rachel Smith on CodePen.
| <p id="offscreen-text" class="offscreen-text"></p> | |
| <p id="text" class="text"></p> | |
| <svg id="svg"> | |
| </svg> | |
| <input type="text" class="input", id="input" /> |
| const selectSVG = id => { | |
| const el = document.getElementById(id); | |
| return new SVGElement(el); | |
| }; | |
| const createSVG = type => { | |
| const el = document.createElementNS('http://www.w3.org/2000/svg', type); | |
| return new SVGElement(el); | |
| }; | |
| class SVGElement { | |
| constructor(element) { | |
| this.element = element; | |
| } | |
| set(attributeName, value) { | |
| this.element.setAttribute(attributeName, value); | |
| } | |
| style(property, value) { | |
| this.element.style[property] = value; | |
| } | |
| } | |
| const colors = [ | |
| { main: '#FBDB4A', shades: ['#FAE073', '#FCE790', '#FADD65', '#E4C650'] }, | |
| { main: '#F3934A', shades: ['#F7B989', '#F9CDAA', '#DD8644', '#F39C59'] }, | |
| { main: '#EB547D', shades: ['#EE7293', '#F191AB', '#D64D72', '#C04567'] }, | |
| { main: '#9F6AA7', shades: ['#B084B6', '#C19FC7', '#916198', '#82588A'] }, | |
| { main: '#5476B3', shades: ['#6382B9', '#829BC7', '#4D6CA3', '#3E5782'] }, | |
| { main: '#2BB19B', shades: ['#4DBFAD', '#73CDBF', '#27A18D', '#1F8171'] }, | |
| { main: '#70B984', shades: ['#7FBE90', '#98CBA6', '#68A87A', '#5E976E'] } | |
| ]; | |
| const svg = selectSVG('svg'); | |
| const text = document.getElementById('text'); | |
| const offscreenText = document.getElementById('offscreen-text'); | |
| const input = document.getElementById('input'); | |
| let width = window.innerWidth; | |
| let height = window.innerHeight; | |
| let textSize = 0; | |
| let textCenter = 0; | |
| const letters = []; | |
| const prompt = ['s', 't', 'a', 'r', 't', ' ', 't', 'y', 'p', 'i', 'n', 'g']; | |
| let runPrompt = true; | |
| const resizePage = () => { | |
| width = window.innerWidth; | |
| height = window.innerHeight; | |
| svg.set('height', height); | |
| svg.set('width', width); | |
| svg.set('viewBox', `0 0 ${width} ${height}`); | |
| resizeLetters(); | |
| } | |
| const resizeLetters = () => { | |
| textSize = width / (letters.length+2); | |
| if (textSize > 100) textSize = 100; | |
| text.style.fontSize = `${textSize}px`; | |
| text.style.height = `${textSize}px`; | |
| text.style.lineHeight = `${textSize}px`; | |
| offscreenText.style.fontSize = `${textSize}px`; | |
| const textRect = text.getBoundingClientRect(); | |
| textCenter = textRect.top + textRect.height/2; | |
| positionLetters(); | |
| }; | |
| const positionLetters = () => { | |
| letters.forEach(letter => { | |
| const timing = letter.shift ? 0.1 : 0; | |
| TweenLite.to(letter.onScreen, timing, {x:letter.offScreen.offsetLeft+'px', ease: Power3.easeInOut}); | |
| letter.shift = true; | |
| }); | |
| } | |
| const animateLetterIn = letter => { | |
| const yOffset = (0.5+Math.random()*0.5) * textSize; | |
| TweenLite.fromTo(letter, 0.4, {scale:0}, {scale:1, ease: Back.easeOut}); | |
| TweenLite.fromTo(letter, 0.4, {opacity:0}, {opacity: 1, ease: Power3.easeOut}); | |
| TweenLite.to(letter, 0.2, {y:-yOffset, ease: Power3.easeInOut}); | |
| TweenLite.to(letter, 0.2, {y:0, ease: Power3.easeInOut, delay: 0.2}); | |
| const rotation = -50 + Math.random()*100; | |
| TweenLite.to(letter, 0.2, {rotation: rotation, ease: Power3.easeInOut}); | |
| TweenLite.to(letter, 0.2, {rotation: 0, ease: Power3.easeInOut, delay: 0.2}); | |
| } | |
| const addDecor = (letter, color) => { | |
| setTimeout(() => { | |
| var rect = letter.getBoundingClientRect(); | |
| const x0 = letter.offsetLeft + letter.offsetWidth/2; | |
| const y0 = textCenter - textSize*0.5; | |
| const shade = color.shades[Math.floor(Math.random()*4)]; | |
| for (var i = 0; i < 8; i++) addTri(x0, y0, shade); | |
| for (var i = 0; i < 8; i++) addCirc(x0, y0); | |
| }, 150); | |
| }; | |
| const addTri = (x0, y0, shade) => { | |
| const tri = createSVG('polygon'); | |
| const a = Math.random(); | |
| const a2 = a + (-0.2 + Math.random()*0.4); | |
| const r = textSize*0.52; | |
| const r2 = r + textSize*Math.random()*0.2; | |
| const x = x0 + r * Math.cos(2 * Math.PI * a); | |
| const y = y0 + r * Math.sin(2 * Math.PI * a); | |
| const x2 = x0 + r2 * Math.cos(2 * Math.PI * a2); | |
| const y2 = y0 + r2 * Math.sin(2 * Math.PI * a2); | |
| const triSize = textSize * 0.1; | |
| const scale = 0.3 + Math.random()*0.7; | |
| const offset = triSize*scale; | |
| tri.set('points', `0,0 ${triSize*2},0 ${triSize},${triSize*2}`); | |
| tri.style('fill', shade); | |
| svg.element.appendChild(tri.element); | |
| TweenLite.fromTo(tri.element, 0.6, {rotation: Math.random()*360, scale: scale, x: x-offset, y: y-offset, opacity: 1}, {x: x2-offset, y: y2-offset, opacity: 0, ease: Power1.easeInOut, onComplete: () => { | |
| svg.element.removeChild(tri.element); | |
| }}); | |
| } | |
| const addCirc = (x0, y0) => { | |
| const circ = createSVG('circle'); | |
| const a = Math.random(); | |
| const r = textSize*0.52; | |
| const r2 = r + textSize; | |
| const x = x0 + r * Math.cos(2 * Math.PI * a); | |
| const y = y0 + r * Math.sin(2 * Math.PI * a); | |
| const x2 = x0 + r2 * Math.cos(2 * Math.PI * a); | |
| const y2 = y0 + r2 * Math.sin(2 * Math.PI * a); | |
| const circSize = textSize * 0.05 * Math.random(); | |
| circ.set('r', circSize); | |
| circ.style('fill', '#eee'); | |
| svg.element.appendChild(circ.element); | |
| TweenLite.fromTo(circ.element, 0.6, {x: x-circSize, y: y-circSize, opacity: 1}, {x: x2-circSize, y: y2-circSize, opacity: 0, ease: Power1.easeInOut, onComplete: () => { | |
| svg.element.removeChild(circ.element); | |
| }}); | |
| } | |
| const addLetter = (char, i) => { | |
| const letter = document.createElement('span'); | |
| const oLetter = document.createElement('span'); | |
| letter.innerHTML = char; | |
| oLetter.innerHTML = char; | |
| text.appendChild(letter); | |
| const color = colors[i%colors.length]; | |
| letter.style.color = color.main; | |
| offscreenText.appendChild(oLetter); | |
| letters[i] = {offScreen: oLetter, onScreen: letter, char: char}; | |
| animateLetterIn(letter); | |
| addDecor(oLetter, color); | |
| } | |
| const addLetters = value => { | |
| value.forEach((char, i) => { | |
| if (letters[i] && letters[i].char !== char) { | |
| letters[i].onScreen.innerHTML = char; | |
| letters[i].offScreen.innerHTML = char; | |
| letters[i].char = char; | |
| } | |
| if (letters[i] === undefined) { | |
| addLetter(char, i); | |
| } | |
| }); | |
| }; | |
| const animateLetterOut = (letter, i) => { | |
| TweenLite.to(letter.onScreen, 0.1, {scale: 0, opacity: 0, ease: Power2.easeIn, onComplete: () => { | |
| console.log('removing'); | |
| console.log(letter); | |
| offscreenText.removeChild(letter.offScreen); | |
| text.removeChild(letter.onScreen); | |
| positionLetters(); | |
| }}); | |
| letters.splice(i, 1); | |
| } | |
| const removeLetters = value => { | |
| for (let i = letters.length-1; i >= 0; i--) { | |
| const letter = letters[i]; | |
| if (value[i] === undefined) { | |
| animateLetterOut(letter, i) | |
| } | |
| } | |
| } | |
| const onInputChange = () => { | |
| const value = input.value === '' ? [] : input.value.toLowerCase().split(''); | |
| addLetters(value); | |
| removeLetters(value); | |
| resizeLetters(); | |
| }; | |
| const keyup = (e) => { | |
| if (runPrompt) { | |
| input.value = ''; | |
| runPrompt = false; | |
| }; | |
| onInputChange(); | |
| } | |
| const addPrompt = (i) => { | |
| setTimeout(() => { | |
| if (runPrompt && prompt[i]) { | |
| input.value = input.value + prompt[i]; | |
| onInputChange(); | |
| addPrompt(i+1); | |
| } | |
| }, 300); | |
| } | |
| resizePage(); | |
| window.addEventListener('resize', resizePage); | |
| input.addEventListener('keyup', keyup); | |
| input.focus(); | |
| addPrompt(0); |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/plugins/CSSPlugin.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/easing/EasePack.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenLite.min.js"></script> |
| html, body { | |
| width: 100%; | |
| height: 100%; | |
| overflow: hidden; | |
| font-family: 'Rubik Mono One', sans-serif; | |
| background: #22292C; | |
| } | |
| svg { | |
| width: 100%; | |
| height: 100%; | |
| position: absolute; | |
| top: 0px; | |
| left: 0px; | |
| z-index: 0; | |
| } | |
| .input { | |
| position: absolute; | |
| z-index: 1; | |
| bottom: 0px; | |
| font-size: 20px; | |
| text-align: center; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| font-family: helvetica, sans-serif; | |
| bottom: 20px; | |
| background: none; | |
| border: 1px solid #ddd; | |
| color: #eee; | |
| } | |
| .text, .offscreen-text { | |
| width: 100%; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| display: block; | |
| position: absolute; | |
| margin: 0; | |
| } | |
| .offscreen-text { | |
| text-align: center; | |
| top: -9999px; | |
| } | |
| .text span { | |
| position: absolute; | |
| } | |
having a bit of fun with GSAP and SVG.
The rainbow color scheme being used here I first saw on https://githubuniverse.com
A Pen by Rachel Smith on CodePen.