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>
关键逻辑说明
- 坐标转换:
toSVGPoint函数将鼠标的屏幕坐标转换为SVG内部的坐标系统,确保计算准确。 - 中间区域判断:通过判断鼠标的x坐标是否在双眼中心(80到120)之间,让瞳孔在该区域内保持水平居中,避免相向移动的问题。
- 眼球边界限制:使用椭圆方程计算偏移量的比例,当鼠标超出眼球范围时,自动将瞳孔位置限制在眼球边缘,保证视觉合理性。
- 独立计算每只眼睛:对左右眼分别计算目标位置,既保证协同动作,又保留各自的眼球边界限制。
内容的提问来源于stack exchange,提问作者Mathieu Préaud
相关产品推荐
相关产品推荐

