如何实现鼠标移动时图片平滑跟随并动态显隐的效果?
解决鼠标跟随图片平滑显隐的问题
我看了你的代码,想要实现类似目标网站的鼠标移动时图片跟随并动态显隐的效果,咱们一步步来修正代码里的问题:
主要问题分析
- 元素位置计算复杂易出错:原来的
getPosition函数用循环offsetParent的方式计算位置,不仅繁琐,还容易因为滚动或嵌套元素出现偏差,现代浏览器用getBoundingClientRect()更高效准确。 - 所有图片同时移动导致重叠:每次鼠标移动时你遍历所有图片并设置相同位置,这样所有图片叠在一起,视觉上只有一张,而且大量DOM操作会影响性能。
- 过渡效果冲突:CSS和JS里重复设置
transition属性,导致动画效果混乱,应该统一管理过渡规则。 - 定时器堆积:每次
mousemove都给每个图片添加定时器,会造成大量定时器同时运行,拖慢页面性能。 - HTML类名笔误:
<div class="giv__photo__block">里的giv应该是gif,虽然不影响选择器,但规范命名能避免后续不必要的bug。
修正后的完整代码
HTML(修正类名笔误)
<div class="gif__portfolio__inner"> <div class="gif__photo__block"> <img src="img/gif/photo-2.png" alt="" class="gif__photo gif__photo__one"> <img src="img/gif/photo-2.png" alt="" class="gif__photo gif__photo__two"> <img src="img/gif/photo-3.png" alt="" class="gif__photo gif__photo__three"> <img src="img/gif/photo-4.png" alt="" class="gif__photo gif__photo__four"> <img src="img/gif/photo-5.png" alt="" class="gif__photo gif__photo__five"> <img src="img/gif/photo-6.png" alt="" class="gif__photo gif__photo__six"> <img src="img/gif/photo-7.png" alt="" class="gif__photo gif__photo__seven"> <img src="img/gif/photo-8.png" alt="" class="gif__photo gif__photo__eight"> </div> </div>
CSS(统一过渡效果,简化规则)
.gif__portfolio__inner { overflow: hidden; position: relative; /* 给父容器设置宽高,确保鼠标能触发事件 */ width: 100vw; height: 500px; background-color: #f0f0f0; } .gif__photo__block { position: relative; width: 100%; height: 100%; } .gif__photo { position: absolute; z-index: 10; opacity: 0; border-radius: 50%; /* 统一过渡:位置平滑移动,透明度和圆角渐变 */ transition: left 0.2s ease-out, top 0.2s ease-out, opacity 0.5s ease-out, border-radius 0.5s ease-out; /* 初始位置设为父容器外,避免初始闪烁 */ left: -100px; top: -100px; width: 80px; height: 80px; object-fit: cover; } .gif__photo.active { opacity: 1; border-radius: 0; }
JavaScript(优化逻辑,避免性能问题)
function initPhotoFollow() { const gifPortfolio = document.querySelector('.gif__portfolio__inner'); const pictures = document.querySelectorAll(".gif__photo"); let currentIndex = 0; // 循环使用图片,避免重复创建元素 let isAnimating = false; // 防止短时间内重复触发动画 // 获取元素相对于视口的位置,替代原来的getPosition function getElementRect(element) { return element.getBoundingClientRect(); } function handleMouseMove(e) { if (isAnimating) return; // 如果有动画在运行,跳过当前触发 const parentRect = getElementRect(gifPortfolio); // 计算鼠标在父容器内的坐标 const mouseX = e.clientX - parentRect.left; const mouseY = e.clientY - parentRect.top; // 取出当前要显示的图片 const currentPic = pictures[currentIndex]; // 计算图片位置(让图片中心对准鼠标) const xPosition = mouseX - currentPic.offsetWidth / 2; const yPosition = mouseY - currentPic.offsetHeight / 2; // 设置图片位置 currentPic.style.left = `${xPosition}px`; currentPic.style.top = `${yPosition}px`; // 激活图片(显示+变圆角) currentPic.classList.add('active'); isAnimating = true; // 动画结束后重置状态,切换到下一张图片 setTimeout(() => { currentPic.classList.remove('active'); // 延迟等待透明度过渡完成后再重置位置,避免闪烁 setTimeout(() => { currentPic.style.left = '-100px'; currentPic.style.top = '-100px'; isAnimating = false; // 切换到下一张图片,循环使用 currentIndex = (currentIndex + 1) % pictures.length; }, 500); }, 400); } gifPortfolio.addEventListener("mousemove", handleMouseMove, false); } // 确保DOM加载完成后执行 document.addEventListener('DOMContentLoaded', initPhotoFollow);
关键优化点说明
- 使用
getBoundingClientRect()计算位置:这个方法直接返回元素相对于视口的矩形位置,计算鼠标在父容器内的坐标更简单准确,避免了原来循环offsetParent的复杂逻辑。 - 循环使用图片:通过
currentIndex循环切换要显示的图片,避免所有图片同时移动,同时减少DOM操作次数,提升性能。 - 添加动画节流:用
isAnimating变量防止短时间内重复触发动画,避免定时器堆积和视觉混乱。 - 统一过渡规则:在CSS里一次性定义所有需要过渡的属性,JS只负责添加/移除类来触发动画,避免过渡属性冲突。
- 明确父容器尺寸:给
.gif__portfolio__inner设置宽高,确保鼠标能在容器内触发mousemove事件,否则容器高度为0的话事件不会触发。
这样修改后,就能实现鼠标移动时,图片依次跟随鼠标位置出现,从圆形变为方形,然后淡出,再切换下一张图片的平滑效果啦~
内容的提问来源于stack exchange,提问作者Павел
相关产品推荐
相关产品推荐

