You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

564 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!-- 轮播组件外层容器,包含左右切换按钮 -->
<div class="phone-carousel-container">
<!-- 左切换按钮 -->
<button class="carousel-nav-btn carousel-prev-btn" aria-label="上一张">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<!-- 手机轮播组件 -->
<div class="phone-carousel-wrapper">
<style>
.phone-carousel-wrapper * {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/**
* 外层容器样式 - 包含左右切换按钮
*/
.phone-carousel-container {
position: relative;
display: flex;
align-items: center;
justify-content: center;
padding: 20px 80px; /* 左右留出按钮空间 */
min-height: 400px;
font-family: 'Microsoft YaHei', Arial, sans-serif;
}
/**
* 左右切换按钮样式
*/
.carousel-nav-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 50px;
height: 50px;
border: none;
border-radius: 50%;
background: rgba(255, 255, 255, 0.9);
color: #333;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transition: all 0.3s ease;
z-index: 10;
}
.carousel-nav-btn:hover {
background: rgba(255, 255, 255, 1);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
transform: translateY(-50%) scale(1.1);
}
.carousel-nav-btn:active {
transform: translateY(-50%) scale(0.95);
}
.carousel-prev-btn {
left: -52px;
}
.carousel-next-btn {
right: -52px;
}
.carousel-nav-btn svg {
width: 24px;
height: 24px;
transition: transform 0.2s ease;
}
.carousel-nav-btn:hover svg {
transform: scale(1.1);
}
.phone-carousel-wrapper {
display: flex;
justify-content: center;
align-items: center;
flex: 1;
}
/**
* 手机模型容器样式
*
*/
.phone-carousel-wrapper .phone-container {
position: relative;
width: 220px;
height: 440px; /* 调整为接近图片比例 306:617 ≈ 1:2 */
background-image: url('<?php echo get_template_directory_uri(); ?>/assets/images/phone-carousel-imgs/phone-bg.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
display: flex;
justify-content: center;
align-items: center;
filter: drop-shadow(0 20px 40px rgba(0, 0, 0, 0.3));
animation: phoneFloat 3s ease-in-out infinite;
}
/**
* 轮播图容器样式
* 位于手机屏幕区域内保持与phone-container相同的宽高比例 (1:2)
*/
.phone-carousel-wrapper .carousel-container {
position: absolute;
width: 180px; /* 调整为适合手机屏幕区域 */
height: 320px; /* 调整为适合手机屏幕区域 */
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
overflow: visible;
cursor: grab; /* 添加拖拽光标 */
}
.phone-carousel-wrapper .carousel-container:active {
cursor: grabbing; /* 拖拽时的光标 */
}
/**
* 轮播图包装器
*/
.phone-carousel-wrapper .carousel-wrapper {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
border-radius: 25px;
}
/**
* 轮播图片样式
*/
.phone-carousel-wrapper .carousel-slide {
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 1s ease-in-out;
}
.phone-carousel-wrapper .carousel-slide.active {
opacity: 1;
}
.phone-carousel-wrapper .carousel-slide img {
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 25px;
}
/**
* 动画效果
*/
@keyframes phoneFloat {
0%, 100% {
transform: translateY(0px);
}
50% {
transform: translateY(-10px);
}
}
.phone-carousel-wrapper .phone-container {
animation: phoneFloat 3s ease-in-out infinite;
}
/**
* 响应式设计
* 移动端也保持相同的1:2宽高比例
*/
@media (max-width: 768px) {
.phone-carousel-container {
padding: 20px 60px; /* 移动端减少左右间距 */
}
.carousel-nav-btn {
width: 40px;
height: 40px;
}
.carousel-nav-btn svg {
width: 20px;
height: 20px;
}
.carousel-prev-btn {
left: 10px;
}
.carousel-next-btn {
right: 10px;
}
.phone-carousel-wrapper .phone-container {
width: 200px;
height: 400px; /* 保持与桌面版相似的比例 */
}
.phone-carousel-wrapper .carousel-container {
width: 160px; /* 调整为适合移动端手机屏幕区域 */
height: 280px; /* 调整为适合移动端手机屏幕区域 */
}
}
</style>
<!-- 手机模型容器 -->
<div class="phone-container">
<!-- 轮播图容器 -->
<div class="carousel-container">
<div class="carousel-wrapper">
<!-- 轮播图片 -->
<div class="carousel-slide active">
<img src="<?php echo get_template_directory_uri(); ?>/assets/images/phone-carousel-imgs/phone-001.png" alt="手机展示图1">
</div>
<div class="carousel-slide">
<img src="<?php echo get_template_directory_uri(); ?>/assets/images/phone-carousel-imgs/phone-002.png" alt="手机展示图2">
</div>
<div class="carousel-slide">
<img src="<?php echo get_template_directory_uri(); ?>/assets/images/phone-carousel-imgs/phone-003.png" alt="手机展示图3">
</div>
<div class="carousel-slide">
<img src="<?php echo get_template_directory_uri(); ?>/assets/images/phone-carousel-imgs/phone-004.png" alt="手机展示图4">
</div>
<div class="carousel-slide">
<img src="<?php echo get_template_directory_uri(); ?>/assets/images/phone-carousel-imgs/phone-005.png" alt="手机展示图5">
</div>
</div>
</div>
</div>
<script>
/**
* 轮播图拖拽切换功能
* 支持触摸和鼠标拖拽操作
*/
class PhoneCarousel {
constructor() {
this.slides = document.querySelectorAll('.carousel-slide');
this.container = document.querySelector('.carousel-container');
this.currentSlide = 0;
this.slideInterval = null;
this.autoPlayDelay = 1500; // 1.5秒自动切换
// 拖拽相关属性
this.isDragging = false;
this.startX = 0;
this.currentX = 0;
this.threshold = 50; // 拖拽阈值,超过此距离才切换
// 性能优化属性
this.isDestroyed = false;
this.boundHandlers = {};
this.resizeTimeout = null;
this.init();
}
/**
* 初始化轮播图
*/
init() {
if (this.isDestroyed) return;
// 检查必要元素是否存在
if (!this.container || this.slides.length === 0) {
console.warn('PhoneCarousel: 必要元素未找到');
return;
}
this.bindEvents();
this.bindNavButtons(); // 绑定导航按钮事件
this.startAutoPlay();
// 添加窗口大小变化监听(防抖处理)
this.boundHandlers.resize = this.debounce(() => {
if (!this.isDestroyed) {
this.handleResize();
}
}, 250);
window.addEventListener('resize', this.boundHandlers.resize);
}
/**
* 防抖函数
*/
debounce(func, wait) {
return (...args) => {
clearTimeout(this.resizeTimeout);
this.resizeTimeout = setTimeout(() => func.apply(this, args), wait);
};
}
/**
* 处理窗口大小变化
*/
handleResize() {
// 重新计算容器尺寸和位置
if (this.container) {
this.resetAutoPlay();
}
}
/**
* 绑定导航按钮事件
*/
bindNavButtons() {
const prevBtn = document.querySelector('.carousel-prev-btn');
const nextBtn = document.querySelector('.carousel-next-btn');
if (prevBtn) {
prevBtn.addEventListener('click', () => {
this.prevSlide();
this.resetAutoPlay(); // 重置自动播放
});
}
if (nextBtn) {
nextBtn.addEventListener('click', () => {
this.nextSlide();
this.resetAutoPlay(); // 重置自动播放
});
}
}
/**
* 绑定事件监听器
*/
bindEvents() {
if (this.isDestroyed || !this.container) return;
// 使用绑定的处理函数,便于后续清理
this.boundHandlers.touchStart = (e) => this.handleStart(e);
this.boundHandlers.touchMove = (e) => this.handleMove(e);
this.boundHandlers.touchEnd = (e) => this.handleEnd(e);
this.boundHandlers.mouseDown = (e) => this.handleStart(e);
this.boundHandlers.mouseMove = (e) => this.handleMove(e);
this.boundHandlers.mouseUp = (e) => this.handleEnd(e);
this.boundHandlers.mouseLeave = (e) => this.handleEnd(e);
this.boundHandlers.dragStart = (e) => e.preventDefault();
this.boundHandlers.mouseEnter = () => this.stopAutoPlay();
this.boundHandlers.mouseLeaveContainer = () => {
if (!this.isDragging) {
this.startAutoPlay();
}
};
// 触摸事件(移动端)
this.container.addEventListener('touchstart', this.boundHandlers.touchStart, { passive: false });
this.container.addEventListener('touchmove', this.boundHandlers.touchMove, { passive: false });
this.container.addEventListener('touchend', this.boundHandlers.touchEnd, { passive: false });
// 鼠标事件(桌面端)
this.container.addEventListener('mousedown', this.boundHandlers.mouseDown);
this.container.addEventListener('mousemove', this.boundHandlers.mouseMove);
this.container.addEventListener('mouseup', this.boundHandlers.mouseUp);
this.container.addEventListener('mouseleave', this.boundHandlers.mouseLeave);
// 防止图片拖拽
this.container.addEventListener('dragstart', this.boundHandlers.dragStart);
// 鼠标悬停暂停自动播放
this.container.addEventListener('mouseenter', this.boundHandlers.mouseEnter);
this.container.addEventListener('mouseleave', this.boundHandlers.mouseLeaveContainer);
}
/**
* 清理事件监听器
*/
unbindEvents() {
if (!this.container || !this.boundHandlers) return;
// 移除所有事件监听器
this.container.removeEventListener('touchstart', this.boundHandlers.touchStart);
this.container.removeEventListener('touchmove', this.boundHandlers.touchMove);
this.container.removeEventListener('touchend', this.boundHandlers.touchEnd);
this.container.removeEventListener('mousedown', this.boundHandlers.mouseDown);
this.container.removeEventListener('mousemove', this.boundHandlers.mouseMove);
this.container.removeEventListener('mouseup', this.boundHandlers.mouseUp);
this.container.removeEventListener('mouseleave', this.boundHandlers.mouseLeave);
this.container.removeEventListener('dragstart', this.boundHandlers.dragStart);
this.container.removeEventListener('mouseenter', this.boundHandlers.mouseEnter);
this.container.removeEventListener('mouseleave', this.boundHandlers.mouseLeaveContainer);
if (this.boundHandlers.resize) {
window.removeEventListener('resize', this.boundHandlers.resize);
}
}
/**
* 处理拖拽开始事件
* @param {Event} e - 事件对象
*/
handleStart(e) {
this.isDragging = true;
this.startX = this.getClientX(e);
this.currentX = this.startX;
this.stopAutoPlay();
// 添加拖拽样式
this.container.style.cursor = 'grabbing';
this.container.style.userSelect = 'none';
}
/**
* 处理拖拽移动事件
* @param {Event} e - 事件对象
*/
handleMove(e) {
if (!this.isDragging) return;
e.preventDefault();
this.currentX = this.getClientX(e);
}
/**
* 处理拖拽结束事件
* @param {Event} e - 事件对象
*/
handleEnd(e) {
if (!this.isDragging) return;
this.isDragging = false;
const deltaX = this.currentX - this.startX;
// 移除拖拽样式
this.container.style.cursor = 'grab';
this.container.style.userSelect = '';
// 根据拖拽距离判断是否切换
if (Math.abs(deltaX) > this.threshold) {
if (deltaX > 0) {
// 向右拖拽,显示上一张
this.prevSlide();
} else {
// 向左拖拽,显示下一张
this.nextSlide();
}
}
// 重新开始自动播放
this.startAutoPlay();
}
/**
* 获取客户端X坐标
* @param {Event} e - 事件对象
* @returns {number} X坐标
*/
getClientX(e) {
return e.type.includes('touch') ? e.touches[0]?.clientX || e.changedTouches[0]?.clientX : e.clientX;
}
/**
* 切换到指定幻灯片
* @param {number} slideIndex - 幻灯片索引
*/
goToSlide(slideIndex) {
// 移除当前活动状态
this.slides[this.currentSlide].classList.remove('active');
// 设置新的活动状态
this.currentSlide = slideIndex;
this.slides[this.currentSlide].classList.add('active');
}
/**
* 切换到上一张幻灯片
*/
prevSlide() {
const prevIndex = this.currentSlide === 0 ? this.slides.length - 1 : this.currentSlide - 1;
this.goToSlide(prevIndex);
}
/**
* 切换到下一张幻灯片
*/
nextSlide() {
const nextIndex = (this.currentSlide + 1) % this.slides.length;
this.goToSlide(nextIndex);
}
/**
* 开始自动播放
*/
startAutoPlay() {
this.slideInterval = setInterval(() => {
this.nextSlide();
}, this.autoPlayDelay);
}
/**
* 停止自动播放
*/
stopAutoPlay() {
if (this.slideInterval) {
clearInterval(this.slideInterval);
this.slideInterval = null;
}
}
/**
* 重置自动播放
*/
resetAutoPlay() {
if (this.isDestroyed) return;
this.stopAutoPlay();
this.startAutoPlay();
}
/**
* 销毁轮播图实例
*/
destroy() {
this.isDestroyed = true;
this.stopAutoPlay();
this.unbindEvents();
// 清理定时器
if (this.resizeTimeout) {
clearTimeout(this.resizeTimeout);
this.resizeTimeout = null;
}
// 清理引用
this.slides = null;
this.container = null;
this.boundHandlers = null;
}
}
// 页面加载完成后初始化轮播图
document.addEventListener('DOMContentLoaded', () => {
// 检查是否已经初始化过(避免重复初始化)
if (window.phoneCarouselInstance) {
window.phoneCarouselInstance.destroy();
}
// 直接初始化轮播图
window.phoneCarouselInstance = new PhoneCarousel();
});
// 页面卸载时清理资源
window.addEventListener('beforeunload', () => {
if (window.phoneCarouselInstance) {
window.phoneCarouselInstance.destroy();
}
});
</script>
</div>
<!-- 右切换按钮 -->
<button class="carousel-nav-btn carousel-next-btn" aria-label="下一张">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>