波浪切换图片特效包含以下特点:
- 波浪形过渡动画 – 使用 SVG 路径和 CSS 遮罩实现平滑的波浪过渡效果
- 响应式设计 – 适配不同屏幕尺寸
- 自定义控制 – 提供波浪速度、高度和自动切换的控制选项
- 交互增强 – 鼠标悬停显示控制按钮,指示器显示当前图片位置
- 现代 UI – 使用 Tailwind CSS 构建的美观界面,包含卡片、阴影和圆角设计片展示
<!DOCTYPE html>
<html>
<head>
<title>波浪切换图片特效</title>
<style>
.slider-container {
position: relative;
width: 100%;
height: 400px;
overflow: hidden;
}
.slide {
position: absolute;
inset: 0;
opacity: 0;
transition: opacity 0.5s ease-in-out;
}
.slide.active {
opacity: 1;
}
.wave-mask {
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="none"><path d="M0,0 L100,0 L100,100 L0,100 Z" fill="white" /></svg>');
mask-size: 100% 100%;
mask-repeat: no-repeat;
}
.slider-controls {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
}
.control-btn {
width: 12px;
height: 12px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.5);
border: none;
cursor: pointer;
transition: background-color 0.3s;
}
.control-btn.active {
background-color: white;
}
</style>
</head>
<body>
<div class="slider-container" id="slider">
<!-- 图片将由JS动态生成 -->
<div class="slider-controls" id="controls"></div>
</div>
<script>
// 图片数据
const images = [
'https://picsum.photos/id/10/800/400',
'https://picsum.photos/id/20/800/400',
'https://picsum.photos/id/30/800/400'
];
let currentIndex = 0;
let isAnimating = false;
const slider = document.getElementById('slider');
const controls = document.getElementById('controls');
let autoSwitchInterval;
// 初始化滑块
function initSlider() {
// 创建图片元素
images.forEach((url, index) => {
const slide = document.createElement('div');
slide.className = `slide ${index === 0 ? 'active' : ''}`;
slide.innerHTML = `<img src="${url}" alt="Slide ${index + 1}" style="width:100%; height:100%; object-fit:cover">`;
slide.dataset.index = index;
slider.insertBefore(slide, controls);
});
// 创建控制按钮
images.forEach((_, index) => {
const btn = document.createElement('button');
btn.className = `control-btn ${index === 0 ? 'active' : ''}`;
btn.dataset.index = index;
btn.addEventListener('click', () => goToSlide(index));
controls.appendChild(btn);
});
// 启动自动切换
startAutoSwitch();
}
// 前往指定幻灯片
function goToSlide(index) {
if (isAnimating || index === currentIndex) return;
isAnimating = true;
const slides = document.querySelectorAll('.slide');
const currentSlide = slides[currentIndex];
const nextSlide = slides[index];
// 更新控制按钮状态
document.querySelectorAll('.control-btn').forEach((btn, i) => {
btn.classList.toggle('active', i === index);
});
// 创建波浪动画
let progress = 0;
const duration = 1000;
const start = performance.now();
function animate(currentTime) {
const elapsed = currentTime - start;
progress = Math.min(elapsed / duration, 1);
// 创建波浪路径
const pathData = createWavePath(progress);
// 应用波浪遮罩
currentSlide.style.maskImage = `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${slider.offsetWidth} ${slider.offsetHeight}" preserveAspectRatio="none"><path d="${pathData}" fill="white" /></svg>')`;
currentSlide.style.maskSize = '100% 100%';
currentSlide.style.maskRepeat = 'no-repeat';
if (progress < 1) {
requestAnimationFrame(animate);
} else {
// 动画完成
currentSlide.classList.remove('active');
nextSlide.classList.add('active');
currentSlide.style.maskImage = 'none';
currentIndex = index;
isAnimating = false;
}
}
nextSlide.style.opacity = '1';
requestAnimationFrame(animate);
}
// 创建波浪路径
function createWavePath(progress) {
const points = [];
const width = slider.offsetWidth;
const height = slider.offsetHeight;
const segments = 20;
const segmentWidth = width / segments;
const waveHeight = 30 * (1 - Math.sin(progress * Math.PI / 2));
for (let i = 0; i <= segments; i++) {
const x = i * segmentWidth;
const y = Math.sin((i / segments) * Math.PI * 2 + progress * Math.PI * 2) * waveHeight;
points.push([x, height + y]);
}
let pathData = `M0,0 L${width},0 L${width},0 `;
points.forEach(point => {
pathData += `L${point[0]},${point[1]} `;
});
pathData += 'Z';
return pathData;
}
// 开始自动切换
function startAutoSwitch() {
if (autoSwitchInterval) clearInterval(autoSwitchInterval);
autoSwitchInterval = setInterval(() => {
const nextIndex = (currentIndex + 1) % images.length;
goToSlide(nextIndex);
}, 5000);
}
// 窗口加载完成后初始化
window.addEventListener('load', initSlider);
</script>
</body>
</html>
图片展示:
