如何实现滚动时的容器凸起弹跳效果?原方案失效求解决
滚动触发容器缩放与图片凸起弹跳效果实现方案
需求说明
开发一个页面效果:页面滚动时,固定容器内的图片随滚动位移,容器根据滚动速度伸缩,通过CSS transform实现中心元素的凸起弹跳(滚动时图片容器放大/缩小),参考同类设计网站的滚动视觉效果。
原代码问题分析
- DOM加载顺序错误:HTML中
<script>放在容器元素之前,JS执行时DOM尚未渲染,无法获取目标节点。 - CSS语法错误:
.container的width: 750缺少单位,应为750px。 - 滚动速度计算逻辑错误:原代码用
window.scrollY与容器高度的差值计算速度,无法反映实时滚动的快慢,应基于滚动位移的差值计算。 - 容器滚动冲突:容器设置
overflow-y: scroll,内部滚动与页面全局滚动冲突,导致效果无法触发。 - 图片布局错误:
flex-wrap: wrap导致图片换行排列,不符合垂直滚动位移的需求。
正确实现方案
HTML代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Scroll Bulge Effect</title> <link rel="stylesheet" href="style.css"> </head> <body> <!-- 撑开页面高度,提供滚动空间 --> <div class="scroll-spacer"></div> <div class="container"> <img src="img/img51.jpg" alt="Image 1" /> <img src="img/img52.jpg" alt="Image 2" /> <img src="img/img64.jpg" alt="Image 3" /> <img src="img/img71.jpg" alt="Image 4" /> </div> <script src="./js/app.js"></script> </body> </html>
CSS代码
body { margin: 0; background-color: rgb(110, 109, 109); } /* 撑开页面高度,确保用户能滚动触发效果 */ .scroll-spacer { height: 300vh; } .container { border-radius: 16px; height: 550px; width: 750px; max-width: 800px; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); overflow: hidden; /* 图片垂直堆叠 */ display: flex; flex-direction: column; } .container img { width: 100%; height: 100%; object-fit: cover; } @media only screen and (min-width: 350px) { .container { width: 80%; } }
JavaScript代码
// 等待DOM完全加载后执行逻辑 document.addEventListener('DOMContentLoaded', () => { const container = document.querySelector('.container'); const images = document.querySelectorAll('.container img'); const containerHeight = container.offsetHeight; const centerPosition = window.innerHeight / 2; // 记录上一次滚动位置,用于计算滚动速度 let lastScrollY = window.scrollY; function updateScrollEffect() { const currentScrollY = window.scrollY; // 计算实时滚动速度(位移差值,放大系数调整灵敏度) const scrollSpeed = Math.abs(currentScrollY - lastScrollY) * 0.5; lastScrollY = currentScrollY; // 容器缩放:限制缩放范围在0.8-1.2之间,避免过度变形 const scaleFactor = Math.min(1.2, Math.max(0.8, 1 + scrollSpeed * 0.01)); container.style.transform = `translate(-50%, -50%) scale(${scaleFactor})`; // 图片位移与凸起效果 images.forEach((image, index) => { // 基础位移:随滚动同步垂直移动 const baseTranslateY = -currentScrollY + (index * containerHeight); // 计算图片中心与页面中心的距离 const imageCenterY = image.getBoundingClientRect().top + containerHeight / 2; const distanceFromCenter = Math.abs(centerPosition - imageCenterY); // 凸起强度:距离中心越近,凸起越明显 const bulgeIntensity = Math.max(0, 1 - distanceFromCenter / (window.innerHeight / 2)); const bulgeTranslateY = -scrollSpeed * bulgeIntensity * 0.3; image.style.transform = `translateY(${baseTranslateY + bulgeTranslateY}px)`; }); } // 使用requestAnimationFrame优化动画流畅度 function rafUpdate() { updateScrollEffect(); requestAnimationFrame(rafUpdate); } requestAnimationFrame(rafUpdate); });
关键实现说明
- DOM加载时机:用
DOMContentLoaded确保JS在DOM渲染完成后执行,避免获取不到元素的问题。 - 滚动速度计算:通过对比当前与上一次的
scrollY值,计算滚动位移差值,得到实时滚动速度。 - 容器缩放:基于滚动速度动态调整容器的
scale值,限制缩放范围避免过度变形。 - 图片凸起逻辑:图片随滚动垂直位移,同时根据与页面中心的距离添加额外位移,距离中心越近,凸起效果越强。
- 性能优化:用
requestAnimationFrame替代直接绑定scroll事件,确保动画流畅且性能友好。
PixiJS优化方案(可选)
如果需要更复杂的图形扭曲效果,可调整PixiJS代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>PixiJS Scroll Bulge Effect</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/6.1.3/pixi.min.js"></script> <style> body { margin: 0; padding: 0; overflow-x: hidden; background-color: #222222; } .scroll-spacer { height: 300vh; } #canvas-container { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80vw; height: 80vh; border-radius: 16px; overflow: hidden; } </style> </head> <body> <div class="scroll-spacer"></div> <div id="canvas-container"></div> <script> const canvasContainer = document.getElementById('canvas-container'); const app = new PIXI.Application({ width: canvasContainer.offsetWidth, height: canvasContainer.offsetHeight, backgroundColor: 0x222222 }); canvasContainer.appendChild(app.view); const container = new PIXI.Container(); app.stage.addChild(container); const loader = new PIXI.Loader(); const images = [ 'img/img51.jpg', 'img/img52.jpg', 'img/img64.jpg', 'img/img71.jpg' ]; images.forEach((img) => loader.add(img)); loader.load(setup); let lastScrollY = window.scrollY; let imageSprites = []; function setup() { imageSprites = images.map((img, index) => { const texture = PIXI.Texture.from(img); const sprite = new PIXI.Sprite(texture); sprite.width = app.screen.width; sprite.height = app.screen.height; sprite.y = index * app.screen.height; container.addChild(sprite); return sprite; }); app.ticker.add(() => { const currentScrollY = window.scrollY; const scrollSpeed = Math.abs(currentScrollY - lastScrollY) * 0.5; lastScrollY = currentScrollY; // 容器缩放 const scale = Math.min(1.2, Math.max(0.8, 1 + scrollSpeed * 0.01)); container.scale.set(scale); container.position.set(app.screen.width/2, app.screen.height/2); container.pivot.set(app.screen.width/2, app.screen.height/2); // 图片位移与中心凸起 imageSprites.forEach((sprite) => { sprite.y = -currentScrollY + (imageSprites.indexOf(sprite) * app.screen.height); // 计算图片中心与容器中心的距离 const centerY = app.screen.height / 2; const spriteCenterY = sprite.y + app.screen.height / 2; const distance = Math.abs(centerY - spriteCenterY); const bulge = Math.max(0, 1 - distance / (app.screen.height / 2)) * scrollSpeed * 0.5; sprite.y -= bulge; }); }); } </script> </body> </html>
内容的提问来源于stack exchange,提问作者Zayd
相关产品推荐
相关产品推荐

