|
1116 | 1116 |
|
1117 | 1117 | const finalIndex = words.length - 1; |
1118 | 1118 | const transitionDuration = 620; |
1119 | | - const holdDuration = 520; |
| 1119 | + const holdDuration = 440; |
1120 | 1120 | const initialHold = 280; |
1121 | | - const finalGlowDelay = 180; |
1122 | | - const timers = []; |
1123 | 1121 | let activeIndex = 0; |
1124 | 1122 | let started = false; |
1125 | 1123 | let metrics = { height: 0, widths: [] }; |
1126 | | - |
1127 | | - const clearTimers = () => { |
1128 | | - while (timers.length) { |
1129 | | - window.clearTimeout(timers.pop()); |
1130 | | - } |
1131 | | - }; |
| 1124 | + let sequenceFrame = 0; |
| 1125 | + let nextStepAt = 0; |
| 1126 | + let lastStepAt = 0; |
1132 | 1127 |
|
1133 | 1128 | const setImmediateTransitions = (enabled) => { |
1134 | 1129 | const value = enabled ? "none" : ""; |
|
1174 | 1169 | } |
1175 | 1170 | }; |
1176 | 1171 |
|
1177 | | - const triggerFinalGlow = () => { |
1178 | | - const finalWord = words[finalIndex]; |
1179 | | - finalWord.classList.remove("about-operator-word--glow"); |
1180 | | - void finalWord.offsetWidth; |
1181 | | - finalWord.classList.add("about-operator-word--glow"); |
1182 | | - timers.push( |
1183 | | - window.setTimeout(() => { |
1184 | | - finalWord.classList.remove("about-operator-word--glow"); |
1185 | | - }, 1150) |
1186 | | - ); |
| 1172 | + const stopSequence = () => { |
| 1173 | + window.cancelAnimationFrame(sequenceFrame); |
| 1174 | + sequenceFrame = 0; |
| 1175 | + }; |
| 1176 | + |
| 1177 | + const tickSequence = (now) => { |
| 1178 | + if (!started || activeIndex >= finalIndex) { |
| 1179 | + sequenceFrame = 0; |
| 1180 | + return; |
| 1181 | + } |
| 1182 | + |
| 1183 | + if (!nextStepAt) { |
| 1184 | + lastStepAt = now; |
| 1185 | + nextStepAt = now + initialHold; |
| 1186 | + } |
| 1187 | + |
| 1188 | + if (now >= nextStepAt) { |
| 1189 | + applyIndex(activeIndex + 1); |
| 1190 | + lastStepAt = now; |
| 1191 | + nextStepAt = now + transitionDuration + holdDuration; |
| 1192 | + |
| 1193 | + if (activeIndex >= finalIndex) { |
| 1194 | + sequenceFrame = 0; |
| 1195 | + return; |
| 1196 | + } |
| 1197 | + } |
| 1198 | + |
| 1199 | + sequenceFrame = window.requestAnimationFrame(tickSequence); |
1187 | 1200 | }; |
1188 | 1201 |
|
1189 | 1202 | const runSequence = () => { |
|
1201 | 1214 |
|
1202 | 1215 | updateMetrics(); |
1203 | 1216 | applyIndex(0, { immediate: true }); |
1204 | | - |
1205 | | - let elapsed = initialHold; |
1206 | | - |
1207 | | - for (let index = 1; index <= finalIndex; index += 1) { |
1208 | | - const currentIndex = index; |
1209 | | - |
1210 | | - timers.push( |
1211 | | - window.setTimeout(() => { |
1212 | | - applyIndex(currentIndex); |
1213 | | - }, elapsed) |
1214 | | - ); |
1215 | | - |
1216 | | - if (currentIndex === finalIndex) { |
1217 | | - timers.push( |
1218 | | - window.setTimeout(() => { |
1219 | | - triggerFinalGlow(); |
1220 | | - }, elapsed + transitionDuration + finalGlowDelay) |
1221 | | - ); |
1222 | | - } |
1223 | | - |
1224 | | - elapsed += transitionDuration + holdDuration; |
1225 | | - } |
| 1217 | + nextStepAt = 0; |
| 1218 | + stopSequence(); |
| 1219 | + sequenceFrame = window.requestAnimationFrame(tickSequence); |
1226 | 1220 | }; |
1227 | 1221 |
|
1228 | 1222 | const refreshLayout = () => { |
1229 | 1223 | updateMetrics(); |
1230 | 1224 | applyIndex(started ? activeIndex : 0, { immediate: true }); |
| 1225 | + |
| 1226 | + if (started && activeIndex < finalIndex && lastStepAt) { |
| 1227 | + nextStepAt = performance.now() + Math.max(140, transitionDuration - (performance.now() - lastStepAt)); |
| 1228 | + } |
1231 | 1229 | }; |
1232 | 1230 |
|
1233 | 1231 | const settledRefresh = createSettledScheduler(refreshLayout); |
|
0 commit comments