You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SVG双眼瞳孔鼠标跟随异常求助:需实现中间区域瞳孔居中

解决SVG双眼瞳孔协同追踪鼠标的问题

问题描述

现有一个包含两只眼睛的SVG,瞳孔通过line元素的marker实现鼠标追踪,但当前效果是两只眼睛的瞳孔各自独立指向鼠标点,导致光标位于双眼之间时,左侧瞳孔右移、右侧瞳孔左移。需要实现:光标处于双眼之间(或附近区域)时,瞳孔保持在眼睛中心;光标在外侧时,瞳孔同步向对应方向移动,且始终不超出眼球范围。

修改后的完整代码

JavaScript

let l1 = document.querySelector("#l1");
let l2 = document.querySelector("#l2");
let svg1 = document.querySelector("#svg1");

// 定义每只眼睛的核心参数:中心坐标、椭圆半径、对应DOM元素
const leftEye = { cx: 80, cy: 45, rx: 20, ry: 22, element: l1 };
const rightEye = { cx: 120, cy: 45, rx: 20, ry: 22, element: l2 };

// 将屏幕坐标转换为SVG内部坐标
const toSVGPoint = (svg, x, y) => {
  let p = new DOMPoint(x, y);
  return p.matrixTransform(svg.getScreenCTM().inverse());
};

// 计算瞳孔的目标位置,确保在眼球范围内且协同动作
const getPupilTarget = (eye, mousePoint) => {
  const { cx, cy, rx, ry } = eye;
  let dx = mousePoint.x - cx;
  let dy = mousePoint.y - cy;

  // 判断鼠标是否处于双眼中间的水平区域(双眼中心之间的范围)
  const isInMiddleZone = (mousePoint.x >= leftEye.cx && mousePoint.x <= rightEye.cx);
  // 中间区域内,瞳孔水平方向保持中心
  if (isInMiddleZone) {
    dx = 0;
  }

  // 计算偏移量是否超出眼球椭圆范围,超出则按比例缩放
  const distanceRatio = Math.sqrt((dx * dx) / (rx * rx) + (dy * dy) / (ry * ry));
  if (distanceRatio > 1) {
    dx /= distanceRatio;
    dy /= distanceRatio;
  }

  return { x: cx + dx, y: cy + dy };
};

document.addEventListener('mousemove', e => {
  let p = toSVGPoint(svg1, e.clientX, e.clientY);
  
  // 更新左眼瞳孔位置
  const leftTarget = getPupilTarget(leftEye, p);
  leftEye.element.setAttribute('x2', leftTarget.x);
  leftEye.element.setAttribute('y2', leftTarget.y);

  // 更新右眼瞳孔位置
  const rightTarget = getPupilTarget(rightEye, p);
  rightEye.element.setAttribute('x2', rightTarget.x);
  rightEye.element.setAttribute('y2', rightTarget.y);
});

CSS

.container{
  width: 20em;
  height: 20em;
  margin: 200px auto;
}

HTML

<div class="container">
  <svg id="svg1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
    <ellipse cx="80" cy="45" rx="20" ry="22" stroke-width="10" stroke="#fdd176"/>
    <ellipse cx="120" cy="45" rx="20" ry="22" stroke-width="10" stroke="#fdd176"/>
    <line marker-start="url(#pupil)" id="l1" x1="80" y1="45" />
    <line marker-start="url(#pupil)" id="l2" x1="120" y1="45" />
    <defs>
      <marker id="pupil" viewBox="0 0 18 18" refX="10" refY="5" markerWidth="37" markerHeight="37" orient="auto-start-reverse">
        <circle fill="#fdd176" r="4" cy="5" cx="5" />
      </marker>
    </defs>
  </svg>
</div>

关键逻辑说明

  1. 坐标转换:toSVGPoint函数将鼠标的屏幕坐标转换为SVG内部的坐标系统,确保计算准确。
  2. 中间区域判断:通过判断鼠标的x坐标是否在双眼中心(80到120)之间,让瞳孔在该区域内保持水平居中,避免相向移动的问题。
  3. 眼球边界限制:使用椭圆方程计算偏移量的比例,当鼠标超出眼球范围时,自动将瞳孔位置限制在眼球边缘,保证视觉合理性。
  4. 独立计算每只眼睛:对左右眼分别计算目标位置,既保证协同动作,又保留各自的眼球边界限制。

内容的提问来源于stack exchange,提问作者Mathieu Préaud

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:24:49