Interactive Liquid Slit-Scan Carousel
Home
Snippets
Interactive Liquid Slit-Scan Carousel
HTML
CSS
JS
<div class="showcase-card" id="showcase-box"> <canvas id="slit-canvas"></canvas> </div>
body { margin: 0; background: #07080b; height: 100vh; display: flex; justify-content: center; align-items: center; font-family: -apple-system, BlinkMacSystemFont, sans-serif; overflow: hidden; } .showcase-card { position: relative; width: 340px; height: 220px; background: #000; border-radius: 20px; overflow: hidden; box-shadow: 0 40px 80px rgba(0, 0, 0, 0.7); border: 1px solid rgba(255, 255, 255, 0.03); } canvas { display: block; width: 100%; height: 100%; cursor: max-gradient; }
const container = document.getElementById('showcase-box'); const canvas = document.getElementById('slit-canvas'); const ctx = canvas.getContext('2d'); let width, height; let mouseX = -1000, targetMouseX = -1000; const totalSlits = 18; let slits = []; function init() { width = container.clientWidth; height = container.clientHeight; canvas.width = width; canvas.height = height; slits = []; const slitWidth = width / totalSlits; for (let i = 0; i < totalSlits; i++) { slits.push({ x: i * slitWidth, width: slitWidth, offsetY: 0, vy: 0 }); } } function draw() { ctx.clearRect(0, 0, width, height); mouseX += (targetMouseX - mouseX) * 0.1; slits.forEach((slit, i) => { const slitCenterX = slit.x + slit.width / 2; const distToMouse = Math.abs(mouseX - slitCenterX); let targetOffsetY = 0; if (distToMouse < 90) { let factor = (90 - distToMouse) / 90; targetOffsetY = Math.sin(factor * Math.PI) * 55; } let fy = (targetOffsetY - slit.offsetY) * 0.2; slit.vy += fy; slit.vy *= 0.8; slit.offsetY += slit.vy; ctx.fillStyle = `hsl(${(i * 15) % 360}, 75%, 50%)`; ctx.fillRect(slit.x, 0, slit.width, height); ctx.save(); ctx.beginPath(); ctx.rect(slit.x, slit.offsetY, slit.width, height - Math.abs(slit.offsetY)); ctx.clip(); ctx.fillStyle = '#12141c'; ctx.fillRect(slit.x, 0, slit.width, height); ctx.fillStyle = 'rgba(255,255,255,0.02)'; ctx.fillRect(slit.x, 0, 1, height); ctx.restore(); }); requestAnimationFrame(draw); } container.addEventListener('mousemove', (e) => { const rect = canvas.getBoundingClientRect(); targetMouseX = e.clientX - rect.left; }); container.addEventListener('mouseleave', () => { targetMouseX = -1000; }); container.addEventListener('touchmove', (e) => { const rect = canvas.getBoundingClientRect(); targetMouseX = e.touches[0].clientX - rect.left; }, { passive: true }); container.addEventListener('touchend', () => { targetMouseX = -1000; }); window.addEventListener('resize', init); init(); draw();
Ad #1
Ad #2
Scroll to Top