Result
Tailwindの設計をヒントにして作ったスクロール連動アニメーション。
少しのJavaScriptとCSSで実装可能、アニメーションもカスタマイズ可能と、軽量で使いやすいのがメリットとなっています。
上記サンプルはブロックごとにクリックで遷移しますがコードはスクロール連動となっています。
CDNも用意されていますので気軽にテスト、導入できます。
< link rel = "stylesheet" href = "https://cdn.jsdelivr.net/gh/Michal-Skoula/simple-scroll-animations@master/release/latest/styles.css" > < script defer src = "https://cdn.jsdelivr.net/gh/Michal-Skoula/simple-scroll-animations@master/release/latest/script.js" ></ script > |
css
:root { --reanimate-on-scroll-by-default: 0 ; --default- animation-duration : 500 ms; --staggered-step-amount: 250 ms; --delay-step-amount: 400 ms; --blur-strength: 3px ; --translate-amount-x: 40% ; --translate-amount-y: 40% ; } @media(prefers-reduced-motion) { .animate. show { transition : none !important ; } } .animate { opacity : 0 ; filter : blur(var(--blur-strength)); transition : transform var(--default-animation-duration) ease-in-out, opacity var(--default-animation-duration), filter calc(var(--default-animation-duration) * 2 / 3 ); } .animate. show , .animate. show .fade-in { opacity : 1 ; filter : blur( 0 ); } .animate.fade-in { opacity : 0 ; filter : blur(var(--blur-strength)); } .noblur { filter : blur( 0 ) !important ; } .animate.bottom-to- top { transform : translateY (var(--translate-amount-y)); } .animate. show .bottom-to- top { transform : translateY ( 0 ); } .animate.right-to- left { transform : translateX (var(--translate-amount-x)); } .animate. show .right-to- left { transform : translateX ( 0 ); } .animate.top-to- bottom { transform : translateY (calc(var(--translate-amount-y) * -1 )); } .animate. show .top-to- bottom { transform : translateY ( 0 ); } .animate.left-to- right { transform : translateX (calc(var(--translate-amount-x) * -1 )); } .animate. show .left-to- right { transform : translateX ( 0 ); } .animate.staggered { transition-delay : calc(var(--delay-step-amount) * var(--index)); } .animate.delay 100 , .animate.delay 200 , .animate.delay 300 , .animate.delay 400 , .animate.delay 500 , .animate.delay 600 , .animate.delay 700 , .animate.delay 800 , .animate.delay 900 , .animate.delay 1000 { transition-delay : calc(var(--delay-step-amount) * var(--delay)); } .animate.duration 100 , .animate.duration 200 , .animate.duration 300 , .animate.duration 400 , .animate.duration 500 , .animate.duration 600 , .animate.duration 700 , .animate.duration 800 , .animate.duration 900 , .animate.duration 1000 { transition-duration: calc(var(--staggered-step-amount) * var(--duration)); } .animate.staggered:nth-child( 2 ) { --index: 1 ; } .animate.staggered:nth-child( 3 ) { --index: 2 ; } .animate.staggered:nth-child( 4 ) { --index: 3 ; } .animate.staggered:nth-child( 5 ) { --index: 4 ; } .animate.staggered:nth-child( 6 ) { --index: 5 ; } .animate.staggered:nth-child( 7 ) { --index: 6 ; } .animate.staggered:nth-child( 8 ) { --index: 7 ; } .animate.staggered:nth-child( 9 ) { --index: 8 ; } .animate.staggered:nth-child( 10 ) { --index: 9 ; } .animate.delay 100 { --delay: 1 ; } .animate.delay 200 { --delay: 2 ; } .animate.delay 300 { --delay: 3 ; } .animate.delay 400 { --delay: 4 ; } .animate.delay 500 { --delay: 5 ; } .animate.delay 600 { --delay: 6 ; } .animate.delay 700 { --delay: 7 ; } .animate.delay 800 { --delay: 8 ; } .animate.delay 900 { --delay: 9 ; } .animate.delay 1000 { --delay: 10 ; } .animate.duration 100 { --duration: 1 ; } .animate.duration 200 { --duration: 2 ; } .animate.duration 300 { --duration: 3 ; } .animate.duration 400 { --duration: 4 ; } .animate.duration 500 { --duration: 5 ; } .animate.duration 600 { --duration: 6 ; } .animate.duration 700 { --duration: 7 ; } .animate.duration 800 { --duration: 8 ; } .animate.duration 900 { --duration: 9 ; } .animate.duration 1000 { --duration: 10 ; } |
Tailwind互換というわけではありませんが同じような感覚で使えると思います。
javascript
const rootElement = document.documentElement; const reanimate = getComputedStyle(rootElement).getPropertyValue( '--reanimate-on-scroll-by-default' ); reanimate.trim(); const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.classList.add( 'show' ) } else if (reanimate == 1 && !entry.target.classList.contains( 'once' )) { entry.target.classList.remove( 'show' ) } else if (reanimate == 0 && entry.target.classList.contains( 'always' )) { entry.target.classList.remove( 'show' ) } else if (reanimate == 0) {} }); }); const toBeAnimated = document.querySelectorAll( '.animate' ); toBeAnimated.forEach((element) => {observer.observe(element)}); |
Intersection Observer APIを利用して、要素がviewportに入ったか、或いは出たかを監視、それに応じてCSSクラスとして.show
を追加・削除することで実現します。よくあるパターンですね。
アニメーションはCSSで制御しているのでCSSの知識があれば様々な表現が可能ですね。