Halftone Reveal Animation
Home
Snippets
Halftone Reveal Animation
HTML
CSS
JS
<div class="container"> <div class="halftone-reveal"> <svg class="clip-svg" width="0" height="0"> <defs> <clipPath id="halftone-clip"> </clipPath> </defs> </svg> <div class="content" style="clip-path: url(#halftone-clip);" /> </div> </div>
* { margin: 0; padding: 0; box-sizing: border-box; } body { background: #f8f8f8; height: 100vh; width: 100dvw; display: flex; align-items: center; justify-content: center; overflow: hidden; } .clip-svg { position: absolute; width: 0; height: 0; pointer-events: none; } .content { width: 350px; height: 240px; border-radius: 10%; background-color: hsla(321, 77%, 83%, 1); background-image: radial-gradient( circle at 5% 1%, hsla(300, 100%, 45.1%, 1) 7%, transparent 84% ), radial-gradient( circle at 7% 81%, hsla(198, 100%, 44.5%, 1) 16%, transparent 53% ), radial-gradient( circle at 90% 50%, hsla(247, 54.1%, 33.3%, 1) 2%, transparent 85% ); background-blend-mode: normal, normal, normal, normal, normal; }
let config = { direction: "bottom", spacing: 20, duration: 1.7, stagger: 0.05 }; function getRevealDelay(row, col, rows, cols) { switch (config.direction) { case "top": return row * config.stagger; case "bottom": return (rows - 1 - row) * config.stagger; case "left": return col * config.stagger; case "right": return (cols - 1 - col) * config.stagger; case "center": const centerRow = (rows - 1) / 2; const centerCol = (cols - 1) / 2; const distance = Math.sqrt( Math.pow(row - centerRow, 2) + Math.pow(col - centerCol, 2) ); return distance * config.stagger; default: return row * config.stagger; } } function createHalftoneEffect() { const clipPath = document.querySelector("#halftone-clip"); const container = document.querySelector(".container"); clipPath.innerHTML = ""; const rect = container.getBoundingClientRect(); const width = rect.width; const height = rect.height; const cols = Math.max(1, Math.floor(width / config.spacing)); const rows = Math.max(1, Math.floor(height / config.spacing)); const actualSpacingX = width / cols; const actualSpacingY = height / rows; const maxRadius = Math.max(actualSpacingX, actualSpacingY); for (let row = 0; row < rows; row++) { for (let col = 0; col < cols; col++) { const x = (col + 0.5) * actualSpacingX; const y = (row + 0.5) * actualSpacingY; const revealDelay = getRevealDelay(row, col, rows, cols); const circle = document.createElementNS( "http://www.w3.org/2000/svg", "circle" ); circle.setAttribute("cx", x); circle.setAttribute("cy", y); circle.setAttribute("r", "0"); const animate = document.createElementNS( "http://www.w3.org/2000/svg", "animate" ); animate.setAttribute("attributeName", "r"); animate.setAttribute("values", `0;${maxRadius};0`); animate.setAttribute("dur", `${config.duration * 2}s`); animate.setAttribute("repeatCount", "indefinite"); animate.setAttribute("fill", "freeze"); animate.setAttribute("begin", `${revealDelay}s`); circle.appendChild(animate); clipPath.appendChild(circle); } } } window.addEventListener("load", function () { createHalftoneEffect(); });
Ad #1
Ad #2
Scroll to Top