189 lines
5.1 KiB
JavaScript
189 lines
5.1 KiB
JavaScript
// ——————————————————————————————————————————————————
|
|
// 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 = [ 'Portfolio', 'Discord', 'Twitter', 'Websites' ];
|
|
|
|
const el = document.querySelector('.text');
|
|
const fx = new TextScramble(el);
|
|
|
|
let counter = 0;
|
|
const next = () => {
|
|
fx.setText(phrases[counter]).then(() => {
|
|
setTimeout(next, 1000 * 1);
|
|
});
|
|
counter = (counter + 1) % phrases.length;
|
|
};
|
|
|
|
next();
|
|
|
|
// by
|
|
// abubakersaeed.netlify.com | @AbubakerSaeed96
|
|
// ============================================
|
|
|
|
// Inspiration:
|
|
// Tilt.js: https://gijsroge.github.io/tilt.js/
|
|
// Andy Merskin's parallax depth cards pen: https://codepen.io/andymerskin/full/XNMWvQ/
|
|
|
|
// Thank You for Viewing
|
|
|
|
class parallaxTiltEffect {
|
|
constructor({ element, tiltEffect }) {
|
|
this.element = element;
|
|
this.container = this.element.querySelector('.container');
|
|
this.size = [ 300, 360 ];
|
|
[ this.w, this.h ] = this.size;
|
|
|
|
this.tiltEffect = tiltEffect;
|
|
|
|
this.mouseOnComponent = false;
|
|
|
|
this.handleMouseMove = this.handleMouseMove.bind(this);
|
|
this.handleMouseEnter = this.handleMouseEnter.bind(this);
|
|
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
|
this.defaultStates = this.defaultStates.bind(this);
|
|
this.setProperty = this.setProperty.bind(this);
|
|
this.init = this.init.bind(this);
|
|
|
|
this.init();
|
|
}
|
|
|
|
handleMouseMove(event) {
|
|
const { offsetX, offsetY } = event;
|
|
|
|
let X;
|
|
let Y;
|
|
|
|
if (this.tiltEffect === 'reverse') {
|
|
X = (offsetX - this.w / 2) / 3 / 3;
|
|
Y = -(offsetY - this.h / 2) / 3 / 3;
|
|
} else if (this.tiltEffect === 'normal') {
|
|
X = -(offsetX - this.w / 2) / 3 / 3;
|
|
Y = (offsetY - this.h / 2) / 3 / 3;
|
|
}
|
|
|
|
this.setProperty('--rY', X.toFixed(2));
|
|
this.setProperty('--rX', Y.toFixed(2));
|
|
|
|
this.setProperty('--bY', 80 - (X / 4).toFixed(2) + '%');
|
|
this.setProperty('--bX', 50 - (Y / 4).toFixed(2) + '%');
|
|
}
|
|
|
|
handleMouseEnter() {
|
|
this.mouseOnComponent = true;
|
|
this.container.classList.add('container--active');
|
|
}
|
|
|
|
handleMouseLeave() {
|
|
this.mouseOnComponent = false;
|
|
this.defaultStates();
|
|
}
|
|
|
|
defaultStates() {
|
|
this.container.classList.remove('container--active');
|
|
this.setProperty('--rY', 0);
|
|
this.setProperty('--rX', 0);
|
|
this.setProperty('--bY', '80%');
|
|
this.setProperty('--bX', '50%');
|
|
}
|
|
|
|
setProperty(p, v) {
|
|
return this.container.style.setProperty(p, v);
|
|
}
|
|
|
|
init() {
|
|
this.element.addEventListener('mousemove', this.handleMouseMove);
|
|
this.element.addEventListener('mouseenter', this.handleMouseEnter);
|
|
this.element.addEventListener('mouseleave', this.handleMouseLeave);
|
|
}
|
|
}
|
|
|
|
const $ = (e) => document.querySelector(e);
|
|
|
|
const WrapPortfolio = new parallaxTiltEffect({
|
|
element: $('.wrap--portfolio'),
|
|
tiltEffect: 'reverse'
|
|
});
|
|
|
|
const WrapHomepage = new parallaxTiltEffect({
|
|
element: $('.wrap--homepage'),
|
|
tiltEffect: 'normal'
|
|
});
|
|
|
|
const WrapYugen = new parallaxTiltEffect({
|
|
element: $('.wrap--yugen'),
|
|
tiltEffect: 'reverse'
|
|
});
|
|
|
|
const WrapThaldrin = new parallaxTiltEffect({
|
|
element: $('.wrap--thaldrin'),
|
|
tiltEffect: 'reverse'
|
|
});
|
|
|
|
const WrapKaito = new parallaxTiltEffect({
|
|
element: $('.wrap--kaito'),
|
|
tiltEffect: 'reverse'
|
|
});
|
|
const WrapYiff = new parallaxTiltEffect({
|
|
element: $('.wrap--yiff'),
|
|
tiltEffect: 'reverse'
|
|
});
|