@@ -26,42 +26,91 @@ function scrambleLogoText() {
2626 const el = document . querySelector ( ".logo-text" ) ;
2727 if ( ! el ) return ;
2828
29- const target = el . textContent ;
30- // 字符池全部使用全角字符(与汉字同宽 1em),避免闪变时标题总宽度抖动
29+ const lines = el . querySelectorAll ( ".logo-line" ) ;
3130 const chars =
3231 "字节引力天津科技有限公司*广告联盟网络01<>#*+=/\|ABCDEFGHKMNRSTWXYZ" ;
3332 const frameInterval = 40 ; // 每帧毫秒数
3433 const lockStagger = 233 ; // 相邻字符锁定时刻间隔(毫秒),总时长约 1.6s
35- const frames = [ ] ;
3634
37- for ( let i = 0 ; i < target . length ; i ++ ) {
38- // 标点不闪变,直接保持
39- const lockAt = target [ i ] . match ( / [ , 。 、 ! ? , . ! ? ] / ) ? 0 : i * lockStagger ;
40- frames . push ( { char : target [ i ] , lockAt } ) ;
41- }
35+ if ( lines . length > 0 ) {
36+ // 存在 .logo-line 分行元素时,针对每个 span 分别计算并更新,保留 HTML 结构
37+ const lineData = [ ] ;
38+ let globalIndex = 0 ;
39+
40+ lines . forEach ( ( lineEl ) => {
41+ const target = lineEl . textContent ;
42+ const frames = [ ] ;
43+ for ( let i = 0 ; i < target . length ; i ++ ) {
44+ const lockAt = target [ i ] . match ( / [ , 。 、 ! ? , . ! ? ] / ) ? 0 : globalIndex * lockStagger ;
45+ frames . push ( { char : target [ i ] , lockAt } ) ;
46+ globalIndex ++ ;
47+ }
48+ lineData . push ( { el : lineEl , target, frames } ) ;
49+ } ) ;
4250
43- const start = performance . now ( ) ;
44- const timer = setInterval ( ( ) => {
45- const elapsed = performance . now ( ) - start ;
46- let done = true ;
47- let output = "" ;
51+ const start = performance . now ( ) ;
52+ const timer = setInterval ( ( ) => {
53+ const elapsed = performance . now ( ) - start ;
54+ let allDone = true ;
55+
56+ for ( const line of lineData ) {
57+ let lineDone = true ;
58+ let output = "" ;
59+
60+ for ( const f of line . frames ) {
61+ if ( elapsed >= f . lockAt ) {
62+ output += f . char ;
63+ } else {
64+ lineDone = false ;
65+ output += chars [ Math . floor ( Math . random ( ) * chars . length ) ] ;
66+ }
67+ }
4868
49- for ( const f of frames ) {
50- if ( elapsed >= f . lockAt ) {
51- output += f . char ;
52- } else {
53- done = false ;
54- output += chars [ Math . floor ( Math . random ( ) * chars . length ) ] ;
69+ line . el . textContent = output ;
70+ if ( ! lineDone ) {
71+ allDone = false ;
72+ }
73+ }
74+
75+ if ( allDone ) {
76+ clearInterval ( timer ) ;
77+ lineData . forEach ( ( line ) => {
78+ line . el . textContent = line . target ;
79+ } ) ;
5580 }
81+ } , frameInterval ) ;
82+ } else {
83+ // 降级处理:无 .logo-line 结构时直接处理整个 el
84+ const target = el . textContent ;
85+ const frames = [ ] ;
86+ for ( let i = 0 ; i < target . length ; i ++ ) {
87+ const lockAt = target [ i ] . match ( / [ , 。 、 ! ? , . ! ? ] / ) ? 0 : i * lockStagger ;
88+ frames . push ( { char : target [ i ] , lockAt } ) ;
5689 }
5790
58- el . textContent = output ;
91+ const start = performance . now ( ) ;
92+ const timer = setInterval ( ( ) => {
93+ const elapsed = performance . now ( ) - start ;
94+ let done = true ;
95+ let output = "" ;
96+
97+ for ( const f of frames ) {
98+ if ( elapsed >= f . lockAt ) {
99+ output += f . char ;
100+ } else {
101+ done = false ;
102+ output += chars [ Math . floor ( Math . random ( ) * chars . length ) ] ;
103+ }
104+ }
59105
60- if ( done ) {
61- clearInterval ( timer ) ;
62- el . textContent = target ;
63- }
64- } , frameInterval ) ;
106+ el . textContent = output ;
107+
108+ if ( done ) {
109+ clearInterval ( timer ) ;
110+ el . textContent = target ;
111+ }
112+ } , frameInterval ) ;
113+ }
65114}
66115
67116// 滚动动画 - IntersectionObserver
0 commit comments