88 lines
2.0 KiB
Vue
88 lines
2.0 KiB
Vue
|
|
<script setup lang="ts">
|
||
|
|
import { useI18n } from 'vue-i18n';
|
||
|
|
import type { LikeBubble } from '../realtime/useAlbumRealtime';
|
||
|
|
|
||
|
|
// 他人点赞气泡(Task 10):右下角浮层,含被赞照片缩略图 + ♥ +delta;点击打开
|
||
|
|
// 该照片大图(定位)。生成/消散/清空(页面隐藏)由 useAlbumRealtime 管理,
|
||
|
|
// 本组件纯展示。减少动画/不可见时根本不会收到气泡(router 门禁)。
|
||
|
|
defineProps<{
|
||
|
|
bubbles: LikeBubble[];
|
||
|
|
}>();
|
||
|
|
|
||
|
|
const emit = defineEmits<{
|
||
|
|
open: [photoId: string];
|
||
|
|
}>();
|
||
|
|
|
||
|
|
const { t } = useI18n();
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<template>
|
||
|
|
<div class="like-bubbles" aria-live="polite">
|
||
|
|
<button
|
||
|
|
v-for="bubble in bubbles"
|
||
|
|
:key="bubble.key"
|
||
|
|
type="button"
|
||
|
|
class="like-bubble"
|
||
|
|
:aria-label="t('viewer.bubbleOpen')"
|
||
|
|
@click="emit('open', bubble.photoId)"
|
||
|
|
>
|
||
|
|
<img v-if="bubble.thumbUrl !== null" class="bubble-thumb" :src="bubble.thumbUrl" alt="" loading="lazy">
|
||
|
|
<span class="bubble-heart" aria-hidden="true">♥</span>
|
||
|
|
<span class="bubble-delta" aria-hidden="true">+{{ bubble.delta }}</span>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
/* 极简基线样式:视觉打磨留待 P4a。 */
|
||
|
|
.like-bubbles {
|
||
|
|
position: fixed;
|
||
|
|
right: 16px;
|
||
|
|
bottom: 24px;
|
||
|
|
z-index: 105;
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 8px;
|
||
|
|
pointer-events: none;
|
||
|
|
}
|
||
|
|
.like-bubble {
|
||
|
|
pointer-events: auto;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 8px;
|
||
|
|
border: 1px solid rgba(255, 255, 255, 0.35);
|
||
|
|
border-radius: 10px;
|
||
|
|
background: rgba(17, 24, 39, 0.88);
|
||
|
|
color: #f9fafb;
|
||
|
|
padding: 6px 12px 6px 6px;
|
||
|
|
cursor: pointer;
|
||
|
|
animation: bubble-rise 300ms ease-out;
|
||
|
|
}
|
||
|
|
.bubble-thumb {
|
||
|
|
width: 36px;
|
||
|
|
height: 36px;
|
||
|
|
border-radius: 6px;
|
||
|
|
object-fit: cover;
|
||
|
|
display: block;
|
||
|
|
}
|
||
|
|
.bubble-heart {
|
||
|
|
color: #ef4444;
|
||
|
|
}
|
||
|
|
.bubble-delta {
|
||
|
|
font-variant-numeric: tabular-nums;
|
||
|
|
}
|
||
|
|
@keyframes bubble-rise {
|
||
|
|
from {
|
||
|
|
opacity: 0;
|
||
|
|
transform: translateY(12px);
|
||
|
|
}
|
||
|
|
to {
|
||
|
|
opacity: 1;
|
||
|
|
transform: translateY(0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.viewer-reduced-motion .like-bubble {
|
||
|
|
animation: none;
|
||
|
|
}
|
||
|
|
</style>
|