This commit is contained in:
2026-04-18 12:06:06 +08:00
parent f62289c97b
commit 1e82051ca5
24 changed files with 315 additions and 214 deletions

View File

@@ -3,52 +3,29 @@
<!-- #ifdef MP-WEIXIN -->
<view class="max-h-[calc(100vh-100px)] m-4">
<!-- canvas 离屏绘制避免 swiper 未挂载项取不到节点 -->
<canvas
id="mpPosterCanvas"
canvas-id="mpPosterCanvas"
type="2d"
class="mp-poster-canvas-hidden"
/>
<canvas id="mpPosterCanvas" canvas-id="mpPosterCanvas" type="2d" class="mp-poster-canvas-hidden" />
<view class="p-2 flex justify-center">
<swiper
:key="swiperMountKey"
class="mp-poster-swiper w-full"
:style="{ height: swiperHeightPx + 'px' }"
:duration="280"
:easing-function="easeOutCubic"
@change="onSwiperChange"
>
<swiper :key="swiperMountKey" class="mp-poster-swiper w-full" :style="{ height: swiperHeightPx + 'px' }"
:current="currentSwiperIndex" :duration="280" :easing-function="easeOutCubic" @change="onSwiperChange">
<swiper-item v-for="(_, idx) in posterSrcList" :key="idx" class="mp-swiper-item">
<view class="mp-poster-item">
<image
v-if="renderedPaths[idx]"
:src="renderedPaths[idx]"
mode="aspectFit"
class="rounded-xl shadow poster-preview-mp"
:style="posterPreviewStyle"
/>
<image v-if="renderedPaths[idx]" :src="renderedPaths[idx]" mode="aspectFit"
class="rounded-xl shadow poster-preview-mp" :style="posterPreviewStyle" />
<view v-else class="text-gray-400 text-sm py-16">生成海报中</view>
</view>
</swiper-item>
</swiper>
</view>
<view
v-if="mode === 'promote'"
class="text-center text-gray-500 text-xs mb-2"
>
<view v-if="mode === 'promote'" class="text-center text-gray-500 text-xs mb-2">
左右滑动切换海报
</view>
<view class="divider">分享与保存</view>
<view class="mp-share-actions">
<button
class="share-mp-btn flex flex-col items-center justify-center"
open-type="share"
plain
@tap="onShareFriendPrepare"
>
<!-- <button class="share-mp-btn flex flex-col items-center justify-center" open-type="share" plain
@tap="onShareFriendPrepare">
<image src="/static/image/icon_share_friends.svg" class="w-10 h-10 rounded-full" />
<text class="text-center mt-1 text-gray-600 text-xs">分享给好友</text>
</button>
</button> -->
<view class="flex flex-col items-center justify-center" @click="savePoster">
<image src="/static/image/icon_share_img.svg" class="w-10 h-10 rounded-full" />
<view class="text-center mt-1 text-gray-600 text-xs">保存图片</view>
@@ -65,14 +42,9 @@
<view class="max-h-[calc(100vh-100px)] m-4">
<view class="p-4 flex justify-center">
<view class="max-h-[70vh] rounded-xl overflow-hidden">
<image
:src="posterImageUrlRemote"
class="rounded-xl shadow poster-image"
:style="{ width: imageWidth + 'px', height: imageHeight + 'px' }"
mode="aspectFit"
@load="onImageLoad"
@error="onImageError"
/>
<image :src="posterImageUrlRemote" class="rounded-xl shadow poster-image"
:style="{ width: imageWidth + 'px', height: imageHeight + 'px' }" mode="aspectFit" @load="onImageLoad"
@error="onImageError" />
</view>
</view>
<view class="divider">分享到好友</view>
@@ -97,7 +69,7 @@ import { getApiBaseUrl, getAgentTabShareTitle, getShareTitle } from '@/utils/run
import { buildPromotionH5Url } from '@/utils/promotionH5Url.js'
import { setMiniPromotionShareFriend } from '@/utils/miniPromotionSharePayload.js'
// #ifdef MP-WEIXIN
import { getPosterSrcList, drawMergedPosterWeixin } from '@/utils/posterQrWeixin.js'
import { getPosterSrcList, drawMergedPosterWeixin, getCachedMergedPosterWeixin } from '@/utils/posterQrWeixin.js'
// #endif
const props = defineProps({
@@ -184,12 +156,40 @@ function updateMpPosterLayout() {
/** 串行生成,避免多索引共用同一 canvas 竞态 */
let mpGenSeq = Promise.resolve()
const mpRenderStateCache = new Map()
function getRenderCacheKey() {
return `${mode.value}::${linkIdentifier.value || ''}`
}
function updateRenderCache(partial) {
const key = getRenderCacheKey()
const prev = mpRenderStateCache.get(key) || { paths: [], lastIndex: 0 }
const next = {
paths: Array.isArray(partial.paths) ? [...partial.paths] : prev.paths,
lastIndex: Number.isFinite(partial.lastIndex) ? partial.lastIndex : prev.lastIndex,
}
mpRenderStateCache.set(key, next)
}
function resetMpPosters() {
mpGenSeq = Promise.resolve()
const n = posterSrcList.value.length
renderedPaths.value = Array.from({ length: n }, () => '')
currentSwiperIndex.value = 0
const cache = mpRenderStateCache.get(getRenderCacheKey())
const initialPaths = Array.from({ length: n }, (_, idx) => {
const saved = cache?.paths?.[idx]
if (saved) return saved
return getCachedMergedPosterWeixin({
linkUrl: generalUrl(),
posterSrc: posterSrcList.value[idx],
mode: mode.value,
index: idx,
}) || ''
})
renderedPaths.value = initialPaths
const cachedIndex = cache?.lastIndex ?? 0
currentSwiperIndex.value = Math.min(Math.max(cachedIndex, 0), Math.max(n - 1, 0))
updateRenderCache({ paths: initialPaths, lastIndex: currentSwiperIndex.value })
}
function generatePosterMp(index) {
@@ -198,7 +198,7 @@ function generatePosterMp(index) {
if (!proxy) return Promise.resolve()
mpGenSeq = mpGenSeq
.catch(() => {})
.catch(() => { })
.then(async () => {
if (renderedPaths.value[index]) return
await new Promise((r) => setTimeout(r, 80))
@@ -224,6 +224,7 @@ function generatePosterMp(index) {
const next = [...renderedPaths.value]
next[index] = temp
renderedPaths.value = next
updateRenderCache({ paths: next })
})
return mpGenSeq
}
@@ -231,6 +232,7 @@ function generatePosterMp(index) {
function onSwiperChange(e) {
const cur = e.detail?.current ?? 0
currentSwiperIndex.value = cur
updateRenderCache({ lastIndex: cur })
if (!renderedPaths.value[cur]) {
// 等 swiper 切换动画走一部分再跑 canvas减轻主线程卡顿
setTimeout(() => {
@@ -249,7 +251,7 @@ watch(show, (v) => {
swiperMountKey.value += 1
resetMpPosters()
nextTick(() => {
generatePosterMp(0).catch((err) => {
generatePosterMp(currentSwiperIndex.value).catch((err) => {
console.error('生成海报失败', err)
uni.showToast({ title: '海报生成失败', icon: 'none' })
})
@@ -264,7 +266,7 @@ watch([mode, linkIdentifier], () => {
swiperMountKey.value += 1
resetMpPosters()
nextTick(() => {
generatePosterMp(0).catch(() => {})
generatePosterMp(currentSwiperIndex.value).catch(() => { })
})
})
})
@@ -319,7 +321,7 @@ function onImageLoad() {
imageWidth.value = size.width
imageHeight.value = size.height
},
fail: () => {},
fail: () => { },
})
}
@@ -518,5 +520,6 @@ function copyUrl() {
.share-mp-btn::after {
border: none;
}
/* #endif */
</style>