From f9cab05d9576f7f957d6ced30b595e26c8a7cd97 Mon Sep 17 00:00:00 2001 From: hassankettany Date: Sun, 28 Dec 2025 17:47:49 +0200 Subject: [PATCH 01/25] Enhanced animations and effects for mobile and desktop breakpoints - Updated hero subtitle styling to include a text stroke effect (better for visibility). - Renamed performance-section to cube-section in HTML for clarity (previously both it and the section above it were called performance-section for some reason) - added conditions for Desktop & Mobile in the config - Added mobile-specific animations for: 1. the hero section: customEffect, called rotateGridEffectMobile, works as intended (no issues from what I can tell) 2. 1. the "performance" section: customEffect, called animateTunnelMobile, works as inteded when in mobile breakpoint (the animation itself still needs tweaking, but it works), but in desktop breakpoint, both the animateTunnel & animateTunnelMobile run simultaneously despite the conditions, I haven't been able to fix this yet - changed the trigger for the cube's effect "spongeExplode" (the key is called hitbox) in mobile to be viewEnter instead of hover, but the problem is (the same with animateTunnel) that, when in desktop, the mobile animation overrides the desktop animation, I haven't been able to fix this yet --- assets/main.mjs | 164 +++++++++++++++++++++++++++++++++++++++++++++- assets/styles.css | 8 +++ index.html | 4 +- 3 files changed, 173 insertions(+), 3 deletions(-) diff --git a/assets/main.mjs b/assets/main.mjs index f8572ffb..73306e67 100644 --- a/assets/main.mjs +++ b/assets/main.mjs @@ -53,6 +53,9 @@ function updateGrid() { } } + +/* this is the old desktop effect with the mouse movement... I added a new effect that also uses mouse +movement but it's a bit different. It's a bit more subtle and it's more like a wind effect. function rotateGridEffect(_containerElement, progress) { const mouseX = progress.x * windowWidth; const mouseY = progress.y * windowHeight; @@ -79,6 +82,43 @@ function rotateGridEffect(_containerElement, progress) { line.style.transform = `translate(${moveX}px, ${moveY}px) rotate(${angle}deg) scaleY(${lengthScale})`; } } +*/ + +const rotateGridEffect = (containerElement, progress) => { + const mouseX = progress.x * windowWidth; + const mouseY = progress.y * windowHeight; + + for (const [line, cache] of lineCache) { + const dx = mouseX - cache.x; + const dy = mouseY - cache.y; + const dist = Math.sqrt(dx * dx + dy * dy); + const angle = dist * 0.5; + const influenceRadius = 3000; + const rawProximity = Math.max(0, 1 - dist / influenceRadius); + const proximity = Math.pow(rawProximity, 1.5); + const scale = 1 + (proximity * 0.9); + const twist = proximity * 100; + line.style.transform = `rotate(${angle + 45 + twist}deg) scaleY(${scale})`; + } +}; + +const rotateGridEffectMobile = (containerElement, progress) => { + + const time = progress * Math.PI * 2 * 3; // 3 full wave cycles per animation loop + + for (const [line, cache] of lineCache) + { + const dist = cache.x + cache.y; + const spatialPhase = dist * 0.004; + const wave = Math.sin(time - spatialPhase); + const windForce = (wave + 1) / 2; + const sharpWind = Math.pow(windForce, 2); + const angle = sharpWind * 360; + const scale = 1 + sharpWind * 0.4; + line.style.transform = `rotate(${angle}deg) scaleY(${scale})`; + } +}; + // --- Tunnel Effect Functions --- const NUM_CIRCLES = 45; @@ -162,6 +202,55 @@ function animateTunnel(_rootElement, progress) { }); } +function animateTunnelMobile(_rootElement, progress) { + // Normalized progress: -1 at top, 0 at center, +1 at bottom + const normalizedProgress = (progress - 0.05) * 2; + + // Create wave phases for organic motion + const wavePhase = progress * Math.PI * 5; // Multiple waves across scroll + const slowWave = Math.sin(wavePhase * 0.5); + const fastWave = Math.sin(wavePhase * 2.5); + + tunnelRings.forEach((ring, index) => { + const depth = 1 - (index / NUM_CIRCLES); // 1 = front, 0 = back + const depthCubed = Math.pow(depth, 4.5); // More dramatic depth falloff + + // --- Y Movement (primary parallax) --- + const baseY = normalizedProgress * depthCubed; + const waveY = slowWave * 5 * depth; // Subtle wave oscillation + const targetY = baseY + waveY; + + // --- X Movement (secondary parallax - swaying) --- + const sway = fastWave * 50 * depthCubed; // Layers sway side to side + const drift = Math.sin(wavePhase + index * 0.3) * 8 * depth; // Phase offset per ring + const targetX = sway + drift; + + // --- Scale (breathing/zoom effect) --- + const breathe = 1 + (slowWave * 0.08 * depth); // Front rings scale more + const zoomPush = 1 + (normalizedProgress * 0.15 * depthCubed); // Zoom based on scroll + const targetScale = breathe * zoomPush; + + // --- Rotation (subtle tilt for depth) --- + const tiltAmount = normalizedProgress * 3 * depthCubed; // degrees + const spinOffset = fastWave * 1.5 * depth; + const targetRotation = tiltAmount + spinOffset; + + // --- Smooth easing (different rates per layer) --- + const ease = 0.02 + (0.08 * depth); + ring.x = ring.x || 0; + ring.y = ring.y || 0; + ring.scale = ring.scale || 1; + ring.rotation = ring.rotation || 0; + + ring.x += (targetX - ring.x) * ease; + ring.y += (targetY - ring.y) * ease; + ring.scale += (targetScale - ring.scale) * ease * 0.5; + ring.rotation += (targetRotation - ring.rotation) * ease; + + ring.element.style.transform = `translate(-50%, -50%) translate3d(${ring.x}px, ${ring.y}px, 0) scale(${ring.scale}) rotate(${ring.rotation}deg)`; + }); +} + // --- Mouse Track Tunnel Functions --- function generateMouseTunnel() { const mount = document.getElementById('mt-tunnel-mount'); @@ -309,6 +398,28 @@ const primitiveInteractions = primitivePrefixes.flatMap(prefix => generateCircle // --- Interact Configuration --- const config = { + + conditions: { + Mobile: { + type: 'media', + predicate: '(max-width: 1000px)' + }, + Desktop: { + type: 'media', + predicate: '(min-width: 1001px)' + }, + + // Hover media queries, when you hover over the page, in mobile + hover: { + type: 'media', + predicate: '(hover: hover)' + }, + notHover: { + type: 'media', + predicate: 'not (hover: hover)' + } + }, + effects: { spongeTumble: { key: 'sponge', @@ -337,6 +448,22 @@ const config = { ], }, }, + spongeExplodeMobile: { + key: 'sponge', + duration: 3600, + fill: 'both', + composite: 'replace', + keyframeEffect: { + keyframes: [ + { '--spacing': '0px', offset: 0, easing: 'cubic-bezier(0.19, 1, 0.22, 1)' }, + { '--spacing': '100px', offset: 0.5, easing: 'cubic-bezier(0.19, 1, 0.22, 1)' }, + { '--spacing': '100px', offset: 0.667, easing: 'cubic-bezier(0.19, 1, 0.22, 1)' }, + { '--spacing': '0px', offset: 1 } + ], + }, + }, + + }, interactions: [ @@ -363,18 +490,40 @@ const config = { { key: 'hitbox', trigger: 'hover', + conditions: ['Desktop'], params: { type: 'alternate' }, effects: [{ effectId: 'spongeExplode' }], }, + { + key: 'hitbox', + trigger: 'viewEnter', + conditions: ['Mobile'], + params: { type: 'alternate' }, + effects: [{ effectId: 'spongeExplodeMobile' }], + }, // Hero Grid Interaction { key: 'hero-grid', trigger: 'pointerMove', params: { hitArea: 'root' }, + conditions: ['Desktop'], effects: [{ customEffect: rotateGridEffect, centeredToTarget: true }] }, - + { + key: 'hero-grid', + trigger: 'viewEnter', + params: {type: 'once'}, + conditions: ['Mobile'], + effects: [ + { + customEffect: rotateGridEffectMobile, + duration: 8000, + iterations: Infinity, + easing: 'linear' + } + ] + }, // Hero Text { key: 'hero-line-1', @@ -745,12 +894,25 @@ const config = { key: 'tunnel-root', trigger: 'pointerMove', params: { hitArea: 'root' }, + conditions: ['Desktop'], effects: [{ customEffect: animateTunnel, centeredToTarget: true, fill: 'both' }] }, + { + key: 'performance-section', + trigger: 'viewProgress', + conditions: ['Mobile'], + effects: [{ + key: 'tunnel-root', + customEffect: animateTunnelMobile, + fill: 'both', + rangeStart: { name: 'cover', offset: { value: 10, type: 'percentage' } }, + rangeEnd: { name: 'cover', offset: { value: 30, type: 'percentage' } } + }] + }, // Cards Hover { diff --git a/assets/styles.css b/assets/styles.css index c1ff8615..078cec3e 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -41,6 +41,14 @@ interact-element { -webkit-mask-image: linear-gradient(to bottom, black 80%, transparent 100%); } + +.text-stroke-black { + -webkit-text-stroke-width: 12px; + -webkit-text-stroke-color: #111111; + paint-order: stroke fill; + filter: drop-shadow( 0px 0px 2px #111111); +} + .grid-cell { display: flex; align-items: center; diff --git a/index.html b/index.html index 62b5e507..f92278a2 100644 --- a/index.html +++ b/index.html @@ -94,7 +94,7 @@

-

+

Create high-performance, beautiful web animations with Interact — from simple micro-interactions to complex, scroll-driven storytelling. Designed for designers and developers alike, with a robust, production-ready API

@@ -150,7 +150,7 @@

+
From 78fa07d8c54344a6cacfae8f383fa87aec5319cf Mon Sep 17 00:00:00 2001 From: hassankettany Date: Sun, 4 Jan 2026 17:30:27 +0200 Subject: [PATCH 02/25] Refactored animations and styles for mobile and desktop responsiveness - Updated navigation button styles for improved hover effects (removed blend mode, gave it outline, and inverted its color on hover). - Adjusted heading sizes and line heights for better readability across sections (aka typography fixes, leading, font size..etc) - Implemented mobile-specific animations for the spread section, including easing adjustments and keyframe effects. - Enhanced z-index management for spread cards in desktop view. (added media query for mobile) - fixed the animation of horizontal track (the website showcase), or rather fixed the sizing and with that I had to change the animation a bit for the mobile (I also added media query in CSS) --- assets/main.mjs | 219 ++++++++++++++++++++++++++++++++++++++++++---- assets/modal.js | 184 ++++++++++++++++++++++++++++---------- assets/styles.css | 61 +++++++++++++ index.html | 32 +++---- 4 files changed, 416 insertions(+), 80 deletions(-) diff --git a/assets/main.mjs b/assets/main.mjs index 73306e67..b920a085 100644 --- a/assets/main.mjs +++ b/assets/main.mjs @@ -375,6 +375,7 @@ const ENTRANCE_DURATION = 1000; const ENTRANCE_EASING = 'cubic-bezier(0.2, 0.8, 0.2, 1)'; const ENTRANCE_OFFSET = '50px'; const ENTRANCE_SLIDE_DIST = '120px'; +const mobileEasing = 'cubic-bezier(0.25, 1, 0.5, 1)'; const HOVER_SCALE = { name: 'hoverScale', @@ -818,60 +819,60 @@ const config = { key: 'word-1', keyframeEffect: { keyframes: [ - { opacity: 0, transform: 'translateY(150px)' }, + { opacity: 0, transform: 'translateY(100px)' }, { opacity: 1, transform: 'translateY(0)' } ] }, - rangeStart: { name: 'cover', offset: { value: 5, type: 'percentage' } }, - rangeEnd: { name: 'cover', offset: { value: 15, type: 'percentage' } }, + rangeStart: { name: 'cover', offset: { value: 25, type: 'percentage' } }, + rangeEnd: { name: 'cover', offset: { value: 40, type: 'percentage' } }, fill: 'both' }, { key: 'word-2', keyframeEffect: { keyframes: [ - { opacity: 0, transform: 'translateY(150px)' }, + { opacity: 0, transform: 'translateY(100px)' }, { opacity: 1, transform: 'translateY(0)' } ] }, - rangeStart: { name: 'cover', offset: { value: 5, type: 'percentage' } }, - rangeEnd: { name: 'cover', offset: { value: 20, type: 'percentage' } }, + rangeStart: { name: 'cover', offset: { value: 28, type: 'percentage' } }, + rangeEnd: { name: 'cover', offset: { value: 43, type: 'percentage' } }, fill: 'both' }, { key: 'word-3', keyframeEffect: { keyframes: [ - { opacity: 0, transform: 'translateY(150px)' }, + { opacity: 0, transform: 'translateY(50px)' }, { opacity: 1, transform: 'translateY(0)' } ] }, - rangeStart: { name: 'cover', offset: { value: 15, type: 'percentage' } }, - rangeEnd: { name: 'cover', offset: { value: 30, type: 'percentage' } }, + rangeStart: { name: 'cover', offset: { value: 31, type: 'percentage' } }, + rangeEnd: { name: 'cover', offset: { value: 46, type: 'percentage' } }, fill: 'both' }, { key: 'word-4', keyframeEffect: { keyframes: [ - { opacity: 0, transform: 'translateY(150px)' }, + { opacity: 0, transform: 'translateY(50px)' }, { opacity: 1, transform: 'translateY(0)' } ] }, - rangeStart: { name: 'cover', offset: { value: 15, type: 'percentage' } }, - rangeEnd: { name: 'cover', offset: { value: 30, type: 'percentage' } }, + rangeStart: { name: 'cover', offset: { value: 34, type: 'percentage' } }, + rangeEnd: { name: 'cover', offset: { value: 49, type: 'percentage' } }, fill: 'both' }, { key: 'word-5', keyframeEffect: { keyframes: [ - { opacity: 0, transform: 'translateY(150px)' }, + { opacity: 0, transform: 'translateY(50px)' }, { opacity: 1, transform: 'translateY(0)' } ] }, - rangeStart: { name: 'cover', offset: { value: 15, type: 'percentage' } }, - rangeEnd: { name: 'cover', offset: { value: 30, type: 'percentage' } }, + rangeStart: { name: 'cover', offset: { value: 38, type: 'percentage' } }, + rangeEnd: { name: 'cover', offset: { value: 53, type: 'percentage' } }, fill: 'both' }, { @@ -950,13 +951,14 @@ const config = { effects: [{ namedEffect: { type: 'FadeIn', distance: '40px', direction: 'bottom' }, duration: 800, delay: 300, fill: 'both' }] }, - // Spread Section + // Spread Section (Desktop) { key: 'spread-section', trigger: 'viewProgress', effects: [ { key: 'spread-card-0', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, @@ -970,6 +972,7 @@ const config = { }, { key: 'spread-card-1', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, @@ -983,6 +986,7 @@ const config = { }, { key: 'spread-card-2', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, @@ -996,6 +1000,7 @@ const config = { }, { key: 'spread-card-3', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, @@ -1009,6 +1014,7 @@ const config = { }, { key: 'spread-card-4', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, @@ -1019,6 +1025,88 @@ const config = { { transform: 'translate(calc(-50% + 32vw), -50%) scale(0.9)' } ] } + }, + + // Spread Section (Mobile) + + { + key: 'spread-card-0', + conditions: ['Mobile'], + fill: 'both', + easing: mobileEasing, + rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 25, type: 'percentage' } }, + keyframeEffect: { + name: 'card-0-scaleDown', + keyframes: [ + { transform: 'translateX(-50%) translateY(0) scale(1)' }, + { transform: 'translateX(-50%) translateY(0) scale(0.85)' } + ] + } + }, + { + key: 'spread-card-1', + conditions: ['Mobile'], + fill: 'both', + easing: mobileEasing, + rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 50, type: 'percentage' } }, + keyframeEffect: { + name: 'card-1-slideUp-scaleDown', + keyframes: [ + { offset: 0, transform: 'translateX(-50%) translateY(100vh) scale(1)' }, + { offset: 0.5, transform: 'translateX(-50%) translateY(0) scale(1)' }, + { offset: 1, transform: 'translateX(-50%) translateY(0) scale(0.85)' } + ] + } + }, + { + key: 'spread-card-2', + conditions: ['Mobile'], + fill: 'both', + easing: mobileEasing, + rangeStart: { name: 'contain', offset: { value: 25, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 75, type: 'percentage' } }, + keyframeEffect: { + name: 'card-2-slideUp-scaleDown', + keyframes: [ + { offset: 0, transform: 'translateX(-50%) translateY(100vh) scale(1)' }, + { offset: 0.5, transform: 'translateX(-50%) translateY(0) scale(1)' }, + { offset: 1, transform: 'translateX(-50%) translateY(0) scale(0.85)' } + ] + } + }, + { + key: 'spread-card-3', + conditions: ['Mobile'], + fill: 'both', + easing: mobileEasing, + rangeStart: { name: 'contain', offset: { value: 50, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, + keyframeEffect: { + name: 'card-3-slideUp-scaleDown', + keyframes: [ + { offset: 0, transform: 'translateX(-50%) translateY(100vh) scale(1)' }, + { offset: 0.5, transform: 'translateX(-50%) translateY(0) scale(1)' }, + { offset: 1, transform: 'translateX(-50%) translateY(0) scale(0.85)' } + ] + } + }, + { + key: 'spread-card-4', + conditions: ['Mobile'], + fill: 'both', + easing: mobileEasing, + rangeStart: { name: 'contain', offset: { value: 75, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, + keyframeEffect: { + name: 'card-4-slideUp-scaleDown', + keyframes: [ + { offset: 0, transform: 'translateX(-50%) translateY(100vh) scale(1)' }, + { offset: 0.5, transform: 'translateX(-50%) translateY(0) scale(1)' }, + { offset: 1, transform: 'translateX(-50%) translateY(0) scale(0.85)' } + ] + } } ] }, @@ -1028,8 +1116,10 @@ const config = { key: 'h-section', trigger: 'viewProgress', effects: [ + // Desktop: h-track movement (9 cards × 600px + 8 gaps × 128px - 600px = 5824px) { key: 'h-track', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, @@ -1041,8 +1131,25 @@ const config = { ] } }, + // Mobile: h-track movement (8 × (95vw + 4vw) = 792vw) + { + key: 'h-track', + conditions: ['Mobile'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, + keyframeEffect: { + name: 'moveLeftMobile', + keyframes: [ + { transform: 'translateX(0)' }, + { transform: 'translateX(-792vw)' } + ] + } + }, + // Desktop card scaling { key: 'h-card-1', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 12.5, type: 'percentage' } }, @@ -1053,6 +1160,7 @@ const config = { }, { key: 'h-card-2', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 25, type: 'percentage' } }, @@ -1060,6 +1168,7 @@ const config = { }, { key: 'h-card-3', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 12.5, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 37.5, type: 'percentage' } }, @@ -1067,6 +1176,7 @@ const config = { }, { key: 'h-card-4', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 25, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 50, type: 'percentage' } }, @@ -1074,6 +1184,7 @@ const config = { }, { key: 'h-card-5', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 37.5, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 62.5, type: 'percentage' } }, @@ -1081,6 +1192,7 @@ const config = { }, { key: 'h-card-6', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 50, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 75, type: 'percentage' } }, @@ -1088,6 +1200,7 @@ const config = { }, { key: 'h-card-7', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 62.5, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 87.5, type: 'percentage' } }, @@ -1095,6 +1208,7 @@ const config = { }, { key: 'h-card-8', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 75, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, @@ -1102,10 +1216,83 @@ const config = { }, { key: 'h-card-9', + conditions: ['Desktop'], fill: 'both', rangeStart: { name: 'contain', offset: { value: 87.5, type: 'percentage' } }, rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, keyframeEffect: { name: 'scaleCenter9', keyframes: [{ transform: 'scale(1)' }, { transform: 'scale(1.3)' }] } + }, + { + key: 'h-card-1', + conditions: ['Mobile'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 12.5, type: 'percentage' } }, + keyframeEffect: { name: 'scaleCenterM1', keyframes: [{ transform: 'scale(1.05)' }, { transform: 'scale(1)' }] } + }, + { + key: 'h-card-2', + conditions: ['Mobile'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 25, type: 'percentage' } }, + keyframeEffect: { name: 'scaleCenterM2', keyframes: [{ transform: 'scale(1)' }, { transform: 'scale(1.05)', offset: 0.5 }, { transform: 'scale(1)' }] } + }, + { + key: 'h-card-3', + conditions: ['Mobile'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 12.5, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 37.5, type: 'percentage' } }, + keyframeEffect: { name: 'scaleCenterM3', keyframes: [{ transform: 'scale(1)' }, { transform: 'scale(1.05)', offset: 0.5 }, { transform: 'scale(1)' }] } + }, + { + key: 'h-card-4', + conditions: ['Mobile'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 25, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 50, type: 'percentage' } }, + keyframeEffect: { name: 'scaleCenterM4', keyframes: [{ transform: 'scale(1)' }, { transform: 'scale(1.05)', offset: 0.5 }, { transform: 'scale(1)' }] } + }, + { + key: 'h-card-5', + conditions: ['Mobile'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 37.5, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 62.5, type: 'percentage' } }, + keyframeEffect: { name: 'scaleCenterM5', keyframes: [{ transform: 'scale(1)' }, { transform: 'scale(1.05)', offset: 0.5 }, { transform: 'scale(1)' }] } + }, + { + key: 'h-card-6', + conditions: ['Mobile'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 50, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 75, type: 'percentage' } }, + keyframeEffect: { name: 'scaleCenterM6', keyframes: [{ transform: 'scale(1)' }, { transform: 'scale(1.05)', offset: 0.5 }, { transform: 'scale(1)' }] } + }, + { + key: 'h-card-7', + conditions: ['Mobile'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 62.5, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 87.5, type: 'percentage' } }, + keyframeEffect: { name: 'scaleCenterM7', keyframes: [{ transform: 'scale(1)' }, { transform: 'scale(1.05)', offset: 0.5 }, { transform: 'scale(1)' }] } + }, + { + key: 'h-card-8', + conditions: ['Mobile'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 75, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, + keyframeEffect: { name: 'scaleCenterM8', keyframes: [{ transform: 'scale(1)' }, { transform: 'scale(1.05)', offset: 0.5 }, { transform: 'scale(1)' }] } + }, + { + key: 'h-card-9', + conditions: ['Mobile'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 87.5, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, + keyframeEffect: { name: 'scaleCenterM9', keyframes: [{ transform: 'scale(1)' }, { transform: 'scale(1.05)' }] } } ] }, diff --git a/assets/modal.js b/assets/modal.js index cdd9e145..4e3455db 100644 --- a/assets/modal.js +++ b/assets/modal.js @@ -3,78 +3,166 @@ const modal = document.getElementById('code-modal'); const modalCode = document.getElementById('modal-code'); const codeSnippets = { - spread: ` + spread: +`const mobileEasing = 'cubic-bezier(0.25, 1, 0.5, 1)'; +... +... +{ key: 'spread-section', trigger: 'viewProgress', effects: [ { - key: 'spread-card-0', - fill: 'both', - rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, - rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, - keyframeEffect: { - name: 'stayCenter', - keyframes: [ - { transform: 'translate(-50%, -50%) scale(1)' }, - { transform: 'translate(-50%, -50%) scale(1.3)' } + key: 'spread-card-0', + conditions: ['Desktop'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, + keyframeEffect: { + name: 'stayCenter', + keyframes: [ + { transform: 'translate(-50%, -50%) scale(1)' }, + { transform: 'translate(-50%, -50%) scale(1.3)' } ] } }, - // ... other cards kept implicitly by leaving config structure similar ... { - key: 'spread-card-1', - fill: 'both', - rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, - rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, - keyframeEffect: { - name: 'spreadLeftInner', - keyframes: [ - { transform: 'translate(-50%, -50%) scale(1)' }, - { transform: 'translate(calc(-50% - 16vw), -50%) scale(1.15)' } + key: 'spread-card-1', + conditions: ['Desktop'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, + keyframeEffect: { + name: 'spreadLeftInner', + keyframes: [ + { transform: 'translate(-50%, -50%) scale(1)' }, + { transform: 'translate(calc(-50% - 16vw), -50%) scale(1.15)' } ] } }, { - key: 'spread-card-2', - fill: 'both', - rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, - rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, - keyframeEffect: { - name: 'spreadRightInner', - keyframes: [ - { transform: 'translate(-50%, -50%) scale(1)' }, - { transform: 'translate(calc(-50% + 16vw), -50%) scale(1.15)' } + key: 'spread-card-2', + conditions: ['Desktop'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, + keyframeEffect: { + name: 'spreadRightInner', + keyframes: [ + { transform: 'translate(-50%, -50%) scale(1)' }, + { transform: 'translate(calc(-50% + 16vw), -50%) scale(1.15)' } ] } }, { - key: 'spread-card-3', - fill: 'both', - rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, - rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, - keyframeEffect: { - name: 'spreadLeftOuter', - keyframes: [ - { transform: 'translate(-50%, -50%) scale(1)' }, - { transform: 'translate(calc(-50% - 32vw), -50%) scale(0.9)' } + key: 'spread-card-3', + conditions: ['Desktop'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, + keyframeEffect: { + name: 'spreadLeftOuter', + keyframes: [ + { transform: 'translate(-50%, -50%) scale(1)' }, + { transform: 'translate(calc(-50% - 32vw), -50%) scale(0.9)' } ] } }, { - key: 'spread-card-4', - fill: 'both', - rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, - rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, - keyframeEffect: { - name: 'spreadRightOuter', - keyframes: [ - { transform: 'translate(-50%, -50%) scale(1)' }, - { transform: 'translate(calc(-50% + 32vw), -50%) scale(0.9)' } + key: 'spread-card-4', + conditions: ['Desktop'], + fill: 'both', + rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, + keyframeEffect: { + name: 'spreadRightOuter', + keyframes: [ + { transform: 'translate(-50%, -50%) scale(1)' }, + { transform: 'translate(calc(-50% + 32vw), -50%) scale(0.9)' } + ] + } + }, + { + key: 'spread-card-0', + conditions: ['Mobile'], + fill: 'both', + easing: mobileEasing, + rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 25, type: 'percentage' } }, + keyframeEffect: { + name: 'card-0-scaleDown', + keyframes: [ + { transform: 'translateX(-50%) translateY(0) scale(1)' }, + { transform: 'translateX(-50%) translateY(0) scale(0.85)' } + ] + } + }, + { + key: 'spread-card-1', + conditions: ['Mobile'], + fill: 'both', + easing: mobileEasing, + rangeStart: { name: 'contain', offset: { value: 0, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 50, type: 'percentage' } }, + keyframeEffect: { + name: 'card-1-slideUp-scaleDown', + keyframes: [ + { offset: 0, transform: 'translateX(-50%) translateY(100vh) scale(1)' }, + { offset: 0.5, transform: 'translateX(-50%) translateY(0) scale(1)' }, + { offset: 1, transform: 'translateX(-50%) translateY(0) scale(0.85)' } + ] + } + }, + { + key: 'spread-card-2', + conditions: ['Mobile'], + fill: 'both', + easing: mobileEasing, + rangeStart: { name: 'contain', offset: { value: 25, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 75, type: 'percentage' } }, + keyframeEffect: { + name: 'card-2-slideUp-scaleDown', + keyframes: [ + { offset: 0, transform: 'translateX(-50%) translateY(100vh) scale(1)' }, + { offset: 0.5, transform: 'translateX(-50%) translateY(0) scale(1)' }, + { offset: 1, transform: 'translateX(-50%) translateY(0) scale(0.85)' } + ] + } + }, + { + key: 'spread-card-3', + conditions: ['Mobile'], + fill: 'both', + easing: mobileEasing, + rangeStart: { name: 'contain', offset: { value: 50, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, + keyframeEffect: { + name: 'card-3-slideUp-scaleDown', + keyframes: [ + { offset: 0, transform: 'translateX(-50%) translateY(100vh) scale(1)' }, + { offset: 0.5, transform: 'translateX(-50%) translateY(0) scale(1)' }, + { offset: 1, transform: 'translateX(-50%) translateY(0) scale(0.85)' } + ] + } + }, + { + key: 'spread-card-4', + conditions: ['Mobile'], + fill: 'both', + easing: mobileEasing, + rangeStart: { name: 'contain', offset: { value: 75, type: 'percentage' } }, + rangeEnd: { name: 'contain', offset: { value: 100, type: 'percentage' } }, + keyframeEffect: { + name: 'card-4-slideUp-scaleDown', + keyframes: [ + { offset: 0, transform: 'translateX(-50%) translateY(100vh) scale(1)' }, + { offset: 0.5, transform: 'translateX(-50%) translateY(0) scale(1)' }, + { offset: 1, transform: 'translateX(-50%) translateY(0) scale(0.85)' } ] } } ] - ` +}, + ` }; function openModal(type) { diff --git a/assets/styles.css b/assets/styles.css index 078cec3e..3abd40d8 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -94,6 +94,52 @@ interact-element { .spread-card-init { transform: translate(-50%, -50%); } +/* Z-Index desktop card spread */ +[data-interact-key="spread-card-0"] .spread-card { z-index: 5; } +[data-interact-key="spread-card-1"] .spread-card { z-index: 4; } +[data-interact-key="spread-card-2"] .spread-card { z-index: 3; } +[data-interact-key="spread-card-3"] .spread-card { z-index: 2; } +[data-interact-key="spread-card-4"] .spread-card { z-index: 1; } + +/* Mobile overrides for card spread */ +@media (max-width: 1000px) { + #spread-section { + height: 500vh !important; + } + + .spread-card { + top: 15vh; + left: 50%; + width: 85vw; + height: 65vh; + max-width: 400px; + background-size: contain; + box-shadow: none; + border-radius: 0; + transform: translateX(-50%); + } + [data-interact-key="spread-card-0"] .spread-card { + transform: translateX(-50%) translateY(0); + z-index: 1; + } + [data-interact-key="spread-card-1"] .spread-card { + transform: translateX(-50%) translateY(100vh); + z-index: 2; + } + [data-interact-key="spread-card-2"] .spread-card { + transform: translateX(-50%) translateY(100vh); + z-index: 3; + } + [data-interact-key="spread-card-3"] .spread-card { + transform: translateX(-50%) translateY(100vh); + z-index: 4; + } + [data-interact-key="spread-card-4"] .spread-card { + transform: translateX(-50%) translateY(100vh); + z-index: 5; + } +} + /* TUNNEL EFFECT STYLES */ .tunnel-container { @@ -167,9 +213,24 @@ interact-element { position: relative; overflow: hidden; border: 1px solid rgba(255, 255, 255, 0.8); +} +/* Mobile responsive for horizontal scroll cards */ +@media (max-width: 1000px) { + .horizontal-track { + gap: 4vw; + padding-left: 2.5vw; /* Centers 95vw card: 50vw - 47.5vw */ + padding-right: 50vw; + } + + .h-card { + width: 95vw; + height: 63.33vw; /* Maintains 1.5:1 aspect ratio */ + border-radius: 12px; + } } + /* NEW COMPONENT STYLES */ .custom-scrollbar::-webkit-scrollbar { width: 3px; diff --git a/index.html b/index.html index f92278a2..548dddfb 100644 --- a/index.html +++ b/index.html @@ -48,12 +48,12 @@ -