@@ -94,68 +94,171 @@ const gkhz = [
9494 }
9595]
9696
97+ // 预创建新闻弹窗元素,避免重复创建
98+ let newsModalOverlay = null ;
99+
100+ function createNewsModal ( ) {
101+ if ( newsModalOverlay ) return newsModalOverlay ;
102+
103+ newsModalOverlay = document . createElement ( 'div' ) ;
104+ newsModalOverlay . className = 'news-modal-overlay' ;
105+ newsModalOverlay . innerHTML = `
106+ <div class="new-content-box">
107+ <div class="close-btn"></div>
108+ <div class="children">
109+ <div class="new-box-title"></div>
110+ <div class="line"></div>
111+ <p class="new-content"></p>
112+ </div>
113+ </div>
114+ ` ;
115+
116+ document . body . appendChild ( newsModalOverlay ) ;
117+
118+ // 为关闭按钮添加事件监听器
119+ const closeBtn = newsModalOverlay . querySelector ( '.close-btn' ) ;
120+ closeBtn . addEventListener ( 'click' , ( e ) => {
121+ e . stopPropagation ( ) ;
122+ hideNewsModal ( ) ;
123+ } ) ;
124+
125+ // 点击背景关闭弹窗
126+ newsModalOverlay . addEventListener ( 'click' , ( e ) => {
127+ if ( e . target === newsModalOverlay ) {
128+ hideNewsModal ( ) ;
129+ }
130+ } ) ;
131+
132+ // 初始隐藏
133+ newsModalOverlay . style . display = 'none' ;
134+
135+ return newsModalOverlay ;
136+ }
137+
138+ function showNewsModal ( newsItem ) {
139+ const modalOverlay = createNewsModal ( ) ;
140+
141+ // 更新内容
142+ modalOverlay . querySelector ( '.new-box-title' ) . textContent = newsItem . title ;
143+
144+ // 对长内容进行处理,提高渲染性能
145+ const contentElement = modalOverlay . querySelector ( '.new-content' ) ;
146+ contentElement . textContent = newsItem . content ;
147+
148+ // 显示弹窗
149+ modalOverlay . style . display = 'flex' ;
150+
151+ // 使用GSAP动画显示弹窗
152+ gsap . set ( modalOverlay , { opacity : 0 } ) ;
153+ gsap . set ( modalOverlay . querySelector ( '.children' ) , { y : 50 , opacity : 0 } ) ;
154+
155+ const tl = gsap . timeline ( ) ;
156+ tl . to ( modalOverlay , {
157+ opacity : 1 ,
158+ duration : 0.3 ,
159+ ease : "power2.out"
160+ } )
161+ . to ( modalOverlay . querySelector ( '.children' ) , {
162+ y : 0 ,
163+ opacity : 1 ,
164+ duration : 0.4 ,
165+ ease : "back.out(1.7)"
166+ } , "-=0.1" ) ;
167+ }
168+
169+ function hideNewsModal ( ) {
170+ const modalOverlay = newsModalOverlay ;
171+ if ( ! modalOverlay || modalOverlay . style . display === 'none' ) return ;
172+
173+ // 使用GSAP动画关闭弹窗
174+ const tl = gsap . timeline ( {
175+ onComplete : ( ) => {
176+ modalOverlay . style . display = 'none' ;
177+ }
178+ } ) ;
179+ tl . to ( modalOverlay . querySelector ( '.children' ) , {
180+ y : 50 ,
181+ opacity : 0 ,
182+ duration : 0.3 ,
183+ ease : "power2.in"
184+ } )
185+ . to ( modalOverlay , {
186+ opacity : 0 ,
187+ duration : 0.2 ,
188+ ease : "power2.in"
189+ } , "-=0.1" ) ;
190+ }
191+
192+ // 防抖函数,防止频繁点击
193+ function debounce ( func , wait ) {
194+ let timeout ;
195+ return function executedFunction ( ...args ) {
196+ const later = ( ) => {
197+ clearTimeout ( timeout ) ;
198+ func ( ...args ) ;
199+ } ;
200+ clearTimeout ( timeout ) ;
201+ timeout = setTimeout ( later , wait ) ;
202+ } ;
203+ }
204+
97205function createNewsItem ( news = [ ] ) {
98206 const newsContainer = document . querySelector ( '.news' ) ;
207+ if ( ! newsContainer ) return ;
208+
99209 newsContainer . innerHTML = news . map ( ( item , index ) =>
100210 `<div class="new-item" data-index="${ index } ">
101211 <div class="title">${ item . title } </div>
102212 <div class="date">${ item . date } </div>
103213 </div>`
104214 ) . join ( '' ) ;
105215
106- newsContainer . addEventListener ( 'click' , ( event ) => {
216+ // 使用事件委托处理点击事件,避免为每个新闻项单独绑定事件
217+ // 添加防抖处理,防止频繁点击
218+ const debouncedClickHandler = debounce ( ( event ) => {
107219 const target = event . target . closest ( '.new-item' ) ;
108220 if ( target ) {
221+ // 添加点击反馈动画
222+ gsap . to ( target , {
223+ scale : 0.98 ,
224+ duration : 0.1 ,
225+ yoyo : true ,
226+ repeat : 1 ,
227+ ease : "power2.out"
228+ } ) ;
229+
109230 const index = target . getAttribute ( 'data-index' ) ;
110231 const newsItem = news [ index ] ;
111232
112- // 移除已存在的弹窗
113- const existingModal = document . querySelector ( '.news-modal-overlay' ) ;
114- if ( existingModal ) {
115- existingModal . remove ( ) ;
116- }
117-
118- // 创建新的弹窗并添加到body
119- const modalOverlay = document . createElement ( 'div' ) ;
120- modalOverlay . className = 'news-modal-overlay' ;
121- modalOverlay . innerHTML = `
122- <div class="new-content-box">
123- <div class="close-btn"></div>
124- <div class="children">
125- <div class="new-box-title">${ newsItem . title } </div>
126- <div class="line"></div>
127- <p class="new-content">${ newsItem . content } </p>
128- </div>
129- </div>
130- ` ;
131-
132- document . body . appendChild ( modalOverlay ) ;
133-
134- // 显示弹窗
135- setTimeout ( ( ) => {
136- modalOverlay . style . opacity = '1' ;
137- } , 10 ) ;
138-
139- const closeBtn = modalOverlay . querySelector ( '.close-btn' ) ;
140- closeBtn . addEventListener ( 'click' , ( e ) => {
141- e . stopPropagation ( ) ;
142- modalOverlay . remove ( ) ;
143- } ) ;
144-
145- // 点击背景关闭弹窗
146- modalOverlay . addEventListener ( 'click' , ( e ) => {
147- if ( e . target === modalOverlay ) {
148- modalOverlay . remove ( ) ;
149- }
150- } ) ;
233+ showNewsModal ( newsItem ) ;
151234 }
152- } ) ;
235+ } , 300 ) ; // 300ms防抖延迟
236+
237+ newsContainer . addEventListener ( 'click' , debouncedClickHandler ) ;
153238}
154239
155240function createPopupVideoOverlay ( videoSrc ) {
156241 // 如果已有弹窗,先清除
157242 const existing = document . querySelector ( '.video-popup-overlay' ) ;
158- if ( existing ) existing . remove ( ) ;
243+ if ( existing ) {
244+ // 使用GSAP动画关闭现有弹窗
245+ const tl = gsap . timeline ( {
246+ onComplete : ( ) => {
247+ existing . remove ( ) ;
248+ }
249+ } ) ;
250+ tl . to ( existing . querySelector ( '.video-popup' ) , {
251+ scale : 0.8 ,
252+ opacity : 0 ,
253+ duration : 0.3 ,
254+ ease : "power2.in"
255+ } )
256+ . to ( existing , {
257+ opacity : 0 ,
258+ duration : 0.2 ,
259+ ease : "power2.in"
260+ } , "-=0.1" ) ;
261+ }
159262
160263 const overlay = document . createElement ( 'div' ) ;
161264 overlay . className = 'video-popup-overlay' ;
@@ -168,14 +271,48 @@ function createPopupVideoOverlay(videoSrc) {
168271
169272 document . body . appendChild ( overlay ) ;
170273
274+ // 使用GSAP动画显示弹窗
275+ gsap . set ( overlay , { opacity : 0 } ) ;
276+ gsap . set ( overlay . querySelector ( '.video-popup' ) , { scale : 0.8 , opacity : 0 } ) ;
277+
278+ const tl = gsap . timeline ( ) ;
279+ tl . to ( overlay , {
280+ opacity : 1 ,
281+ duration : 0.3 ,
282+ ease : "power2.out"
283+ } )
284+ . to ( overlay . querySelector ( '.video-popup' ) , {
285+ scale : 1 ,
286+ opacity : 1 ,
287+ duration : 0.4 ,
288+ ease : "back.out(1.7)"
289+ } , "-=0.1" ) ;
290+
171291 overlay . querySelector ( '.close-video-btn' ) . addEventListener ( 'click' , ( ) => {
172- overlay . remove ( ) ;
292+ // 使用GSAP动画关闭弹窗
293+ const tl = gsap . timeline ( {
294+ onComplete : ( ) => {
295+ overlay . remove ( ) ;
296+ }
297+ } ) ;
298+ tl . to ( overlay . querySelector ( '.video-popup' ) , {
299+ scale : 0.8 ,
300+ opacity : 0 ,
301+ duration : 0.3 ,
302+ ease : "power2.in"
303+ } )
304+ . to ( overlay , {
305+ opacity : 0 ,
306+ duration : 0.2 ,
307+ ease : "power2.in"
308+ } , "-=0.1" ) ;
173309 } ) ;
174310}
175311
176-
177312function initHTKJYYYMSFW ( ) {
178313 const container = document . querySelector ( '.htkj-yyymsfw' ) ;
314+ if ( ! container ) return ;
315+
179316 container . innerHTML = htkjyymfw . map ( ( item , index ) =>
180317 `<div class="item">
181318 <img src="${ item . img } " data-index="${ index } " alt="HTKJYYYMSFW Image">
@@ -185,6 +322,15 @@ function initHTKJYYYMSFW() {
185322 container . addEventListener ( 'click' , ( event ) => {
186323 const img = event . target . closest ( 'img' ) ;
187324 if ( img ) {
325+ // 添加点击反馈动画
326+ gsap . to ( img , {
327+ scale : 0.95 ,
328+ duration : 0.1 ,
329+ yoyo : true ,
330+ repeat : 1 ,
331+ ease : "power2.out"
332+ } ) ;
333+
188334 const index = img . dataset . index ;
189335 const videoSrc = htkjyymfw [ index ] . vodeo ;
190336 createPopupVideoOverlay ( videoSrc ) ;
@@ -194,6 +340,8 @@ function initHTKJYYYMSFW() {
194340
195341function initGJHZ ( ) {
196342 const container = document . querySelector ( '.gjhz' ) ;
343+ if ( ! container ) return ;
344+
197345 container . innerHTML = gkhz . map ( ( item , index ) =>
198346 `<div class="item">
199347 <img src="${ item . img } " data-index="${ index } " alt="GKHZ Image">
@@ -203,6 +351,15 @@ function initGJHZ() {
203351 container . addEventListener ( 'click' , ( event ) => {
204352 const img = event . target . closest ( 'img' ) ;
205353 if ( img ) {
354+ // 添加点击反馈动画
355+ gsap . to ( img , {
356+ scale : 0.95 ,
357+ duration : 0.1 ,
358+ yoyo : true ,
359+ repeat : 1 ,
360+ ease : "power2.out"
361+ } ) ;
362+
206363 const index = img . dataset . index ;
207364 const videoSrc = gkhz [ index ] . vodeo ;
208365 createPopupVideoOverlay ( videoSrc ) ;
@@ -211,14 +368,17 @@ function initGJHZ() {
211368}
212369
213370document . addEventListener ( "DOMContentLoaded" , ( ) => {
371+ // 初始化GSAP ScrollTrigger
372+ gsap . registerPlugin ( ScrollTrigger ) ;
373+
214374 const header = document . querySelector ( '.header' ) ;
215375 const scrollContainer = document . querySelector ( '.container' ) ;
216376 const starBg = document . querySelector ( '.star-bg' ) ;
377+ if ( ! header || ! scrollContainer || ! starBg ) return ;
217378
218379 // 初始化头部控制器
219380 new HeaderController ( header , { scrollContainer } ) ;
220381
221-
222382 // 初始化鼠标控制器
223383 new Mouse ( {
224384 defaultCursor : '../assets/images/common/MouseDefault.svg' ,
@@ -232,6 +392,9 @@ document.addEventListener("DOMContentLoaded", () => {
232392
233393 new MeteorEffect ( starBg )
234394
395+ // 预创建新闻弹窗
396+ createNewsModal ( ) ;
397+
235398 // 创建新闻项
236399 createNewsItem ( news ) ;
237400
@@ -240,4 +403,4 @@ document.addEventListener("DOMContentLoaded", () => {
240403
241404 // 初始化GKHZ
242405 initGJHZ ( ) ;
243- } ) ;
406+ } ) ;
0 commit comments