Skip to content

Instantly share code, notes, and snippets.

@esz3tt
Last active December 3, 2022 19:04
Show Gist options
  • Save esz3tt/b32f6e6c31f5df2d42462e286e5937c4 to your computer and use it in GitHub Desktop.
Save esz3tt/b32f6e6c31f5df2d42462e286e5937c4 to your computer and use it in GitHub Desktop.
Text Scramble Effect
<div class="container">
<div class="text"></div>
</div>
// ——————————————————————————————————————————————————
// TextScramble
// ——————————————————————————————————————————————————
class TextScramble {
constructor(el) {
this.el = el
this.chars = '!<>-_\\/[]{}—=+*^?#________'
this.update = this.update.bind(this)
}
setText(newText) {
const oldText = this.el.innerText
const length = Math.max(oldText.length, newText.length)
const promise = new Promise((resolve) => this.resolve = resolve)
this.queue = []
for (let i = 0; i < length; i++) {
const from = oldText[i] || ''
const to = newText[i] || ''
const start = Math.floor(Math.random() * 40)
const end = start + Math.floor(Math.random() * 40)
this.queue.push({ from, to, start, end })
}
cancelAnimationFrame(this.frameRequest)
this.frame = 0
this.update()
return promise
}
update() {
let output = ''
let complete = 0
for (let i = 0, n = this.queue.length; i < n; i++) {
let { from, to, start, end, char } = this.queue[i]
if (this.frame >= end) {
complete++
output += to
} else if (this.frame >= start) {
if (!char || Math.random() < 0.28) {
char = this.randomChar()
this.queue[i].char = char
}
output += `<span class="dud">${char}</span>`
} else {
output += from
}
}
this.el.innerHTML = output
if (complete === this.queue.length) {
this.resolve()
} else {
this.frameRequest = requestAnimationFrame(this.update)
this.frame++
}
}
randomChar() {
return this.chars[Math.floor(Math.random() * this.chars.length)]
}
}
// ——————————————————————————————————————————————————
// Example
// ——————————————————————————————————————————————————
const phrases = [
'.,',
'sooner or later',
'you\'re going to realize',
'just as I did',
'that there\'s a difference',
'between knowing the path',
'and walking the path'
]
const el = document.querySelector('.text')
const fx = new TextScramble(el)
let counter = 0
const next = () => {
fx.setText(phrases[counter]).then(() => {
setTimeout(next, 800)
})
counter = (counter + 1) % phrases.length
}
next()
@import 'https://fonts.googleapis.com/css?family=Roboto+Mono:100'
html, body
font-family 'Roboto Mono', monospace
background #010101
height 100%
.container
height 100%
width 100%
justify-content center
align-items center
display flex
.text
font-weight 100
font-size 28px
color #FAFAFA
.dud
color #757575
<link href="https://cdnjs.cloudflare.com/ajax/libs/colors.css/3.0.0/colors.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" rel="stylesheet" />
@esz3tt
Copy link
Author

esz3tt commented Nov 25, 2021

Big 1UP to Justin Windle who did the workout.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment