@@ -742,135 +742,141 @@ function initDemoRotator(){
742742}
743743
744744window . addEventListener ( 'load' , ( ) => { try { initDemoRotator ( ) ; } catch ( e ) { } } ) ;
745+ // 1. 初始化变量
745746const track = document . getElementById ( 'track' ) ;
746- let cards = document . querySelectorAll ( '.carousel-card' ) ;
747+ const originalCards = document . querySelectorAll ( '.carousel-card' ) ;
747748const prevBtn = document . getElementById ( 'prevBtn' ) ;
748749const nextBtn = document . getElementById ( 'nextBtn' ) ;
750+ const gapPx = 15 ;
749751
750- // 配置
751- const cardWidthPercent = 60 ;
752- const gapPx = 20 ;
752+ // 动态计算宽度百分比
753+ function getCardWidthPercent ( ) {
754+ return window . innerWidth < 768 ? 75 : 60 ;
755+ }
753756
754- // 1. 克隆首尾
755- const firstClone = cards [ 0 ] . cloneNode ( true ) ;
756- const lastClone = cards [ cards . length - 1 ] . cloneNode ( true ) ;
757+ // 2. 克隆卡片
758+ const firstClone = originalCards [ 0 ] . cloneNode ( true ) ;
759+ const lastClone = originalCards [ originalCards . length - 1 ] . cloneNode ( true ) ;
757760track . appendChild ( firstClone ) ;
758- track . insertBefore ( lastClone , cards [ 0 ] ) ;
761+ track . insertBefore ( lastClone , originalCards [ 0 ] ) ;
759762
760- // 重新获取列表
761763let allCards = document . querySelectorAll ( '.carousel-card' ) ;
762- let currentIndex = 1 ;
764+ let currentIndex = 1 ;
763765let isTransitioning = false ;
764766
765- // 初始化
766- updateTrack ( false ) ;
767- updateActive ( ) ;
768-
769- // --- 按钮事件 ---
770- nextBtn . addEventListener ( 'click' , ( ) => {
767+ // 3. 核心切换函数
768+ function switchSlide ( direction ) {
771769 if ( isTransitioning ) return ;
772- if ( currentIndex >= allCards . length - 1 ) return ; // 保护
773770
774- currentIndex ++ ;
775- isTransitioning = true ;
776- updateTrack ( true ) ;
777- updateActive ( ) ;
778- } ) ;
779-
780- prevBtn . addEventListener ( 'click' , ( ) => {
781- if ( isTransitioning ) return ;
782- if ( currentIndex <= 0 ) return ; // 保护
771+ // 边界保护
772+ if ( direction === 1 && currentIndex >= allCards . length - 1 ) return ;
773+ if ( direction === - 1 && currentIndex <= 0 ) return ;
783774
784- currentIndex -- ;
775+ currentIndex += direction ;
785776 isTransitioning = true ;
786777 updateTrack ( true ) ;
787778 updateActive ( ) ;
788- } ) ;
789-
790- // --- 关键:过渡结束后的无缝重置 ---
791- track . addEventListener ( 'transitionend' , ( ) => {
792- isTransitioning = false ;
779+ }
793780
794- const totalRealCards = allCards . length - 2 ; // 3张
795- let needsReset = false ;
796- let newIndex = currentIndex ;
797-
798- // 判断是否到了克隆边缘
799- if ( currentIndex === allCards . length - 1 ) {
800- newIndex = 1 ; // 回到真实第1张
801- needsReset = true ;
802- } else if ( currentIndex === 0 ) {
803- newIndex = totalRealCards ; // 回到真实最后一张
804- needsReset = true ;
805- }
781+ // === 4. 事件绑定 (暴力修复点击问题) ===
806782
807- if ( needsReset ) {
808- // 执行无缝替换
809- handleReset ( newIndex ) ;
810- }
783+ // 绑定点击
784+ nextBtn . addEventListener ( 'click' , ( e ) => {
785+ e . preventDefault ( ) ;
786+ switchSlide ( 1 ) ;
787+ } ) ;
788+ prevBtn . addEventListener ( 'click' , ( e ) => {
789+ e . preventDefault ( ) ;
790+ switchSlide ( - 1 ) ;
811791} ) ;
812792
813- // --- 核心修复函数:处理重置 ---
814- function handleReset ( targetIndex ) {
815- // 1. 获取当前显示的克隆卡片 和 目标真身卡片
816- const currentCloneCard = allCards [ currentIndex ] ;
817- const targetRealCard = allCards [ targetIndex ] ;
793+ // 绑定触摸结束 (针对部分安卓机)
794+ // 为了防止 Click 和 Touchend 触发两次,我们加个简单的锁
795+ let touchLock = false ;
796+ function handleTouchBtn ( e , dir ) {
797+ e . preventDefault ( ) ; // 防止触发后面的 click
798+ e . stopPropagation ( ) ; // 防止冒泡
799+ if ( touchLock ) return ;
818800
819- // 2. 视频时间同步!(关键修复点)
820- // 把克隆视频的播放时间,赋给真身视频,保证画面不跳变
821- const cloneVideo = currentCloneCard . querySelector ( 'video' ) ;
822- const realVideo = targetRealCard . querySelector ( 'video' ) ;
823- if ( cloneVideo && realVideo ) {
824- realVideo . currentTime = cloneVideo . currentTime ;
825- if ( ! cloneVideo . paused ) realVideo . play ( ) ;
826- }
801+ switchSlide ( dir ) ;
802+
803+ touchLock = true ;
804+ setTimeout ( ( ) => { touchLock = false ; } , 300 ) ;
805+ }
827806
828- // 3. 冻结动画!(关键修复点)
829- // 给 Track 和所有卡片加上 .no-transition,禁止任何 CSS 渐变
830- track . classList . add ( 'no-transition' ) ;
831- allCards . forEach ( c => c . classList . add ( 'no-transition' ) ) ;
807+ nextBtn . addEventListener ( 'touchend' , ( e ) => handleTouchBtn ( e , 1 ) ) ;
808+ prevBtn . addEventListener ( 'touchend' , ( e ) => handleTouchBtn ( e , - 1 ) ) ;
832809
833- // 4. 瞬间改变索引和位置
834- currentIndex = targetIndex ;
835- updateTrack ( false ) ;
836-
837- // 5. 瞬间更新 Active 状态
838- // 此时因为有 no-transition,scale(1) 和 opacity(1) 会立即生效,不会有过渡
839- updateActive ( ) ;
840810
841- // 6. 强制浏览器重绘 (Reflow)
842- // 这一行非常重要,它迫使浏览器立刻应用上面的改变
843- void track . offsetHeight ;
811+ // 5. 触摸滑动支持 (Swipe)
812+ let touchStartX = 0 ;
813+ track . addEventListener ( 'touchstart' , e => { touchStartX = e . changedTouches [ 0 ] . screenX ; } , { passive : true } ) ;
814+ track . addEventListener ( 'touchend' , e => {
815+ const touchEndX = e . changedTouches [ 0 ] . screenX ;
816+ if ( touchEndX < touchStartX - 50 ) switchSlide ( 1 ) ;
817+ if ( touchEndX > touchStartX + 50 ) switchSlide ( - 1 ) ;
818+ } , { passive : true } ) ;
844819
845- // 7. 恢复动画
846- // 使用 requestAnimationFrame 在下一帧移除 no-transition
847- requestAnimationFrame ( ( ) => {
848- track . classList . remove ( 'no-transition' ) ;
849- allCards . forEach ( c => c . classList . remove ( 'no-transition' ) ) ;
850- } ) ;
851- }
852820
853- // --- 基础更新函数 ---
854- function updateTrack ( animate ) {
855- if ( animate ) {
856- track . style . transition = 'transform 0.5s cubic-bezier(0.25, 1, 0.5, 1)' ;
857- } else {
858- track . style . transition = 'none' ;
821+ // 6. 无缝循环处理
822+ track . addEventListener ( 'transitionend' , ( ) => {
823+ isTransitioning = false ;
824+ let targetIndex = - 1 ;
825+
826+ if ( currentIndex === allCards . length - 1 ) targetIndex = 1 ;
827+ else if ( currentIndex === 0 ) targetIndex = allCards . length - 2 ;
828+
829+ if ( targetIndex !== - 1 ) {
830+ // 视频同步
831+ const v1 = allCards [ currentIndex ] . querySelector ( 'video' ) ;
832+ const v2 = allCards [ targetIndex ] . querySelector ( 'video' ) ;
833+ if ( v1 && v2 ) v2 . currentTime = v1 . currentTime ;
834+
835+ // 瞬移
836+ track . classList . add ( 'no-transition' ) ;
837+ allCards . forEach ( c => c . classList . add ( 'no-transition' ) ) ;
838+
839+ currentIndex = targetIndex ;
840+ updateTrack ( false ) ;
841+ updateActive ( ) ;
842+
843+ void track . offsetHeight ; // 强制重绘
844+ requestAnimationFrame ( ( ) => {
845+ track . classList . remove ( 'no-transition' ) ;
846+ allCards . forEach ( c => c . classList . remove ( 'no-transition' ) ) ;
847+ } ) ;
859848 }
860- const centerOffset = 20 ;
861- const val = `calc(${ centerOffset } % - ${ currentIndex } * (${ cardWidthPercent } % + ${ gapPx } px))` ;
849+ } ) ;
850+
851+ // 7. 更新视图逻辑
852+ function updateTrack ( animate ) {
853+ if ( animate ) track . style . transition = 'transform 0.4s ease-out' ;
854+ else track . style . transition = 'none' ;
855+
856+ const wPercent = getCardWidthPercent ( ) ;
857+ const centerOffset = ( 100 - wPercent ) / 2 ;
858+ const val = `calc(${ centerOffset } % - ${ currentIndex } * (${ wPercent } % + ${ gapPx } px))` ;
862859 track . style . transform = `translateX(${ val } )` ;
863860}
864861
865862function updateActive ( ) {
866863 allCards . forEach ( ( card , index ) => {
864+ const v = card . querySelector ( 'video' ) ;
867865 if ( index === currentIndex ) {
868866 card . classList . add ( 'is-active' ) ;
869- // 确保播放
870- const v = card . querySelector ( 'video' ) ;
871- if ( v ) v . play ( ) ;
867+ if ( v ) {
868+ v . muted = true ;
869+ v . playsInline = true ;
870+ v . play ( ) . catch ( ( ) => { } ) ;
871+ }
872872 } else {
873873 card . classList . remove ( 'is-active' ) ;
874+ if ( v ) v . pause ( ) ;
874875 }
875876 } ) ;
876- }
877+ }
878+
879+ // 初始化
880+ updateTrack ( false ) ;
881+ updateActive ( ) ;
882+ window . addEventListener ( 'resize' , ( ) => updateTrack ( false ) ) ;
0 commit comments