( player: Player | null, danmuSettings: DanmuUserSettings, isDanmuEnabled: boolean, )
| 138 | }; |
| 139 | |
| 140 | export const createDanmuOverlay = ( |
| 141 | player: Player | null, |
| 142 | danmuSettings: DanmuUserSettings, |
| 143 | isDanmuEnabled: boolean, |
| 144 | ): DanmuOverlayInstance | null => { |
| 145 | if (!player) { |
| 146 | return null; |
| 147 | } |
| 148 | |
| 149 | const overlayHost = ensureDanmuOverlayHost(player); |
| 150 | if (!overlayHost) { |
| 151 | return null; |
| 152 | } |
| 153 | |
| 154 | overlayHost.innerHTML = ''; |
| 155 | overlayHost.style.setProperty('--danmu-stroke-color', danmuSettings.strokeColor); |
| 156 | overlayHost.style.setProperty('--danmu-opacity', String(isDanmuEnabled ? sanitizeDanmuOpacity(danmuSettings.opacity) : 0)); |
| 157 | |
| 158 | try { |
| 159 | const media = (player as any).video || (player as any).media || undefined; |
| 160 | |
| 161 | let currentEnabled = isDanmuEnabled; |
| 162 | let currentSettings: DanmuUserSettings = { ...danmuSettings }; |
| 163 | let currentOpacity = currentEnabled ? sanitizeDanmuOpacity(currentSettings.opacity) : 0; |
| 164 | |
| 165 | const initialFontSizePx = parseFontSizePx(currentSettings.fontSize); |
| 166 | const initialChannelSize = computeChannelSize(initialFontSizePx); |
| 167 | const initialAreaEnd = sanitizeDanmuArea(currentSettings.area); |
| 168 | |
| 169 | const danmu = new DanmuJs({ |
| 170 | container: overlayHost, |
| 171 | containerStyle: { zIndex: 7 }, |
| 172 | player: media, |
| 173 | comments: [], |
| 174 | area: { start: 0, end: initialAreaEnd, lines: computeAreaLines(overlayHost, currentSettings) }, |
| 175 | channelSize: initialChannelSize, |
| 176 | mouseControl: false, |
| 177 | mouseControlPause: false, |
| 178 | // Increase horizontal gap slightly to reduce bursts; keeps no-overlap stable under load. |
| 179 | bOffset: 800, |
| 180 | chaseEffect: true, |
| 181 | // Delay initialization until explicitly enabled to save CPU/GPU on startup. |
| 182 | defaultOff: true, |
| 183 | } as any); |
| 184 | |
| 185 | let started = false; |
| 186 | const ensureStarted = () => { |
| 187 | if (started) return; |
| 188 | try { |
| 189 | danmu.start(); |
| 190 | started = true; |
| 191 | } catch (error) { |
| 192 | console.warn('[Player] Failed to start danmu.js:', error); |
| 193 | } |
| 194 | }; |
| 195 | |
| 196 | const overlay: DanmuOverlayInstance = { |
| 197 | sendComment: (comment) => { |
no test coverage detected