<!-- CODE = #A078 --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>NETBYZZ A078</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css"> <link rel="stylesheet" href="style.css"> </head> <body> <a href="#" class="button"> <canvas class="button__canvas"></canvas> <span class="button__text">Hover me I am liquid</span> </a> <script src="https://cdn.jsdelivr.net/npm/ola"></script> <script src="script.js"></script> </body> </html>
body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #eef; } .w-100 { width: 100%; height: 20px; position: relative; } .button { position: relative; padding: 1.6em 2.8em; text-decoration: none; } .button__canvas { --offset: 32px; position: absolute; top: calc(var(--offset) * -1); left: calc(var(--offset) * -1); width: calc(100% + var(--offset) * 2); height: calc(100% + var(--offset) * 2); transition: opacity 2s ease; } .button__text { position: relative; color: #ff0; font-weight: bold; letter-spacing: 0.02em; pointer-events: none; } .button:hover .button__canvas { opacity: 0.85; transition: opacity 0.2s ease; } .button:active .button__canvas { opacity: 1; transition: none; }
document.querySelectorAll(".button").forEach((elem) => { const canvas = elem.querySelector(".button__canvas"); const ctx = canvas.getContext("2d"); const offset = 32; const background = "#eef"; const foreground = "#1253c8"; let points = []; let position; const distance = new Ola({ value: offset, }); const size = () => { canvas.width = canvas.getBoundingClientRect().width; canvas.height = canvas.getBoundingClientRect().height; position = new Ola({ x: canvas.width / 2, y: canvas.height / 2, }); const pixelsWidth = canvas.width - offset * 2; const pixelsHeight = canvas.height - offset * 2; const leftTop = [offset, offset]; const rightTop = [canvas.width - offset, offset]; const rightBottom = [canvas.width - offset, canvas.height - offset]; const leftBottom = [offset, canvas.height - offset]; points = []; Array(pixelsWidth) .fill() .forEach((_, index) => { points.push([leftTop[0] + index, leftTop[1]]); }); Array(pixelsHeight) .fill() .forEach((_, index) => { points.push([rightTop[0], rightTop[1] + index]); }); Array(pixelsWidth) .fill() .forEach((_, index) => { points.push([rightBottom[0] - index, rightBottom[1]]); }); Array(pixelsHeight) .fill() .forEach((_, index) => { points.push([leftBottom[0], leftBottom[1] - index]); }); }; size(); const reset = () => { ctx.fillStyle = background; ctx.fillRect(0, 0, canvas.width, canvas.height); }; const draw = () => { ctx.fillStyle = foreground; ctx.beginPath(); points.forEach((point, index) => { const [vx, vy] = attract(point); if (index === 0) ctx.moveTo(vx, vy); else ctx.lineTo(vx, vy); }); ctx.closePath(); ctx.fill(); }; const attract = (point) => { const [x, y] = point; const dx = x - position.x; const dy = y - position.y; const dist = Math.sqrt(dx * dx + dy * dy); const dist2 = Math.max(1, dist); const d = Math.min(dist2, Math.max(12, dist2 / 4 - dist2)); const D = dist2 * distance.value; return [x + (d / D) * (position.x - x), y + (d / D) * (position.y - y)]; }; const loop = () => { reset(); draw(); requestAnimationFrame(loop); }; window.onresize = size; canvas.onmousemove = (e) => { position.set( { x: e.clientX - e.target.getBoundingClientRect().left, y: e.clientY - e.target.getBoundingClientRect().top, }, 200 ); }; canvas.onmouseenter = () => { distance.set( { value: 1, }, 200 ); }; canvas.onmouseleave = () => { position.set( { x: canvas.width / 2, y: canvas.height / 2, }, 2000 ); distance.set( { value: offset, }, 12000 ); }; loop(); });