| 1 |
/* Scroll Reveal — frontend */ |
| 2 |
(function () { |
| 3 |
'use strict'; |
| 4 |
|
| 5 |
/* map animation name → { initial transform, optional extra } */ |
| 6 |
function getInitialTransform(animation, distance, scale, rotate) { |
| 7 |
var d = distance + 'px'; |
| 8 |
switch (animation) { |
| 9 |
case 'fade-up': return 'translateY(' + d + ')'; |
| 10 |
case 'fade-down': return 'translateY(-' + d + ')'; |
| 11 |
case 'fade-left': return 'translateX(' + d + ')'; |
| 12 |
case 'fade-right': return 'translateX(-' + d + ')'; |
| 13 |
case 'slide-up': return 'translateY(' + d + ')'; |
| 14 |
case 'slide-down': return 'translateY(-' + d + ')'; |
| 15 |
case 'slide-left': return 'translateX(' + d + ')'; |
| 16 |
case 'slide-right': return 'translateX(-' + d + ')'; |
| 17 |
case 'zoom-in': return 'scale(' + (scale / 100) + ')'; |
| 18 |
case 'zoom-out': return 'scale(' + (scale > 100 ? scale / 100 : 1.1) + ')'; |
| 19 |
case 'flip-left': return 'perspective(600px) rotateY(-90deg)'; |
| 20 |
case 'flip-right': return 'perspective(600px) rotateY(90deg)'; |
| 21 |
case 'rotate-in': return 'rotate(' + rotate + 'deg)'; |
| 22 |
case 'skew-up': return 'translateY(' + d + ') skewX(' + rotate + 'deg)'; |
| 23 |
case 'bounce-in': return 'scale(0.8)'; |
| 24 |
case 'fade-in': |
| 25 |
default: return 'none'; |
| 26 |
} |
| 27 |
} |
| 28 |
|
| 29 |
function getRevealTransition(animation, easing, duration) { |
| 30 |
var bounce = animation === 'bounce-in' ? 'cubic-bezier(0.34,1.56,0.64,1)' : easing; |
| 31 |
return 'opacity ' + duration + 'ms ' + bounce + ', transform ' + duration + 'ms ' + bounce; |
| 32 |
} |
| 33 |
|
| 34 |
function applyInitial(el, animation, distance, scale, rotate, opacity) { |
| 35 |
var transform = getInitialTransform(animation, distance, scale, rotate); |
| 36 |
el.style.opacity = (opacity / 100).toString(); |
| 37 |
el.style.transform = transform; |
| 38 |
el.style.willChange = 'opacity, transform'; |
| 39 |
} |
| 40 |
|
| 41 |
function applyRevealed(el, transition, delay) { |
| 42 |
el.style.transition = transition; |
| 43 |
if (delay) { el.style.transitionDelay = delay + 'ms'; } |
| 44 |
el.style.opacity = '1'; |
| 45 |
el.style.transform = 'none'; |
| 46 |
} |
| 47 |
|
| 48 |
function init() { |
| 49 |
var prefersReduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; |
| 50 |
|
| 51 |
document.querySelectorAll('.bksr-wrap[data-animation]').forEach(function (wrap) { |
| 52 |
if (wrap._bksrInit) { return; } |
| 53 |
wrap._bksrInit = true; |
| 54 |
|
| 55 |
var animation = wrap.getAttribute('data-animation') || 'fade-up'; |
| 56 |
var duration = parseInt(wrap.getAttribute('data-duration'), 10) || 600; |
| 57 |
var delay = parseInt(wrap.getAttribute('data-delay'), 10) || 0; |
| 58 |
var easing = wrap.getAttribute('data-easing') || 'ease'; |
| 59 |
var distance = parseInt(wrap.getAttribute('data-distance'), 10) || 32; |
| 60 |
var scale = parseInt(wrap.getAttribute('data-scale'), 10) || 95; |
| 61 |
var rotate = parseInt(wrap.getAttribute('data-rotate'), 10) || 0; |
| 62 |
var opacity = parseInt(wrap.getAttribute('data-opacity'), 10) || 0; |
| 63 |
var threshold = (parseInt(wrap.getAttribute('data-threshold'), 10) || 15) / 100; |
| 64 |
var once = wrap.getAttribute('data-once') !== '0'; |
| 65 |
var stagger = wrap.getAttribute('data-stagger') === '1'; |
| 66 |
var staggerDelay = parseInt(wrap.getAttribute('data-stagger-delay'), 10) || 100; |
| 67 |
|
| 68 |
/* skip animation for reduced motion preference */ |
| 69 |
if (prefersReduced) { return; } |
| 70 |
|
| 71 |
var transition = getRevealTransition(animation, easing, duration); |
| 72 |
|
| 73 |
if (stagger) { |
| 74 |
/* animate each direct child individually */ |
| 75 |
var children = Array.prototype.slice.call(wrap.children); |
| 76 |
children.forEach(function (child) { |
| 77 |
applyInitial(child, animation, distance, scale, rotate, opacity); |
| 78 |
}); |
| 79 |
|
| 80 |
var observer = new IntersectionObserver(function (entries) { |
| 81 |
entries.forEach(function (entry) { |
| 82 |
if (!entry.isIntersecting) { return; } |
| 83 |
var kids = Array.prototype.slice.call(entry.target.children); |
| 84 |
kids.forEach(function (child, i) { |
| 85 |
applyRevealed(child, transition, delay + i * staggerDelay); |
| 86 |
}); |
| 87 |
if (once) { observer.unobserve(entry.target); } |
| 88 |
}); |
| 89 |
}, { threshold: threshold }); |
| 90 |
|
| 91 |
observer.observe(wrap); |
| 92 |
|
| 93 |
} else { |
| 94 |
/* animate whole wrapper */ |
| 95 |
applyInitial(wrap, animation, distance, scale, rotate, opacity); |
| 96 |
|
| 97 |
var obs = new IntersectionObserver(function (entries) { |
| 98 |
entries.forEach(function (entry) { |
| 99 |
if (!entry.isIntersecting) { |
| 100 |
if (!once) { |
| 101 |
/* reset */ |
| 102 |
applyInitial(entry.target, animation, distance, scale, rotate, opacity); |
| 103 |
entry.target.style.transition = 'none'; |
| 104 |
} |
| 105 |
return; |
| 106 |
} |
| 107 |
applyRevealed(entry.target, transition, delay); |
| 108 |
if (once) { obs.unobserve(entry.target); } |
| 109 |
}); |
| 110 |
}, { threshold: threshold }); |
| 111 |
|
| 112 |
obs.observe(wrap); |
| 113 |
} |
| 114 |
}); |
| 115 |
} |
| 116 |
|
| 117 |
if (document.readyState === 'loading') { |
| 118 |
document.addEventListener('DOMContentLoaded', init); |
| 119 |
} else { |
| 120 |
init(); |
| 121 |
} |
| 122 |
}()); |
| 123 |
|