1. 修复海报图片

This commit is contained in:
李琦
2026-08-03 17:01:25 +08:00
parent adc40c1834
commit 66a84ced72
3 changed files with 134 additions and 37 deletions

View File

@@ -7,12 +7,12 @@
<span class="hb-bg-xi hb-bg-xi-4"></span>
<span class="hb-bg-xi hb-bg-xi-5"></span>
<span class="hb-bg-xi hb-bg-xi-6"></span>
<svg class="hb-bg-knot hb-bg-knot-1" viewBox="0 0 64 64" fill="none"><use href="#hb-icon-knot" /></svg>
<svg class="hb-bg-knot hb-bg-knot-2" viewBox="0 0 64 64" fill="none"><use href="#hb-icon-knot" /></svg>
<svg class="hb-bg-knot hb-bg-knot-3" viewBox="0 0 64 64" fill="none"><use href="#hb-icon-knot" /></svg>
<svg class="hb-bg-knot hb-bg-knot-4" viewBox="0 0 64 64" fill="none"><use href="#hb-icon-knot" /></svg>
<svg class="hb-bg-lantern hb-bg-lantern-1" viewBox="0 0 40 56" fill="none"><use href="#hb-icon-lantern" /></svg>
<svg class="hb-bg-lantern hb-bg-lantern-2" viewBox="0 0 40 56" fill="none"><use href="#hb-icon-lantern" /></svg>
<svg class="hb-bg-knot hb-bg-knot-1" viewBox="0 0 64 80" fill="none"><use href="#hb-icon-knot" /></svg>
<svg class="hb-bg-knot hb-bg-knot-2" viewBox="0 0 64 80" fill="none"><use href="#hb-icon-knot" /></svg>
<svg class="hb-bg-knot hb-bg-knot-3" viewBox="0 0 64 80" fill="none"><use href="#hb-icon-knot" /></svg>
<svg class="hb-bg-knot hb-bg-knot-4" viewBox="0 0 64 80" fill="none"><use href="#hb-icon-knot" /></svg>
<svg class="hb-bg-lantern hb-bg-lantern-1" viewBox="0 0 48 72" fill="none"><use href="#hb-icon-lantern" /></svg>
<svg class="hb-bg-lantern hb-bg-lantern-2" viewBox="0 0 48 72" fill="none"><use href="#hb-icon-lantern" /></svg>
<i class="hb-bg-spark hb-bg-spark-1"></i>
<i class="hb-bg-spark hb-bg-spark-2"></i>
<i class="hb-bg-spark hb-bg-spark-3"></i>
@@ -20,18 +20,53 @@
</div>
<svg class="hb-sprite" aria-hidden="true">
<symbol id="hb-icon-knot" viewBox="0 0 64 64">
<path d="M32 6c-4 8-14 12-14 22 0 6 4 10 10 12-6 2-10 6-10 12 0 10 10 14 14 22 4-8 14-12 14-22 0-6-4-10-10-12 6-2 10-6 10-12 0-10-10-14-14-22z" stroke="currentColor" stroke-width="2.2" fill="none"/>
<circle cx="32" cy="32" r="5" fill="currentColor" opacity="0.85"/>
<path d="M32 8v10M32 46v10M18 32h10M36 32h10" stroke="currentColor" stroke-width="1.6" opacity="0.7"/>
<path d="M22 18c6 4 14 4 20 0M22 46c6-4 14-4 20 0" stroke="currentColor" stroke-width="1.5" opacity="0.55"/>
<symbol id="hb-icon-knot" viewBox="0 0 64 80">
<!-- 可识别中国结顶环 + 盘长结体 + 流苏 -->
<path d="M32 4c0 0-8 4-8 12 0 4 3 7 8 7s8-3 8-7c0-8-8-12-8-12z" stroke="currentColor" stroke-width="2.2" fill="none"/>
<path d="M32 16v6" stroke="currentColor" stroke-width="2"/>
<!-- 盘长结菱形交织 -->
<path d="M32 24 L44 32 L32 40 L20 32 Z" stroke="currentColor" stroke-width="2.4" fill="currentColor" fill-opacity="0.12"/>
<path d="M32 28 L40 32 L32 36 L24 32 Z" stroke="currentColor" stroke-width="1.8" fill="none"/>
<path d="M26 26 L38 38M38 26 L26 38" stroke="currentColor" stroke-width="1.6" opacity="0.85"/>
<circle cx="32" cy="32" r="3.2" fill="currentColor"/>
<!-- 两侧耳结 -->
<path d="M20 32c-6-2-10 2-8 8 2 4 6 4 8 2M44 32c6-2 10 2 8 8-2 4-6 4-8 2" stroke="currentColor" stroke-width="2" fill="none"/>
<!-- 下坠与流苏 -->
<path d="M32 40v8" stroke="currentColor" stroke-width="2"/>
<path d="M28 48h8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M26 50c-1 8-2 14-4 22M32 50v24M38 50c1 8 2 14 4 22" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/>
<path d="M29 50c-0.5 7-1 13-2 20M35 50c0.5 7 1 13 2 20" stroke="currentColor" stroke-width="1.2" opacity="0.65" stroke-linecap="round"/>
</symbol>
<symbol id="hb-icon-lantern" viewBox="0 0 40 56">
<rect x="16" y="2" width="8" height="5" rx="1" fill="currentColor"/>
<ellipse cx="20" cy="28" rx="14" ry="18" stroke="currentColor" stroke-width="2" fill="currentColor" fill-opacity="0.18"/>
<path d="M8 20h24M8 28h24M8 36h24" stroke="currentColor" stroke-width="1.2" opacity="0.55"/>
<path d="M20 46v6M16 52h8" stroke="currentColor" stroke-width="1.6"/>
<circle cx="20" cy="28" r="3" fill="currentColor"/>
<symbol id="hb-icon-lantern" viewBox="0 0 48 72">
<!-- 可识别红灯笼挂绳 + 金顶 + 鼓形灯身 + + 流苏 -->
<path d="M24 2v5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
<ellipse cx="24" cy="8" rx="3" ry="1.6" fill="currentColor"/>
<!-- 顶盖 -->
<path d="M14 11h20l-2 5H16l-2-5z" fill="currentColor"/>
<rect x="15" y="15" width="18" height="3" rx="1" fill="currentColor" opacity="0.9"/>
<!-- 灯身鼓形 -->
<path
d="M12 20c-4 6-4 22 0 28 3 5 9 8 12 8s9-3 12-8c4-6 4-22 0-28-3-5-9-8-12-8s-9 3-12 8z"
fill="currentColor"
fill-opacity="0.28"
stroke="currentColor"
stroke-width="2.2"
/>
<!-- 横楞 -->
<path d="M9 28c5-2.5 25-2.5 30 0" stroke="currentColor" stroke-width="1.6" opacity="0.75"/>
<path d="M8 36h32" stroke="currentColor" stroke-width="1.8" opacity="0.85"/>
<path d="M9 44c5 2.5 25 2.5 30 0" stroke="currentColor" stroke-width="1.6" opacity="0.75"/>
<!-- 竖框 -->
<path d="M24 18v36" stroke="currentColor" stroke-width="1.2" opacity="0.35"/>
<!-- 中心囍印 -->
<circle cx="24" cy="36" r="7.5" fill="currentColor" fill-opacity="0.2" stroke="currentColor" stroke-width="1.4"/>
<path d="M20.2 32.2h3.2v2.2h-1.4v1.4h1.4v2.2h-3.2v-2.2h1.4v-1.4h-1.4zm4.4 0h3.2v2.2h-1.4v1.4h1.4v2.2h-3.2v-2.2h1.4v-1.4h-1.4zM20.2 38.2h3.2v2.2h-1.4v1.4h1.4v2.2h-3.2v-2.2h1.4v-1.4h-1.4zm4.4 0h3.2v2.2h-1.4v1.4h1.4v2.2h-3.2v-2.2h1.4v-1.4h-1.4z" fill="currentColor"/>
<!-- 底托 -->
<rect x="17" y="52" width="14" height="4" rx="1.2" fill="currentColor"/>
<path d="M24 56v5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
<!-- 流苏 -->
<path d="M20 61c-1 4-2 7-3 10M24 61v11M28 61c1 4 2 7 3 10" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
<path d="M22 61c-0.5 3.5-1 6.5-1.5 9M26 61c0.5 3.5 1 6.5 1.5 9" stroke="currentColor" stroke-width="1.1" opacity="0.65" stroke-linecap="round"/>
</symbol>
<symbol id="hb-icon-cloud" viewBox="0 0 80 36">
<path d="M8 28c0-8 6-14 14-14 2-8 10-12 18-10 4-6 14-8 20-2 8-2 16 4 16 12 0 8-6 12-16 12H18C10 26 8 22 8 28z" stroke="currentColor" stroke-width="1.8" fill="currentColor" fill-opacity="0.12"/>
@@ -322,7 +357,9 @@ async function mountCaptureQr(host) {
const target = isSplit
? (host.querySelector('.sp-left') || host)
: (host.querySelector('.hb-inner') || host)
const qrSize = isSplit ? 64 : 88
// 显示尺寸适中;位图按 3× 渲染,避免 html2canvas 放大后糊成一片
const displaySize = isSplit ? 84 : 108
const renderSize = displaySize * 3
const badge = document.createElement('div')
badge.setAttribute('data-hb-capture-qr', '')
@@ -354,10 +391,28 @@ async function mountCaptureQr(host) {
target.appendChild(badge)
const qrCanvas = badge.querySelector('canvas')
await renderFestiveQr(qrCanvas, pageUrl.value, {
size: qrSize,
size: renderSize,
centerImg: cfg.qrCenterImg,
})
await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)))
// html2canvas 对 canvas 常二次缩放发糊:改用高清位图 img 再截
const qrImg = document.createElement('img')
qrImg.alt = 'qr'
qrImg.width = displaySize
qrImg.height = displaySize
qrImg.style.width = `${displaySize}px`
qrImg.style.height = `${displaySize}px`
qrImg.style.display = 'block'
qrImg.style.borderRadius = '3px'
qrImg.src = qrCanvas.toDataURL('image/png')
qrCanvas.replaceWith(qrImg)
await new Promise((r) => {
if (qrImg.complete) {
requestAnimationFrame(() => requestAnimationFrame(r))
return
}
qrImg.onload = () => requestAnimationFrame(() => requestAnimationFrame(r))
qrImg.onerror = () => r()
})
return () => {
badge.remove()
@@ -670,14 +725,14 @@ onUnmounted(() => {
.hb-bg-xi-4 { bottom: 10%; right: 10%; font-size: 64px; transform: rotate(-8deg); color: rgba(196, 30, 58, 0.18); animation-delay: 0.6s; }
.hb-bg-xi-5 { top: 42%; left: 2%; font-size: 36px; opacity: 0.8; animation-delay: 3s; }
.hb-bg-xi-6 { top: 48%; right: 3%; font-size: 40px; opacity: 0.75; animation-delay: 2.4s; }
.hb-bg-knot { position: absolute; color: rgba(212, 175, 55, 0.28); width: 72px; height: 72px; animation: hb-float 10s ease-in-out infinite; }
.hb-bg-knot-1 { top: 8%; left: 14%; width: 64px; height: 64px; }
.hb-bg-knot-2 { top: 22%; right: 12%; width: 56px; height: 56px; color: rgba(196, 30, 58, 0.28); animation-delay: 1.8s; }
.hb-bg-knot-3 { bottom: 22%; left: 10%; width: 60px; height: 60px; animation-delay: 2.6s; }
.hb-bg-knot-4 { bottom: 12%; right: 14%; width: 70px; height: 70px; color: rgba(240, 215, 140, 0.22); animation-delay: 1s; }
.hb-bg-lantern { position: absolute; color: rgba(240, 215, 140, 0.35); width: 36px; height: 50px; animation: hb-swing 5.5s ease-in-out infinite; }
.hb-bg-lantern-1 { top: 4%; left: 22%; }
.hb-bg-lantern-2 { top: 5%; right: 20%; animation-delay: 1.4s; }
.hb-bg-knot { position: absolute; color: rgba(212, 175, 55, 0.38); width: 56px; height: 70px; animation: hb-float 10s ease-in-out infinite; }
.hb-bg-knot-1 { top: 7%; left: 12%; width: 52px; height: 66px; }
.hb-bg-knot-2 { top: 20%; right: 10%; width: 46px; height: 58px; color: rgba(240, 215, 140, 0.34); animation-delay: 1.8s; }
.hb-bg-knot-3 { bottom: 18%; left: 8%; width: 50px; height: 64px; animation-delay: 2.6s; }
.hb-bg-knot-4 { bottom: 10%; right: 12%; width: 58px; height: 72px; color: rgba(240, 215, 140, 0.3); animation-delay: 1s; }
.hb-bg-lantern { position: absolute; color: rgba(240, 215, 140, 0.48); width: 42px; height: 64px; animation: hb-swing 5.5s ease-in-out infinite; }
.hb-bg-lantern-1 { top: 3%; left: 20%; }
.hb-bg-lantern-2 { top: 4%; right: 18%; animation-delay: 1.4s; }
.hb-bg-spark {
position: absolute; width: 6px; height: 6px; border-radius: 50%;
background: radial-gradient(circle, var(--hb-gold-soft), transparent 70%);
@@ -822,7 +877,8 @@ onUnmounted(() => {
[data-hb-capture-qr] .hb-cq-tr { top: 2px; right: 3px; }
[data-hb-capture-qr] .hb-cq-bl { bottom: 2px; left: 3px; }
[data-hb-capture-qr] .hb-cq-br { bottom: 2px; right: 3px; }
[data-hb-capture-qr] canvas {
[data-hb-capture-qr] canvas,
[data-hb-capture-qr] img {
display: block;
border-radius: 3px;
}

View File

@@ -19,10 +19,10 @@
<div class="hb-inner">
<div class="hb-paper-decor" aria-hidden="true">
<span class="hb-paper-watermark"></span>
<svg class="hb-paper-knot hb-paper-knot-tl" viewBox="0 0 64 64"><use href="#hb-icon-knot" /></svg>
<svg class="hb-paper-knot hb-paper-knot-tr" viewBox="0 0 64 64"><use href="#hb-icon-knot" /></svg>
<svg class="hb-paper-knot hb-paper-knot-bl" viewBox="0 0 64 64"><use href="#hb-icon-knot" /></svg>
<svg class="hb-paper-knot hb-paper-knot-br" viewBox="0 0 64 64"><use href="#hb-icon-knot" /></svg>
<svg class="hb-paper-knot hb-paper-knot-tl" viewBox="0 0 64 80"><use href="#hb-icon-knot" /></svg>
<svg class="hb-paper-knot hb-paper-knot-tr" viewBox="0 0 64 80"><use href="#hb-icon-knot" /></svg>
<svg class="hb-paper-knot hb-paper-knot-bl" viewBox="0 0 64 80"><use href="#hb-icon-knot" /></svg>
<svg class="hb-paper-knot hb-paper-knot-br" viewBox="0 0 64 80"><use href="#hb-icon-knot" /></svg>
<svg class="hb-paper-cloud hb-paper-cloud-t" viewBox="0 0 80 36"><use href="#hb-icon-cloud" /></svg>
<svg class="hb-paper-cloud hb-paper-cloud-b" viewBox="0 0 80 36"><use href="#hb-icon-cloud" /></svg>
<span class="hb-paper-xi hb-paper-xi-l"></span>
@@ -312,7 +312,7 @@ onBeforeUnmount(() => {
position: absolute; left: 50%; top: 52%; transform: translate(-50%, -50%);
font-size: min(220px, 58vw); font-weight: 700; color: rgba(196, 30, 58, 0.1); line-height: 1;
}
.hb-paper-knot { position: absolute; width: 42px; height: 42px; color: rgba(212, 175, 55, 0.45); }
.hb-paper-knot { position: absolute; width: 34px; height: 44px; color: rgba(212, 175, 55, 0.55); }
.hb-paper-knot-tl { top: 10px; left: 10px; }
.hb-paper-knot-tr { top: 10px; right: 10px; transform: scaleX(-1); }
.hb-paper-knot-bl { bottom: 12px; left: 10px; transform: scaleY(-1); }

View File

@@ -7,6 +7,19 @@
]"
>
<div class="sp-card" data-poster-capture>
<div class="sp-frame-ornament sp-frame-ornament-tl" aria-hidden="true">
<svg viewBox="0 0 48 48"><use href="#hb-icon-corner" /></svg>
</div>
<div class="sp-frame-ornament sp-frame-ornament-tr" aria-hidden="true">
<svg viewBox="0 0 48 48"><use href="#hb-icon-corner" /></svg>
</div>
<div class="sp-frame-ornament sp-frame-ornament-bl" aria-hidden="true">
<svg viewBox="0 0 48 48"><use href="#hb-icon-corner" /></svg>
</div>
<div class="sp-frame-ornament sp-frame-ornament-br" aria-hidden="true">
<svg viewBox="0 0 48 48"><use href="#hb-icon-corner" /></svg>
</div>
<div class="sp-card-rim" aria-hidden="true"></div>
<section class="sp-left">
<div class="sp-photo" :class="{ 'is-show': stage >= 1 }">
<img
@@ -230,13 +243,39 @@ onBeforeUnmount(() => {
position: relative;
display: flex;
flex-direction: column;
border: 1.5px solid var(--hb-gold);
border-radius: 10px;
border: 2px solid #d4af37;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 14px 40px rgba(0, 0, 0, 0.35);
box-shadow:
0 0 0 1px rgba(240, 215, 140, 0.35),
0 0 0 5px rgba(122, 0, 0, 0.55),
0 0 0 6px rgba(212, 175, 55, 0.55),
0 16px 42px rgba(0, 0, 0, 0.4);
min-height: min(720px, calc(100dvh - 32px));
background: #8b0000;
}
.sp-card-rim {
position: absolute;
inset: 5px;
border: 1px solid rgba(240, 215, 140, 0.45);
border-radius: 8px;
pointer-events: none;
z-index: 6;
box-shadow: inset 0 0 0 1px rgba(212, 175, 55, 0.18);
}
.sp-frame-ornament {
position: absolute;
width: 28px;
height: 28px;
color: rgba(240, 215, 140, 0.9);
z-index: 7;
pointer-events: none;
}
.sp-frame-ornament svg { width: 100%; height: 100%; display: block; }
.sp-frame-ornament-tl { top: 7px; left: 7px; }
.sp-frame-ornament-tr { top: 7px; right: 7px; transform: scaleX(-1); }
.sp-frame-ornament-bl { bottom: 7px; left: 7px; transform: scaleY(-1); }
.sp-frame-ornament-br { bottom: 7px; right: 7px; transform: scale(-1); }
/* 手机端:自动 = 上下;竖向强制上下;横屏强制左右 */
@media (max-width: 719px) {
@@ -256,6 +295,8 @@ onBeforeUnmount(() => {
.sp-m-landscape .sp-right { width: 46%; padding: 10px 8px; }
.sp-m-landscape .sp-slogan { font-size: clamp(22px, 6vw, 32px); }
.sp-m-landscape .sp-photo { min-height: 100%; }
.sp-frame-ornament { width: 22px; height: 22px; }
.sp-card-rim { inset: 4px; }
}
@media (min-width: 720px) {