
本文详解如何通过纯 css + javascript 实现“下层图片常驻、上层图片仅在鼠标悬停处以圆形(可带渐变)遮罩动态揭示”的交互效果,解决因绝对定位嵌套导致的图像偏移问题。
本文详解如何通过纯 css + javascript 实现“下层图片常驻、上层图片仅在鼠标悬停处以圆形(可带渐变)遮罩动态揭示”的交互效果,解决因绝对定位嵌套导致的图像偏移问题。
要实现「鼠标移动时,在固定位置的底层图片上,用一个可跟随光标的圆形窗口揭示上方图片」的效果,关键在于分离“遮罩容器”与“被揭示内容”的定位逻辑——不能将上层图片嵌套在移动的 .circle 内并设为 position: absolute,否则它会随 .circle 一起位移,导致始终只显示同一区域。
正确的思路是:
✅ 将两幅图片均作为 背景图(background-image) 分别应用在两个层级分明的 dom 元素上;
✅ 底层容器(.container)承载下层图片,并设置 overflow: hidden 限制可见范围;
✅ 上层遮罩(.circle)是一个独立的、可自由移动的圆形 div,其自身设置上层图片为背景,通过 background-position 动态对齐视口;
✅ JavaScript 仅控制 .circle 的 left/top 坐标,使其中心始终锚定鼠标位置。
✅ 推荐 HTML 结构(语义清晰、无冗余嵌套)
<div class="container" style="background-image: url('lower.png')"> <div class="circle" style="background-image: url('upper.png')"></div> </div>
✅ 核心 CSS(重点:背景定位 + 居中控制)
.container { position: relative; width: 100vw; /* 或具体尺寸,如 1920px */ height: 100vh; /* 或具体尺寸,如 1280px */ background-size: cover; background-position: center; overflow: hidden; border-radius: 0; /* 若需整体圆角可设,但非必须 */ } .circle { position: absolute; width: 200px; /* 遮罩直径 */ height: 200px; border-radius: 50%; background-size: cover; background-position: center; /* 初始居中(可选),后续由 JS 覆盖 */ top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 2; /* 可选:添加内阴影或渐变蒙版增强视觉层次 */ box-shadow: 0 0 0 9999px rgba(0,0,0,0.3); } /* 添加柔和渐变边缘(可选增强效果)*/ .circle::before { content: ""; position: absolute; inset: 0; border-radius: 50%; background: radial-gradient( circle at center, transparent 0%, transparent 70%, rgba(0, 0, 0, 0.4) 100% ); }
? 注意:box-shadow 技巧(超大模糊半径遮罩)比 overflow: hidden 更可靠,能避免 .circle 移出容器时出现意外裁剪;若需严格限制在 .container 内,可改用 clip-path: circle(100px at var(–x) var(–y)) 配合 CSS 变量(现代浏览器支持)。
✅ 精准跟随的 JavaScript(修正原逻辑缺陷)
const container = document.querySelector('.container'); const circle = document.querySelector('.circle'); const radius = 100; // 半径 = 宽高 / 2 container.addEventListener('mousemove', (e) => { const rect = container.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; // 设置 circle 的中心点为 (x, y),需减去半径实现“锚点居中” circle.style.left = `${x - radius}px`; circle.style.top = `${y - radius}px`; // 同步更新背景定位,确保上层图片内容随遮罩稳定呈现(关键!) circle.style.backgroundPosition = `${-x + radius}px ${-y + radius}px`; });
? 为什么需要 backgroundPosition 动态调整?
因为 .circle 是移动的容器,而它的背景图(上层图片)默认以自身左上角为原点。要让“鼠标所在位置”始终对应上层图片的同一物理坐标(即实现“窥视”而非“平移”),就必须反向偏移背景图——即 background-position 设为 -(x – r), -(y – r),抵消容器位移。
✅ 进阶优化建议
- 性能优化:对 mousemove 使用 throttle(如 Lodash 的 throttle 或 requestAnimationFrame 包裹),避免高频重排;
- 响应式适配:用 vmin 单位定义 .circle 尺寸(如 width: 20vmin; height: 20vmin;),并动态计算 radius;
- 触屏兼容:补充 touchmove 事件监听,并从 e.touches[0] 读取坐标;
- 无障碍友好:为 .circle 添加 aria-hidden=”true”,避免屏幕阅读器误读。
该方案彻底规避了嵌套绝对定位引发的图像“粘连”问题,结构解耦、样式可控、行为精准,适用于产品页视觉引导、图像对比展示、创意悬停交互等多种场景。