如何让高斯函数(Gaussian Function)实现循环包裹效果?
滚动图片循环淡入淡出问题解决
问题背景
我想要实现滚动时图片淡入淡出的炫酷效果,当前实现按Shift键并向下滚动可查看效果,但首尾图片切换时会出现白屏和“跳变”现象——问题出在计算透明度的高斯函数无法实现循环衔接,而是直接跳变。
当前实现代码
JavaScript
const urls = [ 'https://img.freepik.com/premium-zdjecie/piekny-krajobraz-z-gorskim-jeziorem-i-wysokimi-skalami-z-oswietlonymi-szczytami-odbiciem-zielonej-wody-blekitnym-czystym-niebem-i-zoltym-jesiennym-wschodem-slonca_75780-1924.jpg?w=2000', 'https://img.freepik.com/darmowe-wektory/krajobraz-zachod-slonca-gory-i-jeziora-realistyczne-drzewo-w-sylwetki-lasow-i-gor-wieczorna-panorama-drewna-ilustracja-tlo-dzikiej-przyrody_1150-39419.jpg?w=2000', 'https://i1.adis.ws/i/canon/get-inspired-landscape-photography-tips-1-16.9_e7ae10efb78c4526801d6037f80ab177', 'https://ansee.pl/wp-content/uploads/2021/03/hay-399686_1920-1080x675.jpg' ] const gauss = (x, a, b, c) => { const t1 = (x - b) ** 2 const t2 = 2 * (c ** 2) const t = -t1 / t2 return a * Math.exp(t) } const images = urls.map((url, i) => { const img = document.createElement('img') img.src = url img.className = 'image' document.body.appendChild(img) return img }) let current = 0 addEventListener('mousewheel', ({ deltaY }) => { current = (images.length + current + deltaY / 1000) % images.length images.forEach((image, i) => { const opacity = gauss(i, 1, current, 0.3) image.style.opacity = opacity }) })
CSS
body { height: 100vh; margin: 0; } .image { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); object-fit: cover; width: 100%; height: 100%; }
问题分析
原高斯函数中计算的是线性距离:(x - b) ** 2。当current从接近images.length(模运算后接近0)时,最后一张图(索引images.length-1)和第一张图(索引0)的距离会突然从接近1变为接近images.length-1,导致透明度直接跳变,无法形成平滑的循环过渡。
解决方案
修改高斯函数,计算循环中的最短距离:对于每个图片索引i,取i到current的最短路径(考虑首尾衔接的情况),再用这个最短距离计算高斯值。
完整修改后的JavaScript代码
const urls = [ 'https://img.freepik.com/premium-zdjecie/piekny-krajobraz-z-gorskim-jeziorem-i-wysokimi-skalami-z-oswietlonymi-szczytami-odbiciem-zielonej-wody-blekitnym-czystym-niebem-i-zoltym-jesiennym-wschodem-slonca_75780-1924.jpg?w=2000', 'https://img.freepik.com/darmowe-wektory/krajobraz-zachod-slonca-gory-i-jeziora-realistyczne-drzewo-w-sylwetki-lasow-i-gor-wieczorna-panorama-drewna-ilustracja-tlo-dzikiej-przyrody_1150-39419.jpg?w=2000', 'https://i1.adis.ws/i/canon/get-inspired-landscape-photography-tips-1-16.9_e7ae10efb78c4526801d6037f80ab177', 'https://ansee.pl/wp-content/uploads/2021/03/hay-399686_1920-1080x675.jpg' ] // 新增total参数,传入图片总数以计算循环最短距离 const gauss = (x, a, b, total, c) => { const diff = Math.abs(x - b) // 取循环中的最短距离:要么直接差值,要么绕到另一端的差值 const shortestDiff = Math.min(diff, total - diff) const t1 = shortestDiff ** 2 const t2 = 2 * (c ** 2) const t = -t1 / t2 return a * Math.exp(t) } const images = urls.map((url, i) => { const img = document.createElement('img') img.src = url img.className = 'image' document.body.appendChild(img) return img }) const totalImages = images.length let current = 0 addEventListener('mousewheel', ({ deltaY }) => { current = (totalImages + current + deltaY / 1000) % totalImages images.forEach((image, i) => { // 传入totalImages参数计算循环透明度 const opacity = gauss(i, 1, current, totalImages, 0.3) image.style.opacity = opacity }) })
效果说明
修改后,当滚动到首尾衔接的位置时,高斯曲线会以循环的方式平滑过渡,最后一张图和第一张图的透明度变化自然衔接,不会再出现白屏和跳变现象。
内容的提问来源于stack exchange,提问作者Konrad
相关产品推荐
相关产品推荐

