@@ -396,8 +396,13 @@ function initOrbitAnimation() {
396396 const speed = baseSpeed + ( index * 5 ) ;
397397 const direction = index % 2 === 0 ? 1 : - 1 ; // 1顺时针,-1逆时针
398398
399- // 设置轨道圆的动画
400- circle . style . animation = `orbit ${ speed } s linear ${ direction === 1 ? '' : 'reverse' } infinite` ;
399+ // 使用GSAP创建更平滑的旋转动画
400+ gsap . to ( circle , {
401+ rotation : 360 * direction ,
402+ duration : speed ,
403+ repeat : - 1 ,
404+ ease : "none"
405+ } ) ;
401406
402407 // 在每个轨道上添加点
403408 const dotCount = index === 0 ? 0 : 3 + index ; // 第一个轨道没有点,其他轨道点数递增
@@ -483,62 +488,217 @@ function updateMissionContent(index, direction) { // index: 目标任务的索
483488 ` ;
484489 // 更新任务描述
485490 missionDetailContent . textContent = mission . description ;
486-
487- // 应用滑入动画
488- let slideInAnimation = '' ;
489- if ( direction === 'next' ) {
490- slideInAnimation = `slide-in-up-rotate ${ animationDuration / 1000 } s ease-in-out forwards` ; // 新内容从底部向上滑入并旋转
491- } else if ( direction === 'prev' ) {
492- slideInAnimation = `slide-in-down-rotate ${ animationDuration / 1000 } s ease-in-out forwards` ; // 新内容从顶部向下滑入并旋转
493- } else { // initial 初始加载
494- slideInAnimation = `slide-in-up-rotate ${ animationDuration / 1000 } s ease-in-out forwards` ; // 默认初始动画 (从底部向上滑入并旋转)
495- }
496- infoContainer . style . animation = 'none' ; // 清除之前的动画状态,确保新动画能够触发
497- requestAnimationFrame ( ( ) => { // 确保样式刷新后再应用新动画
498- infoContainer . style . animation = slideInAnimation ;
499- } ) ;
500-
501-
502- setTimeout ( ( ) => {
503- isAnimating = false ; // 动画结束后,重置动画标志
504- } , animationDuration ) ;
505491 } ;
506492
507493 if ( direction === 'initial' ) { // 如果是初始加载
508- infoContainer . style . opacity = '0' ; // 初始时设置为透明,以配合滑入动画
509494 performUpdate ( ) ;
510- requestAnimationFrame ( ( ) => { // 确保 opacity:0 已应用,再开始动画(设为1,动画本身会处理opacity)
511- infoContainer . style . opacity = '1' ;
512- } ) ;
495+ // 使用GSAP动画显示初始内容
496+ gsap . fromTo ( infoContainer ,
497+ { opacity : 0 , y : 50 } ,
498+ { opacity : 1 , y : 0 , duration : 0.8 , ease : "power2.out" }
499+ ) ;
500+ setTimeout ( ( ) => {
501+ isAnimating = false ; // 动画结束后,重置动画标志
502+ } , 800 ) ;
513503 } else {
514- let slideOutAnimation = '' ;
504+ // 使用GSAP Timeline创建更流畅的切换动画
505+ const timeline = gsap . timeline ( {
506+ onComplete : ( ) => {
507+ isAnimating = false ; // 动画结束后,重置动画标志
508+ }
509+ } ) ;
510+
511+ // 根据方向设置滑出动画
515512 if ( direction === 'next' ) {
516- slideOutAnimation = `slide-out-up-rotate ${ animationDuration / 1000 } s ease-in-out forwards` ; // 当前内容向上滑出并旋转
517- } else if ( direction === 'prev' ) {
518- slideOutAnimation = `slide-out-down-rotate ${ animationDuration / 1000 } s ease-in-out forwards` ; // 当前内容向下滑出并旋转
513+ // 向上滑出
514+ timeline . to ( infoContainer , {
515+ y : - window . innerHeight ,
516+ opacity : 0 ,
517+ duration : 0.5 ,
518+ ease : "power2.in"
519+ } ) ;
520+ } else {
521+ // 向下滑出
522+ timeline . to ( infoContainer , {
523+ y : window . innerHeight ,
524+ opacity : 0 ,
525+ duration : 0.5 ,
526+ ease : "power2.in"
527+ } ) ;
519528 }
520- infoContainer . style . animation = 'none' ;
521- requestAnimationFrame ( ( ) => {
522- infoContainer . style . animation = slideOutAnimation ;
523- } ) ;
524529
525- setTimeout ( performUpdate , animationDuration ) ; // 滑出动画结束后执行内容更新和滑入动画
530+ // 在动画中间点更新内容
531+ timeline . add ( ( ) => {
532+ performUpdate ( ) ;
533+ // 重置容器位置和透明度以准备滑入动画
534+ gsap . set ( infoContainer , { y : direction === 'next' ? window . innerHeight : - window . innerHeight , opacity : 0 } ) ;
535+ } , 0.25 ) ;
536+
537+ // 根据方向设置滑入动画
538+ if ( direction === 'next' ) {
539+ // 从下方滑入
540+ timeline . fromTo ( infoContainer ,
541+ { y : window . innerHeight , opacity : 0 } ,
542+ { y : 0 , opacity : 1 , duration : 0.5 , ease : "power2.out" } ,
543+ 0.3
544+ ) ;
545+ } else {
546+ // 从上方滑入
547+ timeline . fromTo ( infoContainer ,
548+ { y : - window . innerHeight , opacity : 0 } ,
549+ { y : 0 , opacity : 1 , duration : 0.5 , ease : "power2.out" } ,
550+ 0.3
551+ ) ;
552+ }
526553 }
527554}
528555
529556// 处理导航按钮点击
530557function handleNavigation ( ) {
531- lastCircle . addEventListener ( "click" , function ( ) {
558+ // 创建波纹效果的函数
559+ function createRipple ( event , button ) {
560+ const circle = document . createElement ( "span" ) ;
561+ circle . classList . add ( "ripple" ) ;
562+ const diameter = Math . max ( button . clientWidth , button . clientHeight ) ;
563+ const radius = diameter / 2 ;
564+ circle . style . width = circle . style . height = `${ diameter } px` ;
565+ circle . style . left = `${ event . clientX - button . getBoundingClientRect ( ) . left - radius } px` ;
566+ circle . style . top = `${ event . clientY - button . getBoundingClientRect ( ) . top - radius } px` ;
567+ button . appendChild ( circle ) ;
568+
569+ // 清理波纹元素
570+ setTimeout ( ( ) => {
571+ circle . remove ( ) ;
572+ } , 600 ) ;
573+ }
574+
575+ lastCircle . addEventListener ( "mouseenter" , function ( ) {
576+ // 悬停进入动画
577+ gsap . to ( lastCircle , {
578+ scale : 1.1 ,
579+ boxShadow : "inset 0px 0px 3px 2px rgba(128, 48, 150), 0 0 0 8px rgba(152, 117, 161, 0.4)" ,
580+ duration : 0.3 ,
581+ ease : "power2.out"
582+ } ) ;
583+
584+ // SVG图标颜色变化
585+ gsap . to ( lastCircle . querySelector ( 'svg path' ) , {
586+ fill : "#a0d8ff" ,
587+ duration : 0.3 ,
588+ ease : "power2.out"
589+ } ) ;
590+ } ) ;
591+
592+ lastCircle . addEventListener ( "mouseleave" , function ( ) {
593+ // 悬停离开动画
594+ gsap . to ( lastCircle , {
595+ scale : 1 ,
596+ boxShadow : "inset 0px 0px 3px 2px rgba(128, 48, 150), 0 0 0 0 rgba(152, 117, 161, 0.4)" ,
597+ duration : 0.3 ,
598+ ease : "power2.out"
599+ } ) ;
600+
601+ // SVG图标颜色恢复
602+ gsap . to ( lastCircle . querySelector ( 'svg path' ) , {
603+ fill : "#ffffff" ,
604+ duration : 0.3 ,
605+ ease : "power2.out"
606+ } ) ;
607+ } ) ;
608+
609+ nextCircle . addEventListener ( "mouseenter" , function ( ) {
610+ // 悬停进入动画
611+ gsap . to ( nextCircle , {
612+ scale : 1.1 ,
613+ boxShadow : "inset 0px 0px 3px 2px rgba(128, 48, 150), 0 0 0 8px rgba(152, 117, 161, 0.4)" ,
614+ duration : 0.3 ,
615+ ease : "power2.out"
616+ } ) ;
617+
618+ // SVG图标颜色变化
619+ gsap . to ( nextCircle . querySelector ( 'svg path' ) , {
620+ fill : "#a0d8ff" ,
621+ duration : 0.3 ,
622+ ease : "power2.out"
623+ } ) ;
624+ } ) ;
625+
626+ nextCircle . addEventListener ( "mouseleave" , function ( ) {
627+ // 悬停离开动画
628+ gsap . to ( nextCircle , {
629+ scale : 1 ,
630+ boxShadow : "inset 0px 0px 3px 2px rgba(128, 48, 150), 0 0 0 0 rgba(152, 117, 161, 0.4)" ,
631+ duration : 0.3 ,
632+ ease : "power2.out"
633+ } ) ;
634+
635+ // SVG图标颜色恢复
636+ gsap . to ( nextCircle . querySelector ( 'svg path' ) , {
637+ fill : "#ffffff" ,
638+ duration : 0.3 ,
639+ ease : "power2.out"
640+ } ) ;
641+ } ) ;
642+
643+ lastCircle . addEventListener ( "click" , function ( event ) {
532644 if ( isAnimating ) return ;
645+
646+ // 创建波纹效果
647+ createRipple ( event , lastCircle ) ;
648+
649+ // 按钮点击动画
650+ const tl = gsap . timeline ( ) ;
651+ tl . to ( lastCircle , {
652+ scale : 0.9 ,
653+ duration : 0.1 ,
654+ ease : "power2.out"
655+ } )
656+ . to ( lastCircle , {
657+ scale : 1 ,
658+ duration : 0.2 ,
659+ ease : "back.out(1.7)"
660+ } ) ;
661+
662+ // SVG图标旋转动画
663+ gsap . to ( lastCircle . querySelector ( 'svg' ) , {
664+ rotation : 450 ,
665+ duration : 0.5 ,
666+ ease : "back.out(1.7)"
667+ } ) ;
668+
533669 currentMissionIndex -- ;
534670 if ( currentMissionIndex < 0 ) {
535671 currentMissionIndex = missionData . length - 1 ; // 循环到最后一个
536672 }
537673 updateMissionContent ( currentMissionIndex , 'prev' ) ;
538674 } ) ;
539675
540- nextCircle . addEventListener ( "click" , function ( ) {
676+ nextCircle . addEventListener ( "click" , function ( event ) {
541677 if ( isAnimating ) return ;
678+
679+ // 创建波纹效果
680+ createRipple ( event , nextCircle ) ;
681+
682+ // 按钮点击动画
683+ const tl = gsap . timeline ( ) ;
684+ tl . to ( nextCircle , {
685+ scale : 0.9 ,
686+ duration : 0.1 ,
687+ ease : "power2.out"
688+ } )
689+ . to ( nextCircle , {
690+ scale : 1 ,
691+ duration : 0.2 ,
692+ ease : "back.out(1.7)"
693+ } ) ;
694+
695+ // SVG图标旋转动画
696+ gsap . to ( nextCircle . querySelector ( 'svg' ) , {
697+ rotation : - 450 ,
698+ duration : 0.5 ,
699+ ease : "back.out(1.7)"
700+ } ) ;
701+
542702 currentMissionIndex ++ ;
543703 if ( currentMissionIndex >= missionData . length ) {
544704 currentMissionIndex = 0 ; // 循环到第一个
@@ -600,12 +760,38 @@ window.addEventListener('resize', () => {
600760
601761// 初始化页面
602762document . addEventListener ( "DOMContentLoaded" , function ( ) {
763+ // 初始化GSAP ScrollTrigger
764+ gsap . registerPlugin ( ScrollTrigger ) ;
765+
603766 initCurrentMissionIndex ( ) ; // 根据localStorage初始化任务索引
604767 StartBackground ( ) ; // 初始化星空背景和流星效果
605768 initOrbitAnimation ( ) ; // 初始化轨道动画
606769 updateMissionContent ( currentMissionIndex , 'initial' ) ; // 初始加载对应的任务数据
607770 handleNavigation ( ) ; // 设置导航按钮的点击事件监听
608771 handleBackButton ( ) ; // 设置返回按钮的点击事件监听
772+
773+ // 添加按钮入场动画
774+ gsap . from ( [ lastCircle , nextCircle ] , {
775+ duration : 0.8 ,
776+ opacity : 0 ,
777+ y : 50 ,
778+ stagger : 0.2 ,
779+ ease : "back.out(1.7)" ,
780+ onComplete : function ( ) {
781+ // 添加按钮发光效果以吸引注意
782+ setTimeout ( ( ) => {
783+ lastCircle . classList . add ( 'glow' ) ;
784+ nextCircle . classList . add ( 'glow' ) ;
785+
786+ // 3秒后移除发光效果
787+ setTimeout ( ( ) => {
788+ lastCircle . classList . remove ( 'glow' ) ;
789+ nextCircle . classList . remove ( 'glow' ) ;
790+ } , 3000 ) ;
791+ } , 500 ) ;
792+ }
793+ } ) ;
794+
609795 // 初始化鼠标控制器
610796 new Mouse ( {
611797 defaultCursor : '../assets/images/common/MouseDefault.svg' ,
0 commit comments