Browse Source

feat(component): add SongLoading and SongSeal

main
Mohan 1 month ago
parent
commit
feab37f25c
  1. 115
      src/components/SongLoading.vue
  2. 92
      src/components/SongSeal.vue

115
src/components/SongLoading.vue

@ -0,0 +1,115 @@
<script setup lang="ts">
/* SongLoading §7
* 墨点如滴入宣纸般扩散敛去合成层动效transform/opacity only
* mask: 全屏遮罩模式弹层加载 */
withDefaults(
defineProps<{
visible?: boolean
mask?: boolean
text?: string
}>(),
{ visible: true, mask: false, text: '墨迹未干' }
)
defineOptions({ name: 'SongLoading' })
</script>
<template>
<div v-if="visible" class="song-loading" :class="{ 'song-loading--mask': mask }">
<div class="song-loading__inner">
<div class="song-loading__inks" aria-hidden="true">
<i class="song-loading__ink" />
<i class="song-loading__ink" />
<i class="song-loading__ink" />
</div>
<div class="song-loading__abs" aria-hidden="true" />
<span v-if="text" class="song-loading__text caption">{{ text }}</span>
</div>
</div>
</template>
<style scoped>
.song-loading {
display: grid;
place-items: center;
padding: 40px 0;
}
.song-loading--mask {
position: fixed;
inset: 0;
z-index: 950;
background: var(--song-bg-elevated);
opacity: 0.96;
}
.song-loading__inner {
display: flex;
flex-direction: column;
align-items: center;
gap: 18px;
}
.song-loading__inks {
position: relative;
width: 64px;
height: 64px;
}
.song-loading__ink {
position: absolute;
left: 50%;
top: 50%;
width: 40px;
height: 40px;
border-radius: 50%;
background: radial-gradient(circle, var(--song-primary) 0%, rgba(79, 120, 184, 0) 68%);
opacity: 0;
transform: translate(-50%, -50%) scale(0.3);
animation: song-ink-soak 2.4s var(--song-ease) infinite;
will-change: transform, opacity;
}
.song-loading__ink:nth-child(2) {
animation-delay: 0.8s;
}
.song-loading__ink:nth-child(3) {
animation-delay: 1.6s;
}
.song-loading__abs {
position: absolute;
left: 50%;
top: 50%;
width: 52px;
height: 52px;
margin: -26px 0 0 -26px;
border: 1px solid var(--song-border-strong);
border-radius: 50%;
}
.song-loading__text {
letter-spacing: 0.4em;
color: var(--song-text-secondary);
}
@keyframes song-ink-soak {
0% {
transform: translate(-50%, -50%) scale(0.3);
opacity: 0;
}
35% {
opacity: 0.85;
}
80% {
opacity: 0;
}
100% {
transform: translate(-50%, -50%) scale(1.15);
opacity: 0;
}
}
</style>

92
src/components/SongSeal.vue

@ -0,0 +1,92 @@
<script setup lang="ts">
/* SongSeal SVG
* 方印 4 田字排布圆印单字连珠双圈
* 颜色走 --song-seal印泥砂红rotate 模拟手钤微妙倾斜 */
import { computed } from 'vue'
const props = withDefaults(
defineProps<{
text?: string
round?: boolean
size?: number
rotate?: number
}>(),
{ text: '檐下', round: false, size: 76, rotate: -2 }
)
defineOptions({ name: 'SongSeal' })
const chars = computed(() => props.text.slice(0, props.round ? 1 : 4).split(''))
const fd = computed<number[]>(() => {
const n = chars.value.length
if (n <= 1) return [0]
if (n === 2) return [0, 1]
return [0, 1, 2, 3]
})
</script>
<template>
<svg
class="song-seal"
:width="size"
:height="size"
:viewBox="round ? '0 0 64 64' : '0 0 72 72'"
role="img"
:aria-label="`印章:${text}`"
:style="{ transform: `rotate(${rotate}deg)` }"
>
<!-- 方印田字界格 + 2×2 文字 -->
<g v-if="!round" fill="none" stroke="currentColor">
<rect x="2" y="2" width="68" height="68" rx="3" />
<line v-if="fd.length > 1" x1="36" y1="2" x2="36" y2="70" />
<line v-if="fd.length > 2" x1="2" y1="36" x2="70" y2="36" />
</g>
<!-- 圆印连珠双圈 + 单字 -->
<g v-else fill="none" stroke="currentColor">
<circle cx="32" cy="32" r="28" />
<circle cx="32" cy="32" r="21" />
</g>
<g
v-if="!round"
fill="currentColor"
fill-opacity="0.88"
font-family="var(--song-font-serif)"
font-weight="600"
text-anchor="middle"
dominant-baseline="central"
>
<text
v-for="(c, i) in chars"
:key="i"
:x="36 + (i % 2 === 0 ? -18 : 18)"
:y="36 + (i < 2 ? -18 : 18)"
opacity="0.72"
>{{ c }}</text>
</g>
<text
v-else
x="32"
y="32"
fill="currentColor"
fill-opacity="0.88"
font-family="var(--song-font-serif)"
font-weight="600"
font-size="22"
text-anchor="middle"
dominant-baseline="central"
>{{ chars[0] }}</text>
</svg>
</template>
<style scoped>
.song-seal {
color: var(--song-seal, #a84635);
display: block;
opacity: 0.9;
transition: transform var(--song-dur-2) var(--song-ease);
}
.song-seal:hover {
opacity: 1;
transform: rotate(-1deg) !important;
}
</style>
Loading…
Cancel
Save