Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions guest-book.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
<span id="month"></span>
</div>

<script src="js/reveal-text.js"></script>
<script src="js/parallax-text.js"></script>
<script src="js/guest-book.js"></script>

</body>
Expand Down
108 changes: 108 additions & 0 deletions js/parallax-text.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const CLASS_NAME = 'parallax-text';
const DEFAULT_SPEED = 1;
const DEFAULT_RANGE = 100;
const DEFAULT_DIRECTION = 'up';

let rafId = null;
const activeElements = new Set();
const lastOffsets = new WeakMap();

const getNumber = (value, fallback) => {
const number = Number(value);
return Number.isFinite(number) ? number : fallback;
};

const getSpeed = (element) =>
getNumber(element.getAttribute('data-parallax-speed'), DEFAULT_SPEED);

const getRange = (element) =>
getNumber(element.getAttribute('data-parallax-range'), DEFAULT_RANGE);

const getDirection = (element) => {
const dir = (
element.getAttribute('data-parallax-direction') || DEFAULT_DIRECTION
).toLowerCase();
return dir === 'down' ? 1 : -1;
};

const clamp = (value, min, max) => Math.min(max, Math.max(min, value));

const update = () => {
if (!activeElements.size) {
rafId = null;
return;
}

const vh = window.innerHeight || 1;

activeElements.forEach((element) => {
const rect = element.getBoundingClientRect();

// Freeze at the last visible offset when out of view.
if (rect.bottom <= 0 || rect.top >= vh) {
activeElements.delete(element);
return;
}

// Subtract previous transform to get natural position.
const lastOffset = lastOffsets.get(element) || 0;
const naturalTop = rect.top - lastOffset;

const elementHeight = rect.height || 1;
const progress = clamp(
(vh - naturalTop) / (vh + elementHeight),
0,
1
);

const range = getRange(element);
const direction = getDirection(element);
const speed = getSpeed(element);

const rawOffset = (progress - 0.5) * 2 * range * direction * speed;
const offset = clamp(rawOffset, -range, range);

lastOffsets.set(element, offset);
element.style.transform = `translate3d(0, ${offset}px, 0)`;
});

rafId = requestAnimationFrame(update);
};

const io = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
const element = entry.target;

if (entry.isIntersecting) {
activeElements.add(element);
} else {
activeElements.delete(element);
}
});

if (activeElements.size && !rafId) {
rafId = requestAnimationFrame(update);
}
},
{
root: null,
rootMargin: '0px',
threshold: 0,
}
);

const init = () => {
document.querySelectorAll(`.${CLASS_NAME}`).forEach((element) => {
element.style.willChange = 'transform';
element.style.transform = 'translate3d(0,0,0)';
lastOffsets.set(element, 0);
io.observe(element);
});
};

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
61 changes: 61 additions & 0 deletions js/reveal-text.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const ROOT_CLASS = 'reveal-text';
const ANIMATE_CLASS = 'animate-fadeInUp';
const HIDDEN_CLASS = 'opacity-0';
const IN_UP_CLASS = 'animate-inUp';
const IN_UP_CHILD_CLASS = 'reveal-text__inup';
const THRESHOLD = 0.05;

const setState = (element, isVisible) => {
if (isVisible) {
element.classList.add(ANIMATE_CLASS);
element.classList.remove(HIDDEN_CLASS);
} else {
element.classList.add(HIDDEN_CLASS);
element.classList.remove(ANIMATE_CLASS);
}

element
.querySelectorAll(`.${IN_UP_CHILD_CLASS}`)
.forEach((child) => {
if (isVisible) {
child.classList.add(IN_UP_CLASS);
} else {
child.classList.remove(IN_UP_CLASS);
}
});
};

const init = () => {
const elements = Array.from(document.querySelectorAll(`.${ROOT_CLASS}`));
if (!elements.length) {
return;
}

elements.forEach((element) => setState(element, false));

if (!('IntersectionObserver' in window)) {
elements.forEach((element) => setState(element, true));
return;
}

const io = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
setState(entry.target, entry.isIntersecting);
});
},
{
root: null,
rootMargin: '0px',
threshold: THRESHOLD,
}
);

elements.forEach((element) => io.observe(element));
};

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
40 changes: 40 additions & 0 deletions styles/general-styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,43 @@ body,
width: 20px;
height: 20px;
}

.opacity-0 {
opacity: 0;
}

@keyframes fadeInUp {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

.animate-fadeInUp {
animation: fadeInUp 600ms ease both;
will-change: opacity;
}

@keyframes inUp {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

.animate-inUp {
animation: inUp 600ms ease both;
will-change: opacity;
}

@media (prefers-reduced-motion: reduce) {
.animate-fadeInUp,
.animate-inUp {
animation: none;
opacity: 1;
}
}