├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── dist ├── favicon.a64e97b2.ico ├── index.b5e7c14d.js ├── index.d73f7ad5.css ├── index.e9a2d1b4.js └── index.html ├── package.json ├── src.zip └── src ├── css └── base.css ├── favicon.ico ├── index.html └── js ├── index.js ├── typeShuffle.js └── utils.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .cache 3 | .parcel-cache 4 | package-lock.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2009 - 2022 [Codrops](https://tympanus.net/codrops) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Type Shuffle Animation 2 | 3 | A shuffling type animation based on [this effect](http://lcd.ertdfgcvb.xyz/). 4 | 5 | ![Type Shuffle](https://tympanus.net/codrops/wp-content/uploads/2023/02/typeshuffle.jpg) 6 | 7 | [Article on Codrops](https://tympanus.net/codrops/?p=70337) 8 | 9 | [Demo](http://tympanus.net/Development/TypeShuffleAnimation/) 10 | 11 | 12 | ## Installation 13 | 14 | Install dependencies: 15 | 16 | ``` 17 | npm install 18 | ``` 19 | 20 | Compile the code for development and start a local server: 21 | 22 | ``` 23 | npm start 24 | ``` 25 | 26 | Create the build: 27 | 28 | ``` 29 | npm run build 30 | ``` 31 | 32 | ## Misc 33 | 34 | Follow Codrops: [Twitter](http://www.twitter.com/codrops), [Facebook](http://www.facebook.com/codrops), [GitHub](https://github.com/codrops), [Instagram](https://www.instagram.com/codropsss/) 35 | 36 | ## License 37 | [MIT](LICENSE) 38 | 39 | Made with :blue_heart: by [Codrops](http://www.codrops.com) 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /dist/favicon.a64e97b2.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/TypeShuffleAnimation/8f171f1f58d4ac8e1ede46109674f5c71005f166/dist/favicon.a64e97b2.ico -------------------------------------------------------------------------------- /dist/index.b5e7c14d.js: -------------------------------------------------------------------------------- 1 | !function(){function t(t){return t&&t.__esModule?t.default:t}const e=(t,e)=>Math.floor(Math.random()*(e-t+1))+t;function o(t,e,o){return e in t?Object.defineProperty(t,e,{value:o,enumerable:!0,configurable:!0,writable:!0}):t[e]=o,t}var s,i={};i=function(){"use strict";var t=document,e=t.createTextNode.bind(t);function o(t,e,o){t.style.setProperty(e,o)}function s(t,e){return t.appendChild(e)}function i(e,o,i,n){var r=t.createElement("span");return o&&(r.className=o),i&&(!n&&r.setAttribute("data-"+o,i),r.textContent=i),e&&s(e,r)||r}function n(t,e){return t.getAttribute("data-"+e)}function r(e,o){return e&&0!=e.length?e.nodeName?[e]:[].slice.call(e[0].nodeName?e:(o||t).querySelectorAll(e)):[]}function l(t){for(var e=[];t--;)e[t]=[];return e}function c(t,e){t&&t.some(e)}function a(t){return function(e){return t[e]}}function h(t,e,s){var i="--"+e,n=i+"-index";c(s,(function(t,e){Array.isArray(t)?c(t,(function(t){o(t,n,e)})):o(t,n,e)})),o(t,i+"-total",s.length)}var u={};function f(t,e,o){var s=o.indexOf(t);if(-1==s)o.unshift(t),c(u[t].depends,(function(e){f(e,t,o)}));else{var i=o.indexOf(e);o.splice(s,1),o.splice(i,0,t)}return o}function m(t,e,o,s){return{by:t,depends:e,key:o,split:s}}function d(t){return f(t,0,[]).map(a(u))}function p(t){u[t.by]=t}function g(t,o,n,l,a){t.normalize();var h=[],u=document.createDocumentFragment();l&&h.push(t.previousSibling);var f=[];return r(t.childNodes).some((function(t){if(!t.tagName||t.hasChildNodes()){if(t.childNodes&&t.childNodes.length)return f.push(t),void h.push.apply(h,g(t,o,n,l,a));var s=t.wholeText||"",r=s.trim();r.length&&(" "===s[0]&&f.push(e(" ")),c(r.split(n),(function(t,e){e&&a&&f.push(i(u,"whitespace"," ",a));var s=i(u,o,t);h.push(s),f.push(s)}))," "===s[s.length-1]&&f.push(e(" ")))}else f.push(t)})),c(f,(function(t){s(u,t)})),t.innerHTML="",s(t,u),h}var v=0;function C(t,e){for(var o in e)t[o]=e[o];return t}var M="words",y=m(M,v,"word",(function(t){return g(t,"word",/\s+/,0,1)})),b="chars",x=m(b,[M],"char",(function(t,e,o){var s=[];return c(o[M],(function(t,o){s.push.apply(s,g(t,"char","",e.whitespace&&o))})),s}));function T(t){var e=(t=t||{}).key;return r(t.target||"[data-splitting]").map((function(o){var s=o["🍌"];if(!t.force&&s)return s;s=o["🍌"]={el:o};var i=d(t.by||n(o,"splitting")||b),r=C({},t);return c(i,(function(t){if(t.split){var i=t.by,n=(e?"-"+e:"")+t.key,l=t.split(o,r,s);n&&h(o,n,l),s[i]=l,o.classList.add(i)}})),o.classList.add("splitting"),s}))}function w(t){var e=(t=t||{}).target=i();return e.innerHTML=t.content,T(t),e.outerHTML}function A(t,e,o){var s=r(e.matching||t.children,t),i={};return c(s,(function(t){var e=Math.round(t[o]);(i[e]||(i[e]=[])).push(t)})),Object.keys(i).map(Number).sort(O).map(a(i))}function O(t,e){return t-e}T.html=w,T.add=p;var D=m("lines",[M],"line",(function(t,e,o){return A(t,{matching:o[M]},"offsetTop")})),P=m("items",v,"item",(function(t,e){return r(e.matching||t.children,t)})),S=m("rows",v,"row",(function(t,e){return A(t,e,"offsetTop")})),L=m("cols",v,"col",(function(t,e){return A(t,e,"offsetLeft")})),N=m("grid",["rows","cols"]),R="layout",k=m(R,v,v,(function(t,e){var l=e.rows=+(e.rows||n(t,"rows")||1),c=e.columns=+(e.columns||n(t,"columns")||1);if(e.image=e.image||n(t,"image")||t.currentSrc||t.src,e.image){var a=r("img",t)[0];e.image=a&&(a.currentSrc||a.src)}e.image&&o(t,"background-image","url("+e.image+")");for(var h=l*c,u=[],f=i(v,"cell-grid");h--;){var m=i(f,"cell");i(m,"cell-inner"),u.push(m)}return s(t,f),u})),E=m("cellRows",[R],"row",(function(t,e,o){var s=e.rows,i=l(s);return c(o[R],(function(t,e,o){i[Math.floor(e/(o.length/s))].push(t)})),i})),H=m("cellColumns",[R],"col",(function(t,e,o){var s=e.columns,i=l(s);return c(o[R],(function(t,e){i[e%s].push(t)})),i})),q=m("cells",["cellRows","cellColumns"],"cell",(function(t,e,o){return o[R]}));return p(y),p(x),p(D),p(P),p(S),p(L),p(N),p(k),p(E),p(H),p(q),T}();class n{constructor(t){o(this,"position",-1),o(this,"cells",[]),this.position=t}}class r{set(t){this.state=t,this.DOM.el.innerHTML=this.state}constructor(t,{position:e,previousCellPosition:s}={}){o(this,"DOM",{el:null}),o(this,"position",-1),o(this,"previousCellPosition",-1),o(this,"original",void 0),o(this,"state",void 0),o(this,"color",void 0),o(this,"originalColor",void 0),o(this,"cache",void 0),this.DOM.el=t,this.original=this.DOM.el.innerHTML,this.state=this.original,this.color=this.originalColor=getComputedStyle(document.documentElement).getPropertyValue("--color-text"),this.position=e,this.previousCellPosition=s}}class l{clearCells(){for(const t of this.lines)for(const e of t.cells)e.set(" ")}getRandomChar(){return this.lettersAndSymbols[Math.floor(Math.random()*this.lettersAndSymbols.length)]}fx1(){let t=0;this.clearCells();const e=(o,s,i=0)=>{s.cache=s.state,44===i?(s.set(s.original),++t,t===this.totalChars&&(this.isAnimating=!1)):0===s.position?s.set(i<9?["*","-","'",'"'][Math.floor(4*Math.random())]:this.getRandomChar()):s.set(o.cells[s.previousCellPosition].cache)," "!=s.cache&&++i,i<45&&setTimeout((()=>e(o,s,i)),15)};for(const t of this.lines)for(const o of t.cells)setTimeout((()=>e(t,o)),200*(t.position+1))}fx2(){let t=0;const e=(o,s,i=0)=>{19===i?(s.set(s.original),s.DOM.el.style.opacity=0,setTimeout((()=>{s.DOM.el.style.opacity=1}),300),++t,t===this.totalChars&&(this.isAnimating=!1)):s.set(this.getRandomChar()),++i<20&&setTimeout((()=>e(o,s,i)),40)};for(const t of this.lines)for(const o of t.cells)setTimeout((()=>e(t,o)),30*(o.position+1))}fx3(){let t=0;this.clearCells();const o=(e,s,i=0)=>{9===i?(s.set(s.original),++t,t===this.totalChars&&(this.isAnimating=!1)):s.set(this.getRandomChar()),++i<10&&setTimeout((()=>o(e,s,i)),80)};for(const t of this.lines)for(const s of t.cells)setTimeout((()=>o(t,s)),e(0,2e3))}fx4(){let t=0;this.clearCells();const e=(o,s,i=0)=>{s.cache=s.state,29===i?(s.set(s.original),++t,t===this.totalChars&&(this.isAnimating=!1)):0===s.position?s.set(["*",":"][Math.floor(2*Math.random())]):s.set(o.cells[s.previousCellPosition].cache)," "!=s.cache&&++i,i<30&&setTimeout((()=>e(o,s,i)),15)};for(const t of this.lines)for(const o of t.cells)setTimeout((()=>e(t,o)),400*Math.abs(this.lines.length/2-t.position))}fx5(){let t=0;this.clearCells();const e=(o,s,i=0)=>{s.cache={state:s.state,color:s.color},29===i?(s.color=s.originalColor,s.DOM.el.style.color=s.color,s.set(s.original),++t,t===this.totalChars&&(this.isAnimating=!1)):0===s.position?(s.color=["#3e775d","#61dca3","#61b3dc"][Math.floor(3*Math.random())],s.DOM.el.style.color=s.color,s.set(i<9?["*","-","'",'"'][Math.floor(4*Math.random())]:this.getRandomChar())):(s.set(o.cells[s.previousCellPosition].cache.state),s.color=o.cells[s.previousCellPosition].cache.color,s.DOM.el.style.color=s.color)," "!=s.cache.state&&++i,i<30&&setTimeout((()=>e(o,s,i)),10)};for(const t of this.lines)for(const o of t.cells)setTimeout((()=>e(t,o)),200*(t.position+1))}fx6(){let t=0;const o=(s,i,n=0)=>{i.cache={state:i.state,color:i.color},14===n?(i.set(i.original),i.color=i.originalColor,i.DOM.el.style.color=i.color,++t,t===this.totalChars&&(this.isAnimating=!1)):(i.set(this.getRandomChar()),i.color=["#2b4539","#61dca3","#61b3dc"][Math.floor(3*Math.random())],i.DOM.el.style.color=i.color),++n<15&&setTimeout((()=>o(s,i,n)),e(30,110))};for(const t of this.lines)for(const e of t.cells)setTimeout((()=>o(t,e)),80*(t.position+1))}trigger(t="fx1"){t in this.effects&&!this.isAnimating&&(this.isAnimating=!0,this.effects[t]())}constructor(e){o(this,"DOM",{el:null}),o(this,"lines",[]),o(this,"lettersAndSymbols",["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","!","@","#","$","&","*","(",")","-","_","+","=","/","[","]","{","}",";",":","<",">",",","0","1","2","3","4","5","6","7","8","9"]),o(this,"effects",{fx1:()=>this.fx1(),fx2:()=>this.fx2(),fx3:()=>this.fx3(),fx4:()=>this.fx4(),fx5:()=>this.fx5(),fx6:()=>this.fx6()}),o(this,"totalChars",0),this.DOM.el=e;const s=t(i)({target:this.DOM.el,by:"lines"});s.forEach((e=>t(i)({target:e.words})));for(const[t,e]of s[0].lines.entries()){const o=new n(t);let s=[],i=0;for(const t of e)for(const e of[...t.querySelectorAll(".char")])s.push(new r(e,{position:i,previousCellPosition:0===i?-1:i-1})),++i;o.cells=s,this.lines.push(o),this.totalChars+=i}}}(s="biu0hfr",new Promise((t=>{WebFont.load({typekit:{id:s},active:t})}))).then((()=>{document.body.classList.remove("loading");const t=document.querySelector(".content"),e=new l(t);e.trigger("fx1"),[...document.querySelectorAll(".effects > button")].forEach((t=>{t.addEventListener("click",(()=>{e.trigger(`fx${t.dataset.fx}`)}))}))}))}(); -------------------------------------------------------------------------------- /dist/index.d73f7ad5.css: -------------------------------------------------------------------------------- 1 | *,:after,:before{box-sizing:border-box}:root{--color-text:#fff;--color-bg:#000;--color-link:#61dca3;--color-link-hover:#fff;font-size:18px}body{color:var(--color-text);background-color:var(--color-bg);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-transform:uppercase;margin:0;font-family:ballinger-mono,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif}.js .loading:before,.js .loading:after{content:"";z-index:1000;position:fixed}.js .loading:before{width:100%;height:100%;background:var(--color-bg);top:0;left:0}.js .loading:after{width:60px;height:60px;opacity:.4;background:var(--color-link);border-radius:50%;margin:-30px 0 0 -30px;animation:.7s linear infinite alternate forwards loaderAnim;top:50%;left:50%}@keyframes loaderAnim{to{opacity:1;transform:scale(.5)}}a{color:var(--color-link);cursor:pointer;outline:none;text-decoration:none}a:not(.frame__title-back){white-space:nowrap;position:relative;overflow:hidden}a:hover{color:var(--color-link-hover);outline:none}a:focus{background:#d3d3d3;outline:none}a:focus:not(:focus-visible){background:0 0}a:focus-visible{background:0 0;outline:2px solid red}.unbutton{font:inherit;cursor:pointer;background:0 0;border:0;margin:0;padding:0}.unbutton:focus{outline:none}main{height:100vh;flex-direction:column;display:flex}.frame{color:var(--color-title);grid-gap:2rem;text-transform:lowercase;align-items:start;margin-bottom:3rem;padding:1rem 2rem;font-size:.75rem}.frame a:not(.frame__title-back):before{content:"";height:1px;width:100%;transform-origin:0%;background:currentColor;transition:transform .3s;position:absolute;top:90%}.frame a:not(.frame__title-back):hover:before{transform-origin:100%;transform:scaleX(0)}.frame__title{grid-area:title;display:flex}.frame__title-main{font-size:inherit;font-weight:inherit;margin:0}.frame__title-back{align-items:flex-end;display:flex;position:relative}.frame__title-back span{display:none}.frame__title-back svg{fill:currentColor}.frame__prev{grid-area:prev;align-self:start}.ascii{color:var(--color-link);opacity:.5;pointer-events:none;position:fixed;top:-1rem;right:0}.content{max-width:830px;gap:1rem;padding:7vh 2rem 0;display:grid}dt{font-weight:700}dd{margin:0}.effects{flex-wrap:wrap;gap:1rem;margin-top:auto;padding:2rem;display:flex}.effects button{color:inherit;white-space:nowrap;text-transform:inherit;font:inherit;cursor:pointer;color:var(--color-link);text-transform:lowercase;background:0 0;border:0;font-size:.75rem}.effects button:before{content:"[";margin-right:.5rem;display:inline-block}.effects button:after{content:"]";margin-left:.5rem;display:inline-block}.effects button:hover{color:var(--color-link-hover)}.splitting .words .word{white-space:nowrap}@media screen and (min-width:53em){.frame{grid-template:"title prev sponsor"/auto auto 1fr;justify-content:start;display:grid}.content{grid-template-columns:15ch 1fr}}.splitting .word,.splitting .char{display:inline-block}.splitting .char{position:relative}.splitting .char:before,.splitting .char:after{content:attr(data-char);visibility:hidden;-webkit-user-select:none;user-select:none;transition:inherit;position:absolute;top:0;left:0}.splitting{--word-center:calc((var(--word-total) - 1)/2);--char-center:calc((var(--char-total) - 1)/2);--line-center:calc((var(--line-total) - 1)/2)}.splitting .word{--word-percent:calc(var(--word-index)/var(--word-total));--line-percent:calc(var(--line-index)/var(--line-total))}.splitting .char{--char-percent:calc(var(--char-index)/var(--char-total));--char-offset:calc(var(--char-index) - var(--char-center));--distance:calc((var(--char-offset)*var(--char-offset))/var(--char-center));--distance-sine:calc(var(--char-offset)/var(--char-center));--distance-percent:calc((var(--distance)/var(--char-center)))}.splitting.cells img{width:100%;display:block}@supports (display:grid ){.splitting.cells{visibility:hidden;background-size:cover;position:relative;overflow:hidden}.splitting .cell-grid{background:inherit;width:100%;height:100%;grid-template:repeat(var(--row-total),1fr)/repeat(var(--col-total),1fr);display:grid;position:absolute;top:0;left:0}.splitting .cell{background:inherit;position:relative;overflow:hidden}.splitting .cell-inner{background:inherit;visibility:visible;width:calc(100%*var(--col-total));height:calc(100%*var(--row-total));left:calc(-100%*var(--col-index));top:calc(-100%*var(--row-index));position:absolute}.splitting .cell{--center-x:calc((var(--col-total) - 1)/2);--center-y:calc((var(--row-total) - 1)/2);--offset-x:calc(var(--col-index) - var(--center-x));--offset-y:calc(var(--row-index) - var(--center-y));--distance-x:calc((var(--offset-x)*var(--offset-x))/var(--center-x));--distance-y:calc((var(--offset-y)*var(--offset-y))/var(--center-y))}} -------------------------------------------------------------------------------- /dist/index.e9a2d1b4.js: -------------------------------------------------------------------------------- 1 | function t(t){return t&&t.__esModule?t.default:t}const e=(t,e)=>Math.floor(Math.random()*(e-t+1))+t;function o(t,e,o){return e in t?Object.defineProperty(t,e,{value:o,enumerable:!0,configurable:!0,writable:!0}):t[e]=o,t}var s,i={};i=function(){var t=document,e=t.createTextNode.bind(t);function o(t,e,o){t.style.setProperty(e,o)}function s(t,e){return t.appendChild(e)}function i(e,o,i,n){var r=t.createElement("span");return o&&(r.className=o),i&&(!n&&r.setAttribute("data-"+o,i),r.textContent=i),e&&s(e,r)||r}function n(t,e){return t.getAttribute("data-"+e)}function r(e,o){return e&&0!=e.length?e.nodeName?[e]:[].slice.call(e[0].nodeName?e:(o||t).querySelectorAll(e)):[]}function l(t){for(var e=[];t--;)e[t]=[];return e}function c(t,e){t&&t.some(e)}function a(t){return function(e){return t[e]}}function h(t,e,s){var i="--"+e,n=i+"-index";c(s,(function(t,e){Array.isArray(t)?c(t,(function(t){o(t,n,e)})):o(t,n,e)})),o(t,i+"-total",s.length)}var u={};function f(t,e,o){var s=o.indexOf(t);if(-1==s)o.unshift(t),c(u[t].depends,(function(e){f(e,t,o)}));else{var i=o.indexOf(e);o.splice(s,1),o.splice(i,0,t)}return o}function m(t,e,o,s){return{by:t,depends:e,key:o,split:s}}function d(t){return f(t,0,[]).map(a(u))}function p(t){u[t.by]=t}function g(t,o,n,l,a){t.normalize();var h=[],u=document.createDocumentFragment();l&&h.push(t.previousSibling);var f=[];return r(t.childNodes).some((function(t){if(!t.tagName||t.hasChildNodes()){if(t.childNodes&&t.childNodes.length)return f.push(t),void h.push.apply(h,g(t,o,n,l,a));var s=t.wholeText||"",r=s.trim();r.length&&(" "===s[0]&&f.push(e(" ")),c(r.split(n),(function(t,e){e&&a&&f.push(i(u,"whitespace"," ",a));var s=i(u,o,t);h.push(s),f.push(s)}))," "===s[s.length-1]&&f.push(e(" ")))}else f.push(t)})),c(f,(function(t){s(u,t)})),t.innerHTML="",s(t,u),h}var v=0;function C(t,e){for(var o in e)t[o]=e[o];return t}var M="words",y=m(M,v,"word",(function(t){return g(t,"word",/\s+/,0,1)})),b="chars",x=m(b,[M],"char",(function(t,e,o){var s=[];return c(o[M],(function(t,o){s.push.apply(s,g(t,"char","",e.whitespace&&o))})),s}));function T(t){var e=(t=t||{}).key;return r(t.target||"[data-splitting]").map((function(o){var s=o["🍌"];if(!t.force&&s)return s;s=o["🍌"]={el:o};var i=d(t.by||n(o,"splitting")||b),r=C({},t);return c(i,(function(t){if(t.split){var i=t.by,n=(e?"-"+e:"")+t.key,l=t.split(o,r,s);n&&h(o,n,l),s[i]=l,o.classList.add(i)}})),o.classList.add("splitting"),s}))}function w(t){var e=(t=t||{}).target=i();return e.innerHTML=t.content,T(t),e.outerHTML}function A(t,e,o){var s=r(e.matching||t.children,t),i={};return c(s,(function(t){var e=Math.round(t[o]);(i[e]||(i[e]=[])).push(t)})),Object.keys(i).map(Number).sort(O).map(a(i))}function O(t,e){return t-e}T.html=w,T.add=p;var D=m("lines",[M],"line",(function(t,e,o){return A(t,{matching:o[M]},"offsetTop")})),P=m("items",v,"item",(function(t,e){return r(e.matching||t.children,t)})),S=m("rows",v,"row",(function(t,e){return A(t,e,"offsetTop")})),L=m("cols",v,"col",(function(t,e){return A(t,e,"offsetLeft")})),N=m("grid",["rows","cols"]),R="layout",k=m(R,v,v,(function(t,e){var l=e.rows=+(e.rows||n(t,"rows")||1),c=e.columns=+(e.columns||n(t,"columns")||1);if(e.image=e.image||n(t,"image")||t.currentSrc||t.src,e.image){var a=r("img",t)[0];e.image=a&&(a.currentSrc||a.src)}e.image&&o(t,"background-image","url("+e.image+")");for(var h=l*c,u=[],f=i(v,"cell-grid");h--;){var m=i(f,"cell");i(m,"cell-inner"),u.push(m)}return s(t,f),u})),E=m("cellRows",[R],"row",(function(t,e,o){var s=e.rows,i=l(s);return c(o[R],(function(t,e,o){i[Math.floor(e/(o.length/s))].push(t)})),i})),H=m("cellColumns",[R],"col",(function(t,e,o){var s=e.columns,i=l(s);return c(o[R],(function(t,e){i[e%s].push(t)})),i})),q=m("cells",["cellRows","cellColumns"],"cell",(function(t,e,o){return o[R]}));return p(y),p(x),p(D),p(P),p(S),p(L),p(N),p(k),p(E),p(H),p(q),T}();class n{constructor(t){o(this,"position",-1),o(this,"cells",[]),this.position=t}}class r{set(t){this.state=t,this.DOM.el.innerHTML=this.state}constructor(t,{position:e,previousCellPosition:s}={}){o(this,"DOM",{el:null}),o(this,"position",-1),o(this,"previousCellPosition",-1),o(this,"original",void 0),o(this,"state",void 0),o(this,"color",void 0),o(this,"originalColor",void 0),o(this,"cache",void 0),this.DOM.el=t,this.original=this.DOM.el.innerHTML,this.state=this.original,this.color=this.originalColor=getComputedStyle(document.documentElement).getPropertyValue("--color-text"),this.position=e,this.previousCellPosition=s}}class l{clearCells(){for(const t of this.lines)for(const e of t.cells)e.set(" ")}getRandomChar(){return this.lettersAndSymbols[Math.floor(Math.random()*this.lettersAndSymbols.length)]}fx1(){let t=0;this.clearCells();const e=(o,s,i=0)=>{s.cache=s.state,44===i?(s.set(s.original),++t,t===this.totalChars&&(this.isAnimating=!1)):0===s.position?s.set(i<9?["*","-","'",'"'][Math.floor(4*Math.random())]:this.getRandomChar()):s.set(o.cells[s.previousCellPosition].cache)," "!=s.cache&&++i,i<45&&setTimeout((()=>e(o,s,i)),15)};for(const t of this.lines)for(const o of t.cells)setTimeout((()=>e(t,o)),200*(t.position+1))}fx2(){let t=0;const e=(o,s,i=0)=>{19===i?(s.set(s.original),s.DOM.el.style.opacity=0,setTimeout((()=>{s.DOM.el.style.opacity=1}),300),++t,t===this.totalChars&&(this.isAnimating=!1)):s.set(this.getRandomChar()),++i<20&&setTimeout((()=>e(o,s,i)),40)};for(const t of this.lines)for(const o of t.cells)setTimeout((()=>e(t,o)),30*(o.position+1))}fx3(){let t=0;this.clearCells();const o=(e,s,i=0)=>{9===i?(s.set(s.original),++t,t===this.totalChars&&(this.isAnimating=!1)):s.set(this.getRandomChar()),++i<10&&setTimeout((()=>o(e,s,i)),80)};for(const t of this.lines)for(const s of t.cells)setTimeout((()=>o(t,s)),e(0,2e3))}fx4(){let t=0;this.clearCells();const e=(o,s,i=0)=>{s.cache=s.state,29===i?(s.set(s.original),++t,t===this.totalChars&&(this.isAnimating=!1)):0===s.position?s.set(["*",":"][Math.floor(2*Math.random())]):s.set(o.cells[s.previousCellPosition].cache)," "!=s.cache&&++i,i<30&&setTimeout((()=>e(o,s,i)),15)};for(const t of this.lines)for(const o of t.cells)setTimeout((()=>e(t,o)),400*Math.abs(this.lines.length/2-t.position))}fx5(){let t=0;this.clearCells();const e=(o,s,i=0)=>{s.cache={state:s.state,color:s.color},29===i?(s.color=s.originalColor,s.DOM.el.style.color=s.color,s.set(s.original),++t,t===this.totalChars&&(this.isAnimating=!1)):0===s.position?(s.color=["#3e775d","#61dca3","#61b3dc"][Math.floor(3*Math.random())],s.DOM.el.style.color=s.color,s.set(i<9?["*","-","'",'"'][Math.floor(4*Math.random())]:this.getRandomChar())):(s.set(o.cells[s.previousCellPosition].cache.state),s.color=o.cells[s.previousCellPosition].cache.color,s.DOM.el.style.color=s.color)," "!=s.cache.state&&++i,i<30&&setTimeout((()=>e(o,s,i)),10)};for(const t of this.lines)for(const o of t.cells)setTimeout((()=>e(t,o)),200*(t.position+1))}fx6(){let t=0;const o=(s,i,n=0)=>{i.cache={state:i.state,color:i.color},14===n?(i.set(i.original),i.color=i.originalColor,i.DOM.el.style.color=i.color,++t,t===this.totalChars&&(this.isAnimating=!1)):(i.set(this.getRandomChar()),i.color=["#2b4539","#61dca3","#61b3dc"][Math.floor(3*Math.random())],i.DOM.el.style.color=i.color),++n<15&&setTimeout((()=>o(s,i,n)),e(30,110))};for(const t of this.lines)for(const e of t.cells)setTimeout((()=>o(t,e)),80*(t.position+1))}trigger(t="fx1"){t in this.effects&&!this.isAnimating&&(this.isAnimating=!0,this.effects[t]())}constructor(e){o(this,"DOM",{el:null}),o(this,"lines",[]),o(this,"lettersAndSymbols",["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","!","@","#","$","&","*","(",")","-","_","+","=","/","[","]","{","}",";",":","<",">",",","0","1","2","3","4","5","6","7","8","9"]),o(this,"effects",{fx1:()=>this.fx1(),fx2:()=>this.fx2(),fx3:()=>this.fx3(),fx4:()=>this.fx4(),fx5:()=>this.fx5(),fx6:()=>this.fx6()}),o(this,"totalChars",0),this.DOM.el=e;const s=t(i)({target:this.DOM.el,by:"lines"});s.forEach((e=>t(i)({target:e.words})));for(const[t,e]of s[0].lines.entries()){const o=new n(t);let s=[],i=0;for(const t of e)for(const e of[...t.querySelectorAll(".char")])s.push(new r(e,{position:i,previousCellPosition:0===i?-1:i-1})),++i;o.cells=s,this.lines.push(o),this.totalChars+=i}}}(s="biu0hfr",new Promise((t=>{WebFont.load({typekit:{id:s},active:t})}))).then((()=>{document.body.classList.remove("loading");const t=document.querySelector(".content"),e=new l(t);e.trigger("fx1"),[...document.querySelectorAll(".effects > button")].forEach((t=>{t.addEventListener("click",(()=>{e.trigger(`fx${t.dataset.fx}`)}))}))})); -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | Type Shuffle Animation | Codrops
 2 |           .'   .*'   `*.   `. ; / .'    .*'`-.
 3 |         .'   .'         \    \ : /   .*'      `-.
 4 |        /    /            ,   ',          -.      `.
 5 |       /    /      _.._   :   '.   .-*""*-.    `.   \
 6 |      /   .'    .-'    `-.   '/ .'         `-.   \   \
 7 |     ,  ,'   .'   .*""*-.\,: ,.-*"*-.        `.       .
 8 |       /   .'   .'       `  `        `.        `.
 9 |    '     /    /                       `.        \ .   `
10 |   ,     .    /                          \        \ `   ;
11 |   :         ,                            \    :   \ `  :
12 |   ;    .                                  \    .   \ \ |
13 |   | '  :   ,                               .    \   ; ;:
14 |   |:   ;                                      `  \  : ;|
15 |   ||   |  ;                                 .     ; ; ;;
16 |   ;:   |  |                                    `  ; | ||
17 |   L.\  |  |  .d$$s.                    .s$$b..  ; : |.:J
18 |  / __`';  | *'   `*Tb._            _.dP*'   `*  | | :__ \
19 | ..' .`.:  |         `*Ts'        `sP*'        ; |   '. `,,
20 | ;  /   ,  ;   .+s**s.   `.           .s**s+.  :_;-.'  \  :
21 | : ,   /:; :   \ *ss* \    ;         / *ss* /    +: \   . ;
22 |  .`  :  :  ,  .+s$$$s+.            .+s$$$s+.  .* ;  ;  ',
23 |   \   *.   ;*d$P*"$$$T$b  ,+**+,  d$P*"$$$T$b*   .*    /
24 |    \     ; ::$; +:$$$:$$;*      *:$; +:$$$:$$;  :     /
25 |          :   T$b._$$$d$P          T$b._$$$d$P   ;
26 |      `._.;  ; `*T$$$P*'            `*T$$$P*'    :._.'
27 |          |; :             '                     |   `.
28 |          ;:  \           :.     ,               : `.  \
29 |          ` \  `._        `*.__.*'               '   \  \
30 |           . *--*'           ""                 ,     ;  .
31 |            \                                  / .       :
32 |             \          .+*"*--*"*+.          /   `      |
33 |              `.       :._.--..--._.;       .';    ;  :  ;
34 |                ;.      `.        .'      .'  |    |  ,
35 |                : `.      `*----*'      .'    |    |    :
36 |                |   `.                .'      |    :   .
37 |                :     `.            .'        :    '  / `.
38 |               /        `-.      .-'          /`. / .'    `.
39 |              /            `****'            .        `.    \
40 |            .'                              ,    '  \   \    \
41 |         _.'                                    /    :   .    ,
42 |    _.-*' `.                               :   :     |   :    :
43 |            `-.                                .         ;    |
44 |               `-.                        .-`           ,     ;
45 |                  `.     `.     .*      .'   `.   `.  .'     ,
46 |                    `.     `-  '      .'       `.          .'
47 |                      `.            .'          `+._     / `.
48 |                        `.        .'             :     / .    \
49 |                          `.    .'               |    :  ;     .
50 |                            `..'                 :    ;    `   ;
51 |                                                  .   :     ;  :
52 |                                                   \   `   /   ;
53 |                                                    `.  \     /
54 |                                                      `-.\  .'
55 | 		

Type Shuffle Animation based on LCD 1

Back to the article
Previous demo
Name
Aria McDonald
Profession
Creative Web Developer
Bio
5 years experience - HTML, CSS, JavaScript. Passion for creativity in the digital space. Problem solver. Hiker, guitar player, culinary enthusiast. Constantly seeking new challenges, growth opportunities.Bringing imaginative ideas to life. Skilled in modern web development frameworks such as React and Angular. Strong understanding of UI/UX design principles and ability to create visually appealing and usable websites.
Projects
Eco Explorer, SkyBridge, SparkSail
Awards
Best User Experience Design, Webby Awards 2021
-------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typeshuffleanimation", 3 | "version": "1.0.0", 4 | "browserslist": "> 0.5%, last 2 versions, not dead", 5 | "description": "", 6 | "scripts": { 7 | "start": "parcel src/index.html --open", 8 | "clean": "rm -rf dist/*", 9 | "build:parcel": "parcel build src/index.html --no-content-hash --no-source-maps --public-url ./", 10 | "build": "npm run clean && npm run build:parcel" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/codrops/[NAME].git" 15 | }, 16 | "keywords": [], 17 | "author": "Codrops", 18 | "license": "MIT", 19 | "homepage": "https://tympanus.net/codrops", 20 | "bugs": { 21 | "url": "https://github.com/codrops/[NAME]/issues" 22 | }, 23 | "devDependencies": { 24 | "parcel": "^2.8.3" 25 | }, 26 | "dependencies": { 27 | "splitting": "^1.0.6" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/TypeShuffleAnimation/8f171f1f58d4ac8e1ede46109674f5c71005f166/src.zip -------------------------------------------------------------------------------- /src/css/base.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::after, 3 | *::before { 4 | box-sizing: border-box; 5 | } 6 | 7 | :root { 8 | font-size: 18px; 9 | --color-text: #fff; 10 | --color-bg: #000; 11 | --color-link: #61dca3; 12 | --color-link-hover: #fff; 13 | } 14 | 15 | body { 16 | margin: 0; 17 | color: var(--color-text); 18 | background-color: var(--color-bg); 19 | font-family: ballinger-mono, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif; 20 | -webkit-font-smoothing: antialiased; 21 | -moz-osx-font-smoothing: grayscale; 22 | text-transform: uppercase; 23 | } 24 | 25 | /* Page Loader */ 26 | .js .loading::before, 27 | .js .loading::after { 28 | content: ''; 29 | position: fixed; 30 | z-index: 1000; 31 | } 32 | 33 | .js .loading::before { 34 | top: 0; 35 | left: 0; 36 | width: 100%; 37 | height: 100%; 38 | background: var(--color-bg); 39 | } 40 | 41 | .js .loading::after { 42 | top: 50%; 43 | left: 50%; 44 | width: 60px; 45 | height: 60px; 46 | margin: -30px 0 0 -30px; 47 | border-radius: 50%; 48 | opacity: 0.4; 49 | background: var(--color-link); 50 | animation: loaderAnim 0.7s linear infinite alternate forwards; 51 | 52 | } 53 | 54 | @keyframes loaderAnim { 55 | to { 56 | opacity: 1; 57 | transform: scale3d(0.5,0.5,1); 58 | } 59 | } 60 | 61 | a { 62 | text-decoration: none; 63 | color: var(--color-link); 64 | outline: none; 65 | cursor: pointer; 66 | } 67 | 68 | a:not(.frame__title-back) { 69 | white-space: nowrap; 70 | overflow: hidden; 71 | position: relative; 72 | } 73 | 74 | a:hover { 75 | color: var(--color-link-hover); 76 | outline: none; 77 | } 78 | 79 | /* Better focus styles from https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible */ 80 | a:focus { 81 | /* Provide a fallback style for browsers 82 | that don't support :focus-visible */ 83 | outline: none; 84 | background: lightgrey; 85 | } 86 | 87 | a:focus:not(:focus-visible) { 88 | /* Remove the focus indicator on mouse-focus for browsers 89 | that do support :focus-visible */ 90 | background: transparent; 91 | } 92 | 93 | a:focus-visible { 94 | /* Draw a very noticeable focus style for 95 | keyboard-focus on browsers that do support 96 | :focus-visible */ 97 | outline: 2px solid red; 98 | background: transparent; 99 | } 100 | 101 | .unbutton { 102 | background: none; 103 | border: 0; 104 | padding: 0; 105 | margin: 0; 106 | font: inherit; 107 | cursor: pointer; 108 | } 109 | 110 | .unbutton:focus { 111 | outline: none; 112 | } 113 | 114 | main { 115 | height: 100vh; 116 | display: flex; 117 | flex-direction: column; 118 | } 119 | 120 | .frame { 121 | color: var(--color-title); 122 | padding: 1rem 2rem; 123 | margin-bottom: 3rem; 124 | grid-gap: 2rem; 125 | align-items: start; 126 | text-transform: lowercase; 127 | font-size: 0.75rem; 128 | } 129 | 130 | .frame a:not(.frame__title-back)::before { 131 | content: ''; 132 | height: 1px; 133 | width: 100%; 134 | background: currentColor; 135 | position: absolute; 136 | top: 90%; 137 | transition: transform 0.3s; 138 | transform-origin: 0% 50%; 139 | } 140 | 141 | .frame a:not(.frame__title-back):hover::before { 142 | transform: scaleX(0); 143 | transform-origin: 100% 50%; 144 | } 145 | 146 | .frame__title { 147 | grid-area: title; 148 | display: flex; 149 | } 150 | 151 | .frame__title-main { 152 | font-size: inherit; 153 | margin: 0; 154 | font-weight: inherit; 155 | } 156 | 157 | .frame__title-back { 158 | position: relative; 159 | display: flex; 160 | align-items: flex-end; 161 | } 162 | 163 | .frame__title-back span { 164 | display: none; 165 | } 166 | 167 | .frame__title-back svg { 168 | fill: currentColor; 169 | } 170 | 171 | .frame__prev { 172 | grid-area: prev; 173 | align-self: start; 174 | } 175 | 176 | .ascii { 177 | color: var(--color-link); 178 | opacity: 0.5; 179 | position: fixed; 180 | right: 0; 181 | top: -1rem; 182 | pointer-events: none; 183 | } 184 | 185 | .content { 186 | display: grid; 187 | gap: 1rem; 188 | max-width: 830px; 189 | padding: 7vh 2rem 0; 190 | } 191 | 192 | dt { 193 | font-weight: bold; 194 | } 195 | 196 | dd { 197 | margin: 0; 198 | } 199 | 200 | .effects { 201 | margin-top: auto; 202 | padding: 2rem; 203 | display: flex; 204 | flex-wrap: wrap; 205 | gap: 1rem; 206 | } 207 | 208 | .effects button { 209 | border: 0; 210 | color: inherit; 211 | white-space: nowrap; 212 | background: none; 213 | text-transform: inherit; 214 | font: inherit; 215 | cursor: pointer; 216 | color: var(--color-link); 217 | font-size: 0.75rem; 218 | text-transform: lowercase; 219 | } 220 | 221 | .effects button::before { 222 | content: "["; 223 | margin-right: 0.5rem; 224 | display: inline-block; 225 | } 226 | 227 | .effects button::after { 228 | content: "]"; 229 | margin-left: 0.5rem; 230 | display: inline-block; 231 | } 232 | 233 | .effects button:hover { 234 | color: var(--color-link-hover); 235 | } 236 | 237 | .splitting .words .word { 238 | white-space: nowrap; 239 | } 240 | 241 | @media screen and (min-width: 53em) { 242 | .frame { 243 | display: grid; 244 | grid-template-columns: auto auto 1fr; 245 | grid-template-rows: auto; 246 | grid-template-areas: 'title prev sponsor'; 247 | justify-content: start; 248 | } 249 | .content { 250 | grid-template-columns: 15ch 1fr; 251 | } 252 | } 253 | 254 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/TypeShuffleAnimation/8f171f1f58d4ac8e1ede46109674f5c71005f166/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Type Shuffle Animation | Codrops 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | 23 | 24 |
25 |
 26 |           .'   .*'   `*.   `. ; / .'    .*'`-.
 27 |         .'   .'         \    \ : /   .*'      `-.
 28 |        /    /            ,   ',          -.      `.
 29 |       /    /      _.._   :   '.   .-*""*-.    `.   \
 30 |      /   .'    .-'    `-.   '/ .'         `-.   \   \
 31 |     ,  ,'   .'   .*""*-.\,: ,.-*"*-.        `.       .
 32 |       /   .'   .'       `  `        `.        `.
 33 |    '     /    /                       `.        \ .   `
 34 |   ,     .    /                          \        \ `   ;
 35 |   :         ,                            \    :   \ `  :
 36 |   ;    .                                  \    .   \ \ |
 37 |   | '  :   ,                               .    \   ; ;:
 38 |   |:   ;                                      `  \  : ;|
 39 |   ||   |  ;                                 .     ; ; ;;
 40 |   ;:   |  |                                    `  ; | ||
 41 |   L.\  |  |  .d$$s.                    .s$$b..  ; : |.:J
 42 |  / __`';  | *'   `*Tb._            _.dP*'   `*  | | :__ \
 43 | ..' .`.:  |         `*Ts'        `sP*'        ; |   '. `,,
 44 | ;  /   ,  ;   .+s**s.   `.           .s**s+.  :_;-.'  \  :
 45 | : ,   /:; :   \ *ss* \    ;         / *ss* /    +: \   . ;
 46 |  .`  :  :  ,  .+s$$$s+.            .+s$$$s+.  .* ;  ;  ',
 47 |   \   *.   ;*d$P*"$$$T$b  ,+**+,  d$P*"$$$T$b*   .*    /
 48 |    \     ; ::$; +:$$$:$$;*      *:$; +:$$$:$$;  :     /
 49 |          :   T$b._$$$d$P          T$b._$$$d$P   ;
 50 |      `._.;  ; `*T$$$P*'            `*T$$$P*'    :._.'
 51 |          |; :             '                     |   `.
 52 |          ;:  \           :.     ,               : `.  \
 53 |          ` \  `._        `*.__.*'               '   \  \
 54 |           . *--*'           ""                 ,     ;  .
 55 |            \                                  / .       :
 56 |             \          .+*"*--*"*+.          /   `      |
 57 |              `.       :._.--..--._.;       .';    ;  :  ;
 58 |                ;.      `.        .'      .'  |    |  ,
 59 |                : `.      `*----*'      .'    |    |    :
 60 |                |   `.                .'      |    :   .
 61 |                :     `.            .'        :    '  / `.
 62 |               /        `-.      .-'          /`. / .'    `.
 63 |              /            `****'            .        `.    \
 64 |            .'                              ,    '  \   \    \
 65 |         _.'                                    /    :   .    ,
 66 |    _.-*' `.                               :   :     |   :    :
 67 |            `-.                                .         ;    |
 68 |               `-.                        .-`           ,     ;
 69 |                  `.     `.     .*      .'   `.   `.  .'     ,
 70 |                    `.     `-  '      .'       `.          .'
 71 |                      `.            .'          `+._     / `.
 72 |                        `.        .'             :     / .    \
 73 |                          `.    .'               |    :  ;     .
 74 |                            `..'                 :    ;    `   ;
 75 |                                                  .   :     ;  :
 76 |                                                   \   `   /   ;
 77 |                                                    `.  \     /
 78 |                                                      `-.\  .'
 79 | 		
80 |
81 |
82 |

Type Shuffle Animation based on LCD 1

83 | 84 | Back to the article 85 | 86 | 87 | 88 | 89 |
90 | Previous demo 91 |
92 |
93 |
Name
94 |
Aria McDonald
95 |
Profession
96 |
Creative Web Developer
97 |
Bio
98 |
5 years experience - HTML, CSS, JavaScript. Passion for creativity in the digital space. Problem solver. Hiker, guitar player, culinary enthusiast. Constantly seeking new challenges, growth opportunities.Bringing imaginative ideas to life. Skilled in modern web development frameworks such as React and Angular. Strong understanding of UI/UX design principles and ability to create visually appealing and usable websites.
99 |
Projects
100 |
Eco Explorer, SkyBridge, SparkSail
101 |
Awards
102 |
Best User Experience Design, Webby Awards 2021
103 |
104 |
105 | 106 | 107 | 108 | 109 | 110 | 111 |
112 |
113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /src/js/index.js: -------------------------------------------------------------------------------- 1 | import { preloadFonts } from './utils'; 2 | import { TypeShuffle } from './typeShuffle'; 3 | 4 | preloadFonts('biu0hfr').then(() => { 5 | document.body.classList.remove('loading'); 6 | 7 | const textElement = document.querySelector('.content'); 8 | 9 | const ts = new TypeShuffle(textElement); 10 | ts.trigger('fx1'); 11 | 12 | [...document.querySelectorAll('.effects > button')].forEach(button => { 13 | button.addEventListener('click', () => { 14 | ts.trigger(`fx${button.dataset.fx}`); 15 | }); 16 | }); 17 | 18 | }); -------------------------------------------------------------------------------- /src/js/typeShuffle.js: -------------------------------------------------------------------------------- 1 | import 'splitting/dist/splitting.css'; 2 | import 'splitting/dist/splitting-cells.css'; 3 | import Splitting from 'splitting'; 4 | import { randomNumber } from './utils'; 5 | 6 | /** 7 | * Class representing one line 8 | */ 9 | class Line { 10 | // line position 11 | position = -1; 12 | // cells/chars 13 | cells = []; 14 | 15 | /** 16 | * Constructor. 17 | * @param {Element} DOM_el - the char element () 18 | */ 19 | constructor(linePosition) { 20 | this.position = linePosition; 21 | } 22 | } 23 | 24 | /** 25 | * Class representing one cell/char 26 | */ 27 | class Cell { 28 | // DOM elements 29 | DOM = { 30 | // the char element () 31 | el: null, 32 | }; 33 | // cell position 34 | position = -1; 35 | // previous cell position 36 | previousCellPosition = -1; 37 | // original innerHTML 38 | original; 39 | // current state/innerHTML 40 | state; 41 | color; 42 | originalColor; 43 | // cached values 44 | cache; 45 | 46 | /** 47 | * Constructor. 48 | * @param {Element} DOM_el - the char element () 49 | */ 50 | constructor(DOM_el, { 51 | position, 52 | previousCellPosition 53 | } = {}) { 54 | this.DOM.el = DOM_el; 55 | this.original = this.DOM.el.innerHTML; 56 | this.state = this.original; 57 | this.color = this.originalColor = getComputedStyle(document.documentElement).getPropertyValue('--color-text'); 58 | this.position = position; 59 | this.previousCellPosition = previousCellPosition; 60 | } 61 | /** 62 | * @param {string} value 63 | */ 64 | set(value) { 65 | this.state = value; 66 | this.DOM.el.innerHTML = this.state; 67 | } 68 | } 69 | 70 | /** 71 | * Class representing the TypeShuffle object 72 | */ 73 | export class TypeShuffle { 74 | // DOM elements 75 | DOM = { 76 | // the main text element 77 | el: null, 78 | }; 79 | // array of Line objs 80 | lines = []; 81 | // array of letters and symbols 82 | lettersAndSymbols = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', '#', '$', '&', '*', '(', ')', '-', '_', '+', '=', '/', '[', ']', '{', '}', ';', ':', '<', '>', ',', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; 83 | // effects and respective methods 84 | effects = { 85 | 'fx1': () => this.fx1(), 86 | 'fx2': () => this.fx2(), 87 | 'fx3': () => this.fx3(), 88 | 'fx4': () => this.fx4(), 89 | 'fx5': () => this.fx5(), 90 | 'fx6': () => this.fx6(), 91 | }; 92 | totalChars = 0; 93 | 94 | /** 95 | * Constructor. 96 | * @param {Element} DOM_el - main text element 97 | */ 98 | constructor(DOM_el) { 99 | this.DOM.el = DOM_el; 100 | // Apply Splitting (two times to have lines, words and chars) 101 | const results = Splitting({ 102 | target: this.DOM.el, 103 | by: 'lines' 104 | }) 105 | results.forEach(s => Splitting({ target: s.words })); 106 | 107 | // for every line 108 | for (const [linePosition, lineArr] of results[0].lines.entries()) { 109 | // create a new Line 110 | const line = new Line(linePosition); 111 | let cells = []; 112 | let charCount = 0; 113 | // for every word of each line 114 | for (const word of lineArr) { 115 | // for every character of each line 116 | for (const char of [...word.querySelectorAll('.char')]) { 117 | cells.push( 118 | new Cell(char, { 119 | position: charCount, 120 | previousCellPosition: charCount === 0 ? -1 : charCount-1 121 | }) 122 | ); 123 | ++charCount; 124 | } 125 | } 126 | line.cells = cells; 127 | this.lines.push(line); 128 | this.totalChars += charCount; 129 | } 130 | 131 | // TODO 132 | // window.addEventListener('resize', () => this.resize()); 133 | } 134 | /** 135 | * clear all the cells chars 136 | */ 137 | clearCells() { 138 | for (const line of this.lines) { 139 | for (const cell of line.cells) { 140 | cell.set(' '); 141 | } 142 | } 143 | } 144 | /** 145 | * 146 | * @returns {string} a random char from this.lettersAndSymbols 147 | */ 148 | getRandomChar() { 149 | return this.lettersAndSymbols[Math.floor(Math.random() * this.lettersAndSymbols.length)]; 150 | } 151 | /** 152 | * Effect 1 - clear cells and animate each line cells (delays per line and per cell) 153 | */ 154 | fx1() { 155 | // max iterations for each cell to change the current value 156 | const MAX_CELL_ITERATIONS = 45; 157 | 158 | let finished = 0; 159 | 160 | // clear all cells values 161 | this.clearCells(); 162 | 163 | // cell's loop animation 164 | // each cell will change its value MAX_CELL_ITERATIONS times 165 | const loop = (line, cell, iteration = 0) => { 166 | // cache the previous value 167 | cell.cache = cell.state; 168 | 169 | // set back the original cell value if at the last iteration 170 | if ( iteration === MAX_CELL_ITERATIONS-1 ) { 171 | cell.set(cell.original); 172 | ++finished; 173 | if ( finished === this.totalChars ) { 174 | this.isAnimating = false; 175 | } 176 | } 177 | // if the cell is the first one in its line then generate a random char 178 | else if ( cell.position === 0 ) { 179 | // show specific characters for the first 9 iterations (looks cooler) 180 | cell.set(iteration < 9 ? 181 | ['*', '-', '\u0027', '\u0022'][Math.floor(Math.random() * 4)] : 182 | this.getRandomChar()); 183 | } 184 | // get the cached value of the previous cell. 185 | // This will result in the illusion that the chars are sliding from left to right 186 | else { 187 | cell.set(line.cells[cell.previousCellPosition].cache); 188 | } 189 | 190 | // doesn't count if it's an empty space 191 | if ( cell.cache != ' ' ) { 192 | ++iteration; 193 | } 194 | 195 | // repeat... 196 | if ( iteration < MAX_CELL_ITERATIONS ) { 197 | setTimeout(() => loop(line, cell, iteration), 15); 198 | } 199 | }; 200 | 201 | // set delays for each cell animation 202 | for (const line of this.lines) { 203 | for (const cell of line.cells) { 204 | setTimeout(() => loop(line, cell), (line.position+1)*200); 205 | } 206 | } 207 | } 208 | fx2() { 209 | const MAX_CELL_ITERATIONS = 20; 210 | let finished = 0; 211 | const loop = (line, cell, iteration = 0) => { 212 | if ( iteration === MAX_CELL_ITERATIONS-1 ) { 213 | cell.set(cell.original); 214 | cell.DOM.el.style.opacity = 0; 215 | setTimeout(() => { 216 | cell.DOM.el.style.opacity = 1; 217 | }, 300); 218 | 219 | ++finished; 220 | if ( finished === this.totalChars ) { 221 | this.isAnimating = false; 222 | } 223 | } 224 | else { 225 | cell.set(this.getRandomChar()); 226 | } 227 | 228 | ++iteration; 229 | if ( iteration < MAX_CELL_ITERATIONS ) { 230 | setTimeout(() => loop(line, cell, iteration), 40); 231 | } 232 | }; 233 | 234 | for (const line of this.lines) { 235 | for (const cell of line.cells) { 236 | setTimeout(() => loop(line, cell), (cell.position+1)*30); 237 | } 238 | } 239 | } 240 | fx3() { 241 | const MAX_CELL_ITERATIONS = 10; 242 | let finished = 0; 243 | this.clearCells(); 244 | 245 | const loop = (line, cell, iteration = 0) => { 246 | if ( iteration === MAX_CELL_ITERATIONS-1 ) { 247 | cell.set(cell.original); 248 | ++finished; 249 | if ( finished === this.totalChars ) { 250 | this.isAnimating = false; 251 | } 252 | } 253 | else { 254 | cell.set(this.getRandomChar()); 255 | } 256 | 257 | ++iteration; 258 | if ( iteration < MAX_CELL_ITERATIONS ) { 259 | setTimeout(() => loop(line, cell, iteration), 80); 260 | } 261 | }; 262 | 263 | for (const line of this.lines) { 264 | for (const cell of line.cells) { 265 | setTimeout(() => loop(line, cell), randomNumber(0,2000)); 266 | } 267 | } 268 | } 269 | fx4() { 270 | const MAX_CELL_ITERATIONS = 30; 271 | let finished = 0; 272 | this.clearCells(); 273 | 274 | const loop = (line, cell, iteration = 0) => { 275 | cell.cache = cell.state; 276 | 277 | if ( iteration === MAX_CELL_ITERATIONS-1 ) { 278 | cell.set(cell.original); 279 | 280 | ++finished; 281 | if ( finished === this.totalChars ) { 282 | this.isAnimating = false; 283 | } 284 | } 285 | else if ( cell.position === 0 ) { 286 | cell.set(['*',':'][Math.floor(Math.random() * 2)]); 287 | } 288 | else { 289 | cell.set(line.cells[cell.previousCellPosition].cache); 290 | } 291 | 292 | if ( cell.cache != ' ' ) { 293 | ++iteration; 294 | } 295 | 296 | if ( iteration < MAX_CELL_ITERATIONS ) { 297 | setTimeout(() => loop(line, cell, iteration), 15); 298 | } 299 | }; 300 | 301 | for (const line of this.lines) { 302 | for (const cell of line.cells) { 303 | setTimeout(() => loop(line, cell), Math.abs(this.lines.length/2-line.position)*400); 304 | } 305 | } 306 | } 307 | fx5() { 308 | // max iterations for each cell to change the current value 309 | const MAX_CELL_ITERATIONS = 30; 310 | let finished = 0; 311 | this.clearCells(); 312 | 313 | const loop = (line, cell, iteration = 0) => { 314 | cell.cache = {'state': cell.state, 'color': cell.color}; 315 | 316 | if ( iteration === MAX_CELL_ITERATIONS-1 ) { 317 | cell.color = cell.originalColor; 318 | cell.DOM.el.style.color = cell.color; 319 | cell.set(cell.original); 320 | 321 | ++finished; 322 | if ( finished === this.totalChars ) { 323 | this.isAnimating = false; 324 | } 325 | } 326 | else if ( cell.position === 0 ) { 327 | cell.color = ['#3e775d', '#61dca3', '#61b3dc'][Math.floor(Math.random() * 3)] 328 | cell.DOM.el.style.color = cell.color 329 | cell.set(iteration < 9 ? 330 | ['*', '-', '\u0027', '\u0022'][Math.floor(Math.random() * 4)] : 331 | this.getRandomChar()); 332 | } 333 | else { 334 | cell.set(line.cells[cell.previousCellPosition].cache.state); 335 | 336 | cell.color = line.cells[cell.previousCellPosition].cache.color 337 | cell.DOM.el.style.color = cell.color 338 | } 339 | 340 | if ( cell.cache.state != ' ' ) { 341 | ++iteration; 342 | } 343 | 344 | if ( iteration < MAX_CELL_ITERATIONS ) { 345 | setTimeout(() => loop(line, cell, iteration), 10); 346 | } 347 | }; 348 | 349 | for (const line of this.lines) { 350 | for (const cell of line.cells) { 351 | setTimeout(() => loop(line, cell), (line.position+1)*200); 352 | } 353 | } 354 | } 355 | fx6() { 356 | // max iterations for each cell to change the current value 357 | const MAX_CELL_ITERATIONS = 15; 358 | let finished = 0; 359 | const loop = (line, cell, iteration = 0) => { 360 | cell.cache = {'state': cell.state, 'color': cell.color}; 361 | 362 | if ( iteration === MAX_CELL_ITERATIONS-1 ) { 363 | cell.set(cell.original); 364 | 365 | cell.color = cell.originalColor; 366 | cell.DOM.el.style.color = cell.color; 367 | 368 | ++finished; 369 | if ( finished === this.totalChars ) { 370 | this.isAnimating = false; 371 | } 372 | } 373 | else { 374 | cell.set(this.getRandomChar()); 375 | 376 | cell.color = ['#2b4539', '#61dca3', '#61b3dc'][Math.floor(Math.random() * 3)] 377 | cell.DOM.el.style.color = cell.color 378 | } 379 | 380 | ++iteration; 381 | if ( iteration < MAX_CELL_ITERATIONS ) { 382 | setTimeout(() => loop(line, cell, iteration), randomNumber(30,110)); 383 | } 384 | }; 385 | 386 | for (const line of this.lines) { 387 | for (const cell of line.cells) { 388 | setTimeout(() => loop(line, cell), (line.position+1)*80); 389 | } 390 | } 391 | } 392 | /** 393 | * call the right effect method (defined in this.effects) 394 | * @param {string} effect - effect type 395 | */ 396 | trigger(effect = 'fx1') { 397 | if ( !(effect in this.effects) || this.isAnimating ) return; 398 | this.isAnimating = true; 399 | this.effects[effect](); 400 | } 401 | } -------------------------------------------------------------------------------- /src/js/utils.js: -------------------------------------------------------------------------------- 1 | // Preload images 2 | const preloadFonts = id => { 3 | return new Promise((resolve) => { 4 | WebFont.load({ 5 | typekit: { 6 | id: id 7 | }, 8 | active: resolve 9 | }); 10 | }); 11 | }; 12 | 13 | const randomNumber = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; 14 | 15 | export { 16 | preloadFonts, 17 | randomNumber 18 | } --------------------------------------------------------------------------------