fix(弹幕组件): 增强弹幕宽度测量逻辑和默认值处理
改进弹幕宽度测量功能,增加重试机制和更保守的默认值计算 优化动画启动时机,确保宽度测量完成后再开始
Showing
1 changed file
with
40 additions
and
9 deletions
| ... | @@ -105,9 +105,16 @@ const WINDOW_WIDTH = SYSTEM_INFO.windowWidth | ... | @@ -105,9 +105,16 @@ const WINDOW_WIDTH = SYSTEM_INFO.windowWidth |
| 105 | 105 | ||
| 106 | // px 转 rpx | 106 | // px 转 rpx |
| 107 | const toRpx = (px) => (px * CONTAINER_WIDTH) / WINDOW_WIDTH | 107 | const toRpx = (px) => (px * CONTAINER_WIDTH) / WINDOW_WIDTH |
| 108 | -// 获取弹幕宽度(rpx) | 108 | +// 获取弹幕宽度 - 增强版,提供更安全的默认值 |
| 109 | const getDanmuWidth = (danmu) => { | 109 | const getDanmuWidth = (danmu) => { |
| 110 | - return danmu?.widthRpx && danmu.widthRpx > 0 ? danmu.widthRpx : DANMU_WIDTH | 110 | + if (danmu.widthRpx && danmu.widthRpx > 0) { |
| 111 | + return danmu.widthRpx | ||
| 112 | + } | ||
| 113 | + // 如果没有测量到宽度,使用更保守的默认值 | ||
| 114 | + // 根据内容长度动态估算,避免过短导致重叠 | ||
| 115 | + const contentLength = danmu.data?.familyName?.length || 0 | ||
| 116 | + const estimatedWidth = Math.max(DANMU_WIDTH, contentLength * 30 + 100) // 每字符约30rpx + 100rpx边距 | ||
| 117 | + return Math.min(estimatedWidth, DANMU_WIDTH * 2) // 最大不超过2倍默认宽度 | ||
| 111 | } | 118 | } |
| 112 | // 重新计算时长,保证速度一致 | 119 | // 重新计算时长,保证速度一致 |
| 113 | const recalculateDuration = (danmu) => { | 120 | const recalculateDuration = (danmu) => { |
| ... | @@ -115,18 +122,37 @@ const recalculateDuration = (danmu) => { | ... | @@ -115,18 +122,37 @@ const recalculateDuration = (danmu) => { |
| 115 | const totalDistance = CONTAINER_WIDTH + width + EXTRA_DISTANCE | 122 | const totalDistance = CONTAINER_WIDTH + width + EXTRA_DISTANCE |
| 116 | danmu.duration = (totalDistance / (props.danmuSpeed * 0.6)) * 1000 | 123 | danmu.duration = (totalDistance / (props.danmuSpeed * 0.6)) * 1000 |
| 117 | } | 124 | } |
| 118 | -// 测量弹幕真实宽度(px),存为 rpx | 125 | +// 测量弹幕真实宽度(px),存为 rpx - 增强版,支持重试和更保守的默认值 |
| 119 | -const measureDanmuWidth = (danmu) => new Promise((resolve) => { | 126 | +const measureDanmuWidth = (danmu, retryCount = 0) => new Promise((resolve) => { |
| 127 | + const maxRetries = 3 | ||
| 128 | + const retryDelay = 50 // 50ms 重试间隔 | ||
| 129 | + | ||
| 120 | try { | 130 | try { |
| 121 | const selector = `#danmu-${danmu.id} .danmu-content` | 131 | const selector = `#danmu-${danmu.id} .danmu-content` |
| 122 | Taro.createSelectorQuery().select(selector).boundingClientRect(rect => { | 132 | Taro.createSelectorQuery().select(selector).boundingClientRect(rect => { |
| 123 | - if (rect && rect.width) { | 133 | + if (rect && rect.width && rect.width > 0) { |
| 124 | danmu.widthRpx = toRpx(rect.width) | 134 | danmu.widthRpx = toRpx(rect.width) |
| 135 | + resolve(getDanmuWidth(danmu)) | ||
| 136 | + } else if (retryCount < maxRetries) { | ||
| 137 | + // 测量失败,延迟重试 | ||
| 138 | + setTimeout(() => { | ||
| 139 | + measureDanmuWidth(danmu, retryCount + 1).then(resolve) | ||
| 140 | + }, retryDelay) | ||
| 141 | + } else { | ||
| 142 | + // 重试失败,使用更保守的默认宽度(比实际内容更大) | ||
| 143 | + danmu.widthRpx = DANMU_WIDTH * 1.5 // 使用1.5倍默认宽度作为安全值 | ||
| 144 | + resolve(getDanmuWidth(danmu)) | ||
| 125 | } | 145 | } |
| 126 | - resolve(getDanmuWidth(danmu)) | ||
| 127 | }).exec() | 146 | }).exec() |
| 128 | } catch (e) { | 147 | } catch (e) { |
| 129 | - resolve(getDanmuWidth(danmu)) | 148 | + if (retryCount < maxRetries) { |
| 149 | + setTimeout(() => { | ||
| 150 | + measureDanmuWidth(danmu, retryCount + 1).then(resolve) | ||
| 151 | + }, retryDelay) | ||
| 152 | + } else { | ||
| 153 | + danmu.widthRpx = DANMU_WIDTH * 1.5 | ||
| 154 | + resolve(getDanmuWidth(danmu)) | ||
| 155 | + } | ||
| 130 | } | 156 | } |
| 131 | }) | 157 | }) |
| 132 | 158 | ||
| ... | @@ -168,7 +194,7 @@ const getDanmuStyle = (danmu, trackIndex) => { | ... | @@ -168,7 +194,7 @@ const getDanmuStyle = (danmu, trackIndex) => { |
| 168 | 194 | ||
| 169 | return { | 195 | return { |
| 170 | transform: `translateX(${danmu.x}rpx)`, | 196 | transform: `translateX(${danmu.x}rpx)`, |
| 171 | - transition: danmu.isMoving ? `transform ${(danmu.remainingDuration ?? danmu.duration)}ms linear` : 'none', | 197 | + transition: danmu.isMoving ? 'transform ' + (danmu.remainingDuration ?? danmu.duration) + 'ms linear' : 'none', |
| 172 | opacity: danmu.opacity || 1, | 198 | opacity: danmu.opacity || 1, |
| 173 | // 明确设置垂直位置 | 199 | // 明确设置垂直位置 |
| 174 | position: 'absolute', | 200 | position: 'absolute', |
| ... | @@ -279,12 +305,17 @@ const addDanmuToTrack = (danmu, targetTrack = null, options = {}) => { | ... | @@ -279,12 +305,17 @@ const addDanmuToTrack = (danmu, targetTrack = null, options = {}) => { |
| 279 | track.danmus.push(danmu) | 305 | track.danmus.push(danmu) |
| 280 | track.lastDanmuTime = Date.now() | 306 | track.lastDanmuTime = Date.now() |
| 281 | 307 | ||
| 282 | - // 启动弹幕动画 | 308 | + // 启动弹幕动画 - 确保宽度测量完成后再开始 |
| 283 | nextTick(async () => { | 309 | nextTick(async () => { |
| 310 | + // 等待宽度测量完成,确保获得准确宽度 | ||
| 284 | await measureDanmuWidth(danmu) | 311 | await measureDanmuWidth(danmu) |
| 285 | recalculateDuration(danmu) | 312 | recalculateDuration(danmu) |
| 313 | + | ||
| 286 | // 重置起始时间,保证时长重新计算后动画正确 | 314 | // 重置起始时间,保证时长重新计算后动画正确 |
| 287 | danmu.startTime = Date.now() | 315 | danmu.startTime = Date.now() |
| 316 | + | ||
| 317 | + // 再次等待一个 tick,确保 DOM 完全更新 | ||
| 318 | + await nextTick() | ||
| 288 | startDanmuAnimation(danmu, track) | 319 | startDanmuAnimation(danmu, track) |
| 289 | }) | 320 | }) |
| 290 | 321 | ... | ... |
-
Please register or login to post a comment