From 2f8bf0d755e9d7777afc3fbe7a60b14c02bbf609 Mon Sep 17 00:00:00 2001 From: empty Date: Wed, 28 Jan 2026 22:42:59 +0800 Subject: [PATCH] style: refine mobile voting dock film strip UI with seamless borders and improved text clarity --- .../src/components/VotingDock.vue | 342 ++++++++++-------- 1 file changed, 189 insertions(+), 153 deletions(-) diff --git a/packages/client-mobile/src/components/VotingDock.vue b/packages/client-mobile/src/components/VotingDock.vue index c242caf..cf32fd7 100644 --- a/packages/client-mobile/src/components/VotingDock.vue +++ b/packages/client-mobile/src/components/VotingDock.vue @@ -20,23 +20,14 @@ const awardFrames = computed(() => { // 点击帧 function handleFrameClick(awardId: string) { if (votingStore.isAwardUsed(awardId)) { - // 已使用的奖项,显示已投状态 return; } - if (votingStore.selectedAwardId === awardId) { votingStore.deselectAward(); } else { votingStore.selectAward(awardId); } } - -// 获取节目简称 -function getProgramShortName(programId: string | null): string { - if (!programId) return ''; - const program = votingStore.programs.find(p => p.id === programId); - return program ? program.name.slice(0, 2) : ''; -} @@ -94,161 +89,202 @@ function getProgramShortName(programId: string | null): string { right: 0; z-index: $z-index-fixed; padding-bottom: env(safe-area-inset-bottom); + pointer-events: none; // 只有内部按钮可点击,避免遮挡页面背景滚动感 } -// 选中提示 .selection-hint { + pointer-events: auto; display: flex; align-items: center; justify-content: center; gap: 8px; background: linear-gradient(135deg, $color-gold 0%, #b8860b 100%); color: #1a1a1a; - padding: 8px 16px; - margin: 0 16px 8px; + padding: 8px 20px; + margin: 0 20px 12px; border-radius: 20px; font-weight: 600; - animation: pulse-hint 1.5s ease-in-out infinite; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); + animation: slide-up 0.3s ease-out; } -.hint-icon { - font-size: 18px; +@keyframes slide-up { + from { transform: translateY(20px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } } -.hint-text { - font-size: 13px; -} - -@keyframes pulse-hint { - 0%, 100% { - box-shadow: 0 4px 12px rgba($color-gold, 0.4); - } - 50% { - box-shadow: 0 4px 20px rgba($color-gold, 0.6); - } +.film-strip-outer { + pointer-events: auto; + padding: 10px 0 0; + background: transparent; } .film-strip { - background: linear-gradient(180deg, #1a1a1a 0%, #2a2a2a 50%, #1a1a1a 100%); - border-top: 2px solid rgba($color-gold, 0.3); - box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.5); - padding: 0; + position: relative; + background: #000; + color: #fff; + padding: 24px 0; + + // ========================================== + // 无缝复刻:齿孔紧贴边缘黑边 (Gapless) + // ========================================== + $hole-w: 12px; + $hole-h: 14px; + $hole-gap: 5px; + $total-step: $hole-w + $hole-gap; + $border-h: 4px; // 稍微加粗一点边缘 + + -webkit-mask-image: + linear-gradient(black, black), // 1. 顶部最外沿黑边 + repeating-linear-gradient(to right, transparent, transparent $hole-w, black $hole-w, black $total-step), // 2. 上排齿孔 + linear-gradient(black, black), // 3. 中间胶片主体 + repeating-linear-gradient(to right, transparent, transparent $hole-w, black $hole-w, black $total-step), // 4. 下排齿孔 + linear-gradient(black, black); // 5. 底部最外沿黑边 + + -webkit-mask-size: + 100% $border-h, + 100% $hole-h, + 100% calc(100% - #{2 * $border-h + 2 * $hole-h}), // 移除 inner-gap,严丝合缝 + 100% $hole-h, + 100% $border-h; + + -webkit-mask-position: + 0 0, + 0 #{$border-h}, // 齿孔直接紧跟黑边 + 0 50%, + 0 calc(100% - #{$border-h}), // 底部同理 + 0 100%; + + -webkit-mask-repeat: no-repeat, repeat-x, no-repeat, repeat-x, no-repeat; + + &::before { + content: ''; + position: absolute; + inset: 0; + background: transparent; + pointer-events: none; + z-index: -1; + } } -// 齿孔行 -.sprocket-row { +.film-header { + position: absolute; + top: 24px; // 根据实际贴合后的视觉位置微调 + width: 100%; display: flex; - justify-content: space-around; - padding: 0 12px; - height: 10px; - background: #111; - - &.top { - border-bottom: 1px solid #333; - } - - &.bottom { - border-top: 1px solid #333; - } + justify-content: space-between; + padding: 0 40px; + font-family: 'Courier New', monospace; + font-size: 6px; + color: rgba($color-gold, 0.25); + letter-spacing: 4px; + text-transform: uppercase; + font-weight: 600; } -.sprocket-hole { - width: 6px; - height: 5px; - background: #0a0a0a; - border-radius: 1px; - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.8); - margin: 2.5px 0; -} - -// 帧容器 .frames-container { overflow-x: auto; overflow-y: hidden; scrollbar-width: none; - -ms-overflow-style: none; - - &::-webkit-scrollbar { - display: none; - } + &::-webkit-scrollbar { display: none; } } .frames-scroll { display: flex; - padding: 8px 12px; - gap: 8px; + padding: 0 10px; min-width: max-content; } -// 胶片帧(奖项) .film-frame { - width: 64px; - height: 72px; - background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%); - border: 1px solid #444; - border-radius: 3px; + width: 80px; + height: 80px; + border-right: 2px solid #151515; + border-left: 2px solid #050505; display: flex; flex-direction: column; - align-items: center; - justify-content: center; - cursor: pointer; - transition: all 0.2s ease; position: relative; - flex-shrink: 0; - - &:active { - transform: scale(0.95); - } - - // 已使用 - &.is-used { - background: linear-gradient(135deg, #1a3a1a 0%, #0a2a0a 100%); - border-color: #2a5a2a; - opacity: 0.8; - - .frame-icon { - opacity: 0.6; - } - - .frame-name { - color: #4a8a4a; - } - } - - // 选中状态 - &.is-selected { - background: linear-gradient(135deg, $color-primary 0%, $color-primary-dark 100%); - border-color: $color-gold; - box-shadow: - 0 0 12px rgba($color-gold, 0.5), - inset 0 1px 2px rgba(255, 255, 255, 0.2); - transform: translateY(-4px); - - .frame-name { - color: $color-gold; - } - } -} - -.frame-icon { - font-size: 22px; - line-height: 1; -} - -.frame-name { - font-size: 10px; - color: #888; - margin-top: 4px; - white-space: nowrap; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); overflow: hidden; - text-overflow: ellipsis; - max-width: 58px; - text-align: center; -} -.voted-program { - font-size: 9px; - color: #4a8a4a; - margin-top: 2px; + .frame-content { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 4px; + background: #111; + margin: 2px; + border-radius: 2px; + box-shadow: inset 0 0 10px rgba(0,0,0,0.8); + transition: inherit; + position: relative; + z-index: 1; + } + + .frame-icon { + font-size: 24px; + filter: drop-shadow(0 0 5px rgba(0,0,0,0.5)); + } + + .frame-name { + font-size: 12px; + color: #fff; // 响应建议:黑底白字,极致清晰 + font-family: 'Kaiti', serif; + text-shadow: 0 1px 2px rgba(0,0,0,0.8); + } + + .voted-mark { + position: absolute; + top: 4px; + right: 4px; + font-size: 10px; + color: #4ade80; + background: rgba(0, 0, 0, 0.7); + padding: 1px 4px; + border-radius: 4px; + font-weight: bold; + border: 1px solid rgba(74, 222, 128, 0.3); + box-shadow: 0 0 8px rgba(0,0,0,0.5); + z-index: 10; + } + + .frame-footer { + height: 14px; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 6px; + font-family: 'Courier New', monospace; + font-size: 9px; + color: #666; + } + + // 状态处理 + &.is-used { + opacity: 0.6; + .frame-content { + background: #081108; // 微微泛绿,模拟冲洗感 + } + } + + &.is-selected { + transform: scale(1.05) translateY(-5px); + z-index: 10; + border-color: rgba($color-gold, 0.5); + + .frame-content { + background: radial-gradient(circle at center, rgba($color-gold, 0.3) 0%, #111 80%); + box-shadow: + 0 0 20px rgba($color-gold, 0.2), + inset 0 0 15px rgba($color-gold, 0.1); + } + + .frame-name { color: #fff; font-weight: bold; } + + .frame-number { color: $color-gold; text-shadow: 0 0 5px $color-gold; } + } + + &:active { transform: scale(0.95); } }