fix: 升级了一些视觉、各种单图相册效果、日历效果、多种模板任选
This commit is contained in:
@@ -29,9 +29,13 @@ var configSectionKeys = map[string][]string{
|
||||
"copy": {
|
||||
"formalText1", "formalText2", "formalPageHeight",
|
||||
},
|
||||
"theme": {
|
||||
"siteTheme", "sectionIndexEnabled", "calendarStyle", "endingFrameStyle",
|
||||
},
|
||||
"visual": {
|
||||
"introExitEffect", "scrollMode", "freeScrollInterval", "pageSwitchDuration", "firstScreenDuration",
|
||||
"petalEnabled", "bubbleEnabled", "introEnabled", "danmakuEnabled", "danmakuShowTime", "nineGridAnimate",
|
||||
"giftEffectMode",
|
||||
},
|
||||
"music": {
|
||||
"musicList", "activeMusicUrl", "musicPlayMode", "musicRandomEnabled",
|
||||
|
||||
@@ -10,13 +10,18 @@ const props = defineProps({
|
||||
bubbleEnabled: { type: Boolean, default: true },
|
||||
/** 预览/抽屉等遮罩时关闭粒子绘制 */
|
||||
active: { type: Boolean, default: true },
|
||||
/** 主题 fx 色板注入:空则用默认暖彩 */
|
||||
petalPalette: { type: Array, default: null },
|
||||
heartPalette: { type: Array, default: null },
|
||||
})
|
||||
|
||||
const canvasRef = ref(null)
|
||||
const petals = []
|
||||
const hearts = []
|
||||
const petalColors = ['#f7dce3', '#fbeee3', '#f3c9d6', '#efdcc8', '#f9e7d6']
|
||||
const heartColors = ['#c9b08a', '#e2b3c0', '#d4af37', '#e8c9d2']
|
||||
const DEFAULT_PETALS = ['#f7dce3', '#fbeee3', '#f3c9d6', '#efdcc8', '#f9e7d6']
|
||||
const DEFAULT_HEARTS = ['#c9b08a', '#e2b3c0', '#d4af37', '#e8c9d2']
|
||||
const petalColorsOf = () => (props.petalPalette?.length ? props.petalPalette : DEFAULT_PETALS)
|
||||
const heartColorsOf = () => (props.heartPalette?.length ? props.heartPalette : DEFAULT_HEARTS)
|
||||
|
||||
let ctx = null
|
||||
let rafId = 0
|
||||
@@ -39,7 +44,8 @@ function resetPetal(p, initial = false) {
|
||||
p.rotation = rand(0, Math.PI * 2)
|
||||
p.rotationSpeed = rand(-0.9, 0.9)
|
||||
p.opacity = rand(0.28, 0.7)
|
||||
p.color = petalColors[Math.floor(rand(0, petalColors.length))]
|
||||
const pc = petalColorsOf()
|
||||
p.color = pc[Math.floor(rand(0, pc.length))]
|
||||
}
|
||||
|
||||
function resetHeart(h, initial = false) {
|
||||
@@ -50,7 +56,8 @@ function resetHeart(h, initial = false) {
|
||||
h.sway = rand(8, 26)
|
||||
h.phase = rand(0, Math.PI * 2)
|
||||
h.opacity = rand(0.1, 0.32)
|
||||
h.color = heartColors[Math.floor(rand(0, heartColors.length))]
|
||||
const hc = heartColorsOf()
|
||||
h.color = hc[Math.floor(rand(0, hc.length))]
|
||||
}
|
||||
|
||||
function seedParticles() {
|
||||
|
||||
@@ -30,7 +30,7 @@ const GLOW_BLUR = isIOS ? 4 : 10
|
||||
const GLOW_ALPHA = 0.55
|
||||
const DPR_CAP = isIOS ? 1.5 : 2
|
||||
|
||||
/** 多色爱心:主色 */
|
||||
/** 多色爱心:主色(可被主题 fx 色板覆盖) */
|
||||
const HEART_COLORS = [
|
||||
'#ff2442', '#ff2d55', '#ff4770', '#ff6b8a',
|
||||
'#f472b6', '#ec4899', '#e879f9', '#c084fc',
|
||||
@@ -47,6 +47,14 @@ const GLOW_COLORS = [
|
||||
'#fdba74', '#f5d0b0', '#ffffff',
|
||||
]
|
||||
|
||||
const props = defineProps({
|
||||
/** 主题色板注入:空则用默认 */
|
||||
colors: { type: Array, default: null },
|
||||
glowColors: { type: Array, default: null },
|
||||
})
|
||||
const heartPalette = () => (props.colors?.length ? props.colors : HEART_COLORS)
|
||||
const glowPalette = () => (props.glowColors?.length ? props.glowColors : GLOW_COLORS)
|
||||
|
||||
const pick = (arr) => arr[Math.floor(Math.random() * arr.length)]
|
||||
const rand = (min, max) => min + Math.random() * (max - min)
|
||||
|
||||
@@ -199,8 +207,8 @@ function createHeart(x, y, now) {
|
||||
birthTime: now,
|
||||
maxAge: rand(HEART_MIN_LIFE, HEART_MAX_LIFE),
|
||||
baseSize: rand(HEART_MIN_SIZE, HEART_MAX_SIZE),
|
||||
color: pick(HEART_COLORS),
|
||||
glowColor: pick(GLOW_COLORS),
|
||||
color: pick(heartPalette()),
|
||||
glowColor: pick(glowPalette()),
|
||||
peakScale: rand(0.75, 1.35),
|
||||
riseSpeed: rand(RISE_SPEED_MIN, RISE_SPEED_MAX),
|
||||
driftAmp: rand(DRIFT_AMP_MIN, DRIFT_AMP_MAX),
|
||||
|
||||
@@ -214,8 +214,8 @@
|
||||
:class="{ 'g-heart-done': heartDone }"
|
||||
>
|
||||
<div
|
||||
v-for="(slot, imgIdx) in HEART_SLOTS"
|
||||
:key="imgIdx"
|
||||
v-for="(slot, imgIdx) in heartSlots"
|
||||
:key="`${page.heartLayout || 'classic'}-${imgIdx}`"
|
||||
class="g-heart-slot"
|
||||
:class="{ 'g-heart-placeholder': imgIdx >= shownCount }"
|
||||
:style="heartSlotStyle(slot)"
|
||||
@@ -225,7 +225,7 @@
|
||||
v-reveal="{ variant: heartReveal(imgIdx), immediate: true }"
|
||||
class="absolute inset-0 g-heart-cell"
|
||||
:class="frameClass"
|
||||
:style="heartCellShape(imgIdx)"
|
||||
:style="heartCellShape(slot)"
|
||||
>
|
||||
<img
|
||||
:src="displayAt(imgIdx)"
|
||||
@@ -309,7 +309,7 @@
|
||||
import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue'
|
||||
import { borderClass, borderStyleMeta } from '@/composables/borderStyles'
|
||||
import { createSequentialMount } from '@/composables/useSequentialMount'
|
||||
import { HEART_SLOTS, heartSlotStyle, heartFocusCssAt, heartCellShape } from '@/composables/heartCollage'
|
||||
import { heartSlotsFor, heartSlotStyle, heartFocusCssAt, heartCellShape } from '@/composables/heartCollage'
|
||||
import SectionHeading from '@/components/themes/SectionHeading.vue'
|
||||
|
||||
const NINE_STEP_MS = 300
|
||||
@@ -735,16 +735,21 @@ function polaroidTransform(i) {
|
||||
}
|
||||
|
||||
/** 爱心:左右对称入场,中轴与心尖用缩放 */
|
||||
/** 当前爱心拼接方案的槽位(page.heartLayout 可选,classic 默认) */
|
||||
const heartSlots = computed(() => heartSlotsFor(props.page))
|
||||
|
||||
function heartReveal(i) {
|
||||
if (i === 5 || i === 12) return 'scale'
|
||||
if (i === 0 || i === 2 || i === 3 || i === 4 || i === 9) return 'left'
|
||||
if (i === 1 || i === 6 || i === 7 || i === 8 || i === 11) return 'right'
|
||||
return 'up'
|
||||
// 按槽位横向位置决定入场方向:左半从左入、右半从右入、中轴缩放
|
||||
const slot = heartSlots.value[i]
|
||||
if (!slot) return 'up'
|
||||
const cx = slot.l + slot.w / 2
|
||||
if (cx > 42 && cx < 58) return 'scale'
|
||||
return cx <= 42 ? 'left' : 'right'
|
||||
}
|
||||
|
||||
/** 爱心拼合完成:触发一次心跳 + 小心心飘散 */
|
||||
const heartDone = computed(() =>
|
||||
props.page?.type === 'heart-collage' && shownCount.value >= HEART_SLOTS.length
|
||||
props.page?.type === 'heart-collage' && shownCount.value >= heartSlots.value.length
|
||||
)
|
||||
|
||||
</script>
|
||||
|
||||
@@ -201,6 +201,156 @@ html[data-theme='pixel'] .sh-note {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* —— 水墨:楷体 + 墨横 + 朱砂点 —— */
|
||||
html[data-theme='inkwash'] .sh-kicker {
|
||||
font-family: var(--font-kai);
|
||||
letter-spacing: 0.4em;
|
||||
}
|
||||
html[data-theme='inkwash'] .sh-title {
|
||||
font-family: var(--font-kai);
|
||||
letter-spacing: 0.36em;
|
||||
text-indent: 0.36em;
|
||||
}
|
||||
html[data-theme='inkwash'] .sh-rule-bottom {
|
||||
display: block;
|
||||
width: 46px;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, transparent, var(--c-ink) 30%, var(--c-ink) 70%, transparent);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* —— 电影:场记板 SCENE —— */
|
||||
html[data-theme='cinema'] .sh-index {
|
||||
display: block;
|
||||
font-family: var(--font-number);
|
||||
font-style: normal;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.2em;
|
||||
color: var(--c-on-primary);
|
||||
background: var(--c-primary);
|
||||
padding: 3px 10px;
|
||||
border-radius: 3px;
|
||||
transform: rotate(-2deg);
|
||||
}
|
||||
html[data-theme='cinema'] .sh-index::before {
|
||||
content: 'SCENE ';
|
||||
}
|
||||
html[data-theme='cinema'] .sh-title {
|
||||
font-family: var(--font-serif);
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.2em;
|
||||
}
|
||||
html[data-theme='cinema'] .sh-kicker {
|
||||
font-family: var(--font-cinzel);
|
||||
}
|
||||
|
||||
/* —— 胶片:帧号标记 —— */
|
||||
html[data-theme='kodak'] .sh-index {
|
||||
display: block;
|
||||
font-family: var(--font-number);
|
||||
font-style: normal;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.16em;
|
||||
color: #ff9a3c;
|
||||
text-shadow: 0 0 6px rgba(255, 130, 30, 0.55);
|
||||
}
|
||||
html[data-theme='kodak'] .sh-index::before {
|
||||
content: '▸ ';
|
||||
}
|
||||
html[data-theme='kodak'] .sh-index::after {
|
||||
content: 'A';
|
||||
}
|
||||
html[data-theme='kodak'] .sh-title {
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.14em;
|
||||
}
|
||||
|
||||
/* —— 文艺:罗马分幕 —— */
|
||||
html[data-theme='arthouse'] .sh {
|
||||
gap: 10px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
html[data-theme='arthouse'] .sh-kicker {
|
||||
font-family: var(--font-display);
|
||||
letter-spacing: 0.56em;
|
||||
}
|
||||
html[data-theme='arthouse'] .sh-title {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.3em;
|
||||
text-indent: 0.3em;
|
||||
}
|
||||
html[data-theme='arthouse'] .sh-rule-bottom {
|
||||
display: block;
|
||||
width: 1px;
|
||||
height: 22px;
|
||||
background: rgba(var(--c-line-rgb), 0.6);
|
||||
}
|
||||
|
||||
/* —— 旅行:邮戳圆章 —— */
|
||||
html[data-theme='passport'] .sh-index {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-family: var(--font-number);
|
||||
font-style: normal;
|
||||
font-size: 13px;
|
||||
color: var(--c-primary);
|
||||
border: 2px solid rgba(var(--c-primary-rgb), 0.55);
|
||||
border-radius: 50%;
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
html[data-theme='passport'] .sh-kicker {
|
||||
font-family: var(--font-number);
|
||||
letter-spacing: 0.34em;
|
||||
}
|
||||
html[data-theme='passport'] .sh-title {
|
||||
letter-spacing: 0.22em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* —— 手账:标签贴 + 胶带 —— */
|
||||
html[data-theme='scrapbook'] .sh-title {
|
||||
font-family: var(--font-longcang);
|
||||
padding: 6px 20px;
|
||||
background: var(--c-card);
|
||||
border: 1px dashed rgba(var(--c-primary-rgb), 0.45);
|
||||
border-radius: 6px;
|
||||
transform: rotate(-1.4deg);
|
||||
box-shadow: 0 6px 16px rgba(69, 58, 48, 0.1);
|
||||
}
|
||||
html[data-theme='scrapbook'] .sh-kicker {
|
||||
font-family: var(--font-liujian);
|
||||
text-transform: none;
|
||||
letter-spacing: 0.18em;
|
||||
}
|
||||
|
||||
/* —— Y2K:窗口标题条 —— */
|
||||
html[data-theme='y2k'] .sh-title {
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.1em;
|
||||
background: linear-gradient(180deg, #7a4ae0 10%, #4a90e0 50%, #e04ad0 100%);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
html[data-theme='y2k'] .sh-kicker {
|
||||
font-family: var(--font-pixel);
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.2em;
|
||||
color: var(--c-line);
|
||||
text-transform: none;
|
||||
}
|
||||
html[data-theme='y2k'] .sh-rule-bottom {
|
||||
display: block;
|
||||
width: 60px;
|
||||
height: 4px;
|
||||
border-radius: 2px;
|
||||
background: repeating-linear-gradient(90deg, #7a4ae0 0 8px, #4adce0 8px 16px);
|
||||
}
|
||||
|
||||
/* —— 极简主义:小字 + 一条细线 + 大留白 —— */
|
||||
html[data-theme='zen'] .sh {
|
||||
gap: 12px;
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div class="ca-root">
|
||||
<span v-reveal="'down'" class="ca-chapter">CHAPITRE UN</span>
|
||||
|
||||
<div class="ca-mid">
|
||||
<i v-reveal="'down'" class="ca-line" aria-hidden="true"></i>
|
||||
<div v-reveal="{ variant: 'scale', delay: 180 }" class="ca-photo">
|
||||
<img :src="heroImg" class="ca-img" fetchpriority="high" decoding="async" alt="封面" />
|
||||
</div>
|
||||
<i v-reveal="'up'" class="ca-line" aria-hidden="true"></i>
|
||||
</div>
|
||||
|
||||
<div v-reveal="{ variant: 'up', delay: 320 }" class="ca-copy">
|
||||
<span class="ca-names">{{ groom }} <em>et</em> {{ bride }}</span>
|
||||
<span class="ca-date">{{ date }} — {{ lunar }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/** 文艺影调封面:严格对称构图、上下轴线、法式分幕字 */
|
||||
defineProps({
|
||||
heroImg: { type: String, default: '' },
|
||||
groom: { type: String, default: '' },
|
||||
bride: { type: String, default: '' },
|
||||
date: { type: String, default: '' },
|
||||
lunar: { type: String, default: '' },
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ca-root {
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
padding: max(40px, env(safe-area-inset-top)) 30px 48px;
|
||||
background: var(--c-bg);
|
||||
overflow: hidden;
|
||||
}
|
||||
.ca-chapter {
|
||||
font-family: var(--font-display);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.66em;
|
||||
text-indent: 0.66em;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
.ca-mid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
.ca-line {
|
||||
width: 1px;
|
||||
height: 30px;
|
||||
background: rgba(var(--c-line-rgb), 0.7);
|
||||
}
|
||||
.ca-photo {
|
||||
width: min(58vw, 215px);
|
||||
aspect-ratio: 4 / 5;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ca-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
filter: saturate(0.82) contrast(1.03);
|
||||
}
|
||||
.ca-copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.ca-names {
|
||||
font-family: var(--font-display);
|
||||
font-size: clamp(23px, 7vw, 30px);
|
||||
letter-spacing: 0.16em;
|
||||
color: var(--c-ink);
|
||||
}
|
||||
.ca-names em {
|
||||
font-style: italic;
|
||||
font-size: 0.62em;
|
||||
color: var(--c-muted);
|
||||
margin: 0 0.3em;
|
||||
}
|
||||
.ca-date {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.3em;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
</style>
|
||||
152
vite-tailwindcss/src/components/themes/covers/CoverCinema.vue
Normal file
152
vite-tailwindcss/src/components/themes/covers/CoverCinema.vue
Normal file
@@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<div class="cc-root">
|
||||
<div v-reveal="'down'" class="cc-top">
|
||||
<span class="cc-badge">NOW SHOWING</span>
|
||||
<span class="cc-studio">LOVE PICTURES 出品</span>
|
||||
</div>
|
||||
|
||||
<div v-reveal="{ variant: 'scale', delay: 150 }" class="cc-poster">
|
||||
<img :src="heroImg" class="cc-img" fetchpriority="high" decoding="async" alt="封面" />
|
||||
<span class="cc-poster-veil" aria-hidden="true"></span>
|
||||
<span class="cc-corner" aria-hidden="true">彩色 · 宽银幕</span>
|
||||
</div>
|
||||
|
||||
<div v-reveal="{ variant: 'up', delay: 280 }" class="cc-title-wrap">
|
||||
<span class="cc-title">THE WEDDING</span>
|
||||
<span class="cc-starring">领衔主演</span>
|
||||
<span class="cc-names">{{ groom }} · {{ bride }}</span>
|
||||
</div>
|
||||
|
||||
<div v-reveal="{ variant: 'up', delay: 380 }" class="cc-release">
|
||||
<i aria-hidden="true"></i>
|
||||
<span>{{ date }} 隆重献映 · {{ lunar }}</span>
|
||||
<i aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/** 复古电影海报封面:NOW SHOWING 角标 + 主演字幕 + 上映日期 */
|
||||
defineProps({
|
||||
heroImg: { type: String, default: '' },
|
||||
groom: { type: String, default: '' },
|
||||
bride: { type: String, default: '' },
|
||||
date: { type: String, default: '' },
|
||||
lunar: { type: String, default: '' },
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cc-root {
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
padding: max(24px, env(safe-area-inset-top)) 26px 34px;
|
||||
background:
|
||||
var(--t-bg-texture),
|
||||
radial-gradient(ellipse at 50% 0%, rgba(212, 162, 74, 0.16), transparent 55%),
|
||||
var(--c-bg);
|
||||
overflow: hidden;
|
||||
}
|
||||
.cc-top {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.cc-badge {
|
||||
padding: 4px 10px;
|
||||
font-family: var(--font-cinzel);
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.24em;
|
||||
color: var(--c-on-primary);
|
||||
background: var(--c-primary);
|
||||
border-radius: 3px;
|
||||
transform: rotate(-3deg);
|
||||
box-shadow: 2px 3px 0 rgba(58, 44, 34, 0.25);
|
||||
}
|
||||
.cc-studio {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.2em;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
.cc-poster {
|
||||
position: relative;
|
||||
width: min(70vw, 260px);
|
||||
aspect-ratio: 3 / 4;
|
||||
border: 3px solid var(--c-ink);
|
||||
outline: 1px solid rgba(var(--c-line-rgb), 0.8);
|
||||
outline-offset: 5px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 18px 44px rgba(58, 44, 34, 0.3);
|
||||
}
|
||||
.cc-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
filter: sepia(0.16) saturate(1.12) contrast(1.04);
|
||||
}
|
||||
.cc-poster-veil {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(180deg, transparent 62%, rgba(38, 24, 16, 0.42));
|
||||
pointer-events: none;
|
||||
}
|
||||
.cc-corner {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
bottom: 8px;
|
||||
font-family: var(--font-serif);
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.16em;
|
||||
color: rgba(248, 240, 226, 0.92);
|
||||
}
|
||||
.cc-title-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
.cc-title {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(30px, 9.4vw, 42px);
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--c-primary);
|
||||
text-shadow: 2px 2px 0 rgba(212, 162, 74, 0.4);
|
||||
}
|
||||
.cc-starring {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.5em;
|
||||
text-indent: 0.5em;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
.cc-names {
|
||||
font-family: var(--font-serif);
|
||||
font-weight: 700;
|
||||
font-size: clamp(19px, 5.6vw, 24px);
|
||||
letter-spacing: 0.2em;
|
||||
color: var(--c-ink);
|
||||
}
|
||||
.cc-release {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
font-family: var(--font-serif);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.16em;
|
||||
color: var(--c-ink-soft);
|
||||
}
|
||||
.cc-release i {
|
||||
width: 34px;
|
||||
height: 1px;
|
||||
background: rgba(var(--c-line-rgb), 0.8);
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex flex-col items-center justify-between py-12 flex-1 w-full relative border-[0.5px] border-[var(--c-primary)]/20 bg-[var(--c-card)]/40">
|
||||
<div class="flex flex-col items-center justify-between py-12 flex-1 w-full relative border-[0.5px] border-[var(--c-primary)]/20 bg-[var(--c-card)]/20">
|
||||
<div v-reveal="'down'" class="text-center mt-6 z-10">
|
||||
<span class="cover-kicker calligraphy text-[10px] tracking-[0.5em] uppercase text-[var(--c-primary)] font-bold block">Wedding Invitation</span>
|
||||
<div class="w-6 h-[0.5px] bg-[var(--c-primary)] mx-auto opacity-50 mt-4"></div>
|
||||
|
||||
114
vite-tailwindcss/src/components/themes/covers/CoverInkwash.vue
Normal file
114
vite-tailwindcss/src/components/themes/covers/CoverInkwash.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="ci-root">
|
||||
<svg class="ci-mount" viewBox="0 0 400 160" preserveAspectRatio="none" aria-hidden="true">
|
||||
<path d="M0 128C60 84 120 108 170 92 230 72 260 108 320 96 360 88 380 104 400 96V160H0Z" fill="var(--c-primary)" fill-opacity="0.1" />
|
||||
<path d="M0 144C80 112 150 136 220 118 290 100 340 128 400 116V160H0Z" fill="var(--c-primary)" fill-opacity="0.16" />
|
||||
</svg>
|
||||
|
||||
<span v-reveal="'down'" class="ci-kicker">山 水 有 相 逢</span>
|
||||
|
||||
<div v-reveal="{ variant: 'scale', delay: 160 }" class="ci-photo">
|
||||
<img :src="heroImg" class="ci-img" fetchpriority="high" decoding="async" alt="封面" />
|
||||
</div>
|
||||
|
||||
<div v-reveal="{ variant: 'up', delay: 280 }" class="ci-names">
|
||||
<span class="vertical-text">{{ groom }}</span>
|
||||
<b class="ci-seal" aria-hidden="true">囍</b>
|
||||
<span class="vertical-text">{{ bride }}</span>
|
||||
</div>
|
||||
|
||||
<div v-reveal="{ variant: 'up', delay: 360 }" class="ci-date">{{ date }} · {{ lunar }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/** 水墨山水封面:宣纸底 + 泼墨山影 + 竖排名 + 朱砂印 */
|
||||
defineProps({
|
||||
heroImg: { type: String, default: '' },
|
||||
groom: { type: String, default: '' },
|
||||
bride: { type: String, default: '' },
|
||||
date: { type: String, default: '' },
|
||||
lunar: { type: String, default: '' },
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ci-root {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 26px;
|
||||
padding: max(30px, env(safe-area-inset-top)) 26px 90px;
|
||||
background: var(--c-bg);
|
||||
overflow: hidden;
|
||||
}
|
||||
.ci-mount {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
pointer-events: none;
|
||||
}
|
||||
.ci-kicker {
|
||||
font-family: var(--font-kai);
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.6em;
|
||||
text-indent: 0.6em;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
.ci-photo {
|
||||
width: min(62vw, 235px);
|
||||
aspect-ratio: 3 / 4;
|
||||
padding: 8px;
|
||||
background: var(--c-card);
|
||||
border: 1px solid rgba(var(--c-primary-rgb), 0.28);
|
||||
box-shadow: 0 16px 40px rgba(42, 46, 42, 0.14);
|
||||
}
|
||||
.ci-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
filter: saturate(0.85) contrast(1.02);
|
||||
}
|
||||
.ci-names {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 20px;
|
||||
font-family: var(--font-kai);
|
||||
font-size: clamp(21px, 6vw, 26px);
|
||||
letter-spacing: 0.3em;
|
||||
color: var(--c-ink);
|
||||
}
|
||||
.ci-names .vertical-text {
|
||||
writing-mode: vertical-rl;
|
||||
max-height: 120px;
|
||||
}
|
||||
.ci-seal {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 6px;
|
||||
font-family: var(--font-kai);
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
color: #fdf6f0;
|
||||
background: #b5493f;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.ci-date {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.24em;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
</style>
|
||||
127
vite-tailwindcss/src/components/themes/covers/CoverKodak.vue
Normal file
127
vite-tailwindcss/src/components/themes/covers/CoverKodak.vue
Normal file
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<div class="ck-root">
|
||||
<div v-reveal="'down'" class="ck-strip" aria-hidden="true">
|
||||
<i></i><i></i><i></i>
|
||||
</div>
|
||||
|
||||
<div v-reveal="{ variant: 'scale', delay: 150 }" class="ck-frame">
|
||||
<span class="ck-holes ck-holes-top" aria-hidden="true"></span>
|
||||
<div class="ck-photo">
|
||||
<img :src="heroImg" class="ck-img" fetchpriority="high" decoding="async" alt="封面" />
|
||||
<span class="ck-stamp">{{ dateStamp }}</span>
|
||||
</div>
|
||||
<span class="ck-holes ck-holes-bottom" aria-hidden="true"></span>
|
||||
<span class="ck-label">WEDDING FILM · 400</span>
|
||||
</div>
|
||||
|
||||
<div v-reveal="{ variant: 'up', delay: 300 }" class="ck-names">{{ groom }} ♥ {{ bride }}</div>
|
||||
<div v-reveal="{ variant: 'up', delay: 380 }" class="ck-sub">{{ date }} · {{ lunar }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
/** 胶片日记封面:齿孔胶片框 + 复古日期水印 + 柯达色条 */
|
||||
const props = defineProps({
|
||||
heroImg: { type: String, default: '' },
|
||||
groom: { type: String, default: '' },
|
||||
bride: { type: String, default: '' },
|
||||
date: { type: String, default: '' },
|
||||
lunar: { type: String, default: '' },
|
||||
})
|
||||
|
||||
/** 提取 'YY MM DD 形式的复古水印 */
|
||||
const dateStamp = computed(() => {
|
||||
const m = /(\d{4})年(\d{1,2})月(\d{1,2})日/.exec(props.date || '')
|
||||
if (!m) return "'26 09 04"
|
||||
return `'${m[1].slice(2)} ${m[2].padStart(2, '0')} ${m[3].padStart(2, '0')}`
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ck-root {
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 24px;
|
||||
padding: max(28px, env(safe-area-inset-top)) 26px 40px;
|
||||
background:
|
||||
var(--t-bg-texture),
|
||||
var(--c-bg);
|
||||
overflow: hidden;
|
||||
}
|
||||
/* 柯达色条 */
|
||||
.ck-strip {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
.ck-strip i {
|
||||
width: 26px;
|
||||
height: 8px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.ck-strip i:nth-child(1) { background: #e0782e; }
|
||||
.ck-strip i:nth-child(2) { background: #d84438; }
|
||||
.ck-strip i:nth-child(3) { background: #e8b02e; }
|
||||
/* 胶片框 */
|
||||
.ck-frame {
|
||||
width: min(76vw, 280px);
|
||||
background: #201d18;
|
||||
border-radius: 8px;
|
||||
padding: 6px 10px 8px;
|
||||
box-shadow: 0 18px 42px rgba(56, 48, 42, 0.34);
|
||||
}
|
||||
.ck-holes {
|
||||
display: block;
|
||||
height: 9px;
|
||||
margin: 4px 2px;
|
||||
background-image: repeating-linear-gradient(90deg, rgba(250, 246, 236, 0.9) 0 10px, transparent 10px 21px);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.ck-photo {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ck-img {
|
||||
width: 100%;
|
||||
aspect-ratio: 3 / 4;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
filter: sepia(0.18) saturate(1.15) contrast(1.02) brightness(1.02);
|
||||
}
|
||||
/* 复古橙色日期水印 */
|
||||
.ck-stamp {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
bottom: 8px;
|
||||
font-family: var(--font-number);
|
||||
font-size: 15px;
|
||||
letter-spacing: 0.08em;
|
||||
color: #ff9a3c;
|
||||
text-shadow: 0 0 7px rgba(255, 130, 30, 0.75);
|
||||
}
|
||||
.ck-label {
|
||||
display: block;
|
||||
text-align: center;
|
||||
padding-top: 3px;
|
||||
font-size: 8.5px;
|
||||
letter-spacing: 0.3em;
|
||||
color: rgba(250, 246, 236, 0.6);
|
||||
}
|
||||
.ck-names {
|
||||
font-size: clamp(21px, 6.4vw, 27px);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.14em;
|
||||
color: var(--c-ink);
|
||||
}
|
||||
.ck-sub {
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.2em;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
</style>
|
||||
209
vite-tailwindcss/src/components/themes/covers/CoverPassport.vue
Normal file
209
vite-tailwindcss/src/components/themes/covers/CoverPassport.vue
Normal file
@@ -0,0 +1,209 @@
|
||||
<template>
|
||||
<div class="cpp-root">
|
||||
<div v-reveal="'down'" class="cpp-head">
|
||||
<span class="cpp-air">LOVE AIRLINES</span>
|
||||
<span class="cpp-class">FIRST CLASS</span>
|
||||
</div>
|
||||
|
||||
<div v-reveal="{ variant: 'scale', delay: 150 }" class="cpp-ticket">
|
||||
<div class="cpp-photo">
|
||||
<img :src="heroImg" class="cpp-img" fetchpriority="high" decoding="async" alt="封面" />
|
||||
</div>
|
||||
<div class="cpp-info">
|
||||
<div class="cpp-route">
|
||||
<span class="cpp-port"><b>SGL</b><i>单身</i></span>
|
||||
<span class="cpp-plane" aria-hidden="true">
|
||||
<svg viewBox="0 0 90 20"><path d="M2 12h30M58 12h30" stroke="var(--c-line)" stroke-width="1" stroke-dasharray="3 3" fill="none"/><path d="M40 10l12 2-12 2 3-2z" fill="var(--c-primary)"/></svg>
|
||||
</span>
|
||||
<span class="cpp-port"><b>MRD</b><i>已婚</i></span>
|
||||
</div>
|
||||
<div class="cpp-row">
|
||||
<span class="cpp-cell"><i>PASSENGERS</i><b>{{ groom }} / {{ bride }}</b></span>
|
||||
</div>
|
||||
<div class="cpp-row">
|
||||
<span class="cpp-cell"><i>DATE</i><b>{{ date }}</b></span>
|
||||
<span class="cpp-cell"><i>GATE</i><b>FOREVER</b></span>
|
||||
</div>
|
||||
<div class="cpp-barcode" aria-hidden="true"><i v-for="n in 22" :key="n"></i></div>
|
||||
</div>
|
||||
<span class="cpp-stamp" aria-hidden="true">APPROVED ♥</span>
|
||||
</div>
|
||||
|
||||
<div v-reveal="{ variant: 'up', delay: 320 }" class="cpp-foot">{{ lunar }} · 一起飞往余生</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/** 环球旅行封面:登机牌(单身 → 已婚 航线、乘客栏、条码、盖章) */
|
||||
defineProps({
|
||||
heroImg: { type: String, default: '' },
|
||||
groom: { type: String, default: '' },
|
||||
bride: { type: String, default: '' },
|
||||
date: { type: String, default: '' },
|
||||
lunar: { type: String, default: '' },
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cpp-root {
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 26px;
|
||||
padding: max(28px, env(safe-area-inset-top)) 22px 40px;
|
||||
background:
|
||||
var(--t-bg-texture),
|
||||
var(--c-bg);
|
||||
overflow: hidden;
|
||||
}
|
||||
.cpp-head {
|
||||
width: min(100%, 330px);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-family: var(--font-number);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.26em;
|
||||
color: var(--c-primary);
|
||||
}
|
||||
.cpp-class {
|
||||
color: var(--c-line);
|
||||
}
|
||||
.cpp-ticket {
|
||||
position: relative;
|
||||
width: min(100%, 330px);
|
||||
border-radius: 12px;
|
||||
background: var(--c-card);
|
||||
border: 1px solid rgba(var(--c-primary-rgb), 0.3);
|
||||
box-shadow: 0 16px 40px rgba(44, 54, 64, 0.16);
|
||||
overflow: hidden;
|
||||
}
|
||||
/* 登机牌撕边 */
|
||||
.cpp-ticket::before,
|
||||
.cpp-ticket::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 58%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: var(--c-bg);
|
||||
border: 1px solid rgba(var(--c-primary-rgb), 0.3);
|
||||
}
|
||||
.cpp-ticket::before { left: -9px; }
|
||||
.cpp-ticket::after { right: -9px; }
|
||||
.cpp-photo {
|
||||
height: 168px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.cpp-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.cpp-info {
|
||||
padding: 14px 16px 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.cpp-route {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
.cpp-port {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
.cpp-port b {
|
||||
font-family: var(--font-number);
|
||||
font-size: 24px;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--c-primary);
|
||||
}
|
||||
.cpp-port i {
|
||||
font-style: normal;
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.2em;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
.cpp-plane {
|
||||
flex: 1;
|
||||
}
|
||||
.cpp-plane svg {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
display: block;
|
||||
}
|
||||
.cpp-row {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
border-top: 1px dashed rgba(var(--c-primary-rgb), 0.25);
|
||||
padding-top: 9px;
|
||||
}
|
||||
.cpp-cell {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
}
|
||||
.cpp-cell i {
|
||||
font-style: normal;
|
||||
font-family: var(--font-number);
|
||||
font-size: 8px;
|
||||
letter-spacing: 0.22em;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
.cpp-cell b {
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--c-ink);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.cpp-barcode {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 2px;
|
||||
height: 20px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.cpp-barcode i {
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background: var(--c-ink);
|
||||
opacity: 0.8;
|
||||
}
|
||||
.cpp-barcode i:nth-child(2n) { height: 72%; width: 1px; }
|
||||
.cpp-barcode i:nth-child(3n) { height: 86%; width: 3px; }
|
||||
/* 邮戳 */
|
||||
.cpp-stamp {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 130px;
|
||||
padding: 5px 10px;
|
||||
font-family: var(--font-number);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.14em;
|
||||
color: #b5493f;
|
||||
border: 2px solid #b5493f;
|
||||
border-radius: 6px;
|
||||
transform: rotate(-12deg);
|
||||
opacity: 0.85;
|
||||
background: rgba(255, 252, 246, 0.55);
|
||||
}
|
||||
.cpp-foot {
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.24em;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
</style>
|
||||
157
vite-tailwindcss/src/components/themes/covers/CoverScrapbook.vue
Normal file
157
vite-tailwindcss/src/components/themes/covers/CoverScrapbook.vue
Normal file
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div class="cs-root">
|
||||
<span v-reveal="'down'" class="cs-kicker">○ my wedding journal ○</span>
|
||||
|
||||
<div v-reveal="{ variant: 'scale', delay: 150 }" class="cs-photo-wrap">
|
||||
<span class="cs-tape cs-tape-l" aria-hidden="true"></span>
|
||||
<span class="cs-tape cs-tape-r" aria-hidden="true"></span>
|
||||
<div class="cs-photo">
|
||||
<img :src="heroImg" class="cs-img" fetchpriority="high" decoding="async" alt="封面" />
|
||||
<span class="cs-caption">us ♥</span>
|
||||
</div>
|
||||
<span class="cs-sticker cs-sticker-1" aria-hidden="true">♥</span>
|
||||
<span class="cs-sticker cs-sticker-2" aria-hidden="true">★</span>
|
||||
</div>
|
||||
|
||||
<div v-reveal="{ variant: 'up', delay: 300 }" class="cs-names">
|
||||
<span>{{ groom }}</span>
|
||||
<em>&</em>
|
||||
<span>{{ bride }}</span>
|
||||
</div>
|
||||
|
||||
<div v-reveal="{ variant: 'up', delay: 380 }" class="cs-date-tag">
|
||||
<span>{{ date }}</span>
|
||||
<small>{{ lunar }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/** 手账拼贴封面:胶带斜贴拍立得 + 贴纸 + 手写标签 */
|
||||
defineProps({
|
||||
heroImg: { type: String, default: '' },
|
||||
groom: { type: String, default: '' },
|
||||
bride: { type: String, default: '' },
|
||||
date: { type: String, default: '' },
|
||||
lunar: { type: String, default: '' },
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cs-root {
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 24px;
|
||||
padding: max(28px, env(safe-area-inset-top)) 26px 42px;
|
||||
background:
|
||||
var(--t-bg-texture),
|
||||
var(--c-bg);
|
||||
overflow: hidden;
|
||||
}
|
||||
.cs-kicker {
|
||||
font-family: var(--font-liujian);
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.2em;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
.cs-photo-wrap {
|
||||
position: relative;
|
||||
transform: rotate(-2.5deg);
|
||||
}
|
||||
.cs-photo {
|
||||
width: min(64vw, 240px);
|
||||
padding: 10px 10px 34px;
|
||||
background: var(--c-card);
|
||||
box-shadow: 0 12px 32px rgba(69, 58, 48, 0.18);
|
||||
border: 1px solid rgba(69, 58, 48, 0.08);
|
||||
}
|
||||
.cs-img {
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
.cs-caption {
|
||||
display: block;
|
||||
padding-top: 8px;
|
||||
text-align: center;
|
||||
font-family: var(--font-liujian);
|
||||
font-size: 15px;
|
||||
color: var(--c-ink-soft);
|
||||
}
|
||||
.cs-tape {
|
||||
position: absolute;
|
||||
width: 74px;
|
||||
height: 24px;
|
||||
background: rgba(138, 168, 138, 0.5);
|
||||
box-shadow: 0 1px 4px rgba(69, 58, 48, 0.16);
|
||||
z-index: 2;
|
||||
}
|
||||
.cs-tape-l { top: -12px; left: -22px; transform: rotate(-38deg); }
|
||||
.cs-tape-r { top: -10px; right: -24px; transform: rotate(32deg); background: rgba(216, 168, 72, 0.45); }
|
||||
.cs-sticker {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 6px rgba(69, 58, 48, 0.24);
|
||||
}
|
||||
.cs-sticker-1 {
|
||||
right: -16px;
|
||||
bottom: 22px;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
font-size: 15px;
|
||||
background: #c47a5a;
|
||||
transform: rotate(10deg);
|
||||
}
|
||||
.cs-sticker-2 {
|
||||
left: -14px;
|
||||
bottom: 52px;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
font-size: 12px;
|
||||
background: #8aa88a;
|
||||
transform: rotate(-14deg);
|
||||
}
|
||||
.cs-names {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
font-family: var(--font-longcang);
|
||||
font-size: clamp(26px, 8vw, 34px);
|
||||
color: var(--c-ink);
|
||||
}
|
||||
.cs-names em {
|
||||
font-style: normal;
|
||||
font-size: 0.6em;
|
||||
color: var(--c-primary);
|
||||
}
|
||||
.cs-date-tag {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
padding: 8px 18px;
|
||||
background: var(--c-card);
|
||||
border: 1px dashed rgba(var(--c-primary-rgb), 0.5);
|
||||
border-radius: 8px;
|
||||
transform: rotate(1.6deg);
|
||||
font-family: var(--font-liujian);
|
||||
}
|
||||
.cs-date-tag span {
|
||||
font-size: 14px;
|
||||
color: var(--c-ink-soft);
|
||||
}
|
||||
.cs-date-tag small {
|
||||
font-size: 11px;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
</style>
|
||||
186
vite-tailwindcss/src/components/themes/covers/CoverY2k.vue
Normal file
186
vite-tailwindcss/src/components/themes/covers/CoverY2k.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<div class="cy-root">
|
||||
<div v-reveal="{ variant: 'scale', delay: 100 }" class="cy-window">
|
||||
<div class="cy-titlebar">
|
||||
<span class="cy-title-text">wedding.exe</span>
|
||||
<span class="cy-btns" aria-hidden="true"><i>▁</i><i>▢</i><i>✕</i></span>
|
||||
</div>
|
||||
<div class="cy-body">
|
||||
<div class="cy-photo">
|
||||
<img :src="heroImg" class="cy-img" fetchpriority="high" decoding="async" alt="封面" />
|
||||
</div>
|
||||
<div class="cy-names">{{ groom }} ♥ {{ bride }}</div>
|
||||
<div class="cy-date">{{ date }} ☆ {{ lunar }}</div>
|
||||
<div class="cy-progress" aria-hidden="true">
|
||||
<span class="cy-progress-label">loading love… 99%</span>
|
||||
<span class="cy-progress-track"><i></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-reveal="{ variant: 'up', delay: 320 }" class="cy-cursor" aria-hidden="true">
|
||||
<span class="cy-star">✦</span>
|
||||
<span class="cy-hint">click to continue</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/** 千禧蒸汽波封面:复古 UI 窗口(标题栏/加载条)+ 渐变镜面字 + 星星光标 */
|
||||
defineProps({
|
||||
heroImg: { type: String, default: '' },
|
||||
groom: { type: String, default: '' },
|
||||
bride: { type: String, default: '' },
|
||||
date: { type: String, default: '' },
|
||||
lunar: { type: String, default: '' },
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cy-root {
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 30px;
|
||||
padding: max(28px, env(safe-area-inset-top)) 24px 44px;
|
||||
background:
|
||||
var(--t-bg-texture),
|
||||
linear-gradient(160deg, #ece8f8 0%, #e0ecf8 48%, #f4e4f4 100%);
|
||||
overflow: hidden;
|
||||
}
|
||||
.cy-window {
|
||||
width: min(100%, 330px);
|
||||
border: 2px solid #9088b8;
|
||||
border-radius: 10px;
|
||||
background: var(--c-card);
|
||||
box-shadow:
|
||||
inset 1px 1px 0 rgba(255, 255, 255, 0.9),
|
||||
6px 8px 0 rgba(122, 74, 224, 0.18),
|
||||
0 20px 46px rgba(84, 74, 112, 0.22);
|
||||
overflow: hidden;
|
||||
}
|
||||
.cy-titlebar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 7px 10px;
|
||||
background: linear-gradient(90deg, #7a4ae0, #4a90e0 55%, #4adce0);
|
||||
color: #fff;
|
||||
}
|
||||
.cy-title-text {
|
||||
font-family: var(--font-pixel);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.cy-btns {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
.cy-btns i {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 8px;
|
||||
font-style: normal;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
color: #544a70;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.cy-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 18px 18px 20px;
|
||||
}
|
||||
.cy-photo {
|
||||
width: 100%;
|
||||
border: 2px solid #b8b0d8;
|
||||
border-radius: 6px;
|
||||
padding: 4px;
|
||||
background: #fff;
|
||||
}
|
||||
.cy-img {
|
||||
width: 100%;
|
||||
aspect-ratio: 4 / 3;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border-radius: 3px;
|
||||
filter: saturate(1.15) contrast(1.02);
|
||||
}
|
||||
/* 渐变镜面字 */
|
||||
.cy-names {
|
||||
font-size: clamp(23px, 7vw, 29px);
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.08em;
|
||||
background: linear-gradient(180deg, #7a4ae0 8%, #4a90e0 44%, #4adce0 58%, #e04ad0 100%);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
filter: drop-shadow(0 2px 0 rgba(255, 255, 255, 0.8)) drop-shadow(0 4px 10px rgba(122, 74, 224, 0.3));
|
||||
}
|
||||
.cy-date {
|
||||
font-family: var(--font-pixel);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--c-ink-soft);
|
||||
}
|
||||
.cy-progress {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.cy-progress-label {
|
||||
font-family: var(--font-pixel);
|
||||
font-size: 9px;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
.cy-progress-track {
|
||||
display: block;
|
||||
height: 12px;
|
||||
border: 2px solid #9088b8;
|
||||
border-radius: 4px;
|
||||
padding: 1px;
|
||||
background: #fff;
|
||||
}
|
||||
.cy-progress-track i {
|
||||
display: block;
|
||||
width: 99%;
|
||||
height: 100%;
|
||||
border-radius: 2px;
|
||||
background: repeating-linear-gradient(90deg, #7a4ae0 0 8px, #4adce0 8px 16px);
|
||||
animation: cyLoad 1.6s steps(8) infinite;
|
||||
}
|
||||
@keyframes cyLoad {
|
||||
0% { background-position: 0 0; }
|
||||
100% { background-position: 32px 0; }
|
||||
}
|
||||
.cy-cursor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.cy-star {
|
||||
font-size: 20px;
|
||||
color: #7a4ae0;
|
||||
animation: cyTwinkle 1.2s steps(2) infinite;
|
||||
}
|
||||
@keyframes cyTwinkle {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.45; transform: scale(0.85); }
|
||||
}
|
||||
.cy-hint {
|
||||
font-family: var(--font-pixel);
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.16em;
|
||||
color: var(--c-muted);
|
||||
}
|
||||
</style>
|
||||
@@ -16,7 +16,11 @@ export const BORDER_STYLES = [
|
||||
{ value: 'glow', label: '柔光金晕', group: 'base' },
|
||||
{ value: 'stamp', label: '邮票齿孔', group: 'postcard', shaped: true },
|
||||
{ value: 'postcard', label: '复古明信片', group: 'postcard' },
|
||||
{ value: 'arch', label: '拱门', group: 'postcard', ratio: '3 / 4' },
|
||||
{ value: 'wreath', label: '花环相框', group: 'postcard', ratio: '3 / 4' },
|
||||
{ value: 'baroque', label: '巴洛克金镜', group: 'postcard', ratio: '3 / 4' },
|
||||
{ value: 'fan', label: '折扇扇面', group: 'postcard', shaped: true, ratio: '4 / 3' },
|
||||
{ value: 'moonwin', label: '月洞圆窗', group: 'postcard', ratio: '1 / 1' },
|
||||
{ value: 'ribbon', label: '蝴蝶结缎带', group: 'postcard', ratio: '3 / 4' },
|
||||
{ value: 'oval', label: '椭圆相框', group: 'postcard', ratio: '3 / 4' },
|
||||
{ value: 'circle', label: '正圆相框', group: 'postcard', ratio: '1 / 1' },
|
||||
{ value: 'heart', label: '心形', group: 'postcard', shaped: true, ratio: '1 / 1' },
|
||||
@@ -44,12 +48,17 @@ export const BORDER_STYLE_GROUPS = [
|
||||
{ key: 'scenic', label: '横幅 · 云雾花', items: BORDER_STYLES.filter((b) => b.group === 'scenic') },
|
||||
]
|
||||
|
||||
/** 旧配置值映射:拱门样式已下线,历史数据自动呈现为花环相框 */
|
||||
const LEGACY_BORDER_MAP = { arch: 'wreath' }
|
||||
|
||||
/** 取样式元数据(未知值回落白卡纸) */
|
||||
export function borderStyleMeta(style) {
|
||||
return BORDER_STYLES.find((b) => b.value === style) || BORDER_STYLES.find((b) => b.value === 'mat')
|
||||
const v = LEGACY_BORDER_MAP[style] || style
|
||||
return BORDER_STYLES.find((b) => b.value === v) || BORDER_STYLES.find((b) => b.value === 'mat')
|
||||
}
|
||||
|
||||
export function borderClass(style) {
|
||||
const v = BORDER_STYLES.some((b) => b.value === style) ? style : 'mat'
|
||||
const mapped = LEGACY_BORDER_MAP[style] || style
|
||||
const v = BORDER_STYLES.some((b) => b.value === mapped) ? mapped : 'mat'
|
||||
return 'gf-frame gf-' + v
|
||||
}
|
||||
|
||||
@@ -1,32 +1,157 @@
|
||||
import { clampHeroFocus, heroFocusCss } from '@/composables/posterDefaults'
|
||||
|
||||
/** 爱心拼接槽位:百分比绝对定位(顶开叉、中最宽、底收尖)
|
||||
* r : 格子圆角(外缘格用大弧度圆角把方块阶梯"磨"成心形弧线)
|
||||
* clip : 格子裁切(心尖用 V 形三角收出尖端)
|
||||
* 槽位索引与角色保持不变,存量配置的图片顺序不受影响 */
|
||||
export const HEART_SLOTS = [
|
||||
{ l: 6, t: 0, w: 38, h: 14, r: '50% 50% 6px 6px' }, // 0 左上叶(半圆顶)
|
||||
{ l: 56, t: 0, w: 38, h: 14, r: '50% 50% 6px 6px' }, // 1 右上叶(半圆顶)
|
||||
{ l: 0, t: 14.8, w: 13, h: 15, r: '55% 6px 6px 6px' }, // 2 左外上(肩部外弧)
|
||||
{ l: 0, t: 30.6, w: 13, h: 14.6, r: '6px 6px 6px 55%' }, // 3 左外下(收腰起弧)
|
||||
{ l: 14, t: 14.8, w: 26, h: 30.4 }, // 4 左大图
|
||||
{ l: 41, t: 14.8, w: 18, h: 30.4 }, // 5 中轴
|
||||
{ l: 60, t: 14.8, w: 26, h: 30.4 }, // 6 右大图
|
||||
{ l: 87, t: 14.8, w: 13, h: 15, r: '6px 55% 6px 6px' }, // 7 右外上(肩部外弧)
|
||||
{ l: 87, t: 30.6, w: 13, h: 14.6, r: '6px 6px 55% 6px' }, // 8 右外下(收腰起弧)
|
||||
{ l: 8, t: 46.2, w: 16, h: 16, r: '6px 6px 6px 65%' }, // 9 左下收腰(底外弧)
|
||||
{ l: 25, t: 46.2, w: 50, h: 16 }, // 10 中下横
|
||||
{ l: 76, t: 46.2, w: 16, h: 16, r: '6px 6px 65% 6px' }, // 11 右下收腰(底外弧)
|
||||
{ l: 34, t: 63, w: 32, h: 16.5, clip: 'polygon(0 0, 100% 0, 50% 100%)' }, // 12 心尖(V 形)
|
||||
/**
|
||||
* 爱心拼接:多套拼法方案(后台可选,classic 为默认)
|
||||
* 槽位字段:
|
||||
* - l/t/w/h : 百分比绝对定位
|
||||
* - r : 圆角(弧形磨边)
|
||||
* - clip : clip-path(V 形心尖等)
|
||||
* - shape : 'heart' —— 整格心形 mask 裁切
|
||||
* - label : 后台拼图板槽位名
|
||||
*/
|
||||
|
||||
export const HEART_MASK_CSS = "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%23000' d='M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z'/%3E%3C/svg%3E\") no-repeat 50% 50% / 100% 100%"
|
||||
const HEART_MASK_SVG = HEART_MASK_CSS
|
||||
|
||||
/* —— classic 经典阶梯(v1 原始矩形,默认) —— */
|
||||
const SLOTS_CLASSIC = [
|
||||
{ l: 10, t: 0, w: 32, h: 13, label: '左上叶' },
|
||||
{ l: 58, t: 0, w: 32, h: 13, label: '右上叶' },
|
||||
{ l: 0, t: 14, w: 14, h: 15.5, label: '左外上' },
|
||||
{ l: 0, t: 30.5, w: 14, h: 15.5, label: '左外下' },
|
||||
{ l: 15, t: 14, w: 26, h: 32, label: '左大图' },
|
||||
{ l: 42, t: 14, w: 16, h: 32, label: '中轴' },
|
||||
{ l: 59, t: 14, w: 26, h: 32, label: '右大图' },
|
||||
{ l: 86, t: 14, w: 14, h: 15.5, label: '右外上' },
|
||||
{ l: 86, t: 30.5, w: 14, h: 15.5, label: '右外下' },
|
||||
{ l: 10, t: 47, w: 13, h: 16, label: '左下' },
|
||||
{ l: 24, t: 47, w: 52, h: 16, label: '中下横' },
|
||||
{ l: 77, t: 47, w: 13, h: 16, label: '右下' },
|
||||
{ l: 38, t: 64, w: 24, h: 14, label: '心尖' },
|
||||
]
|
||||
|
||||
export const HEART_SLOT_COUNT = HEART_SLOTS.length
|
||||
|
||||
export const HEART_SLOT_LABELS = [
|
||||
'左上叶', '右上叶', '左外上', '左外下', '左大图', '中轴', '右大图',
|
||||
'右外上', '右外下', '左下', '中下横', '右下', '心尖',
|
||||
/* —— soft 圆弧心(外缘大圆角 + V 形心尖) —— */
|
||||
const SLOTS_SOFT = [
|
||||
{ l: 6, t: 0, w: 38, h: 14, r: '50% 50% 6px 6px', label: '左上叶' },
|
||||
{ l: 56, t: 0, w: 38, h: 14, r: '50% 50% 6px 6px', label: '右上叶' },
|
||||
{ l: 0, t: 14.8, w: 13, h: 15, r: '55% 6px 6px 6px', label: '左外上' },
|
||||
{ l: 0, t: 30.6, w: 13, h: 14.6, r: '6px 6px 6px 55%', label: '左外下' },
|
||||
{ l: 14, t: 14.8, w: 26, h: 30.4, label: '左大图' },
|
||||
{ l: 41, t: 14.8, w: 18, h: 30.4, label: '中轴' },
|
||||
{ l: 60, t: 14.8, w: 26, h: 30.4, label: '右大图' },
|
||||
{ l: 87, t: 14.8, w: 13, h: 15, r: '6px 55% 6px 6px', label: '右外上' },
|
||||
{ l: 87, t: 30.6, w: 13, h: 14.6, r: '6px 6px 55% 6px', label: '右外下' },
|
||||
{ l: 8, t: 46.2, w: 16, h: 16, r: '6px 6px 6px 65%', label: '左下' },
|
||||
{ l: 25, t: 46.2, w: 50, h: 16, label: '中下横' },
|
||||
{ l: 76, t: 46.2, w: 16, h: 16, r: '6px 6px 65% 6px', label: '右下' },
|
||||
{ l: 34, t: 63, w: 32, h: 16.5, clip: 'polygon(0 0, 100% 0, 50% 100%)', label: '心尖' },
|
||||
]
|
||||
|
||||
/* —— ring 环绕心(中央大圆 + 12 小圆沿心形轮廓) —— */
|
||||
const SLOTS_RING = [
|
||||
{ l: 30, t: 24, w: 40, h: 32, r: '50%', label: '中央主图' },
|
||||
{ l: 43, t: 5.4, w: 14, h: 11.2, r: '50%', label: '凹口' },
|
||||
{ l: 28, t: 0, w: 14, h: 11.2, r: '50%', label: '左叶顶' },
|
||||
{ l: 58, t: 0, w: 14, h: 11.2, r: '50%', label: '右叶顶' },
|
||||
{ l: 13, t: 3.4, w: 14, h: 11.2, r: '50%', label: '左叶外' },
|
||||
{ l: 73, t: 3.4, w: 14, h: 11.2, r: '50%', label: '右叶外' },
|
||||
{ l: 3, t: 20.4, w: 14, h: 11.2, r: '50%', label: '左肩' },
|
||||
{ l: 83, t: 20.4, w: 14, h: 11.2, r: '50%', label: '右肩' },
|
||||
{ l: 9, t: 39.4, w: 14, h: 11.2, r: '50%', label: '左腰' },
|
||||
{ l: 77, t: 39.4, w: 14, h: 11.2, r: '50%', label: '右腰' },
|
||||
{ l: 25, t: 57.4, w: 14, h: 11.2, r: '50%', label: '左下' },
|
||||
{ l: 61, t: 57.4, w: 14, h: 11.2, r: '50%', label: '右下' },
|
||||
{ l: 43, t: 72, w: 14, h: 11.2, r: '50%', label: '心尖' },
|
||||
]
|
||||
|
||||
/* —— grid 格子心(等大方格阶梯,像素心) —— */
|
||||
const SLOTS_GRID = (() => {
|
||||
const W = 18
|
||||
const H = 14.4
|
||||
const colX = [5, 24, 43, 62, 81]
|
||||
const rowY = [2, 20, 38, 56]
|
||||
const cells = [
|
||||
[0, 1], [0, 3],
|
||||
[1, 0], [1, 1], [1, 2], [1, 3], [1, 4],
|
||||
[2, 1], [2, 2], [2, 3],
|
||||
[3, 2],
|
||||
]
|
||||
const labels = ['左叶', '右叶', '左耳', '左颊', '中心', '右颊', '右耳', '左收', '中收', '右收', '心尖']
|
||||
return cells.map(([row, col], i) => ({
|
||||
l: colX[col], t: rowY[row], w: W, h: H, r: '4px', label: labels[i] || `格${i + 1}`,
|
||||
}))
|
||||
})()
|
||||
|
||||
/* —— wings 比翼心(两大翼 + 中缝收尖列,少图大脸友好) —— */
|
||||
const SLOTS_WINGS = [
|
||||
{ l: 2, t: 3, w: 46, h: 54, r: '48% 46% 8% 62% / 42% 38% 8% 34%', label: '左翼' },
|
||||
{ l: 52, t: 3, w: 46, h: 54, r: '46% 48% 62% 8% / 38% 42% 34% 8%', label: '右翼' },
|
||||
{ l: 42.5, t: 59, w: 15, h: 12, r: '50%', label: '收心一' },
|
||||
{ l: 44, t: 72.5, w: 12, h: 9.6, r: '50%', label: '收心二' },
|
||||
{ l: 45.5, t: 83.5, w: 9, h: 7.2, r: '50%', label: '心尖' },
|
||||
]
|
||||
|
||||
/* —— mosaic 马赛克心(大小方块混拼) —— */
|
||||
const SLOTS_MOSAIC = [
|
||||
{ l: 8, t: 2, w: 22, h: 15, r: '6px', label: '左叶大' },
|
||||
{ l: 70, t: 2, w: 22, h: 15, r: '6px', label: '右叶大' },
|
||||
{ l: 32, t: 6, w: 16, h: 11, r: '5px', label: '左叶内' },
|
||||
{ l: 52, t: 6, w: 16, h: 11, r: '5px', label: '右叶内' },
|
||||
{ l: 4, t: 19, w: 30, h: 21, r: '6px', label: '左脸' },
|
||||
{ l: 66, t: 19, w: 30, h: 21, r: '6px', label: '右脸' },
|
||||
{ l: 36, t: 19, w: 28, h: 14, r: '5px', label: '中上' },
|
||||
{ l: 36, t: 35, w: 28, h: 10, r: '4px', label: '中带' },
|
||||
{ l: 12, t: 42, w: 24, h: 15, r: '5px', label: '左收' },
|
||||
{ l: 64, t: 42, w: 24, h: 15, r: '5px', label: '右收' },
|
||||
{ l: 38, t: 47, w: 24, h: 12, r: '4px', label: '中横' },
|
||||
{ l: 26, t: 59, w: 16, h: 11, r: '4px', label: '左下' },
|
||||
{ l: 58, t: 59, w: 16, h: 11, r: '4px', label: '右下' },
|
||||
{ l: 43, t: 61, w: 14, h: 14, r: '4px', label: '中下' },
|
||||
{ l: 44, t: 77, w: 12, h: 8.5, r: '4px', label: '心尖' },
|
||||
]
|
||||
|
||||
/* —— orbit 星环心(心形大图 + 8 小圆环绕) —— */
|
||||
const SLOTS_ORBIT = [
|
||||
{ l: 20, t: 16, w: 60, h: 46, shape: 'heart', label: '心形主图' },
|
||||
{ l: 22, t: 0, w: 12, h: 9.6, r: '50%', label: '左上星' },
|
||||
{ l: 66, t: 0, w: 12, h: 9.6, r: '50%', label: '右上星' },
|
||||
{ l: 4, t: 18, w: 12, h: 9.6, r: '50%', label: '左星' },
|
||||
{ l: 84, t: 18, w: 12, h: 9.6, r: '50%', label: '右星' },
|
||||
{ l: 12, t: 42, w: 12, h: 9.6, r: '50%', label: '左下星' },
|
||||
{ l: 76, t: 42, w: 12, h: 9.6, r: '50%', label: '右下星' },
|
||||
{ l: 30, t: 63, w: 12, h: 9.6, r: '50%', label: '尖左星' },
|
||||
{ l: 58, t: 63, w: 12, h: 9.6, r: '50%', label: '尖右星' },
|
||||
]
|
||||
|
||||
export const HEART_LAYOUTS = [
|
||||
{ key: 'classic', label: '经典阶梯', desc: '矩形块阶梯拼合(默认)', slots: SLOTS_CLASSIC },
|
||||
{ key: 'soft', label: '圆弧心', desc: '外缘弧形磨边 + V 形心尖', slots: SLOTS_SOFT },
|
||||
{ key: 'ring', label: '环绕心', desc: '中央大圆 + 小圆沿轮廓环绕', slots: SLOTS_RING },
|
||||
{ key: 'grid', label: '格子心', desc: '等大方格像素心', slots: SLOTS_GRID },
|
||||
{ key: 'wings', label: '比翼心', desc: '两大翼 + 收尖圆列(图少脸大)', slots: SLOTS_WINGS },
|
||||
{ key: 'mosaic', label: '马赛克心', desc: '大小方块混拼', slots: SLOTS_MOSAIC },
|
||||
{ key: 'orbit', label: '星环心', desc: '心形整图 + 星点环绕', slots: SLOTS_ORBIT },
|
||||
]
|
||||
|
||||
const HEART_LAYOUT_MAP = Object.fromEntries(HEART_LAYOUTS.map((x) => [x.key, x]))
|
||||
|
||||
export function normalizeHeartLayout(key) {
|
||||
return HEART_LAYOUT_MAP[key] ? key : 'classic'
|
||||
}
|
||||
|
||||
export function heartLayoutMeta(key) {
|
||||
return HEART_LAYOUT_MAP[normalizeHeartLayout(key)]
|
||||
}
|
||||
|
||||
/** 取页面配置对应的槽位数组 */
|
||||
export function heartSlotsFor(page) {
|
||||
return heartLayoutMeta(page?.heartLayout).slots
|
||||
}
|
||||
|
||||
/* —— 兼容旧引用:默认方案 —— */
|
||||
export const HEART_SLOTS = SLOTS_CLASSIC
|
||||
export const HEART_SLOT_COUNT = SLOTS_CLASSIC.length
|
||||
|
||||
export function heartSlotStyle(slot) {
|
||||
return {
|
||||
left: `${slot.l}%`,
|
||||
@@ -36,17 +161,21 @@ export function heartSlotStyle(slot) {
|
||||
}
|
||||
}
|
||||
|
||||
export function heartSlotLabel(i) {
|
||||
return HEART_SLOT_LABELS[i] || `图${i + 1}`
|
||||
export function heartSlotLabel(page, i) {
|
||||
const slots = heartSlotsFor(page)
|
||||
return slots[i]?.label || `图${i + 1}`
|
||||
}
|
||||
|
||||
/** 格子形状样式(圆角/裁切),作用在展示格上让整体轮廓贴近心形 */
|
||||
export function heartCellShape(i) {
|
||||
const s = HEART_SLOTS[i]
|
||||
if (!s) return {}
|
||||
/** 格子形状样式(圆角/裁切/心形 mask),作用在展示格上 */
|
||||
export function heartCellShape(slot) {
|
||||
if (!slot) return {}
|
||||
const style = {}
|
||||
if (s.r) style.borderRadius = s.r
|
||||
if (s.clip) style.clipPath = s.clip
|
||||
if (slot.r) style.borderRadius = slot.r
|
||||
if (slot.clip) style.clipPath = slot.clip
|
||||
if (slot.shape === 'heart') {
|
||||
style.webkitMask = HEART_MASK_SVG
|
||||
style.mask = HEART_MASK_SVG
|
||||
}
|
||||
return style
|
||||
}
|
||||
|
||||
|
||||
@@ -57,11 +57,11 @@ html[data-theme='guofeng-new'] {
|
||||
--c-muted: #8b8a80;
|
||||
--c-ink: #2c302c;
|
||||
--c-ink-soft: #4a4f48;
|
||||
--c-paper: #faf8f2;
|
||||
--c-bg: #f6f3ec;
|
||||
--c-bg-deep: #ece7db;
|
||||
--c-paper: #f7f5e8;
|
||||
--c-bg: #f2efe0;
|
||||
--c-bg-deep: #e6e2ce;
|
||||
--c-card: #fffefa;
|
||||
--c-on-primary: #f6f3ec;
|
||||
--c-on-primary: #f2efe0;
|
||||
--t-font-title: var(--font-serif);
|
||||
--t-font-kicker: var(--font-kai);
|
||||
--t-font-body: var(--font-serif);
|
||||
@@ -79,9 +79,9 @@ html[data-theme='guofeng-old'] {
|
||||
--c-muted: #9a8578;
|
||||
--c-ink: #3a2428;
|
||||
--c-ink-soft: #5c3a40;
|
||||
--c-paper: #fdf6f0;
|
||||
--c-bg: #f9efe6;
|
||||
--c-bg-deep: #f2e3d4;
|
||||
--c-paper: #fcf4e6;
|
||||
--c-bg: #f8ecda;
|
||||
--c-bg-deep: #f0dec4;
|
||||
--c-card: #fffaf4;
|
||||
--c-on-primary: #f7e7c3;
|
||||
--t-font-title: var(--font-mashan);
|
||||
@@ -101,9 +101,9 @@ html[data-theme='french'] {
|
||||
--c-muted: #9c8b90;
|
||||
--c-ink: #3d3136;
|
||||
--c-ink-soft: #5c454e;
|
||||
--c-paper: #fdfafa;
|
||||
--c-bg: #fdf6f4;
|
||||
--c-bg-deep: #f7e9e7;
|
||||
--c-paper: #fdf4f6;
|
||||
--c-bg: #fceef0;
|
||||
--c-bg-deep: #f7e2e4;
|
||||
--c-card: #ffffff;
|
||||
--c-on-primary: #ffffff;
|
||||
--t-font-title: var(--font-calligraphy);
|
||||
@@ -115,17 +115,17 @@ html[data-theme='french'] {
|
||||
}
|
||||
/* 现代简约:暖灰低饱和 · 无衬线 · 细几何 */
|
||||
html[data-theme='modern'] {
|
||||
--c-primary: #8a8578;
|
||||
--c-primary-deep: #757064;
|
||||
--c-primary-rgb: 138, 133, 120;
|
||||
--c-line: #a39e91;
|
||||
--c-line-rgb: 163, 158, 145;
|
||||
--c-muted: #8f8c85;
|
||||
--c-ink: #2e2d2a;
|
||||
--c-ink-soft: #4a4842;
|
||||
--c-paper: #fbfaf8;
|
||||
--c-bg: #f7f6f3;
|
||||
--c-bg-deep: #edebe6;
|
||||
--c-primary: #6e7b84;
|
||||
--c-primary-deep: #5c6a74;
|
||||
--c-primary-rgb: 110, 123, 132;
|
||||
--c-line: #93a4ae;
|
||||
--c-line-rgb: 147, 164, 174;
|
||||
--c-muted: #84909a;
|
||||
--c-ink: #2a3238;
|
||||
--c-ink-soft: #46525c;
|
||||
--c-paper: #f8fafb;
|
||||
--c-bg: #f1f4f6;
|
||||
--c-bg-deep: #e4eaee;
|
||||
--c-card: #ffffff;
|
||||
--c-on-primary: #ffffff;
|
||||
--t-font-title: var(--font-sans);
|
||||
@@ -145,9 +145,9 @@ html[data-theme='forest'] {
|
||||
--c-muted: #86928a;
|
||||
--c-ink: #29322c;
|
||||
--c-ink-soft: #3d4a40;
|
||||
--c-paper: #f9fcf9;
|
||||
--c-bg: #f3f8f2;
|
||||
--c-bg-deep: #e9f0e7;
|
||||
--c-paper: #f3f9f2;
|
||||
--c-bg: #ecf4ea;
|
||||
--c-bg-deep: #dfeadd;
|
||||
--c-card: #ffffff;
|
||||
--c-on-primary: #ffffff;
|
||||
--t-font-title: var(--font-longcang);
|
||||
@@ -189,9 +189,9 @@ html[data-theme='magazine'] {
|
||||
--c-muted: #7d786f;
|
||||
--c-ink: #1f1c19;
|
||||
--c-ink-soft: #3a352e;
|
||||
--c-paper: #faf8f4;
|
||||
--c-bg: #f5f2ec;
|
||||
--c-bg-deep: #eae6dd;
|
||||
--c-paper: #f7f4ec;
|
||||
--c-bg: #f2eee4;
|
||||
--c-bg-deep: #e4dfd0;
|
||||
--c-card: #ffffff;
|
||||
--c-on-primary: #ffffff;
|
||||
--t-font-title: var(--font-display);
|
||||
@@ -211,11 +211,11 @@ html[data-theme='newspaper'] {
|
||||
--c-muted: #6f6b62;
|
||||
--c-ink: #262420;
|
||||
--c-ink-soft: #423e35;
|
||||
--c-paper: #fbf9f2;
|
||||
--c-bg: #f6f3ea;
|
||||
--c-bg-deep: #ebe7da;
|
||||
--c-paper: #f9f5e4;
|
||||
--c-bg: #f4efdc;
|
||||
--c-bg-deep: #e7e0c8;
|
||||
--c-card: #fffdf6;
|
||||
--c-on-primary: #fbf9f2;
|
||||
--c-on-primary: #f9f5e4;
|
||||
--t-font-title: var(--font-serif);
|
||||
--t-font-kicker: var(--font-display);
|
||||
--t-font-body: var(--font-serif);
|
||||
@@ -233,9 +233,9 @@ html[data-theme='pixel'] {
|
||||
--c-muted: #8a7f74;
|
||||
--c-ink: #33261f;
|
||||
--c-ink-soft: #54433a;
|
||||
--c-paper: #fdf9ee;
|
||||
--c-bg: #faf3e6;
|
||||
--c-bg-deep: #f0e6d2;
|
||||
--c-paper: #fcf5e0;
|
||||
--c-bg: #f9f0da;
|
||||
--c-bg-deep: #eee0c0;
|
||||
--c-card: #fffdf4;
|
||||
--c-on-primary: #fffdf4;
|
||||
--t-font-title: var(--font-pixel);
|
||||
@@ -245,21 +245,219 @@ html[data-theme='pixel'] {
|
||||
--t-radius-btn: 0px;
|
||||
--t-bg-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Crect x='0' y='0' width='4' height='4' fill='%2333261f' fill-opacity='0.02'/%3E%3Crect x='4' y='4' width='4' height='4' fill='%2333261f' fill-opacity='0.02'/%3E%3C/svg%3E");
|
||||
}
|
||||
/* 极简主义:黑白留白 · 细线 · 轴线 */
|
||||
html[data-theme='zen'] {
|
||||
--c-primary: #2a2a28;
|
||||
--c-primary-deep: #111111;
|
||||
--c-primary-rgb: 42, 42, 40;
|
||||
--c-line: #8f8d88;
|
||||
--c-line-rgb: 143, 141, 136;
|
||||
--c-muted: #85837e;
|
||||
--c-ink: #262624;
|
||||
--c-ink-soft: #444442;
|
||||
--c-paper: #fdfdfc;
|
||||
--c-bg: #fafaf8;
|
||||
--c-bg-deep: #f0f0ed;
|
||||
/* 水彩画境:雾蓝淡彩 · 晕染 · 柔圆 */
|
||||
html[data-theme='watercolor'] {
|
||||
--c-primary: #7f9bb3;
|
||||
--c-primary-deep: #6a86a0;
|
||||
--c-primary-rgb: 127, 155, 179;
|
||||
--c-line: #a8c0d0;
|
||||
--c-line-rgb: 168, 192, 208;
|
||||
--c-muted: #8b939c;
|
||||
--c-ink: #33404a;
|
||||
--c-ink-soft: #52626e;
|
||||
--c-paper: #f5f9fc;
|
||||
--c-bg: #edf4fa;
|
||||
--c-bg-deep: #dde9f2;
|
||||
--c-card: #ffffff;
|
||||
--c-on-primary: #ffffff;
|
||||
--t-font-title: var(--font-calligraphy);
|
||||
--t-font-kicker: var(--font-sans);
|
||||
--t-font-body: var(--font-sans);
|
||||
--t-radius-card: 20px;
|
||||
--t-radius-btn: 999px;
|
||||
--t-bg-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cg fill-opacity='0.05'%3E%3Ccircle cx='40' cy='48' r='26' fill='%237f9bb3'/%3E%3Ccircle cx='138' cy='30' r='18' fill='%23c8a8c0'/%3E%3Ccircle cx='120' cy='128' r='30' fill='%2388b8a8'/%3E%3Ccircle cx='34' cy='140' r='16' fill='%23a8c0d0'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
/* 樱花物语:粉白渐彩 · 花瓣 */
|
||||
html[data-theme='sakura'] {
|
||||
--c-primary: #d98a9e;
|
||||
--c-primary-deep: #c67689;
|
||||
--c-primary-rgb: 217, 138, 158;
|
||||
--c-line: #e8b4c2;
|
||||
--c-line-rgb: 232, 180, 194;
|
||||
--c-muted: #a08d93;
|
||||
--c-ink: #43333a;
|
||||
--c-ink-soft: #64505a;
|
||||
--c-paper: #fdf3f6;
|
||||
--c-bg: #fcedf1;
|
||||
--c-bg-deep: #f6dfe6;
|
||||
--c-card: #ffffff;
|
||||
--c-on-primary: #ffffff;
|
||||
--t-font-title: var(--font-calligraphy);
|
||||
--t-font-kicker: "Pinyon Script", cursive;
|
||||
--t-font-body: var(--font-sans);
|
||||
--t-radius-card: 16px;
|
||||
--t-radius-btn: 999px;
|
||||
--t-bg-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cg fill='%23e8a8bc' fill-opacity='0.08'%3E%3Cellipse cx='26' cy='30' rx='5' ry='3.2' transform='rotate(-24 26 30)'/%3E%3Cellipse cx='104' cy='18' rx='4' ry='2.6' transform='rotate(18 104 18)'/%3E%3Cellipse cx='120' cy='96' rx='5.5' ry='3.4' transform='rotate(-40 120 96)'/%3E%3Cellipse cx='48' cy='112' rx='4.4' ry='2.8' transform='rotate(30 48 112)'/%3E%3Cellipse cx='78' cy='64' rx='3.6' ry='2.2' transform='rotate(-12 78 64)'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
/* 水墨山水:宣纸墨韵 · 朱砂印 */
|
||||
html[data-theme='inkwash'] {
|
||||
--c-primary: #3a3f3a;
|
||||
--c-primary-deep: #262a26;
|
||||
--c-primary-rgb: 58, 63, 58;
|
||||
--c-line: #8a9088;
|
||||
--c-line-rgb: 138, 144, 136;
|
||||
--c-muted: #82887e;
|
||||
--c-ink: #2a2e2a;
|
||||
--c-ink-soft: #4a504a;
|
||||
--c-paper: #f7f7f0;
|
||||
--c-bg: #f2f2ea;
|
||||
--c-bg-deep: #e2e2d6;
|
||||
--c-card: #fdfdfa;
|
||||
--c-on-primary: #f2f2ea;
|
||||
--t-font-title: var(--font-kai);
|
||||
--t-font-kicker: var(--font-kai);
|
||||
--t-font-body: var(--font-serif);
|
||||
--t-radius-card: 2px;
|
||||
--t-radius-btn: 2px;
|
||||
--t-bg-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cg fill='%233a3f3a' fill-opacity='0.028'%3E%3Cpath d='M0 158q50-36 100-10t100-16v68H0z'/%3E%3C/g%3E%3Cg fill='%233a3f3a' fill-opacity='0.02'%3E%3Ccircle cx='150' cy='40' r='2'/%3E%3Ccircle cx='36' cy='60' r='1.4'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
/* 复古电影:上映海报 · 做旧奶油 */
|
||||
html[data-theme='cinema'] {
|
||||
--c-primary: #b8322e;
|
||||
--c-primary-deep: #962622;
|
||||
--c-primary-rgb: 184, 50, 46;
|
||||
--c-line: #d4a24a;
|
||||
--c-line-rgb: 212, 162, 74;
|
||||
--c-muted: #97887a;
|
||||
--c-ink: #3a2c22;
|
||||
--c-ink-soft: #5c4738;
|
||||
--c-paper: #f5ead4;
|
||||
--c-bg: #efe0c8;
|
||||
--c-bg-deep: #e2d0ae;
|
||||
--c-card: #fbf4e8;
|
||||
--c-on-primary: #fbf4e8;
|
||||
--t-font-title: var(--font-display);
|
||||
--t-font-kicker: var(--font-cinzel);
|
||||
--t-font-body: var(--font-serif);
|
||||
--t-radius-card: 6px;
|
||||
--t-radius-btn: 4px;
|
||||
--t-bg-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='90' height='90'%3E%3Cg fill='%233a2c22' fill-opacity='0.026'%3E%3Ccircle cx='16' cy='26' r='1.1'/%3E%3Ccircle cx='58' cy='12' r='1.5'/%3E%3Ccircle cx='80' cy='46' r='1'/%3E%3Ccircle cx='34' cy='62' r='1.3'/%3E%3Ccircle cx='68' cy='80' r='1.1'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
/* 胶片日记:柯达暖调 · 齿孔框 */
|
||||
html[data-theme='kodak'] {
|
||||
--c-primary: #e0782e;
|
||||
--c-primary-deep: #c86420;
|
||||
--c-primary-rgb: 224, 120, 46;
|
||||
--c-line: #3c3a34;
|
||||
--c-line-rgb: 60, 58, 52;
|
||||
--c-muted: #96897a;
|
||||
--c-ink: #38302a;
|
||||
--c-ink-soft: #5c5044;
|
||||
--c-paper: #fbf4de;
|
||||
--c-bg: #f8f0d8;
|
||||
--c-bg-deep: #eddfba;
|
||||
--c-card: #fffdf6;
|
||||
--c-on-primary: #fffdf6;
|
||||
--t-font-title: var(--font-sans);
|
||||
--t-font-kicker: var(--font-number);
|
||||
--t-font-body: var(--font-sans);
|
||||
--t-radius-card: 8px;
|
||||
--t-radius-btn: 8px;
|
||||
--t-bg-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Cg fill='%2338302a' fill-opacity='0.022'%3E%3Ccircle cx='20' cy='30' r='1.2'/%3E%3Ccircle cx='66' cy='14' r='1'/%3E%3Ccircle cx='84' cy='60' r='1.3'/%3E%3Ccircle cx='38' cy='78' r='1'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
/* 文艺影调:对称构图 · 衬线留白 */
|
||||
html[data-theme='arthouse'] {
|
||||
--c-primary: #4a4640;
|
||||
--c-primary-deep: #322f2a;
|
||||
--c-primary-rgb: 74, 70, 64;
|
||||
--c-line: #9a948a;
|
||||
--c-line-rgb: 154, 148, 138;
|
||||
--c-muted: #8c877d;
|
||||
--c-ink: #363330;
|
||||
--c-ink-soft: #5a564e;
|
||||
--c-paper: #f4efdc;
|
||||
--c-bg: #f1ede2;
|
||||
--c-bg-deep: #e3decd;
|
||||
--c-card: #fdfbf7;
|
||||
--c-on-primary: #f4efdc;
|
||||
--t-font-title: var(--font-display);
|
||||
--t-font-kicker: var(--font-display);
|
||||
--t-font-body: var(--font-serif);
|
||||
--t-radius-card: 0px;
|
||||
--t-radius-btn: 0px;
|
||||
--t-bg-texture: none;
|
||||
}
|
||||
/* 环球旅行:护照蓝金 · 登机牌 */
|
||||
html[data-theme='passport'] {
|
||||
--c-primary: #2e4a68;
|
||||
--c-primary-deep: #223a54;
|
||||
--c-primary-rgb: 46, 74, 104;
|
||||
--c-line: #b08a4a;
|
||||
--c-line-rgb: 176, 138, 74;
|
||||
--c-muted: #85898c;
|
||||
--c-ink: #2c3640;
|
||||
--c-ink-soft: #4a5662;
|
||||
--c-paper: #f4f7fa;
|
||||
--c-bg: #eceff2;
|
||||
--c-bg-deep: #dde3ea;
|
||||
--c-card: #fcfaf4;
|
||||
--c-on-primary: #eceff2;
|
||||
--t-font-title: var(--font-display);
|
||||
--t-font-kicker: var(--font-number);
|
||||
--t-font-body: var(--font-sans);
|
||||
--t-radius-card: 10px;
|
||||
--t-radius-btn: 6px;
|
||||
--t-bg-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cg fill='none' stroke='%232e4a68' stroke-opacity='0.04' stroke-width='1' stroke-dasharray='4 5'%3E%3Cpath d='M-10 120C40 60 120 100 170 30'/%3E%3C/g%3E%3Cg fill='%232e4a68' fill-opacity='0.05'%3E%3Cpath d='M96 66l10 4-10 4 2-4z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
/* 手账拼贴:牛皮纸 · 胶带贴纸 */
|
||||
html[data-theme='scrapbook'] {
|
||||
--c-primary: #c47a5a;
|
||||
--c-primary-deep: #a86446;
|
||||
--c-primary-rgb: 196, 122, 90;
|
||||
--c-line: #8aa88a;
|
||||
--c-line-rgb: 138, 168, 138;
|
||||
--c-muted: #9a8f80;
|
||||
--c-ink: #453a30;
|
||||
--c-ink-soft: #665748;
|
||||
--c-paper: #fbf2dc;
|
||||
--c-bg: #f7ecd4;
|
||||
--c-bg-deep: #ecdcba;
|
||||
--c-card: #fffcf4;
|
||||
--c-on-primary: #fffcf4;
|
||||
--t-font-title: var(--font-longcang);
|
||||
--t-font-kicker: var(--font-liujian);
|
||||
--t-font-body: var(--font-sans);
|
||||
--t-radius-card: 6px;
|
||||
--t-radius-btn: 8px;
|
||||
--t-bg-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='60'%3E%3Cg fill='%23453a30' fill-opacity='0.03'%3E%3Ccircle cx='12' cy='12' r='1.4'/%3E%3Ccircle cx='42' cy='30' r='1.4'/%3E%3Ccircle cx='20' cy='48' r='1.4'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
/* 千禧蒸汽波:紫青渐变 · 复古窗口 */
|
||||
html[data-theme='y2k'] {
|
||||
--c-primary: #7a4ae0;
|
||||
--c-primary-deep: #6238c0;
|
||||
--c-primary-rgb: 122, 74, 224;
|
||||
--c-line: #4adce0;
|
||||
--c-line-rgb: 74, 220, 224;
|
||||
--c-muted: #8d87a0;
|
||||
--c-ink: #322a48;
|
||||
--c-ink-soft: #544a70;
|
||||
--c-paper: #efeafa;
|
||||
--c-bg: #e7e1f8;
|
||||
--c-bg-deep: #d4cbee;
|
||||
--c-card: #faf8ff;
|
||||
--c-on-primary: #faf8ff;
|
||||
--t-font-title: var(--font-sans);
|
||||
--t-font-kicker: var(--font-pixel);
|
||||
--t-font-body: var(--font-sans);
|
||||
--t-radius-card: 8px;
|
||||
--t-radius-btn: 6px;
|
||||
--t-bg-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cg fill='%237a4ae0' fill-opacity='0.05'%3E%3Cpath d='M30 20l2.4 6 6 2.4-6 2.4-2.4 6-2.4-6-6-2.4 6-2.4z'/%3E%3Cpath d='M92 74l1.8 4.4 4.4 1.8-4.4 1.8-1.8 4.4-1.8-4.4-4.4-1.8 4.4-1.8z'/%3E%3C/g%3E%3Cg fill='%234adce0' fill-opacity='0.05'%3E%3Ccircle cx='72' cy='28' r='2.4'/%3E%3Ccircle cx='24' cy='92' r='2'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
/* 极简主义:暖白留白 · 细线 · 轴线 */
|
||||
html[data-theme='zen'] {
|
||||
--c-primary: #57503f;
|
||||
--c-primary-deep: #3e392c;
|
||||
--c-primary-rgb: 87, 80, 63;
|
||||
--c-line: #a89f8e;
|
||||
--c-line-rgb: 168, 159, 142;
|
||||
--c-muted: #8f887a;
|
||||
--c-ink: #3c362e;
|
||||
--c-ink-soft: #5c5548;
|
||||
--c-paper: #fdfbf7;
|
||||
--c-bg: #faf7f2;
|
||||
--c-bg-deep: #f0ebe2;
|
||||
--c-card: #fffef9;
|
||||
--c-on-primary: #fdfbf7;
|
||||
--t-font-title: var(--font-sans);
|
||||
--t-font-kicker: var(--font-sans);
|
||||
--t-font-body: var(--font-sans);
|
||||
|
||||
@@ -127,20 +127,24 @@
|
||||
:where(.gf-postcard, .gf-film, .gf-stamp, .gf-arch, .gf-oval, .gf-circle, .gf-leaf,
|
||||
.gf-heart, .gf-flower, .gf-hexagon, .gf-scallop, .gf-wave, .gf-zigzag,
|
||||
.gf-torn, .gf-ticket, .gf-bevel,
|
||||
.gf-wreath, .gf-baroque, .gf-fan, .gf-moonwin, .gf-ribbon,
|
||||
.gf-cloud, .gf-mist, .gf-bloom, .gf-panorama, .gf-ovalwide) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 边缘渐隐内晕:照片边缘向纸色柔和过渡,与边框/裁切形状融为一体
|
||||
(mask/clip 类的伪元素会随形状一并裁切,晕自动贴合轮廓) */
|
||||
/* 边缘渐隐内晕:照片边缘向页面纸色柔和过渡,与边框/裁切形状融为一体
|
||||
(mask/clip 类的伪元素会随形状一并裁切,晕自动贴合轮廓;
|
||||
颜色用 color-mix 跟随主题纸色,深浅主题都能融入页面) */
|
||||
.gf-heart::after, .gf-flower::after, .gf-scallop::after, .gf-wave::after,
|
||||
.gf-zigzag::after, .gf-torn::after, .gf-stamp::after, .gf-ticket::after,
|
||||
.gf-hexagon::after, .gf-bevel::after, .gf-arch::after, .gf-oval::after,
|
||||
.gf-circle::after, .gf-leaf::after, .gf-cloud::after, .gf-ovalwide::after {
|
||||
.gf-circle::after, .gf-leaf::after, .gf-cloud::after, .gf-ovalwide::after,
|
||||
.gf-wreath::after, .gf-baroque::after, .gf-fan::after, .gf-moonwin::after,
|
||||
.gf-ribbon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(ellipse 62% 62% at 50% 50%, rgba(255, 252, 246, 0) 55%, rgba(255, 252, 246, 0.48) 100%);
|
||||
background: radial-gradient(ellipse 64% 64% at 50% 50%, transparent 50%, color-mix(in srgb, var(--c-paper, #fffcf6) 62%, transparent) 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -205,39 +209,143 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 拱门(顶部全圆,婚礼穹顶造型) */
|
||||
.gf-arch {
|
||||
padding: 8px;
|
||||
border: 1px solid rgba(201, 169, 120, 0.5);
|
||||
border-radius: 999px 999px 14px 14px;
|
||||
box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.65), 0 14px 32px rgba(120, 90, 60, 0.16);
|
||||
background: #fff;
|
||||
}
|
||||
.gf-arch > img {
|
||||
border-radius: 992px 992px 8px 8px;
|
||||
}
|
||||
/* 拱顶花饰圆章(呼应尾页门框 keystone) */
|
||||
.gf-arch::before {
|
||||
content: '❀';
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
/* 花环相框(月桂双枝环抱椭圆照片,arch 为历史别名) */
|
||||
.gf-arch,
|
||||
.gf-wreath {
|
||||
padding: 22px;
|
||||
border: 0;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
color: #b08e63;
|
||||
background: rgba(253, 251, 247, 0.92);
|
||||
box-shadow: 0 0 0 1px rgba(201, 169, 120, 0.45), 0 2px 6px rgba(120, 90, 60, 0.18);
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
}
|
||||
.gf-arch > img,
|
||||
.gf-wreath > img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.gf-arch::before,
|
||||
.gf-wreath::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -4px;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 250'%3E%3Cg fill='none' stroke='%238a9a76' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M104 240C48 230 16 178 20 112 22 70 38 38 66 18'/%3E%3Cpath d='M96 240c56-10 88-62 84-128-2-42-18-74-46-94'/%3E%3C/g%3E%3Cg fill='%239ab48a'%3E%3Cellipse cx='34' cy='196' rx='11' ry='4.6' transform='rotate(38 34 196)'/%3E%3Cellipse cx='23' cy='164' rx='11' ry='4.6' transform='rotate(64 23 164)'/%3E%3Cellipse cx='19' cy='128' rx='11' ry='4.6' transform='rotate(82 19 128)'/%3E%3Cellipse cx='24' cy='92' rx='11' ry='4.6' transform='rotate(102 24 92)'/%3E%3Cellipse cx='38' cy='56' rx='11' ry='4.6' transform='rotate(124 38 56)'/%3E%3Cellipse cx='60' cy='30' rx='11' ry='4.6' transform='rotate(146 60 30)'/%3E%3Cellipse cx='166' cy='196' rx='11' ry='4.6' transform='rotate(-38 166 196)'/%3E%3Cellipse cx='177' cy='164' rx='11' ry='4.6' transform='rotate(-64 177 164)'/%3E%3Cellipse cx='181' cy='128' rx='11' ry='4.6' transform='rotate(-82 181 128)'/%3E%3Cellipse cx='176' cy='92' rx='11' ry='4.6' transform='rotate(-102 176 92)'/%3E%3Cellipse cx='162' cy='56' rx='11' ry='4.6' transform='rotate(-124 162 56)'/%3E%3Cellipse cx='140' cy='30' rx='11' ry='4.6' transform='rotate(-146 140 30)'/%3E%3C/g%3E%3Cg fill='%23b8cba6' opacity='.85'%3E%3Cellipse cx='46' cy='222' rx='9' ry='3.8' transform='rotate(22 46 222)'/%3E%3Cellipse cx='154' cy='222' rx='9' ry='3.8' transform='rotate(-22 154 222)'/%3E%3Cellipse cx='84' cy='10' rx='9' ry='3.8' transform='rotate(168 84 10)'/%3E%3Cellipse cx='116' cy='10' rx='9' ry='3.8' transform='rotate(12 116 10)'/%3E%3C/g%3E%3Cg fill='%23e4aeb6'%3E%3Cg transform='translate(100 244)'%3E%3Ccircle cx='0' cy='-4' r='3.4'/%3E%3Ccircle cx='3.8' cy='1.4' r='3.4'/%3E%3Ccircle cx='-3.8' cy='1.4' r='3.4'/%3E%3Ccircle r='1.8' fill='%23c9a24a'/%3E%3C/g%3E%3Cg transform='translate(30 40) scale(.8)'%3E%3Ccircle cx='0' cy='-4' r='3.4'/%3E%3Ccircle cx='3.8' cy='1.4' r='3.4'/%3E%3Ccircle cx='-3.8' cy='1.4' r='3.4'/%3E%3Ccircle r='1.8' fill='%23c9a24a'/%3E%3C/g%3E%3Cg transform='translate(170 40) scale(.8)'%3E%3Ccircle cx='0' cy='-4' r='3.4'/%3E%3Ccircle cx='3.8' cy='1.4' r='3.4'/%3E%3Ccircle cx='-3.8' cy='1.4' r='3.4'/%3E%3Ccircle r='1.8' fill='%23c9a24a'/%3E%3C/g%3E%3C/g%3E%3Cg fill='%23d98a9e' opacity='.7'%3E%3Ccircle cx='52' cy='210' r='2.6'/%3E%3Ccircle cx='148' cy='210' r='2.6'/%3E%3Ccircle cx='26' cy='76' r='2.4'/%3E%3Ccircle cx='174' cy='76' r='2.4'/%3E%3C/g%3E%3C/svg%3E") center / 100% 100% no-repeat;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 巴洛克金镜(凡尔赛宫廷椭圆镜:金渐变边 + 顶部卷草冠饰) */
|
||||
.gf-baroque {
|
||||
padding: 9px;
|
||||
border: 3.5px solid transparent;
|
||||
border-radius: 50%;
|
||||
background:
|
||||
linear-gradient(180deg, #fffdf8, #fbf4e6) padding-box,
|
||||
linear-gradient(140deg, #ead7aa 0%, #b08d54 26%, #f6ecd0 48%, #a8834a 72%, #e2cb98 100%) border-box;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(140, 108, 60, 0.5),
|
||||
0 0 0 6px rgba(246, 236, 208, 0.55),
|
||||
0 0 0 7.5px rgba(176, 141, 84, 0.5),
|
||||
0 16px 36px rgba(120, 90, 50, 0.22);
|
||||
}
|
||||
.gf-baroque > img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.gf-baroque::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -21px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 84px;
|
||||
height: 26px;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 84 26'%3E%3Cg fill='none' stroke='%23b08d54' stroke-width='1.6' stroke-linecap='round'%3E%3Cpath d='M42 22c-6-2-8-7-5-11 2.6-3.4 7-2.4 7 1 0 2.6-2.6 3.4-4 1.6'/%3E%3Cpath d='M42 22c6-2 8-7 5-11-2.6-3.4-7-2.4-7 1 0 2.6 2.6 3.4 4 1.6'/%3E%3Cpath d='M30 21C20 19 14 13 16 7c4 2 8 6 10 11'/%3E%3Cpath d='M54 21c10-2 16-8 14-14-4 2-8 6-10 11'/%3E%3Cpath d='M14 8C10 6 6 6 3 9c3 2 6 2 9 1'/%3E%3Cpath d='M70 8c4-2 8-2 11 1-3 2-6 2-9 1'/%3E%3C/g%3E%3Ccircle cx='42' cy='6' r='2.6' fill='%23c9a24a'/%3E%3C/svg%3E") center / contain no-repeat;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 折扇扇面(底心圆点展开的扇形:clip 扇角 ∩ mask 环带,附扇骨放射线)
|
||||
环带 mask 内外缘放宽渐变即自带羽化,clip 斜边与顶边由 ::after 贴边雾软化 */
|
||||
.gf-fan {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
clip-path: polygon(50% 126%, -14% -6%, 114% -6%);
|
||||
-webkit-clip-path: polygon(50% 126%, -14% -6%, 114% -6%);
|
||||
-webkit-mask: radial-gradient(146% 146% at 50% 126%, transparent 27%, #000 33%, #000 93%, transparent 98.5%);
|
||||
mask: radial-gradient(146% 146% at 50% 126%, transparent 27%, #000 33%, #000 93%, transparent 98.5%);
|
||||
}
|
||||
/* 扇骨:自底心放射的细白线 + 弧沿描边 */
|
||||
.gf-fan::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
repeating-conic-gradient(from -57deg at 50% 126%, rgba(255, 253, 246, 0.6) 0 0.5deg, transparent 0.5deg 16.2deg),
|
||||
radial-gradient(146% 146% at 50% 126%, transparent 91%, rgba(190, 155, 105, 0.45) 92.5%, rgba(190, 155, 105, 0.45) 95%, transparent 96.5%),
|
||||
radial-gradient(146% 146% at 50% 126%, transparent 30%, rgba(190, 155, 105, 0.4) 31.5%, rgba(190, 155, 105, 0.4) 34%, transparent 35.5%);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
/* 扇面贴边雾:顶边下坠 + 左右斜边 + 内弧收口(覆盖通用椭圆晕,全部贴合扇形轮廓) */
|
||||
.gf-fan::after {
|
||||
background:
|
||||
linear-gradient(180deg, color-mix(in srgb, var(--c-paper, #fffcf6) 78%, transparent), transparent 15%),
|
||||
linear-gradient(68deg, color-mix(in srgb, var(--c-paper, #fffcf6) 72%, transparent), transparent 26%),
|
||||
linear-gradient(292deg, color-mix(in srgb, var(--c-paper, #fffcf6) 72%, transparent), transparent 26%),
|
||||
radial-gradient(146% 146% at 50% 126%, transparent 28%, color-mix(in srgb, var(--c-paper, #fffcf6) 55%, transparent) 34%, transparent 42%);
|
||||
}
|
||||
|
||||
/* 月洞圆窗(正圆开窗 + 双环窗线 + 右上探入梅枝) */
|
||||
.gf-moonwin {
|
||||
padding: 9px;
|
||||
border: 0;
|
||||
border-radius: 50%;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
}
|
||||
.gf-moonwin > img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.gf-moonwin::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid rgba(110, 120, 105, 0.6);
|
||||
box-shadow:
|
||||
inset 0 0 0 4px rgba(255, 255, 255, 0.6),
|
||||
inset 0 0 0 5px rgba(110, 120, 105, 0.35);
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cg fill='none' stroke='%236e7869' stroke-width='1.1' stroke-linecap='round'%3E%3Cpath d='M96 22C84 30 72 34 58 34'/%3E%3Cpath d='M84 27c-2-5-1-9 3-12'/%3E%3Cpath d='M70 32c-1-4 0-7 3-9'/%3E%3C/g%3E%3Cg fill='%23d9a0a5'%3E%3Ccircle cx='80' cy='24' r='2.6'/%3E%3Ccircle cx='68' cy='30' r='2.2'/%3E%3Ccircle cx='58' cy='33' r='1.8'/%3E%3C/g%3E%3Ccircle cx='74' cy='21' r='1.2' fill='%23c9a24a'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 蝴蝶结缎带框(顶部系结垂带的礼物相框) */
|
||||
.gf-ribbon {
|
||||
padding: 13px 11px 11px;
|
||||
border: 1px solid rgba(217, 160, 165, 0.55);
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(180deg, #fffafa, #fdf1f3);
|
||||
box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.7), 0 12px 28px rgba(190, 120, 130, 0.18);
|
||||
}
|
||||
.gf-ribbon > img {
|
||||
border-radius: 7px;
|
||||
}
|
||||
.gf-ribbon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -15px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 66px;
|
||||
height: 44px;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 66 44'%3E%3Cg fill='%23e093a3'%3E%3Cpath d='M33 12C26 4 14 2 8 8c-5 5-3 13 4 15 8 2 17-4 21-11z' fill='%23e8a8b2'/%3E%3Cpath d='M33 12c7-8 19-10 25-4 5 5 3 13-4 15-8 2-17-4-21-11z' fill='%23e8a8b2'/%3E%3Cpath d='M33 12C28 7 20 6 16 10c-3 3-2 8 2 9 6 2 12-2 15-7z' fill='%23f2c4cc' opacity='.9'/%3E%3Cpath d='M33 12c5-5 13-6 17-2 3 3 2 8-2 9-6 2-12-2-15-7z' fill='%23f2c4cc' opacity='.9'/%3E%3Cpath d='M27 20l-7 20 8-3 3 5 4-22z' fill='%23e8a8b2'/%3E%3Cpath d='M39 20l7 20-8-3-3 5-4-22z' fill='%23dfa0aa'/%3E%3Ccircle cx='33' cy='13' r='5' fill='%23d9899a'/%3E%3Ccircle cx='33' cy='13' r='2.2' fill='%23f2c4cc'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
|
||||
filter: drop-shadow(0 3px 5px rgba(190, 120, 130, 0.3));
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* 椭圆相框 */
|
||||
.gf-oval {
|
||||
padding: 7px;
|
||||
@@ -305,6 +413,16 @@
|
||||
clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
|
||||
-webkit-clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
|
||||
}
|
||||
/* 六向贴形边雾:每条边各一道向内渐隐,取代不贴形的通用椭圆晕 */
|
||||
.gf-hexagon::after {
|
||||
background:
|
||||
linear-gradient(210deg, color-mix(in srgb, var(--c-paper, #fffcf6) 62%, transparent), transparent 17%),
|
||||
linear-gradient(270deg, color-mix(in srgb, var(--c-paper, #fffcf6) 62%, transparent), transparent 17%),
|
||||
linear-gradient(330deg, color-mix(in srgb, var(--c-paper, #fffcf6) 62%, transparent), transparent 17%),
|
||||
linear-gradient(30deg, color-mix(in srgb, var(--c-paper, #fffcf6) 62%, transparent), transparent 17%),
|
||||
linear-gradient(90deg, color-mix(in srgb, var(--c-paper, #fffcf6) 62%, transparent), transparent 17%),
|
||||
linear-gradient(150deg, color-mix(in srgb, var(--c-paper, #fffcf6) 62%, transparent), transparent 17%);
|
||||
}
|
||||
|
||||
/* 八瓣花(多层 radial mask 合成花形,推荐 1:1) */
|
||||
.gf-flower {
|
||||
@@ -313,35 +431,35 @@
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
-webkit-mask:
|
||||
radial-gradient(19% 19% at 50% 19%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 71.9% 28.1%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 81% 50%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 71.9% 71.9%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 50% 81%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 28.1% 71.9%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 19% 50%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 28.1% 28.1%, #000 98%, #0000 100%),
|
||||
radial-gradient(34% 34% at 50% 50%, #000 98%, #0000 100%);
|
||||
radial-gradient(19% 19% at 50% 19%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 71.9% 28.1%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 81% 50%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 71.9% 71.9%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 50% 81%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 28.1% 71.9%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 19% 50%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 28.1% 28.1%, #000 68%, #0000 99%),
|
||||
radial-gradient(34% 34% at 50% 50%, #000 82%, #0000 100%);
|
||||
mask:
|
||||
radial-gradient(19% 19% at 50% 19%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 71.9% 28.1%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 81% 50%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 71.9% 71.9%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 50% 81%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 28.1% 71.9%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 19% 50%, #000 98%, #0000 100%),
|
||||
radial-gradient(19% 19% at 28.1% 28.1%, #000 98%, #0000 100%),
|
||||
radial-gradient(34% 34% at 50% 50%, #000 98%, #0000 100%);
|
||||
radial-gradient(19% 19% at 50% 19%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 71.9% 28.1%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 81% 50%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 71.9% 71.9%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 50% 81%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 28.1% 71.9%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 19% 50%, #000 68%, #0000 99%),
|
||||
radial-gradient(19% 19% at 28.1% 28.1%, #000 68%, #0000 99%),
|
||||
radial-gradient(34% 34% at 50% 50%, #000 82%, #0000 100%);
|
||||
}
|
||||
|
||||
/* 心形(SVG mask 裁切,推荐 1:1) */
|
||||
/* 心形(SVG mask 裁切 + 内嵌高斯模糊羽化边,推荐 1:1) */
|
||||
.gf-heart {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%23000' d='M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z'/%3E%3C/svg%3E") no-repeat 50% 50% / 100% 100%;
|
||||
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%23000' d='M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z'/%3E%3C/svg%3E") no-repeat 50% 50% / 100% 100%;
|
||||
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cdefs%3E%3Cfilter id='f' x='-20%25' y='-20%25' width='140%25' height='140%25'%3E%3CfeGaussianBlur stdDeviation='12'/%3E%3C/filter%3E%3C/defs%3E%3Cpath filter='url(%23f)' fill='%23000' d='M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z'/%3E%3C/svg%3E") no-repeat 50% 50% / 100% 100%;
|
||||
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cdefs%3E%3Cfilter id='f' x='-20%25' y='-20%25' width='140%25' height='140%25'%3E%3CfeGaussianBlur stdDeviation='12'/%3E%3C/filter%3E%3C/defs%3E%3Cpath filter='url(%23f)' fill='%23000' d='M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z'/%3E%3C/svg%3E") no-repeat 50% 50% / 100% 100%;
|
||||
}
|
||||
|
||||
/* 扇贝花边(四边外凸圆弧波浪) */
|
||||
@@ -541,12 +659,12 @@
|
||||
box-shadow: none;
|
||||
background: #fff;
|
||||
-webkit-mask:
|
||||
radial-gradient(var(--gf-c), #000 98%, #0000) 0 0 / calc(3.4 * var(--gf-c)) calc(3.4 * var(--gf-c)) round,
|
||||
radial-gradient(calc(0.68 * var(--gf-c)), #000 98%, #0000) calc(1.7 * var(--gf-c)) calc(1.7 * var(--gf-c)) / calc(3.4 * var(--gf-c)) calc(3.4 * var(--gf-c)) round,
|
||||
radial-gradient(var(--gf-c), #000 62%, #0000 99%) 0 0 / calc(3.4 * var(--gf-c)) calc(3.4 * var(--gf-c)) round,
|
||||
radial-gradient(calc(0.68 * var(--gf-c)), #000 62%, #0000 99%) calc(1.7 * var(--gf-c)) calc(1.7 * var(--gf-c)) / calc(3.4 * var(--gf-c)) calc(3.4 * var(--gf-c)) round,
|
||||
linear-gradient(#000 0 0) 50% / calc(100% - 2.4 * var(--gf-c)) calc(100% - 2.4 * var(--gf-c)) no-repeat;
|
||||
mask:
|
||||
radial-gradient(var(--gf-c), #000 98%, #0000) 0 0 / calc(3.4 * var(--gf-c)) calc(3.4 * var(--gf-c)) round,
|
||||
radial-gradient(calc(0.68 * var(--gf-c)), #000 98%, #0000) calc(1.7 * var(--gf-c)) calc(1.7 * var(--gf-c)) / calc(3.4 * var(--gf-c)) calc(3.4 * var(--gf-c)) round,
|
||||
radial-gradient(var(--gf-c), #000 62%, #0000 99%) 0 0 / calc(3.4 * var(--gf-c)) calc(3.4 * var(--gf-c)) round,
|
||||
radial-gradient(calc(0.68 * var(--gf-c)), #000 62%, #0000 99%) calc(1.7 * var(--gf-c)) calc(1.7 * var(--gf-c)) / calc(3.4 * var(--gf-c)) calc(3.4 * var(--gf-c)) round,
|
||||
linear-gradient(#000 0 0) 50% / calc(100% - 2.4 * var(--gf-c)) calc(100% - 2.4 * var(--gf-c)) no-repeat;
|
||||
}
|
||||
/* 右上飘着的小云 */
|
||||
@@ -692,9 +810,45 @@
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 异形裁切类的投影:mask 会裁掉 box-shadow,由父级 drop-shadow 跟随轮廓补投影 */
|
||||
/* ———— 云雾加持:拱门/椭圆/云朵边缘的飘动雾层 ————
|
||||
覆盖上方通用渐隐层:顶部柔光 + 两团缓慢漂移的白雾 + 底部升雾 + 周缘主题色淡晕。
|
||||
雾团层带显式 background-size,靠 background-position 关键帧漂移。 */
|
||||
.gf-arch::after,
|
||||
.gf-wreath::after,
|
||||
.gf-moonwin::after,
|
||||
.gf-oval::after,
|
||||
.gf-ovalwide::after,
|
||||
.gf-cloud::after {
|
||||
background:
|
||||
radial-gradient(120% 60% at 50% 0%, rgba(255, 255, 255, 0.32), transparent 46%) 50% 0 / 100% 100%,
|
||||
radial-gradient(50% 50% at 50% 50%, rgba(255, 255, 255, 0.5), transparent 72%) 8% 96% / 64% 34%,
|
||||
radial-gradient(50% 50% at 50% 50%, rgba(255, 255, 255, 0.4), transparent 74%) 92% 99% / 56% 28%,
|
||||
linear-gradient(0deg, color-mix(in srgb, var(--c-paper, #fffcf6) 58%, transparent), transparent 30%) 0 0 / 100% 100%,
|
||||
radial-gradient(ellipse 72% 72% at 50% 46%, transparent 56%, color-mix(in srgb, var(--c-paper, #fffcf6) 46%, transparent) 100%) 0 0 / 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
animation: gfFogDrift 15s ease-in-out infinite alternate;
|
||||
}
|
||||
@keyframes gfFogDrift {
|
||||
from {
|
||||
background-position: 50% 0, 0% 96%, 100% 99%, 0 0, 0 0;
|
||||
}
|
||||
to {
|
||||
background-position: 50% 0, 34% 90%, 64% 96%, 0 0, 0 0;
|
||||
}
|
||||
}
|
||||
/* 晨雾框的底部双雾团:缓慢涌动 */
|
||||
.gf-mist::before {
|
||||
animation: gfMistSway 11s ease-in-out infinite alternate;
|
||||
}
|
||||
@keyframes gfMistSway {
|
||||
from { transform: translateX(-7px) scaleY(1); }
|
||||
to { transform: translateX(7px) scaleY(1.12); }
|
||||
}
|
||||
|
||||
/* 异形裁切类的投影:mask 会裁掉 box-shadow,由父级 drop-shadow 跟随轮廓补投影
|
||||
(投影刻意压淡:裁切形状靠边缘虚化融入页面,重阴影会显得生硬) */
|
||||
.gf-shape-shadow {
|
||||
filter: drop-shadow(0 10px 18px rgba(96, 72, 50, 0.22));
|
||||
filter: drop-shadow(0 6px 12px rgba(96, 72, 50, 0.12));
|
||||
}
|
||||
|
||||
/* 辅助:图片保持原始宽高比(单图铺满+保比例时使用,覆盖 gf-frame 的 height:100%) */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
64
vite-tailwindcss/src/utils/calendarStyles.js
Normal file
64
vite-tailwindcss/src/utils/calendarStyles.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 日历(Save The Date 屏)视觉模板
|
||||
* - 配置值 'auto'(默认)= 跟随整站风格模板的推荐样式
|
||||
* - 也可手动指定任意样式,与主题解耦
|
||||
* 样式类挂在 .calendar-card 上:cal-style-<key>(CSS 见 theme-packs.css)
|
||||
*/
|
||||
export const DEFAULT_CALENDAR_STYLE = 'auto'
|
||||
|
||||
export const CALENDAR_STYLES = [
|
||||
{ key: 'classic', label: '香槟金卡', desc: '经典白卡金线(默认样式)' },
|
||||
{ key: 'french', label: '法式蕾丝', desc: '扇贝蕾丝边 · 花体粉金' },
|
||||
{ key: 'mono', label: '瑞士网格', desc: '粗黑标题 · 红黑双色' },
|
||||
{ key: 'picnic', label: '野餐格纹', desc: '绿白格布 · 樱桃婚期' },
|
||||
{ key: 'greenhouse', label: '玻璃温室', desc: '尖顶花房 · 玻璃格线' },
|
||||
{ key: 'herbarium', label: '植物标本', desc: '压花蕨叶 · 胶带标签' },
|
||||
{ key: 'fullmoon', label: '林间满月', desc: '夜空满月 · 桂枝剪影' },
|
||||
{ key: 'gardensign', label: '花园吊牌', desc: '木牌麻绳 · 白漆手写' },
|
||||
{ key: 'starry', label: '星夜弯月', desc: '闪烁星空 · 金月婚期' },
|
||||
{ key: 'aquarelle', label: '水彩晕染', desc: '多彩晕斑 · 水痕婚期' },
|
||||
{ key: 'hanami', label: '樱吹雪', desc: '飘落花瓣 · 樱花婚期' },
|
||||
{ key: 'redgold', label: '中式红金', desc: '宫墙红底 · 通栏金幅' },
|
||||
{ key: 'neochina', label: '月洞窗', desc: '圆窗题字 · 黛绿玉环' },
|
||||
{ key: 'ink', label: '水墨素卡', desc: '文人左排 · 朱砂闲章' },
|
||||
{ key: 'paper', label: '报刊粗格', desc: '表格网格 · 铅字排印' },
|
||||
{ key: 'magazine', label: '杂志内页', desc: '巨字刊号 · 黑白对比' },
|
||||
{ key: 'pixel', label: '像素方格', desc: '游戏窗口 · 标题栏' },
|
||||
{ key: 'minimal', label: '极简数字', desc: '非对称 · 超大细字' },
|
||||
{ key: 'journal', label: '手账贴纸', desc: '胶带贴纸 · 手绘圈' },
|
||||
{ key: 'ticket', label: '复古票根', desc: '票号条码 · 撕票口' },
|
||||
{ key: 'boarding', label: '登机牌', desc: '航司蓝条 · 舱位信息' },
|
||||
{ key: 'film', label: '胶片场记', desc: '上下齿孔 · 对焦婚期' },
|
||||
{ key: 'marquee', label: '影院灯牌', desc: '灯泡流转 · 金星婚期' },
|
||||
{ key: 'y2k', label: '千禧玻璃', desc: '玻璃拟态 · 光泽扫过' },
|
||||
{ key: 'gallery', label: '画廊白盒', desc: '画框标签 · 红点已定' },
|
||||
]
|
||||
|
||||
/** 各风格模板的专属日历样式(20 主题一对一,ticket 保留为手动可选) */
|
||||
const THEME_CALENDAR_MAP = {
|
||||
champagne: 'classic',
|
||||
french: 'french',
|
||||
modern: 'mono',
|
||||
forest: 'picnic',
|
||||
night: 'starry',
|
||||
watercolor: 'aquarelle',
|
||||
sakura: 'hanami',
|
||||
'guofeng-old': 'redgold',
|
||||
'guofeng-new': 'neochina',
|
||||
inkwash: 'ink',
|
||||
newspaper: 'paper',
|
||||
magazine: 'magazine',
|
||||
pixel: 'pixel',
|
||||
zen: 'minimal',
|
||||
scrapbook: 'journal',
|
||||
passport: 'boarding',
|
||||
cinema: 'marquee',
|
||||
kodak: 'film',
|
||||
arthouse: 'gallery',
|
||||
y2k: 'y2k',
|
||||
}
|
||||
|
||||
export function resolveCalendarStyle(value, themeKey) {
|
||||
if (value && value !== 'auto' && CALENDAR_STYLES.some((s) => s.key === value)) return value
|
||||
return THEME_CALENDAR_MAP[themeKey] || 'classic'
|
||||
}
|
||||
@@ -33,6 +33,16 @@ export const SITE_THEMES = [
|
||||
desc: '深蓝烫金 · 暗色主题 · 星月点缀',
|
||||
primary: '#c9a96a', line: '#8fa3c0', bg: '#101b2e',
|
||||
},
|
||||
{
|
||||
key: 'watercolor', kind: 'pack', label: '水彩画境',
|
||||
desc: '清透淡彩 · 晕染纹理 · 柔圆卡片',
|
||||
primary: '#7f9bb3', line: '#a8c0d0', bg: '#f7fafc',
|
||||
},
|
||||
{
|
||||
key: 'sakura', kind: 'pack', label: '樱花物语',
|
||||
desc: '粉白渐彩 · 花瓣纷落 · 春日气息',
|
||||
primary: '#d98a9e', line: '#e8b4c2', bg: '#fdf5f7',
|
||||
},
|
||||
// ———— 完整模板 ————
|
||||
{
|
||||
key: 'guofeng-new', kind: 'layout', label: '新中式',
|
||||
@@ -61,8 +71,43 @@ export const SITE_THEMES = [
|
||||
},
|
||||
{
|
||||
key: 'zen', kind: 'layout', label: '极简主义',
|
||||
desc: '黑白留白 · 细线 · 轴线排版',
|
||||
primary: '#2a2a28', line: '#8f8d88', bg: '#fafaf8',
|
||||
desc: '暖白留白 · 细线 · 轴线排版',
|
||||
primary: '#57503f', line: '#a89f8e', bg: '#faf7f2',
|
||||
},
|
||||
{
|
||||
key: 'inkwash', kind: 'layout', label: '水墨山水',
|
||||
desc: '宣纸墨韵 · 卷轴排版 · 朱砂印',
|
||||
primary: '#3a3f3a', line: '#8a9088', bg: '#f6f6f2',
|
||||
},
|
||||
{
|
||||
key: 'cinema', kind: 'layout', label: '复古电影',
|
||||
desc: '上映海报 · 主演字幕 · 场记章节',
|
||||
primary: '#b8322e', line: '#d4a24a', bg: '#f2e8d8',
|
||||
},
|
||||
{
|
||||
key: 'kodak', kind: 'layout', label: '胶片日记',
|
||||
desc: '胶卷齿孔 · 日期水印 · 柯达暖调',
|
||||
primary: '#e0782e', line: '#3c3a34', bg: '#faf6ec',
|
||||
},
|
||||
{
|
||||
key: 'arthouse', kind: 'layout', label: '文艺影调',
|
||||
desc: '对称构图 · 分幕章节 · 衬线留白',
|
||||
primary: '#4a4640', line: '#9a948a', bg: '#f4f1ea',
|
||||
},
|
||||
{
|
||||
key: 'passport', kind: 'layout', label: '环球旅行',
|
||||
desc: '护照登机牌 · 邮戳航线 · 出发到永远',
|
||||
primary: '#2e4a68', line: '#b08a4a', bg: '#f2f0e8',
|
||||
},
|
||||
{
|
||||
key: 'scrapbook', kind: 'layout', label: '手账拼贴',
|
||||
desc: '胶带贴纸 · 牛皮纸 · 手写标签',
|
||||
primary: '#c47a5a', line: '#8aa88a', bg: '#faf4e8',
|
||||
},
|
||||
{
|
||||
key: 'y2k', kind: 'layout', label: '千禧蒸汽波',
|
||||
desc: '复古窗口 · 渐变镜面字 · 星星光标',
|
||||
primary: '#7a4ae0', line: '#4adce0', bg: '#ece8f8',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -79,6 +124,7 @@ const LEGACY_THEME_MAP = {
|
||||
ocean: 'night',
|
||||
lilac: 'french',
|
||||
sunset: 'champagne',
|
||||
filmnoir: 'arthouse',
|
||||
}
|
||||
|
||||
export function normalizeSiteTheme(key) {
|
||||
@@ -102,13 +148,151 @@ export function isDarkTheme(key) {
|
||||
return !!siteThemeMeta(key).dark
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
主题特效色板(fx):点赞爱心 / 高光 / 花瓣 / 漂浮爱心
|
||||
未定义专属色板的主题回落到默认(香槟婚礼暖彩)
|
||||
============================================================ */
|
||||
const DEFAULT_FX = {
|
||||
like: [
|
||||
'#ff2442', '#ff2d55', '#ff4770', '#ff6b8a',
|
||||
'#f472b6', '#ec4899', '#e879f9', '#c084fc',
|
||||
'#a78bfa', '#818cf8', '#60a5fa', '#38bdf8',
|
||||
'#2dd4bf', '#34d399', '#fbbf24', '#f59e0b',
|
||||
'#fb923c', '#a88c6b', '#d4a574', '#e8b4b8',
|
||||
'#f43f5e', '#fb7185', '#fda4af',
|
||||
],
|
||||
likeGlow: [
|
||||
'#ff8da6', '#ffb0c4', '#f9a8d4', '#e9d5ff',
|
||||
'#c4b5fd', '#93c5fd', '#6ee7b7', '#fde68a',
|
||||
'#fdba74', '#f5d0b0', '#ffffff',
|
||||
],
|
||||
petal: ['#f7dce3', '#fbeee3', '#f3c9d6', '#efdcc8', '#f9e7d6'],
|
||||
bubble: ['#c9b08a', '#e2b3c0', '#d4af37', '#e8c9d2'],
|
||||
}
|
||||
|
||||
const THEME_FX = {
|
||||
night: {
|
||||
like: ['#c9a96a', '#e8cf96', '#8fa3c0', '#d4b060', '#f0e0b0', '#a8b8d8'],
|
||||
likeGlow: ['#f5e6b8', '#ffffff', '#c7d4ea'],
|
||||
petal: ['#c9a96a', '#8fa3c0', '#e8cf96', '#5a7099', '#d8c8a0'],
|
||||
bubble: ['#c9a96a', '#8fa3c0', '#ac9060'],
|
||||
},
|
||||
'guofeng-old': {
|
||||
like: ['#c9414e', '#e05a66', '#c9a24a', '#e8c878', '#a8323e', '#f0a0a0'],
|
||||
likeGlow: ['#f7e7c3', '#ffd9d0', '#ffe9a8'],
|
||||
petal: ['#f2c4c4', '#e8a8a8', '#e8d0a0', '#f0dcc8'],
|
||||
bubble: ['#c9a24a', '#d98a8a', '#b84a55'],
|
||||
},
|
||||
'guofeng-new': {
|
||||
like: ['#486156', '#6f8f80', '#b5493f', '#b0885a', '#8fae9c'],
|
||||
likeGlow: ['#d8e4dc', '#f0d8d0', '#e8d8c0'],
|
||||
petal: ['#d8e4d8', '#e8ded0', '#f0d0c8', '#dce8dc'],
|
||||
bubble: ['#8fae9c', '#c9a080', '#b5493f'],
|
||||
},
|
||||
magazine: {
|
||||
like: ['#1f1c19', '#4a453e', '#8a8378', '#b8ac9c', '#d4c8b8'],
|
||||
likeGlow: ['#ffffff', '#e8e0d4'],
|
||||
petal: ['#e8e2d8', '#d8d2c6', '#c8c2b6'],
|
||||
bubble: ['#8a8378', '#b0a898'],
|
||||
},
|
||||
newspaper: {
|
||||
like: ['#2b2822', '#57524a', '#8a8478', '#6b675e', '#a8a294'],
|
||||
likeGlow: ['#f6f3ea', '#dcd6c8'],
|
||||
petal: ['#e8e2d2', '#dcd6c4', '#d0cab8'],
|
||||
bubble: ['#8a8478', '#a8a294'],
|
||||
},
|
||||
pixel: {
|
||||
like: ['#c94f7c', '#5f9ea0', '#e8a03c', '#7a4ae0', '#4aa34a', '#e0557a'],
|
||||
likeGlow: ['#ffd9e8', '#c8f0f0', '#ffe9b8'],
|
||||
petal: ['#f7c8da', '#c0e4e4', '#f5e0b8', '#e0d0f5'],
|
||||
bubble: ['#c94f7c', '#5f9ea0', '#e8a03c'],
|
||||
},
|
||||
zen: {
|
||||
like: ['#57503f', '#7a7260', '#a89f8e', '#c4bcac'],
|
||||
likeGlow: ['#fffef9', '#ede8dc'],
|
||||
petal: ['#ece7dc', '#e0dace', '#d6cfc0'],
|
||||
bubble: ['#a89f8e', '#c0b8a8'],
|
||||
},
|
||||
watercolor: {
|
||||
like: ['#7f9bb3', '#a8c0d0', '#c8a8c0', '#88b8a8', '#d0b8d8'],
|
||||
likeGlow: ['#e8f0f8', '#ffffff'],
|
||||
petal: ['#dce8f0', '#e8dce8', '#d8e8e0', '#f0e8f0'],
|
||||
bubble: ['#a8c0d0', '#c8a8c0', '#88b8a8'],
|
||||
},
|
||||
sakura: {
|
||||
like: ['#d98a9e', '#e8a8b8', '#f0c0cc', '#c67689', '#f5d5dc'],
|
||||
likeGlow: ['#ffe4ec', '#ffffff'],
|
||||
petal: ['#f8d5de', '#f5c8d4', '#fce4ea', '#f0b8c8'],
|
||||
bubble: ['#e8b4c2', '#d98a9e', '#f0c0cc'],
|
||||
},
|
||||
inkwash: {
|
||||
like: ['#3a3f3a', '#5c625c', '#8a9088', '#b5493f', '#b8bcb4'],
|
||||
likeGlow: ['#eef0ec', '#ffffff'],
|
||||
petal: ['#dfe2dc', '#d0d4cc', '#e8eae4'],
|
||||
bubble: ['#8a9088', '#b5493f'],
|
||||
},
|
||||
cinema: {
|
||||
like: ['#b8322e', '#d4552e', '#d4a24a', '#8c2320', '#e8c878'],
|
||||
likeGlow: ['#ffe9c8', '#ffd9c0'],
|
||||
petal: ['#f0dcc0', '#e8d0b0', '#f5e6cc'],
|
||||
bubble: ['#d4a24a', '#b8322e'],
|
||||
},
|
||||
kodak: {
|
||||
like: ['#e0782e', '#e89a3c', '#c85a28', '#f0b860', '#a84a20'],
|
||||
likeGlow: ['#ffe0b8', '#ffefd0'],
|
||||
petal: ['#f5dcbe', '#f0d0a8', '#f8e8cc'],
|
||||
bubble: ['#e0782e', '#e89a3c'],
|
||||
},
|
||||
arthouse: {
|
||||
like: ['#4a4640', '#6e6a62', '#9a948a', '#b8b2a6'],
|
||||
likeGlow: ['#f4f1ea', '#ffffff'],
|
||||
petal: ['#e6e2d8', '#dcd8cc', '#d0ccc0'],
|
||||
bubble: ['#9a948a', '#b8b2a6'],
|
||||
},
|
||||
passport: {
|
||||
like: ['#2e4a68', '#4a6a8c', '#b08a4a', '#6a88a8', '#d0a860'],
|
||||
likeGlow: ['#e0ecf5', '#f0e4c8'],
|
||||
petal: ['#d8e2ec', '#e8e0cc', '#ccd8e4'],
|
||||
bubble: ['#4a6a8c', '#b08a4a'],
|
||||
},
|
||||
scrapbook: {
|
||||
like: ['#c47a5a', '#8aa88a', '#d8a848', '#a86a8c', '#6a9ab0'],
|
||||
likeGlow: ['#ffe8d0', '#e0f0e0'],
|
||||
petal: ['#f0dcc8', '#d8e8d0', '#f5e8c8', '#e8d0dc'],
|
||||
bubble: ['#c47a5a', '#8aa88a', '#d8a848'],
|
||||
},
|
||||
y2k: {
|
||||
like: ['#7a4ae0', '#4adce0', '#e04ad0', '#8c8cff', '#4a90e0'],
|
||||
likeGlow: ['#e0d8ff', '#c8f8ff', '#ffd8f8'],
|
||||
petal: ['#e0d8f8', '#d0f0f8', '#f0d8f0'],
|
||||
bubble: ['#7a4ae0', '#4adce0', '#e04ad0'],
|
||||
},
|
||||
}
|
||||
|
||||
/** 取主题特效色板(点赞/花瓣/漂浮爱心) */
|
||||
export function themeFx(key) {
|
||||
const k = normalizeSiteTheme(key)
|
||||
return { ...DEFAULT_FX, ...(THEME_FX[k] || {}) }
|
||||
}
|
||||
|
||||
/** 应用主题到整页(html[data-theme]),前台按配置、后台跟随编辑值 */
|
||||
const THEME_CACHE_KEY = 'wedding-site-theme'
|
||||
|
||||
export function applySiteTheme(key) {
|
||||
const k = normalizeSiteTheme(key)
|
||||
document.documentElement.dataset.theme = k
|
||||
try { localStorage.setItem(THEME_CACHE_KEY, k) } catch { /* 隐私模式等场景忽略 */ }
|
||||
if (k === 'pixel') ensurePixelCjkFont()
|
||||
}
|
||||
|
||||
/**
|
||||
* 上次访问缓存的主题:配置接口返回前先用它渲染 Loading 屏与首屏,
|
||||
* 二次访问即可看到专属主题 Loading,且避免主题闪变(首次访问回退默认)。
|
||||
*/
|
||||
export function cachedSiteTheme() {
|
||||
try { return normalizeSiteTheme(localStorage.getItem(THEME_CACHE_KEY) || DEFAULT_SITE_THEME) } catch { return DEFAULT_SITE_THEME }
|
||||
}
|
||||
|
||||
/**
|
||||
* 中文像素字形按需加载:
|
||||
* 本地 @fontsource 包只含 latin 子集(英文/数字像素),中日韩全量字形
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,16 +29,16 @@
|
||||
<div class="boot-frame boot-frame-br" aria-hidden="true"></div>
|
||||
|
||||
<div class="boot-stage">
|
||||
<span class="boot-kicker calligraphy">WEDDING INVITATION</span>
|
||||
<span class="boot-kicker calligraphy">{{ bootKickerText }}</span>
|
||||
<div class="boot-rule" aria-hidden="true"><span></span><i>✦</i><span></span></div>
|
||||
<div class="boot-seal-wrap">
|
||||
<i class="boot-seal-ring boot-seal-ring-a" aria-hidden="true"></i>
|
||||
<i class="boot-seal-ring boot-seal-ring-b" aria-hidden="true"></i>
|
||||
<div class="boot-seal calligraphy">囍</div>
|
||||
<div class="boot-seal calligraphy">{{ bootSealChar }}</div>
|
||||
</div>
|
||||
<div class="boot-rule boot-rule-flip" aria-hidden="true"><span></span><i>✦</i><span></span></div>
|
||||
<p class="boot-names calligraphy-strong">{{ data.groom }}<span>&</span>{{ data.bride }}</p>
|
||||
<p class="boot-hint calligraphy">请柬装载中</p>
|
||||
<p class="boot-hint calligraphy">{{ bootHintText }}</p>
|
||||
<div class="boot-dots" aria-hidden="true"><i></i><i></i><i></i></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -82,6 +82,77 @@
|
||||
<div class="em-go-date em-go-lunar">{{ data.lunar }} 恭候光临</div>
|
||||
<div class="emblem-hint"><span class="dot"></span>轻触展开婚书</div>
|
||||
</template>
|
||||
<!-- 水墨山水:卷轴帖 -->
|
||||
<template v-else-if="siteThemeKey === 'inkwash'">
|
||||
<i class="em-ink-rod" aria-hidden="true"></i>
|
||||
<div class="em-ink-title">谨 订 婚 期</div>
|
||||
<div class="em-ink-names">{{ data.groom }} 与 {{ data.bride }}</div>
|
||||
<div class="em-ink-date">{{ data.date }} · {{ data.lunar }}</div>
|
||||
<span class="em-ink-seal" aria-hidden="true">囍</span>
|
||||
<i class="em-ink-rod" aria-hidden="true"></i>
|
||||
<div class="emblem-hint"><span class="dot"></span>轻触展卷</div>
|
||||
</template>
|
||||
<!-- 复古电影:三段式上映海报(横幅 / 主标 / 制作名单) -->
|
||||
<template v-else-if="siteThemeKey === 'cinema'">
|
||||
<div class="em-cn-top">
|
||||
<span class="em-cn-badge">COMING SOON</span>
|
||||
<span class="em-cn-studio">LOVE PICTURES 荣誉出品</span>
|
||||
</div>
|
||||
<div class="em-cn-mid">
|
||||
<div class="em-cn-title">THE WEDDING</div>
|
||||
<div class="em-cn-star">领衔主演</div>
|
||||
<div class="em-cn-names">{{ data.groom }} · {{ data.bride }}</div>
|
||||
<div class="em-cn-date">{{ data.date }} 隆重献映</div>
|
||||
</div>
|
||||
<div class="em-cn-bottom">
|
||||
<div class="em-cn-credits">出品·缘分 / 监制·时光 / 特别出演·各位亲友</div>
|
||||
<div class="emblem-hint"><span class="dot"></span>轻触入场</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 胶片日记:胶卷盒 -->
|
||||
<template v-else-if="siteThemeKey === 'kodak'">
|
||||
<span class="em-kd-strip" aria-hidden="true"><i></i><i></i><i></i></span>
|
||||
<div class="em-kd-title">WEDDING FILM</div>
|
||||
<div class="em-kd-sub">COLOR · 35mm · 24 EXP</div>
|
||||
<div class="em-kd-names">{{ data.groom }} ♥ {{ data.bride }}</div>
|
||||
<div class="em-kd-date">{{ data.date }}</div>
|
||||
<div class="emblem-hint"><span class="dot"></span>轻触冲印</div>
|
||||
</template>
|
||||
<!-- 文艺影调:分幕页 -->
|
||||
<template v-else-if="siteThemeKey === 'arthouse'">
|
||||
<span class="em-ah-chapter">— CHAPITRE UN —</span>
|
||||
<div class="em-ah-names">{{ data.groom }} <em>et</em> {{ data.bride }}</div>
|
||||
<i class="em-ah-line" aria-hidden="true"></i>
|
||||
<div class="em-ah-date">{{ data.date }}</div>
|
||||
<div class="emblem-hint"><span class="dot"></span>轻触开场</div>
|
||||
</template>
|
||||
<!-- 环球旅行:护照封面 -->
|
||||
<template v-else-if="siteThemeKey === 'passport'">
|
||||
<span class="em-pp-top">LOVE REPUBLIC</span>
|
||||
<span class="em-pp-crest" aria-hidden="true">✈</span>
|
||||
<div class="em-pp-title">PASSPORT</div>
|
||||
<div class="em-pp-sub">TO FOREVER</div>
|
||||
<div class="em-pp-names">{{ data.groom }} / {{ data.bride }}</div>
|
||||
<div class="emblem-hint"><span class="dot"></span>轻触通关</div>
|
||||
</template>
|
||||
<!-- 手账拼贴:日记封面 -->
|
||||
<template v-else-if="siteThemeKey === 'scrapbook'">
|
||||
<span class="em-sb-tape" aria-hidden="true"></span>
|
||||
<div class="em-sb-label">
|
||||
<span class="em-sb-small">our story ♥</span>
|
||||
<div class="em-sb-names">{{ data.groom }} & {{ data.bride }}</div>
|
||||
<span class="em-sb-date">{{ data.date }}</span>
|
||||
</div>
|
||||
<div class="emblem-hint"><span class="dot"></span>轻触翻开手账</div>
|
||||
</template>
|
||||
<!-- 千禧蒸汽波:开机窗口 -->
|
||||
<template v-else-if="siteThemeKey === 'y2k'">
|
||||
<div class="em-yk-bar"><span>welcome.exe</span><i aria-hidden="true">✕</i></div>
|
||||
<div class="em-yk-names">{{ data.groom }} ♥ {{ data.bride }}</div>
|
||||
<div class="em-yk-date">{{ data.date }} ☆ {{ data.lunar }}</div>
|
||||
<div class="em-yk-load" aria-hidden="true"><i></i></div>
|
||||
<div class="emblem-hint"><span class="dot"></span>click to start</div>
|
||||
</template>
|
||||
<!-- 杂志封面:刊头式幕布 -->
|
||||
<template v-else-if="siteThemeKey === 'magazine'">
|
||||
<span class="em-mag-logo">VOWS</span>
|
||||
@@ -142,6 +213,8 @@
|
||||
:petal-enabled="data.petalEnabled"
|
||||
:bubble-enabled="data.bubbleEnabled"
|
||||
:active="effectsActive"
|
||||
:petal-palette="themeFxObj.petal"
|
||||
:heart-palette="themeFxObj.bubble"
|
||||
/>
|
||||
|
||||
<DanmakuLayer
|
||||
@@ -447,7 +520,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LikeBurst ref="likeBurstRef" />
|
||||
<LikeBurst ref="likeBurstRef" :colors="themeFxObj.like" :glow-colors="themeFxObj.likeGlow" />
|
||||
<GiftEffect v-if="giftFxBackend === 'classic'" ref="giftEffectRef" @play-start="onGiftPlayStart" />
|
||||
<GiftEffect3D v-else ref="giftEffectRef" @play-start="onGiftPlayStart" @fallback="onGiftFxFallback" />
|
||||
<GiftFeed ref="giftFeedRef" />
|
||||
@@ -569,7 +642,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 2. 邀请文案页(风格包=经典竖排;完整模板=各自专属排版) -->
|
||||
<div class="page-section bg-[var(--c-bg)] items-center px-8 relative justify-center" :class="data.scrollMode === 'snap' ? 'snap-start' : ''" :style="formalPageHeightStyle">
|
||||
<div class="page-section items-center px-8 relative justify-center" :class="data.scrollMode === 'snap' ? 'snap-start' : ''" :style="formalPageHeightStyle">
|
||||
<template v-if="!isLayoutTheme">
|
||||
<div class="absolute top-0 left-0 w-32 h-32 border-l-[0.5px] border-t-[0.5px] border-[var(--c-primary)]/30 m-6"></div>
|
||||
<div class="absolute bottom-0 right-0 w-32 h-32 border-r-[0.5px] border-b-[0.5px] border-[var(--c-primary)]/30 m-6"></div>
|
||||
@@ -596,6 +669,56 @@
|
||||
<span class="fgo-foot">两 姓 联 姻 · 一 堂 缔 约</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 水墨:竖排 + 墨线 + 朱印 -->
|
||||
<div v-else-if="siteThemeKey === 'inkwash'" v-reveal="'blur'" class="formal-ink">
|
||||
<div class="fi-cols">
|
||||
<span class="vertical-text fi-main">{{ data.formalText1 }}</span>
|
||||
<span class="vertical-text fi-main fi-main-2">{{ data.formalText2 }}</span>
|
||||
</div>
|
||||
<span class="fi-seal" aria-hidden="true">囍</span>
|
||||
</div>
|
||||
<!-- 电影:台词字幕卡 -->
|
||||
<div v-else-if="siteThemeKey === 'cinema'" v-reveal="'up'" class="formal-cn">
|
||||
<span class="fc-time">00:52:13,600 → 00:52:20,140</span>
|
||||
<p class="fc-line">“{{ data.formalText1 }}”</p>
|
||||
<p class="fc-line fc-line-2">“{{ data.formalText2 }}”</p>
|
||||
<span class="fc-note">—— 选自《我们的婚礼》未删减版</span>
|
||||
</div>
|
||||
<!-- 胶片:相纸背书 -->
|
||||
<div v-else-if="siteThemeKey === 'kodak'" v-reveal="'up'" class="formal-kd">
|
||||
<span class="fk-head">— 相纸背面的话 —</span>
|
||||
<p class="fk-line">{{ data.formalText1 }}</p>
|
||||
<p class="fk-line">{{ data.formalText2 }}</p>
|
||||
<span class="fk-stamp">{{ data.date }}</span>
|
||||
</div>
|
||||
<!-- 文艺:居中引言 + 罗马数字 -->
|
||||
<div v-else-if="siteThemeKey === 'arthouse'" v-reveal="'up'" class="formal-ah">
|
||||
<span class="fa-num">Ⅰ</span>
|
||||
<p class="fa-line">{{ data.formalText1 }}</p>
|
||||
<p class="fa-line fa-line-2">{{ data.formalText2 }}</p>
|
||||
</div>
|
||||
<!-- 旅行:签证页 -->
|
||||
<div v-else-if="siteThemeKey === 'passport'" v-reveal="'up'" class="formal-pp">
|
||||
<span class="fp-visa" aria-hidden="true">VISA · APPROVED</span>
|
||||
<p class="fp-line">{{ data.formalText1 }}</p>
|
||||
<p class="fp-line">{{ data.formalText2 }}</p>
|
||||
<span class="fp-foot">✈ DESTINATION: FOREVER</span>
|
||||
</div>
|
||||
<!-- 手账:便签纸 -->
|
||||
<div v-else-if="siteThemeKey === 'scrapbook'" v-reveal="'up'" class="formal-sb">
|
||||
<span class="fs-clip" aria-hidden="true"></span>
|
||||
<p class="fs-line">{{ data.formalText1 }}</p>
|
||||
<p class="fs-line">{{ data.formalText2 }}</p>
|
||||
</div>
|
||||
<!-- Y2K:对话框窗口 -->
|
||||
<div v-else-if="siteThemeKey === 'y2k'" v-reveal="'up'" class="formal-yk">
|
||||
<div class="fy-bar"><span>message.txt</span><i aria-hidden="true">✕</i></div>
|
||||
<div class="fy-body">
|
||||
<p class="fy-line">{{ data.formalText1 }}</p>
|
||||
<p class="fy-line">{{ data.formalText2 }}</p>
|
||||
<span class="fy-btn" aria-hidden="true">OK ♥</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 杂志:编辑部引言(pull-quote) -->
|
||||
<div v-else-if="siteThemeKey === 'magazine'" v-reveal="'up'" class="formal-mag">
|
||||
<span class="fm-quote" aria-hidden="true">“</span>
|
||||
@@ -643,7 +766,7 @@
|
||||
class="page-section p-6 relative"
|
||||
:class="data.scrollMode === 'snap' ? 'snap-start' : ''"
|
||||
:style="pageHeightStyle(page)">
|
||||
<div class="absolute inset-0 bg-[var(--c-card)]/40 -z-10"></div>
|
||||
<div class="absolute inset-0 bg-[var(--c-card)]/20 -z-10"></div>
|
||||
<div class="absolute top-0 left-0 w-24 h-24 border-l-[0.5px] border-t-[0.5px] border-[var(--c-primary)]/30 m-6 pointer-events-none z-0"></div>
|
||||
<div class="absolute bottom-0 right-0 w-24 h-24 border-r-[0.5px] border-b-[0.5px] border-[var(--c-primary)]/30 m-6 pointer-events-none z-0"></div>
|
||||
<div class="w-full h-full flex flex-col justify-center py-6 relative z-10">
|
||||
@@ -651,7 +774,7 @@
|
||||
:page="page"
|
||||
:nine-grid-animate="!!data.nineGridAnimate"
|
||||
:layout-theme="isLayoutTheme"
|
||||
:page-index="pIdx + 2"
|
||||
:page-index="sectionIndex(pIdx + 2)"
|
||||
@preview-image="openImagePreview"
|
||||
@ready="onGalleryReady"
|
||||
/>
|
||||
@@ -660,10 +783,10 @@
|
||||
|
||||
<!-- 婚礼日历 -->
|
||||
<div class="page-section wedding-calendar-page px-6 py-10 relative" :class="data.scrollMode === 'snap' ? 'snap-start' : ''">
|
||||
<div v-reveal="'up'" class="calendar-card">
|
||||
<div v-reveal="'up'" class="calendar-card" :class="calStyleClass">
|
||||
<SectionHeading
|
||||
v-if="isLayoutTheme"
|
||||
:index="(data.photoPages?.length || 0) + 2"
|
||||
:index="sectionIndex((data.photoPages?.length || 0) + 2)"
|
||||
kicker="Save The Date"
|
||||
:title="`${calendarTitle.year} · ${calendarTitle.monthText}`"
|
||||
/>
|
||||
@@ -730,7 +853,7 @@
|
||||
<SectionHeading
|
||||
v-if="isLayoutTheme"
|
||||
v-reveal="'up'"
|
||||
:index="(data.photoPages?.length || 0) + 3"
|
||||
:index="sectionIndex((data.photoPages?.length || 0) + 3)"
|
||||
kicker="Wedding Schedule"
|
||||
title="婚礼流程"
|
||||
/>
|
||||
@@ -756,18 +879,11 @@
|
||||
|
||||
<!-- 尾页 -->
|
||||
<div class="page-section ending-page px-6 py-8 flex flex-col items-center justify-center relative" :class="data.scrollMode === 'snap' ? 'snap-start' : ''">
|
||||
<!-- 门框装饰仅风格包主题渲染;完整模板走各自的收尾排版 -->
|
||||
<!-- 尾页装饰仅风格包主题渲染(幕布/花环/格窗/星环/缎带,后台可选);完整模板走各自的收尾排版 -->
|
||||
<template v-if="!isLayoutTheme">
|
||||
<div v-reveal="'blur'" class="ending-frame" aria-hidden="true">
|
||||
<span class="ending-keystone">
|
||||
<i class="ek-line"></i>
|
||||
<span class="ek-mark">❀</span>
|
||||
<i class="ek-line ek-line-r"></i>
|
||||
</span>
|
||||
<span class="ending-vine ending-vine-l"></span>
|
||||
<span class="ending-vine ending-vine-r"></span>
|
||||
<div v-reveal="'blur'" class="ending-decor" :class="`ending-style-${endingStyleKey}`" aria-hidden="true">
|
||||
<i class="ed-a"></i><i class="ed-b"></i><i class="ed-c"></i><i class="ed-d"></i>
|
||||
</div>
|
||||
<div v-reveal="{ variant: 'scale', delay: 220 }" class="ending-corners" aria-hidden="true"><i></i><i></i><i></i><i></i></div>
|
||||
</template>
|
||||
<div class="w-full max-w-[340px] flex flex-col items-center z-10 relative">
|
||||
<SectionHeading v-if="isLayoutTheme" v-reveal="'down'" title="With Love" kicker="Ending" />
|
||||
@@ -809,6 +925,13 @@
|
||||
<span v-else-if="siteThemeKey === 'newspaper'" class="elf elf-news">── 本版终 · THE END ──</span>
|
||||
<span v-else-if="siteThemeKey === 'magazine'" class="elf elf-mag">BACK COVER · VOWS MAGAZINE</span>
|
||||
<span v-else-if="siteThemeKey === 'pixel'" class="elf elf-pixel">♥ GAME CLEAR · THANK YOU FOR PLAYING ♥</span>
|
||||
<span v-else-if="siteThemeKey === 'inkwash'" class="elf elf-ink">水墨长卷 <b>囍</b> 落款于此</span>
|
||||
<span v-else-if="siteThemeKey === 'cinema'" class="elf elf-cn">THE END · 全 剧 终</span>
|
||||
<span v-else-if="siteThemeKey === 'kodak'" class="elf elf-kd">DEVELOPED WITH LOVE · 24 EXP</span>
|
||||
<span v-else-if="siteThemeKey === 'arthouse'" class="elf elf-ah">fin — merci</span>
|
||||
<span v-else-if="siteThemeKey === 'passport'" class="elf elf-pp">BON VOYAGE · 旅途愉快 ✈</span>
|
||||
<span v-else-if="siteThemeKey === 'scrapbook'" class="elf elf-sb">❀ to be continued…</span>
|
||||
<span v-else-if="siteThemeKey === 'y2k'" class="elf elf-yk">☆ see u at the wedding ☆</span>
|
||||
<span v-else class="elf elf-zen">fin</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1029,7 +1152,8 @@ import {
|
||||
import { sendVisitBeacon, getFingerprint } from '@/utils/visitBeacon'
|
||||
import { optimizeImageUrl, isIOS } from '@/composables/useAndroidOptimize'
|
||||
import { DANMAKU_COLORS, DANMAKU_GRADIENT_COLORS, DEFAULT_DANMAKU_COLOR, BLESSING_STYLES } from '@/utils/danmakuColors'
|
||||
import { applySiteTheme, normalizeSiteTheme, themeLayoutKind } from '@/utils/themes'
|
||||
import { applySiteTheme, normalizeSiteTheme, themeLayoutKind, themeFx, cachedSiteTheme } from '@/utils/themes'
|
||||
import { resolveCalendarStyle } from '@/utils/calendarStyles'
|
||||
import SectionHeading from '@/components/themes/SectionHeading.vue'
|
||||
import CoverClassic from '@/components/themes/covers/CoverClassic.vue'
|
||||
import CoverMagazine from '@/components/themes/covers/CoverMagazine.vue'
|
||||
@@ -1038,6 +1162,13 @@ import CoverPixel from '@/components/themes/covers/CoverPixel.vue'
|
||||
import CoverZen from '@/components/themes/covers/CoverZen.vue'
|
||||
import CoverGuofengNew from '@/components/themes/covers/CoverGuofengNew.vue'
|
||||
import CoverGuofengOld from '@/components/themes/covers/CoverGuofengOld.vue'
|
||||
import CoverInkwash from '@/components/themes/covers/CoverInkwash.vue'
|
||||
import CoverCinema from '@/components/themes/covers/CoverCinema.vue'
|
||||
import CoverKodak from '@/components/themes/covers/CoverKodak.vue'
|
||||
import CoverArthouse from '@/components/themes/covers/CoverArthouse.vue'
|
||||
import CoverPassport from '@/components/themes/covers/CoverPassport.vue'
|
||||
import CoverScrapbook from '@/components/themes/covers/CoverScrapbook.vue'
|
||||
import CoverY2k from '@/components/themes/covers/CoverY2k.vue'
|
||||
import { parseLrc, formatAudioTime, findLrcIndex } from '@/utils/parseLrc'
|
||||
import CanvasEffects from '@/components/CanvasEffects.vue'
|
||||
import DanmakuLayer from '@/components/DanmakuLayer.vue'
|
||||
@@ -1055,7 +1186,10 @@ const DEFAULTS = {
|
||||
musicCacheEnabled: true, musicCacheHours: 24,
|
||||
danmakuEnabled: true, danmakuShowTime: true, nineGridAnimate: false,
|
||||
giftEffectMode: 'classic', // 礼物特效方案:classic 经典粒子 / three 立体炫效 / auto 按设备
|
||||
siteTheme: 'champagne', // 整站主题:champagne/crimson/sage/ocean/lilac/sunset
|
||||
siteTheme: cachedSiteTheme(), // 整站风格模板(初始取上次缓存,Loading 屏即主题化)
|
||||
endingFrameStyle: 'curtain', // 尾页装饰:curtain 幕布 / moonwreath 花环 / frwindow 格窗 / starring 星环 / ribbons 缎带
|
||||
sectionIndexEnabled: true, // 完整模板的章节编号(01/02…)是否显示
|
||||
calendarStyle: 'auto', // 日历样式:auto=跟随主题,或指定 calendarStyles.js 中的样式
|
||||
|
||||
freeScrollInterval: 50, // 自由滚动速度:滚动间隔 ms(越小越快)
|
||||
pageSwitchDuration: 800, // 页面切换速度:翻页动画时长 ms
|
||||
@@ -1225,6 +1359,26 @@ watch(() => data.value.siteTheme, (t) => applySiteTheme(t), { immediate: true })
|
||||
/** 当前主题(归一化后)与布局类型(pack=风格包沿用经典排版 / layout=完整模板) */
|
||||
const siteThemeKey = computed(() => normalizeSiteTheme(data.value.siteTheme))
|
||||
const isLayoutTheme = computed(() => themeLayoutKind(siteThemeKey.value) === 'layout')
|
||||
/** Loading 屏专属文案:中心印记 / 加载提示 / 顶部小字(缺省回退经典请柬) */
|
||||
const BOOT_THEME_TEXT = {
|
||||
'guofeng-old': { hint: '花轿启程中', kicker: '大喜之日' },
|
||||
'guofeng-new': { hint: '良辰将至', kicker: '喜结良缘' },
|
||||
magazine: { seal: 'V', hint: 'COVER STORY LOADING', kicker: 'VOWS MAGAZINE' },
|
||||
newspaper: { seal: '讯', hint: '头版排印中', kicker: 'THE WEDDING TIMES' },
|
||||
pixel: { seal: '♥', hint: 'NOW LOADING', kicker: 'WEDDING QUEST' },
|
||||
zen: { seal: '静', hint: '静候片刻', kicker: 'SLOW WEDDING' },
|
||||
inkwash: { seal: '墨', hint: '墨韵晕染中', kicker: '水墨婚书' },
|
||||
cinema: { seal: '影', hint: '影片装带中', kicker: 'NOW SHOWING' },
|
||||
kodak: { seal: '35', hint: 'DEVELOPING…', kicker: 'FILM MEMORIES' },
|
||||
arthouse: { seal: 'A', hint: '布展中', kicker: 'ART HOUSE' },
|
||||
passport: { seal: '✈', hint: '安检放行中', kicker: 'BOARDING PASS' },
|
||||
scrapbook: { seal: '贴', hint: '手帐拼贴中', kicker: 'OUR SCRAPBOOK' },
|
||||
y2k: { seal: '★', hint: 'LOADING 98%', kicker: 'WEDDING.EXE' },
|
||||
}
|
||||
const bootThemeText = computed(() => BOOT_THEME_TEXT[siteThemeKey.value] || {})
|
||||
const bootSealChar = computed(() => bootThemeText.value.seal || '囍')
|
||||
const bootHintText = computed(() => bootThemeText.value.hint || '请柬装载中')
|
||||
const bootKickerText = computed(() => bootThemeText.value.kicker || 'WEDDING INVITATION')
|
||||
/** 封面组件:风格包共用经典封面,完整模板各用专属排版 */
|
||||
const coverComponent = computed(() => ({
|
||||
'guofeng-new': CoverGuofengNew,
|
||||
@@ -1233,13 +1387,31 @@ const coverComponent = computed(() => ({
|
||||
newspaper: CoverNewspaper,
|
||||
pixel: CoverPixel,
|
||||
zen: CoverZen,
|
||||
inkwash: CoverInkwash,
|
||||
cinema: CoverCinema,
|
||||
kodak: CoverKodak,
|
||||
arthouse: CoverArthouse,
|
||||
passport: CoverPassport,
|
||||
scrapbook: CoverScrapbook,
|
||||
y2k: CoverY2k,
|
||||
}[siteThemeKey.value] || CoverClassic))
|
||||
/** 章节编号:可在后台关闭(传 0 时 SectionHeading 不渲染编号) */
|
||||
const sectionIndex = (n) => (data.value.sectionIndexEnabled === false ? 0 : n)
|
||||
/** 主题特效色板:点赞爱心 / 花瓣 / 漂浮爱心随模板换色 */
|
||||
const themeFxObj = computed(() => themeFx(siteThemeKey.value))
|
||||
/** 日历样式:auto 跟随主题推荐,可在后台手动指定 */
|
||||
const calStyleClass = computed(() => `cal-style-${resolveCalendarStyle(data.value.calendarStyle, siteThemeKey.value)}`)
|
||||
/** 尾页装饰样式(风格包主题下生效,未知值回落幕布) */
|
||||
const ENDING_FRAME_STYLES = ['curtain', 'moonwreath', 'frwindow', 'starring', 'ribbons']
|
||||
const endingStyleKey = computed(() => (ENDING_FRAME_STYLES.includes(data.value.endingFrameStyle) ? data.value.endingFrameStyle : 'curtain'))
|
||||
/** 幕布印章符号:各风格包的辨识元素(空 = 默认羽毛图标) */
|
||||
const curtainSealChar = computed(() => ({
|
||||
french: '❀',
|
||||
forest: '❧',
|
||||
night: '☾',
|
||||
modern: '◆',
|
||||
watercolor: '❋',
|
||||
sakura: '❀',
|
||||
}[siteThemeKey.value] || ''))
|
||||
/** 邀请文案页首符号:随风格包变化 */
|
||||
const formalMark = computed(() => ({
|
||||
@@ -1247,6 +1419,8 @@ const formalMark = computed(() => ({
|
||||
forest: '❦',
|
||||
night: '☾',
|
||||
modern: '◇',
|
||||
watercolor: '❋',
|
||||
sakura: '✿',
|
||||
}[siteThemeKey.value] || '❀'))
|
||||
const likeCount = ref(0)
|
||||
const likePulse = ref(false)
|
||||
@@ -3735,6 +3909,16 @@ watch(
|
||||
radial-gradient(circle at 82% 82%, rgba(var(--c-primary-rgb),0.12), transparent 28%);
|
||||
}
|
||||
.calendar-card {
|
||||
/* 成套配色变量:默认为香槟金卡观感,各 cal-style-* 皮肤整组覆盖(见 theme-packs.css) */
|
||||
--cal-title: #3f3428;
|
||||
--cal-sub: #806f5c;
|
||||
--cal-kicker: var(--c-primary);
|
||||
--cal-week: var(--c-primary);
|
||||
--cal-day: #4b443c;
|
||||
--cal-sel-ink: #763342;
|
||||
--cal-sel-bg: rgba(253, 251, 247, 0.9);
|
||||
--cal-rule: rgba(var(--c-primary-rgb), 0.45);
|
||||
--cal-title-rule: rgba(var(--c-primary-rgb), 0.8);
|
||||
width: min(100%, 352px); margin: 0 auto; padding: 30px 24px 28px;
|
||||
color: var(--c-ink-soft); text-align: center; position: relative;
|
||||
background:
|
||||
@@ -3749,19 +3933,19 @@ watch(
|
||||
border: 1px solid rgba(var(--c-primary-rgb),0.16); pointer-events: none;
|
||||
}
|
||||
.calendar-kicker {
|
||||
color: var(--c-primary); font-family: var(--font-display);
|
||||
color: var(--cal-kicker); font-family: var(--font-display);
|
||||
font-size: 10px; letter-spacing: 0.46em; padding-left: 0.46em; text-transform: uppercase;
|
||||
}
|
||||
.calendar-title {
|
||||
display: flex; align-items: center; justify-content: center; gap: 14px;
|
||||
margin-top: 12px; color: #3f3428; font-family: var(--font-display);
|
||||
margin-top: 12px; color: var(--cal-title); font-family: var(--font-display);
|
||||
font-size: 30px; line-height: 1.2; letter-spacing: 0.12em;
|
||||
}
|
||||
.calendar-title i {
|
||||
width: 34px; height: 1px; background: linear-gradient(90deg, transparent, rgba(var(--c-primary-rgb),0.8), transparent);
|
||||
width: 34px; height: 1px; background: linear-gradient(90deg, transparent, var(--cal-title-rule), transparent);
|
||||
}
|
||||
.calendar-date-line {
|
||||
margin: 10px auto 22px; color: #806f5c; font-size: 11px;
|
||||
margin: 10px auto 22px; color: var(--cal-sub); font-size: 11px;
|
||||
letter-spacing: 0.16em; font-family: var(--font-serif);
|
||||
}
|
||||
.calendar-weekdays,
|
||||
@@ -3769,12 +3953,12 @@ watch(
|
||||
display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px;
|
||||
}
|
||||
.calendar-weekdays {
|
||||
margin-bottom: 9px; color: var(--c-primary); font-size: 10px;
|
||||
margin-bottom: 9px; color: var(--cal-week); font-size: 10px;
|
||||
font-family: var(--font-kai); font-weight: 700;
|
||||
}
|
||||
.calendar-day {
|
||||
position: relative; aspect-ratio: 1; display: flex; align-items: center; justify-content: center;
|
||||
color: #4b443c; font-family: var(--font-number); font-size: 15px;
|
||||
color: var(--cal-day); font-family: var(--font-number); font-size: 15px;
|
||||
}
|
||||
.calendar-day.is-empty { visibility: hidden; }
|
||||
.calendar-day-num {
|
||||
@@ -3783,8 +3967,8 @@ watch(
|
||||
.calendar-day.is-selected .calendar-day-num {
|
||||
width: 24px; height: 24px; border-radius: 9999px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #763342; font-weight: 900; font-size: 16px;
|
||||
background: rgba(253,251,247,0.9);
|
||||
color: var(--cal-sel-ink); font-weight: 900; font-size: 16px;
|
||||
background: var(--cal-sel-bg);
|
||||
box-shadow: 0 0 0 1px rgba(151,64,82,0.12), 0 4px 10px rgba(91,47,36,0.1);
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,0.82);
|
||||
}
|
||||
@@ -3843,11 +4027,11 @@ watch(
|
||||
}
|
||||
.calendar-note {
|
||||
display: flex; align-items: center; justify-content: center; gap: 10px;
|
||||
margin-top: 24px; color: #806f5c; font-family: var(--font-kai);
|
||||
margin-top: 24px; color: var(--cal-sub); font-family: var(--font-kai);
|
||||
font-size: 13px; font-weight: 700; letter-spacing: 0.2em;
|
||||
}
|
||||
.calendar-note i {
|
||||
width: 20px; height: 1px; background: rgba(var(--c-primary-rgb),0.45);
|
||||
width: 20px; height: 1px; background: var(--cal-rule);
|
||||
}
|
||||
|
||||
/* ---------- 揭幕前 Loading(象牙金主题) ---------- */
|
||||
@@ -3864,10 +4048,10 @@ watch(
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(ellipse at 50% 42%, rgba(255, 252, 246, 0.96), transparent 55%),
|
||||
radial-gradient(ellipse at 50% 42%, rgba(255, 252, 246, 0.72), transparent 55%),
|
||||
radial-gradient(ellipse at 18% 78%, rgba(var(--c-line-rgb), 0.14), transparent 42%),
|
||||
radial-gradient(ellipse at 82% 18%, rgba(var(--c-primary-rgb), 0.12), transparent 40%),
|
||||
linear-gradient(165deg, var(--c-paper) 0%, #f7efe4 46%, #eddcc6 100%);
|
||||
linear-gradient(165deg, var(--c-paper) 0%, var(--c-bg) 46%, var(--c-bg-deep) 100%);
|
||||
transition: opacity 0.48s ease, visibility 0.48s ease, transform 0.48s ease;
|
||||
}
|
||||
.boot-loading.is-leaving {
|
||||
@@ -4022,7 +4206,7 @@ watch(
|
||||
color: var(--boot-gold);
|
||||
background:
|
||||
radial-gradient(circle at 34% 28%, rgba(255, 255, 255, 0.65), transparent 46%),
|
||||
linear-gradient(160deg, #fffaf3, #f3e6d6);
|
||||
linear-gradient(160deg, var(--c-card), var(--c-bg-deep));
|
||||
border: 1.5px solid rgba(var(--c-primary-rgb), 0.55);
|
||||
box-shadow:
|
||||
inset 0 0 0 5px rgba(var(--c-primary-rgb), 0.08),
|
||||
@@ -4050,7 +4234,8 @@ watch(
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.38em;
|
||||
padding-left: 0.38em;
|
||||
color: rgba(128, 106, 77, 0.78);
|
||||
color: var(--boot-ink);
|
||||
opacity: 0.78;
|
||||
}
|
||||
.boot-dots {
|
||||
display: flex;
|
||||
@@ -4510,76 +4695,209 @@ watch(
|
||||
linear-gradient(180deg, rgba(253,251,247,0.92) 0%, rgba(247,246,242,0.98) 100%),
|
||||
radial-gradient(circle at 50% 12%, rgba(232,201,210,0.2), transparent 34%);
|
||||
}
|
||||
.ending-frame {
|
||||
position: absolute; top: 28px; right: 22px; bottom: 28px; left: 22px;
|
||||
border: 1px solid rgba(var(--c-primary-rgb),0.26); border-radius: 160px 160px 24px 24px;
|
||||
background: rgba(255,255,255,0.28); pointer-events: none;
|
||||
/* 内侧淡金光晕,增强"门内"纵深感 */
|
||||
box-shadow: inset 0 0 46px rgba(201, 169, 120, 0.13);
|
||||
}
|
||||
.ending-frame::before {
|
||||
content: ''; position: absolute; inset: 9px;
|
||||
border: 0.5px solid rgba(var(--c-primary-rgb),0.18); border-radius: 150px 150px 18px 18px;
|
||||
}
|
||||
/* 拱顶 keystone:花饰圆章 + 两侧延伸细线,圆章用页面底色垫片盖住穿过的拱线 */
|
||||
.ending-keystone {
|
||||
position: absolute; top: 0; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex; align-items: center; gap: 7px;
|
||||
color: #b08e63;
|
||||
}
|
||||
.ek-mark {
|
||||
width: 34px; height: 34px; border-radius: 50%;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 15px; line-height: 1;
|
||||
background: var(--c-paper);
|
||||
border: 1px solid rgba(var(--c-primary-rgb), 0.38);
|
||||
box-shadow: 0 0 0 4px var(--c-paper), inset 0 0 8px rgba(201, 169, 120, 0.2);
|
||||
}
|
||||
.ek-line {
|
||||
width: 24px; height: 1px;
|
||||
background: linear-gradient(90deg, transparent, rgba(var(--c-primary-rgb), 0.55));
|
||||
}
|
||||
.ek-line-r { transform: scaleX(-1); }
|
||||
.reveal--in .ek-mark {
|
||||
animation: keystonePop 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) 0.35s both;
|
||||
}
|
||||
@keyframes keystonePop {
|
||||
0% { opacity: 0; transform: scale(0.4) rotate(-90deg); }
|
||||
100% { opacity: 1; transform: scale(1) rotate(0deg); }
|
||||
}
|
||||
/* 门框底部两侧对称藤蔓花枝 */
|
||||
.ending-vine {
|
||||
position: absolute; bottom: 10px;
|
||||
width: 74px; height: 48px;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 96 60'%3E%3Cg fill='none' stroke='%23a88c6b' stroke-width='1.2'%3E%3Cpath d='M4 56C24 50 44 40 60 26 70 17 78 10 92 6'/%3E%3Cpath d='M24 48q-3-9 4-14 4 7-4 14z' fill='%23c9a978' stroke='none' opacity='.6'/%3E%3Cpath d='M42 38q-1-9 7-12 2 8-7 12z' fill='%23c9a978' stroke='none' opacity='.55'/%3E%3Cpath d='M60 26q0-9 8-11 1 8-8 11z' fill='%23c9a978' stroke='none' opacity='.5'/%3E%3Cpath d='M76 15q1-8 9-9 0 8-9 9z' fill='%23c9a978' stroke='none' opacity='.45'/%3E%3Ccircle cx='92' cy='6' r='2' fill='%23d9a0a5' stroke='none' opacity='.65'/%3E%3C/g%3E%3C/svg%3E") no-repeat center / contain;
|
||||
opacity: 0.55;
|
||||
}
|
||||
.ending-vine-l { left: 10px; }
|
||||
.ending-vine-r { right: 10px; transform: scaleX(-1); }
|
||||
/* 四角雕花:细线 L 角 + 圆点收尾
|
||||
容器必须自带 absolute 尺寸:v-reveal 的 transform 会把它变成
|
||||
absolute 子元素的 containing block,没有 inset 时入场动画期间四角会错位 */
|
||||
.ending-corners {
|
||||
/* ---------- 尾页装饰(幕布/花环/格窗/星环/缎带,后台可选) ----------
|
||||
容器自带 inset:0:v-reveal 的 transform 会让它成为 absolute 子元素的
|
||||
containing block,无尺寸时入场动画期间装饰会错位 */
|
||||
.ending-decor {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ending-corners i {
|
||||
position: absolute; width: 46px; height: 46px; border-color: rgba(var(--c-primary-rgb),0.3);
|
||||
.ending-decor i {
|
||||
position: absolute;
|
||||
}
|
||||
.ending-corners i::after {
|
||||
content: ''; position: absolute; width: 4px; height: 4px;
|
||||
border-radius: 50%; background: rgba(var(--c-primary-rgb), 0.42);
|
||||
|
||||
/* —— 舞台幕布:顶部帷幔垂坠 + 两侧束幕 + 金色束带 —— */
|
||||
.ending-style-curtain {
|
||||
--ed-cloth: color-mix(in srgb, var(--c-primary) 78%, #6e2836);
|
||||
--ed-cloth-dark: color-mix(in srgb, var(--c-primary) 58%, #431722);
|
||||
--ed-gold: rgba(216, 178, 110, 0.9);
|
||||
}
|
||||
.ending-style-curtain::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -6px;
|
||||
right: -6px;
|
||||
height: 84px;
|
||||
background:
|
||||
radial-gradient(43px 52px at 46px 2px, var(--ed-cloth) 68%, transparent 70%) 0 4px / 92px 80px repeat-x,
|
||||
radial-gradient(43px 48px at 0 0, var(--ed-cloth-dark) 68%, transparent 70%) 46px 8px / 92px 74px repeat-x,
|
||||
linear-gradient(180deg, var(--ed-cloth-dark), var(--ed-cloth) 82%) 0 0 / 100% 30px no-repeat;
|
||||
filter: drop-shadow(0 6px 10px rgba(60, 20, 30, 0.3));
|
||||
}
|
||||
.ending-style-curtain::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 5px;
|
||||
background: linear-gradient(90deg, rgba(216, 178, 110, 0.4), var(--ed-gold) 30%, var(--ed-gold) 70%, rgba(216, 178, 110, 0.4));
|
||||
}
|
||||
.ending-style-curtain .ed-a,
|
||||
.ending-style-curtain .ed-b {
|
||||
top: -8px;
|
||||
bottom: -8px;
|
||||
width: 74px;
|
||||
background:
|
||||
repeating-linear-gradient(90deg, var(--ed-cloth) 0 9px, var(--ed-cloth-dark) 9px 17px, var(--ed-cloth) 17px 26px);
|
||||
filter: drop-shadow(0 0 14px rgba(60, 20, 30, 0.35));
|
||||
}
|
||||
.ending-style-curtain .ed-a {
|
||||
left: -10px;
|
||||
clip-path: polygon(0 0, 100% 0, 56% 40%, 42% 50%, 78% 62%, 100% 100%, 0 100%);
|
||||
}
|
||||
.ending-style-curtain .ed-b {
|
||||
right: -10px;
|
||||
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 22% 62%, 58% 50%, 44% 40%);
|
||||
}
|
||||
.ending-style-curtain .ed-c,
|
||||
.ending-style-curtain .ed-d {
|
||||
top: 47%;
|
||||
width: 34px;
|
||||
height: 13px;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(180deg, #ecd2a0, #c9a054 70%);
|
||||
box-shadow: 0 2px 5px rgba(90, 60, 20, 0.35);
|
||||
}
|
||||
.ending-style-curtain .ed-c {
|
||||
left: 2px;
|
||||
transform: rotate(9deg);
|
||||
}
|
||||
.ending-style-curtain .ed-d {
|
||||
right: 2px;
|
||||
transform: rotate(-9deg);
|
||||
}
|
||||
|
||||
/* —— 满月花环:正圆双枝花环环抱内容 —— */
|
||||
.ending-style-moonwreath::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: min(92vw, 400px);
|
||||
height: min(92vw, 400px);
|
||||
transform: translate(-50%, -50%);
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cg fill='none' stroke='%23a8b894' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M100 190a90 90 0 0 1-88-72'/%3E%3Cpath d='M100 190a90 90 0 0 0 88-72'/%3E%3Cpath d='M12 82A90 90 0 0 1 96 10'/%3E%3Cpath d='M188 82A90 90 0 0 0 104 10'/%3E%3C/g%3E%3Cg fill='%23b0c29a'%3E%3Cellipse cx='22' cy='140' rx='9' ry='3.8' transform='rotate(58 22 140)'/%3E%3Cellipse cx='12' cy='108' rx='9' ry='3.8' transform='rotate(84 12 108)'/%3E%3Cellipse cx='14' cy='72' rx='9' ry='3.8' transform='rotate(108 14 72)'/%3E%3Cellipse cx='30' cy='40' rx='9' ry='3.8' transform='rotate(134 30 40)'/%3E%3Cellipse cx='58' cy='18' rx='9' ry='3.8' transform='rotate(156 58 18)'/%3E%3Cellipse cx='178' cy='140' rx='9' ry='3.8' transform='rotate(-58 178 140)'/%3E%3Cellipse cx='188' cy='108' rx='9' ry='3.8' transform='rotate(-84 188 108)'/%3E%3Cellipse cx='186' cy='72' rx='9' ry='3.8' transform='rotate(-108 186 72)'/%3E%3Cellipse cx='170' cy='40' rx='9' ry='3.8' transform='rotate(-134 170 40)'/%3E%3Cellipse cx='142' cy='18' rx='9' ry='3.8' transform='rotate(-156 142 18)'/%3E%3Cellipse cx='58' cy='182' rx='8' ry='3.4' transform='rotate(22 58 182)'/%3E%3Cellipse cx='142' cy='182' rx='8' ry='3.4' transform='rotate(-22 142 182)'/%3E%3C/g%3E%3Cg fill='%23e4aeb6'%3E%3Cg transform='translate(100 12)'%3E%3Ccircle cy='-4' r='3.2'/%3E%3Ccircle cx='3.6' cy='1.2' r='3.2'/%3E%3Ccircle cx='-3.6' cy='1.2' r='3.2'/%3E%3Ccircle r='1.7' fill='%23c9a24a'/%3E%3C/g%3E%3Cg transform='translate(100 188)'%3E%3Ccircle cy='-4' r='3.2'/%3E%3Ccircle cx='3.6' cy='1.2' r='3.2'/%3E%3Ccircle cx='-3.6' cy='1.2' r='3.2'/%3E%3Ccircle r='1.7' fill='%23c9a24a'/%3E%3C/g%3E%3Cg transform='translate(14 96) scale(.82)'%3E%3Ccircle cy='-4' r='3.2'/%3E%3Ccircle cx='3.6' cy='1.2' r='3.2'/%3E%3Ccircle cx='-3.6' cy='1.2' r='3.2'/%3E%3Ccircle r='1.7' fill='%23c9a24a'/%3E%3C/g%3E%3Cg transform='translate(186 96) scale(.82)'%3E%3Ccircle cy='-4' r='3.2'/%3E%3Ccircle cx='3.6' cy='1.2' r='3.2'/%3E%3Ccircle cx='-3.6' cy='1.2' r='3.2'/%3E%3Ccircle r='1.7' fill='%23c9a24a'/%3E%3C/g%3E%3C/g%3E%3Cg fill='%23d9a0a5' opacity='.6'%3E%3Ccircle cx='40' cy='166' r='2.2'/%3E%3Ccircle cx='160' cy='166' r='2.2'/%3E%3Ccircle cx='40' cy='30' r='2.2'/%3E%3Ccircle cx='160' cy='30' r='2.2'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.ending-style-moonwreath::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 6%;
|
||||
width: 60%;
|
||||
height: 26px;
|
||||
transform: translateX(-50%);
|
||||
background: radial-gradient(50% 100% at 50% 50%, rgba(168, 184, 148, 0.2), transparent 75%);
|
||||
}
|
||||
|
||||
/* —— 法式格窗:细格线落地窗 + 窗台横板 —— */
|
||||
.ending-style-frwindow::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 26px 20px 44px;
|
||||
border: 2px solid rgba(var(--c-primary-rgb), 0.4);
|
||||
border-radius: 3px;
|
||||
background:
|
||||
linear-gradient(rgba(var(--c-primary-rgb), 0.35), rgba(var(--c-primary-rgb), 0.35)) 50% 0 / 2.5px 100% no-repeat,
|
||||
repeating-linear-gradient(0deg, transparent 0 calc(25% - 1px), rgba(var(--c-primary-rgb), 0.24) calc(25% - 1px) 25%),
|
||||
repeating-linear-gradient(90deg, transparent 0 calc(16.666% - 1px), rgba(var(--c-primary-rgb), 0.16) calc(16.666% - 1px) 16.666%),
|
||||
radial-gradient(120% 60% at 50% 0%, rgba(255, 255, 255, 0.35), transparent 50%);
|
||||
box-shadow: inset 0 0 40px rgba(255, 255, 255, 0.35), 0 14px 30px rgba(var(--c-primary-rgb), 0.12);
|
||||
}
|
||||
.ending-style-frwindow::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
right: 12px;
|
||||
bottom: 32px;
|
||||
height: 7px;
|
||||
border-radius: 3px;
|
||||
background: linear-gradient(180deg, rgba(var(--c-primary-rgb), 0.4), rgba(var(--c-primary-rgb), 0.26));
|
||||
box-shadow: 0 5px 8px rgba(var(--c-primary-rgb), 0.16);
|
||||
}
|
||||
.ending-style-frwindow .ed-a {
|
||||
left: 50%;
|
||||
top: 49%;
|
||||
width: 5px;
|
||||
height: 22px;
|
||||
margin-left: 5px;
|
||||
border-radius: 4px;
|
||||
background: rgba(var(--c-primary-rgb), 0.5);
|
||||
}
|
||||
|
||||
/* —— 星光圆环:光点环缓旋 + 四角光斑 —— */
|
||||
.ending-style-starring::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(34% 26% at 6% 4%, rgba(232, 201, 135, 0.22), transparent 70%),
|
||||
radial-gradient(30% 24% at 96% 8%, rgba(232, 201, 135, 0.16), transparent 70%),
|
||||
radial-gradient(32% 24% at 4% 94%, rgba(232, 201, 135, 0.16), transparent 70%),
|
||||
radial-gradient(36% 28% at 96% 96%, rgba(232, 201, 135, 0.22), transparent 70%);
|
||||
}
|
||||
.ending-style-starring .ed-a {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: min(88vw, 384px);
|
||||
height: min(88vw, 384px);
|
||||
transform: translate(-50%, -50%);
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Ccircle cx='100' cy='100' r='92' fill='none' stroke='%23d9c288' stroke-width='2.6' stroke-dasharray='0.1 15.5' stroke-linecap='round'/%3E%3Ccircle cx='100' cy='100' r='80' fill='none' stroke='%23d9c288' stroke-width='1.4' stroke-dasharray='0.1 10.4' stroke-linecap='round' opacity='.55'/%3E%3Cg fill='%23d9c288'%3E%3Cpath d='M100 2l1.8 4.4L106 8l-4.2 1.6L100 14l-1.8-4.4L94 8l4.2-1.6z'/%3E%3Cpath d='M100 186l1.8 4.4 4.2 1.6-4.2 1.6-1.8 4.4-1.8-4.4-4.2-1.6 4.2-1.6z'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
|
||||
animation: edRingSpin 70s linear infinite;
|
||||
filter: drop-shadow(0 0 10px rgba(217, 194, 136, 0.4));
|
||||
}
|
||||
@keyframes edRingSpin {
|
||||
to { transform: translate(-50%, -50%) rotate(360deg); }
|
||||
}
|
||||
.ending-style-starring::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 60%;
|
||||
height: 46%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: radial-gradient(50% 50% at 50% 50%, rgba(255, 250, 236, 0.3), transparent 72%);
|
||||
}
|
||||
|
||||
/* —— 缎带环绕:上下飘带 + 对角蝴蝶结 —— */
|
||||
.ending-style-ribbons::before,
|
||||
.ending-style-ribbons::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -12px;
|
||||
right: -12px;
|
||||
height: 46px;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 420 46' preserveAspectRatio='none'%3E%3Cpath d='M-4 30C56 8 116 8 178 24s124 18 246-8' fill='none' stroke='%23e8a8b2' stroke-width='7' stroke-linecap='round' opacity='.8'/%3E%3Cpath d='M-4 34C56 12 116 12 178 28s124 18 246-8' fill='none' stroke='%23d9c288' stroke-width='2.4' stroke-linecap='round' opacity='.75'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
|
||||
}
|
||||
.ending-style-ribbons::before {
|
||||
top: 22px;
|
||||
}
|
||||
.ending-style-ribbons::after {
|
||||
bottom: 22px;
|
||||
transform: scaleX(-1) scaleY(-1);
|
||||
}
|
||||
.ending-style-ribbons .ed-a,
|
||||
.ending-style-ribbons .ed-b {
|
||||
width: 56px;
|
||||
height: 38px;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 66 44'%3E%3Cg fill='%23e093a3'%3E%3Cpath d='M33 12C26 4 14 2 8 8c-5 5-3 13 4 15 8 2 17-4 21-11z' fill='%23e8a8b2'/%3E%3Cpath d='M33 12c7-8 19-10 25-4 5 5 3 13-4 15-8 2-17-4-21-11z' fill='%23e8a8b2'/%3E%3Cpath d='M27 20l-7 20 8-3 3 5 4-22z' fill='%23e8a8b2'/%3E%3Cpath d='M39 20l7 20-8-3-3 5-4-22z' fill='%23dfa0aa'/%3E%3Ccircle cx='33' cy='13' r='5' fill='%23d9899a'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
|
||||
filter: drop-shadow(0 3px 5px rgba(190, 120, 130, 0.35));
|
||||
}
|
||||
.ending-style-ribbons .ed-a {
|
||||
top: 40px;
|
||||
left: 16px;
|
||||
transform: rotate(-14deg);
|
||||
}
|
||||
.ending-style-ribbons .ed-b {
|
||||
bottom: 40px;
|
||||
right: 16px;
|
||||
transform: rotate(168deg);
|
||||
}
|
||||
.ending-corners i:nth-child(1) { top: 44px; left: 38px; border-top: 1px solid; border-left: 1px solid; }
|
||||
.ending-corners i:nth-child(1)::after { top: -2.5px; left: -2.5px; }
|
||||
.ending-corners i:nth-child(2) { top: 44px; right: 38px; border-top: 1px solid; border-right: 1px solid; }
|
||||
.ending-corners i:nth-child(2)::after { top: -2.5px; right: -2.5px; }
|
||||
.ending-corners i:nth-child(3) { left: 38px; bottom: 44px; border-bottom: 1px solid; border-left: 1px solid; }
|
||||
.ending-corners i:nth-child(3)::after { bottom: -2.5px; left: -2.5px; }
|
||||
.ending-corners i:nth-child(4) { right: 38px; bottom: 44px; border-right: 1px solid; border-bottom: 1px solid; }
|
||||
.ending-corners i:nth-child(4)::after { bottom: -2.5px; right: -2.5px; }
|
||||
.ending-kicker {
|
||||
display: flex; flex-direction: column; align-items: center; gap: 10px;
|
||||
margin-bottom: 18px; color: var(--c-primary); font-size: 10px; letter-spacing: 0.45em;
|
||||
|
||||
Reference in New Issue
Block a user