רקע הלפטון

טקסטורה עריכתית במקום תמונת סטוק או גרדיאנט: נקודות שגודלן נקבע לפי המרחק מהמוקד. המוקד הולך אחרי הסמן, ובלי סמן (מובייל) הוא נע לבד במסלול איטי. אין טשטוש ואין זוהר, הציור עוצר מחוץ למסך, ובמתג תנועה כבוי נשארת תמונה אחת יפה. רקע הלפטון הוא רכיב מתוך רכיבים של כליקס. מה שמורידים הוא רכיב שלם, גם כ-HTML עצמאי וגם כרכיב React. אין ספרייה חיצונית, אין שלב בנייה, ואם כליקס ייעלם מחר הקוד ימשיך לעבוד. מתאים לעמודים ולמסמכים שעוסקים ברקע, קנבס, הירו ובלי ספריות. באותה סדרה יש גם שאלות נפוצות, ציר תהליך וקרוסלת המלצות.

רכיב · רקע · קנבס · הירו · בלי ספריות

<!-- רקע הלפטון: אלמנט עם klix-halftone, והתוכן בתוכו יושב מעל הנקודות.
     צבעים: data-ink ו-data-accent -->
<style>
.klix-halftone{position:relative;overflow:hidden;background:#0b1020}
.klix-halftone>canvas{position:absolute;inset:0;width:100%;height:100%;display:block}
.klix-halftone>*:not(canvas){position:relative}
</style>

<section class="klix-halftone" data-ink="#334155" data-accent="#f97316" style="width:100%;min-height:22rem;display:grid;place-items:center;border-radius:1rem">
  <h2 style="margin:0;font-size:2.2rem;font-weight:900;color:#fff;text-align:center">הסטודיו שלכם, בלי רקע גנרי</h2>
</section>

<script>
(function () {
  function off() { return document.documentElement.getAttribute('data-motion') === 'off'; }

  document.querySelectorAll('.klix-halftone').forEach(function (root) {
    var canvas = document.createElement('canvas');
    canvas.setAttribute('aria-hidden', 'true');
    root.insertBefore(canvas, root.firstChild);
    var ctx = canvas.getContext('2d');
    if (!ctx) return;
    var ink = root.getAttribute('data-ink') || '#334155';
    var accent = root.getAttribute('data-accent') || '#f97316';
    var w = 0, h = 0, dpr = 1, fx = 0, fy = 0, px = -1, py = -1;
    var visible = true, raf = 0, last = performance.now(), t = 0;

    function draw() {
      var step = w < 640 * dpr ? 22 * dpr : 18 * dpr;
    var maxR = step * 0.46, reach = Math.max(w, h) * 0.42;
    ctx.clearRect(0, 0, w, h);
    for (var y = step / 2; y < h; y += step) {
      for (var x = step / 2; x < w; x += step) {
        var dx = x - fx, dy = y - fy;
        var k = 1 - Math.min(Math.sqrt(dx * dx + dy * dy) / reach, 1);
        var r = 0.55 * dpr + maxR * k * k;
        ctx.fillStyle = k > 0.62 ? accent : ink;
        ctx.beginPath(); ctx.arc(x, y, r, 0, 6.283); ctx.fill();
      }
    }
    }

    function size() {
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      w = canvas.width = Math.round(root.clientWidth * dpr);
      h = canvas.height = Math.round(root.clientHeight * dpr);
      if (!fx) { fx = w * 0.66; fy = h * 0.4; }
      draw(); // שינוי width מוחק את הקנבס
    }

    function frame(now) {
      raf = 0;
      var dt = Math.min(Math.max((now - last) / 1000, 0), 0.05);
      last = now; t += dt;
      // בלי סמן: מסלול ליסז'ו איטי, כדי שהרקע לא ייראה מת
      var tx = px >= 0 ? px : w * (0.5 + 0.32 * Math.sin(t * 0.37));
      var ty = py >= 0 ? py : h * (0.5 + 0.3 * Math.sin(t * 0.53 + 1));
      var ease = 1 - Math.exp(-dt * 6);
      fx += (tx - fx) * ease; fy += (ty - fy) * ease;
      draw();
      if (visible && !off()) raf = requestAnimationFrame(frame);
    }

    function wake() {
      if (!raf && visible && !off()) { last = performance.now(); raf = requestAnimationFrame(frame); }
    }

    size();
    if ('ResizeObserver' in window) new ResizeObserver(size).observe(root);
    else window.addEventListener('resize', size);
    if ('IntersectionObserver' in window) {
      new IntersectionObserver(function (en) { visible = en[0].isIntersecting; wake(); }).observe(root);
    }
    root.addEventListener('pointermove', function (e) {
      var r = root.getBoundingClientRect();
      px = (e.clientX - r.left) * dpr; py = (e.clientY - r.top) * dpr;
      wake();
    });
    root.addEventListener('pointerleave', function () { px = py = -1; });
    wake();
  });
})();
</script>