feat(弹幕组件): 重构弹幕组件并添加API数据支持
重构弹幕组件逻辑,移除mock数据改用API获取真实弹幕数据 优化弹幕动画逻辑和轨道管理,提高弹幕显示效果 调整弹幕样式和间距,增强用户体验
Showing
4 changed files
with
197 additions
and
66 deletions
| 1 | /* | 1 | /* |
| 2 | * @Date: 2023-12-22 10:29:37 | 2 | * @Date: 2023-12-22 10:29:37 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-09-18 17:23:51 | 4 | + * @LastEditTime: 2025-10-28 17:03:04 |
| 5 | * @FilePath: /lls_program/src/api/points.js | 5 | * @FilePath: /lls_program/src/api/points.js |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | */ | 7 | */ |
| ... | @@ -72,5 +72,10 @@ export const getPointListAPI = (params) => fn(fetch.get(Api.POINT_LIST, params)) | ... | @@ -72,5 +72,10 @@ export const getPointListAPI = (params) => fn(fetch.get(Api.POINT_LIST, params)) |
| 72 | * @returns {number} response.data.current_family[].step - 步数 | 72 | * @returns {number} response.data.current_family[].step - 步数 |
| 73 | * @returns {number} response.data.current_family[].rank - 排名 | 73 | * @returns {number} response.data.current_family[].rank - 排名 |
| 74 | * @returns {number} response.data.current_family[].country - 区县 | 74 | * @returns {number} response.data.current_family[].country - 区县 |
| 75 | + * @returns {Array} response.data.bullet_families[] - 弹幕家庭列表 | ||
| 76 | + * @returns {string} response.data.bullet_families[].id - 家庭ID | ||
| 77 | + * @returns {string} response.data.bullet_families[].name - 家庭名称 | ||
| 78 | + * @returns {string} response.data.bullet_families[].avatar_url - 头像URL | ||
| 79 | + * @returns {string} response.data.bullet_families[].note - 家庭介绍 | ||
| 75 | */ | 80 | */ |
| 76 | export const getStepLeaderboardAPI = (params) => fn(fetch.get(Api.STEP_LEADERBOARD, params)); | 81 | export const getStepLeaderboardAPI = (params) => fn(fetch.get(Api.STEP_LEADERBOARD, params)); | ... | ... |
| ... | @@ -51,6 +51,10 @@ | ... | @@ -51,6 +51,10 @@ |
| 51 | <script setup> | 51 | <script setup> |
| 52 | import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue' | 52 | import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue' |
| 53 | import Taro from '@tarojs/taro' | 53 | import Taro from '@tarojs/taro' |
| 54 | +// 导入接口 | ||
| 55 | +import { getStepLeaderboardAPI } from '@/api/points' | ||
| 56 | +// 默认头像 | ||
| 57 | +const defaultAvatar = 'https://cdn.ipadbiz.cn/lls_prog/images/%E5%85%A8%E5%AE%B6%E7%A6%8F3_%E5%89%AF%E6%9C%AC.jpg?imageMogr2/strip/quality/60' | ||
| 54 | 58 | ||
| 55 | // Props | 59 | // Props |
| 56 | const props = defineProps({ | 60 | const props = defineProps({ |
| ... | @@ -72,7 +76,7 @@ const props = defineProps({ | ... | @@ -72,7 +76,7 @@ const props = defineProps({ |
| 72 | // 弹幕速度 (rpx/s) | 76 | // 弹幕速度 (rpx/s) |
| 73 | danmuSpeed: { | 77 | danmuSpeed: { |
| 74 | type: Number, | 78 | type: Number, |
| 75 | - default: 120 // 提高默认速度,让弹幕移动更流畅 | 79 | + default: 150 // 提高默认速度,让弹幕移动更流畅 |
| 76 | }, | 80 | }, |
| 77 | // 轨道数量 | 81 | // 轨道数量 |
| 78 | trackCount: { | 82 | trackCount: { |
| ... | @@ -99,19 +103,26 @@ const tracks = reactive( | ... | @@ -99,19 +103,26 @@ const tracks = reactive( |
| 99 | })) | 103 | })) |
| 100 | ) | 104 | ) |
| 101 | 105 | ||
| 102 | -// Mock 数据 - 家庭弹幕内容 | 106 | +// 弹幕数据 - 从API获取 |
| 103 | -const baseFamilyData = { | 107 | +const familyDanmus = ref([]) |
| 104 | - familyName: '幸福之家', | 108 | + |
| 105 | - familyIntro: '7口之家,父母健康,有儿有女,孩子活泼可爱,我们工作顺利', | 109 | +// 获取弹幕数据 |
| 106 | - avatar: 'https://cdn.ipadbiz.cn/lls_prog/images/%E5%85%A8%E5%AE%B6%E7%A6%8F3_%E5%89%AF%E6%9C%AC.jpg?imageMogr2/strip/quality/60', | 110 | +const fetchDanmuData = async () => { |
| 111 | + try { | ||
| 112 | + const { code, data } = await getStepLeaderboardAPI({ type: 'institution' }) | ||
| 113 | + if (code && data?.bullet_families) { | ||
| 114 | + familyDanmus.value = data.bullet_families.map(family => ({ | ||
| 115 | + id: family.id, | ||
| 116 | + familyName: family.name, | ||
| 117 | + familyIntro: family.note, | ||
| 118 | + avatar: family.avatar_url || defaultAvatar | ||
| 119 | + })) | ||
| 120 | + } | ||
| 121 | + } catch (error) { | ||
| 122 | + console.error('获取弹幕数据失败:', error) | ||
| 123 | + } | ||
| 107 | } | 124 | } |
| 108 | 125 | ||
| 109 | -// 生成1000条相同的弹幕数据 | ||
| 110 | -const mockFamilyDanmus = Array.from({ length: 1000 }, (_, index) => ({ | ||
| 111 | - id: (index + 1).toString(), | ||
| 112 | - ...baseFamilyData | ||
| 113 | -})) | ||
| 114 | - | ||
| 115 | // 获取弹幕样式 | 126 | // 获取弹幕样式 |
| 116 | const getDanmuStyle = (danmu, trackIndex) => { | 127 | const getDanmuStyle = (danmu, trackIndex) => { |
| 117 | // 计算垂直位置,确保每个轨道有明确的垂直间距 | 128 | // 计算垂直位置,确保每个轨道有明确的垂直间距 |
| ... | @@ -140,27 +151,44 @@ const checkTrackSpace = (track, danmuWidth = 350) => { | ... | @@ -140,27 +151,44 @@ const checkTrackSpace = (track, danmuWidth = 350) => { |
| 140 | return true | 151 | return true |
| 141 | } | 152 | } |
| 142 | 153 | ||
| 143 | - // 检查最后一个弹幕的位置和时间 | 154 | + // 清理已经完全移出屏幕的弹幕 |
| 144 | - const lastDanmu = track.danmus[track.danmus.length - 1] | 155 | + track.danmus = track.danmus.filter(danmu => { |
| 145 | - const timeSinceLastDanmu = now - track.lastDanmuTime | 156 | + const elapsedTime = now - danmu.startTime |
| 157 | + const totalDistance = 750 + danmuWidth + 100 | ||
| 158 | + const moveDistance = (elapsedTime / danmu.duration) * totalDistance | ||
| 159 | + const currentX = 750 - moveDistance | ||
| 146 | 160 | ||
| 147 | - // 更精确的位置计算 | 161 | + // 如果弹幕已经完全移出屏幕左侧,则移除 |
| 148 | - const elapsedTime = now - lastDanmu.startTime | 162 | + return currentX > -danmuWidth |
| 149 | - const totalDistance = 750 + danmuWidth + 100 // 总移动距离 | 163 | + }) |
| 150 | - const moveDistance = Math.min((elapsedTime / lastDanmu.duration) * totalDistance, totalDistance) | ||
| 151 | - const lastDanmuCurrentX = 750 - moveDistance | ||
| 152 | 164 | ||
| 153 | - // 调整安全间距,考虑3行文字的情况 | 165 | + // 重新检查轨道是否为空 |
| 154 | - const minSafeDistance = danmuWidth * 0.4 // 增加到40%,为3行文字预留更多空间 | 166 | + if (track.danmus.length === 0) { |
| 155 | - const safeDistance = danmuWidth + minSafeDistance // 弹幕宽度 + 40%弹幕长度作为间距 | 167 | + return true |
| 156 | - const hasEnoughSpace = lastDanmuCurrentX < (750 - safeDistance) | 168 | + } |
| 169 | + | ||
| 170 | + // 检查所有弹幕的位置,确保新弹幕不会与任何现有弹幕重叠 | ||
| 171 | + for (const existingDanmu of track.danmus) { | ||
| 172 | + const elapsedTime = now - existingDanmu.startTime | ||
| 173 | + const totalDistance = 750 + danmuWidth + 100 | ||
| 174 | + const moveDistance = Math.min((elapsedTime / existingDanmu.duration) * totalDistance, totalDistance) | ||
| 175 | + const existingDanmuX = 750 - moveDistance | ||
| 157 | 176 | ||
| 158 | - // 适当增加时间间隔,确保3行文字弹幕有足够的显示时间 | 177 | + // 计算弹幕的右边界位置 |
| 159 | - const minTimeInterval = 1800 // 从1600ms增加到1800ms,为3行文字预留更多时间 | 178 | + const existingDanmuRightEdge = existingDanmuX + danmuWidth |
| 160 | - const hasEnoughTime = timeSinceLastDanmu >= minTimeInterval | 179 | + |
| 180 | + // 如果现有弹幕的右边界还在屏幕内或刚出屏幕,则需要更严格的间距检查 | ||
| 181 | + const safeDistance = danmuWidth * 0.8 // 增加到80%的弹幕宽度作为安全距离 | ||
| 182 | + if (existingDanmuRightEdge > (750 - safeDistance)) { | ||
| 183 | + return false | ||
| 184 | + } | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + // 检查最后一个弹幕的时间间隔 - 更严格的时间控制 | ||
| 188 | + const timeSinceLastDanmu = now - track.lastDanmuTime | ||
| 189 | + const minTimeInterval = 2500 // 增加到2.5秒,确保足够的时间间隔 | ||
| 161 | 190 | ||
| 162 | - // 双重检查:时间和空间都满足才允许放置 | 191 | + return timeSinceLastDanmu >= minTimeInterval |
| 163 | - return hasEnoughTime && hasEnoughSpace | ||
| 164 | } | 192 | } |
| 165 | 193 | ||
| 166 | // 找到可用的轨道 - 随机选择而非按顺序 | 194 | // 找到可用的轨道 - 随机选择而非按顺序 |
| ... | @@ -201,6 +229,11 @@ const addDanmuToTrack = (danmu) => { | ... | @@ -201,6 +229,11 @@ const addDanmuToTrack = (danmu) => { |
| 201 | const track = findAvailableTrack() | 229 | const track = findAvailableTrack() |
| 202 | if (!track) return false | 230 | if (!track) return false |
| 203 | 231 | ||
| 232 | + // 更严格的最终检查 - 确保轨道真的有足够空间 | ||
| 233 | + if (!checkTrackSpace(track)) { | ||
| 234 | + return false | ||
| 235 | + } | ||
| 236 | + | ||
| 204 | track.danmus.push(danmu) | 237 | track.danmus.push(danmu) |
| 205 | track.lastDanmuTime = Date.now() | 238 | track.lastDanmuTime = Date.now() |
| 206 | 239 | ||
| ... | @@ -225,17 +258,37 @@ const startDanmuAnimation = (danmu, track) => { | ... | @@ -225,17 +258,37 @@ const startDanmuAnimation = (danmu, track) => { |
| 225 | // 设置动画 - 从右侧移动到左侧屏幕外 | 258 | // 设置动画 - 从右侧移动到左侧屏幕外 |
| 226 | danmu.isMoving = true | 259 | danmu.isMoving = true |
| 227 | 260 | ||
| 261 | + // 计算当前弹幕应该在的位置(基于已经运行的时间) | ||
| 262 | + const now = Date.now() | ||
| 263 | + const elapsedTime = now - danmu.startTime | ||
| 264 | + const totalDistance = 750 + 350 + 100 // 总移动距离 | ||
| 265 | + | ||
| 266 | + // 如果弹幕已经运行超过预定时间,直接重置 | ||
| 267 | + if (elapsedTime >= danmu.duration) { | ||
| 268 | + resetDanmuForLoop(danmu, track) | ||
| 269 | + return | ||
| 270 | + } | ||
| 271 | + | ||
| 272 | + const currentProgress = elapsedTime / danmu.duration | ||
| 273 | + const currentPosition = 750 - (currentProgress * totalDistance) | ||
| 274 | + | ||
| 275 | + // 设置当前位置 | ||
| 276 | + danmu.x = currentPosition | ||
| 277 | + | ||
| 228 | // 使用 nextTick 确保 DOM 更新后再启动动画 | 278 | // 使用 nextTick 确保 DOM 更新后再启动动画 |
| 229 | nextTick(() => { | 279 | nextTick(() => { |
| 230 | // 移动到左侧屏幕外,确保弹幕完全消失 | 280 | // 移动到左侧屏幕外,确保弹幕完全消失 |
| 231 | danmu.x = -(450) // 移动距离 = 弹幕宽度(350) + 额外距离(100) | 281 | danmu.x = -(450) // 移动距离 = 弹幕宽度(350) + 额外距离(100) |
| 232 | }) | 282 | }) |
| 233 | 283 | ||
| 284 | + // 计算剩余动画时间 | ||
| 285 | + const remainingTime = danmu.duration - elapsedTime | ||
| 286 | + | ||
| 234 | // 监听动画结束事件,实现循环 | 287 | // 监听动画结束事件,实现循环 |
| 235 | const timer = setTimeout(() => { | 288 | const timer = setTimeout(() => { |
| 236 | // 动画结束后,重置弹幕位置到右侧,开始新的循环 | 289 | // 动画结束后,重置弹幕位置到右侧,开始新的循环 |
| 237 | resetDanmuForLoop(danmu, track) | 290 | resetDanmuForLoop(danmu, track) |
| 238 | - }, danmu.duration) | 291 | + }, remainingTime) |
| 239 | 292 | ||
| 240 | animationTimers.value.set(danmu.id, timer) | 293 | animationTimers.value.set(danmu.id, timer) |
| 241 | } | 294 | } |
| ... | @@ -247,17 +300,21 @@ const resetDanmuForLoop = (danmu, track) => { | ... | @@ -247,17 +300,21 @@ const resetDanmuForLoop = (danmu, track) => { |
| 247 | // 立即停止当前动画 | 300 | // 立即停止当前动画 |
| 248 | danmu.isMoving = false | 301 | danmu.isMoving = false |
| 249 | 302 | ||
| 303 | + // 从轨道中移除这个弹幕,避免重叠检查时的干扰 | ||
| 304 | + removeDanmuFromTrack(danmu, track) | ||
| 305 | + | ||
| 250 | // 使用 nextTick 确保 DOM 更新 | 306 | // 使用 nextTick 确保 DOM 更新 |
| 251 | nextTick(() => { | 307 | nextTick(() => { |
| 252 | - // 重置弹幕位置到右侧 | 308 | + // 重新创建弹幕数据,避免状态污染 |
| 253 | - danmu.x = 750 // 回到右侧起始位置 | 309 | + const newDanmuData = { ...danmu.data } |
| 310 | + const newDanmu = createDanmu(newDanmuData) | ||
| 254 | 311 | ||
| 255 | - // 短暂延迟后重新开始动画 | 312 | + // 延迟重新添加,确保轨道空间检查正确 |
| 256 | setTimeout(() => { | 313 | setTimeout(() => { |
| 257 | if (isPlaying.value) { | 314 | if (isPlaying.value) { |
| 258 | - startDanmuAnimation(danmu, track) | 315 | + addDanmuToTrack(newDanmu) |
| 259 | } | 316 | } |
| 260 | - }, 50) // 减少延迟时间 | 317 | + }, 100 + Math.random() * 200) // 随机延迟100-300ms,避免同时重置造成的重叠 |
| 261 | }) | 318 | }) |
| 262 | } | 319 | } |
| 263 | 320 | ||
| ... | @@ -277,13 +334,66 @@ const sendDanmu = (data) => { | ... | @@ -277,13 +334,66 @@ const sendDanmu = (data) => { |
| 277 | addDanmuToTrack(danmu) | 334 | addDanmuToTrack(danmu) |
| 278 | } | 335 | } |
| 279 | 336 | ||
| 337 | +// 初始化满屏弹幕显示 | ||
| 338 | +const initFullScreenDanmus = () => { | ||
| 339 | + if (!isPlaying.value) return | ||
| 340 | + | ||
| 341 | + const screenWidth = 750 // 屏幕宽度 | ||
| 342 | + const danmuWidth = 350 // 弹幕宽度 | ||
| 343 | + | ||
| 344 | + // 为每个轨道创建初始弹幕,确保轨道间距合理 | ||
| 345 | + tracks.forEach((track, trackIndex) => { | ||
| 346 | + // 每个轨道放置2个弹幕,避免过度拥挤 | ||
| 347 | + const danmusPerTrack = 2 | ||
| 348 | + | ||
| 349 | + for (let i = 0; i < danmusPerTrack; i++) { | ||
| 350 | + const danmuData = familyDanmus.value[currentDanmuIndex % familyDanmus.value.length] | ||
| 351 | + currentDanmuIndex++ | ||
| 352 | + | ||
| 353 | + // 计算弹幕持续时间 | ||
| 354 | + const duration = (screenWidth + danmuWidth + 100) / (props.danmuSpeed / 1000) | ||
| 355 | + | ||
| 356 | + // 计算已经运行的时间,确保弹幕间有足够间距 | ||
| 357 | + // 第一个弹幕运行时间较长,第二个弹幕运行时间较短,形成间距 | ||
| 358 | + const baseRunTime = i * (duration * 0.4) // 每个弹幕间隔40%的动画时长 | ||
| 359 | + const randomOffset = Math.random() * (duration * 0.2) // 添加20%的随机偏移 | ||
| 360 | + const randomRunTime = baseRunTime + randomOffset | ||
| 361 | + const startTime = Date.now() - randomRunTime | ||
| 362 | + | ||
| 363 | + // 计算初始位置,基于已经运行的时间 | ||
| 364 | + const totalDistance = screenWidth + danmuWidth + 100 | ||
| 365 | + const progress = Math.min(randomRunTime / duration, 0.9) // 最多运行90%,确保弹幕可见 | ||
| 366 | + const initialX = screenWidth - (progress * totalDistance) | ||
| 367 | + | ||
| 368 | + const danmu = { | ||
| 369 | + id: `init-${trackIndex}-${i}-${Date.now()}-${Math.random()}`, | ||
| 370 | + data: danmuData, | ||
| 371 | + x: initialX, | ||
| 372 | + duration: duration, | ||
| 373 | + isMoving: true, // 立即开始移动 | ||
| 374 | + opacity: 1, | ||
| 375 | + startTime: startTime // 使用计算出的开始时间 | ||
| 376 | + } | ||
| 377 | + | ||
| 378 | + track.danmus.push(danmu) | ||
| 379 | + // 更新轨道的最后弹幕时间,确保后续弹幕有合理间隔 | ||
| 380 | + track.lastDanmuTime = Date.now() - (danmusPerTrack - i - 1) * 1500 // 每个弹幕间隔1.5秒 | ||
| 381 | + | ||
| 382 | + // 立即启动动画,不延迟 | ||
| 383 | + nextTick(() => { | ||
| 384 | + startDanmuAnimation(danmu, track) | ||
| 385 | + }) | ||
| 386 | + } | ||
| 387 | + }) | ||
| 388 | +} | ||
| 389 | + | ||
| 280 | // 当前弹幕索引,用于顺序显示 | 390 | // 当前弹幕索引,用于顺序显示 |
| 281 | let currentDanmuIndex = 0 | 391 | let currentDanmuIndex = 0 |
| 282 | 392 | ||
| 283 | // 发送顺序弹幕 | 393 | // 发送顺序弹幕 |
| 284 | const sendSequentialDanmu = () => { | 394 | const sendSequentialDanmu = () => { |
| 285 | // 按顺序获取弹幕数据 | 395 | // 按顺序获取弹幕数据 |
| 286 | - const danmuData = mockFamilyDanmus[currentDanmuIndex % mockFamilyDanmus.length] | 396 | + const danmuData = familyDanmus.value[currentDanmuIndex % familyDanmus.value.length] |
| 287 | sendDanmu(danmuData) | 397 | sendDanmu(danmuData) |
| 288 | 398 | ||
| 289 | // 更新索引,循环使用 | 399 | // 更新索引,循环使用 |
| ... | @@ -297,11 +407,22 @@ const startAutoSend = () => { | ... | @@ -297,11 +407,22 @@ const startAutoSend = () => { |
| 297 | 407 | ||
| 298 | const sendNext = () => { | 408 | const sendNext = () => { |
| 299 | if (isPlaying.value) { | 409 | if (isPlaying.value) { |
| 300 | - // 移除弹幕数量限制,直接发送弹幕 | 410 | + // 智能发送弹幕,避免过度拥挤 |
| 411 | + const availableTrackCount = tracks.filter(track => checkTrackSpace(track)).length | ||
| 412 | + | ||
| 413 | + // 只有当有足够可用轨道时才发送弹幕 | ||
| 414 | + if (availableTrackCount > 0) { | ||
| 301 | sendSequentialDanmu() | 415 | sendSequentialDanmu() |
| 302 | } | 416 | } |
| 303 | - // 优化发送间隔,在保持合理间距的同时增加密度 | 417 | + } |
| 304 | - autoSendTimer = setTimeout(sendNext, 1000 + Math.random() * 600) // 1.0-1.6秒随机间隔,适度增加发送频率 | 418 | + |
| 419 | + // 动态调整发送间隔,根据可用轨道数量 | ||
| 420 | + const availableTrackCount = tracks.filter(track => checkTrackSpace(track)).length | ||
| 421 | + const baseInterval = 1200 // 基础间隔1.2秒 | ||
| 422 | + const intervalMultiplier = Math.max(1, (6 - availableTrackCount) * 0.3) // 可用轨道越少,间隔越长 | ||
| 423 | + const dynamicInterval = baseInterval * intervalMultiplier + Math.random() * 400 | ||
| 424 | + | ||
| 425 | + autoSendTimer = setTimeout(sendNext, dynamicInterval) | ||
| 305 | } | 426 | } |
| 306 | 427 | ||
| 307 | sendNext() | 428 | sendNext() |
| ... | @@ -366,21 +487,23 @@ defineExpose({ | ... | @@ -366,21 +487,23 @@ defineExpose({ |
| 366 | }) | 487 | }) |
| 367 | 488 | ||
| 368 | // 生命周期 | 489 | // 生命周期 |
| 369 | -onMounted(() => { | 490 | +onMounted(async () => { |
| 370 | - // 自动开始播放并发送初始弹幕 | 491 | + // 首先获取弹幕数据 |
| 492 | + await fetchDanmuData() | ||
| 493 | + | ||
| 494 | + // 自动开始播放 | ||
| 371 | isPlaying.value = true | 495 | isPlaying.value = true |
| 372 | 496 | ||
| 373 | - // 页面加载后,优化初始弹幕发送,保持合理间距和密度 | 497 | + // 使用nextTick确保DOM完全渲染后再初始化弹幕 |
| 374 | - setTimeout(() => { | 498 | + nextTick(() => { |
| 375 | - for (let i = 0; i < 4; i++) { // 适度增加初始弹幕数量 | 499 | + // 立即初始化满屏弹幕,避免白屏状态 |
| 376 | - setTimeout(() => { | 500 | + initFullScreenDanmus() |
| 377 | - sendSequentialDanmu() | ||
| 378 | - }, i * 800) // 调整发送间隔到0.8秒,平衡密度和间距 | ||
| 379 | - } | ||
| 380 | - }, 400) // 减少初始延迟 | ||
| 381 | 501 | ||
| 382 | - // 启动自动发送 | 502 | + // 稍微延迟启动自动发送,让初始弹幕先稳定显示 |
| 503 | + setTimeout(() => { | ||
| 383 | startAutoSend() | 504 | startAutoSend() |
| 505 | + }, 1000) // 1秒后开始自动发送新弹幕 | ||
| 506 | + }) | ||
| 384 | }) | 507 | }) |
| 385 | 508 | ||
| 386 | onUnmounted(() => { | 509 | onUnmounted(() => { |
| ... | @@ -429,8 +552,8 @@ onUnmounted(() => { | ... | @@ -429,8 +552,8 @@ onUnmounted(() => { |
| 429 | box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12); | 552 | box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12); |
| 430 | backdrop-filter: blur(12rpx); | 553 | backdrop-filter: blur(12rpx); |
| 431 | border: 2rpx solid rgba(255, 255, 255, 0.4); | 554 | border: 2rpx solid rgba(255, 255, 255, 0.4); |
| 432 | - max-width: 350rpx; | 555 | + max-width: 400rpx; |
| 433 | - min-width: 300rpx; | 556 | + min-width: 350rpx; |
| 434 | margin: 8rpx 0; | 557 | margin: 8rpx 0; |
| 435 | transition: all 0.3s ease; | 558 | transition: all 0.3s ease; |
| 436 | } | 559 | } |
| ... | @@ -485,6 +608,9 @@ onUnmounted(() => { | ... | @@ -485,6 +608,9 @@ onUnmounted(() => { |
| 485 | margin-right: 12rpx; | 608 | margin-right: 12rpx; |
| 486 | flex-shrink: 0; | 609 | flex-shrink: 0; |
| 487 | text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.1); | 610 | text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.1); |
| 611 | + overflow: hidden; | ||
| 612 | + text-overflow: ellipsis; | ||
| 613 | + -webkit-line-clamp: 1; | ||
| 488 | } | 614 | } |
| 489 | 615 | ||
| 490 | .member-badge { | 616 | .member-badge { | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-01-09 00:00:00 | 2 | * @Date: 2025-01-09 00:00:00 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-10-28 16:24:46 | 4 | + * @LastEditTime: 2025-10-28 16:58:49 |
| 5 | * @FilePath: /lls_program/src/components/RankingCard.vue | 5 | * @FilePath: /lls_program/src/components/RankingCard.vue |
| 6 | * @Description: 排行榜卡片组件 | 6 | * @Description: 排行榜卡片组件 |
| 7 | --> | 7 | --> |
| ... | @@ -43,7 +43,7 @@ | ... | @@ -43,7 +43,7 @@ |
| 43 | <!-- 排行榜日期 --> | 43 | <!-- 排行榜日期 --> |
| 44 | <view class="rank-date relative"> | 44 | <view class="rank-date relative"> |
| 45 | <!-- <view class="flex items-center justify-center"><text class="mr-2">截止昨日</text>{{ currentDate }}<text v-if="activeTab !== 'shanghai'" class="ml-2">排名</text><IconFont name="ask" size="16" class="ml-2" @click="handleRankAskClick"></IconFont></view> --> | 45 | <!-- <view class="flex items-center justify-center"><text class="mr-2">截止昨日</text>{{ currentDate }}<text v-if="activeTab !== 'shanghai'" class="ml-2">排名</text><IconFont name="ask" size="16" class="ml-2" @click="handleRankAskClick"></IconFont></view> --> |
| 46 | - <view class="flex items-center justify-center"><text class="mr-2">截止昨日</text>{{ currentDate }}<text class="ml-2">排名</text><IconFont name="ask" size="16" class="ml-2" @click="handleRankAskClick"></IconFont></view> | 46 | + <view class="flex items-center justify-center"><text class="mr-2">截止昨日</text>{{ currentDate }}<text class="ml-2">排名</text><IconFont v-if="activeTab !== 'support'" name="ask" size="16" class="ml-2" @click="handleRankAskClick"></IconFont></view> |
| 47 | <view v-if="activeTab === 'support'" class="absolute font-bold text-white bg-orange-500 top-0 rounded-full px-4 py-1" style="right: -30rpx; top: -5rpx; font-size: 23rpx; z-index: 999;" @tap="joinOrganization">助力码</view> | 47 | <view v-if="activeTab === 'support'" class="absolute font-bold text-white bg-orange-500 top-0 rounded-full px-4 py-1" style="right: -30rpx; top: -5rpx; font-size: 23rpx; z-index: 999;" @tap="joinOrganization">助力码</view> |
| 48 | </view> | 48 | </view> |
| 49 | 49 | ||
| ... | @@ -532,11 +532,11 @@ const danmuRef = ref(null) | ... | @@ -532,11 +532,11 @@ const danmuRef = ref(null) |
| 532 | // 处理弹幕点击事件 | 532 | // 处理弹幕点击事件 |
| 533 | const handleDanmuClick = (familyData) => { | 533 | const handleDanmuClick = (familyData) => { |
| 534 | console.log('弹幕点击:', familyData) | 534 | console.log('弹幕点击:', familyData) |
| 535 | - Taro.showToast({ | 535 | + // Taro.showToast({ |
| 536 | - title: `点击了${familyData.familyName}`, | 536 | + // title: `点击了${familyData.familyName}`, |
| 537 | - icon: 'none', | 537 | + // icon: 'none', |
| 538 | - duration: 2000 | 538 | + // duration: 2000 |
| 539 | - }) | 539 | + // }) |
| 540 | } | 540 | } |
| 541 | 541 | ||
| 542 | // 处理弹幕悬停事件 | 542 | // 处理弹幕悬停事件 | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-09-01 13:07:52 | 2 | * @Date: 2025-09-01 13:07:52 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-10-28 16:04:30 | 4 | + * @LastEditTime: 2025-10-28 16:36:47 |
| 5 | * @FilePath: /lls_program/src/pages/FamilyRank/index.vue | 5 | * @FilePath: /lls_program/src/pages/FamilyRank/index.vue |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | --> | 7 | --> |
| ... | @@ -286,11 +286,11 @@ const danmuRef = ref(null) | ... | @@ -286,11 +286,11 @@ const danmuRef = ref(null) |
| 286 | // 处理弹幕点击事件 | 286 | // 处理弹幕点击事件 |
| 287 | const handleDanmuClick = (familyData) => { | 287 | const handleDanmuClick = (familyData) => { |
| 288 | console.log('弹幕点击:', familyData) | 288 | console.log('弹幕点击:', familyData) |
| 289 | - Taro.showToast({ | 289 | + // Taro.showToast({ |
| 290 | - title: `点击了${familyData.familyName}`, | 290 | + // title: `点击了${familyData.familyName}`, |
| 291 | - icon: 'none', | 291 | + // icon: 'none', |
| 292 | - duration: 2000 | 292 | + // duration: 2000 |
| 293 | - }) | 293 | + // }) |
| 294 | } | 294 | } |
| 295 | 295 | ||
| 296 | // 处理弹幕悬停事件 | 296 | // 处理弹幕悬停事件 | ... | ... |
-
Please register or login to post a comment