עיניים עוקבות

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

רכיב · ריחוף · בלי ספריות · מסקוט

<!-- עיניים עוקבות — החליפו את כתובת התמונה ומדדו את מרכזי האישונים -->
<div class="klix-eyes">
  <img src="/images/mascot.png" alt="">
</div>

<style>
  .klix-eyes{position:relative;display:inline-block;max-width:20rem}
  .klix-eyes img{display:block;width:100%}
  .klix-eyes .pupil{position:absolute;border-radius:999px;pointer-events:none;
    aspect-ratio:1;transform:translate(-50%,-50%);transition:transform .1s ease-out}
  .klix-eyes .pupil i{position:absolute;left:22%;top:18%;width:26%;height:26%;
    border-radius:999px;background:rgba(255,255,255,.9)}
</style>

<script>
(function () {
  // ═══ ערכו כאן ═══
  // מרכזי האישונים באחוזים מרוחב וגובה התמונה. חשוב למדוד מהקובץ עצמו
  // ולא בעין — סטייה של אחוז אחד נראית כמו פזילה.
  var EYES = [{ x: 38, y: 47.7 }, { x: 62, y: 47.7 }];
  var PUPIL = 8;      // קוטר האישון, באחוזים מרוחב התמונה
  var REACH = 2.2;    // כמה הוא זז, באותם אחוזים
  var COLOR = '#14212a';
  // ═════════════════

  var box = document.querySelector('.klix-eyes');
  if (!box) return;

  var pupils = EYES.map(function (eye) {
    var el = document.createElement('span');
    el.className = 'pupil';
    el.setAttribute('aria-hidden', 'true');
    el.style.width = PUPIL + '%';
    el.style.left = eye.x + '%';
    el.style.top = eye.y + '%';
    el.style.background = COLOR;
    el.appendChild(document.createElement('i'));
    box.appendChild(el);
    return el;
  });

  // במכשירי מגע אין סמן, והאישונים פשוט נחים במרכז
  if (window.matchMedia('(pointer: coarse)').matches) return;

  var pending = false, mx = 0, my = 0;

  function apply() {
    pending = false;
    var r = box.getBoundingClientRect();
    if (!r.width) return;
    for (var i = 0; i < EYES.length; i++) {
      var cx = r.left + (EYES[i].x / 100) * r.width;
      var cy = r.top + (EYES[i].y / 100) * r.height;
      var dx = mx - cx, dy = my - cy;
      var dist = Math.hypot(dx, dy) || 1;
      // רוויה: ככל שהסמן רחוק התזוזה מתקרבת ל-REACH, בלי קפיצה כשהוא צמוד
      var pull = Math.min(1, dist / 260);
      var move = (REACH / 100) * r.width * pull;
      pupils[i].style.transform = 'translate(calc(-50% + ' + ((dx / dist) * move) +
                                 'px), calc(-50% + ' + ((dy / dist) * move) + 'px))';
    }
  }

  window.addEventListener('pointermove', function (e) {
    mx = e.clientX; my = e.clientY;
    if (!pending) { pending = true; requestAnimationFrame(apply); }
  }, { passive: true });
})();
</script>