f
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user