feat(specialSharePage): 分享页自定义背景图

This commit is contained in:
woody 2025-07-14 17:48:22 +08:00
parent c330e4e12e
commit 097dfd1939
5 changed files with 174 additions and 129 deletions

View File

@ -1,7 +1,7 @@
<script> <script>
import VConsole from 'vconsole' // import VConsole from 'vconsole'
import { setToken } from '@/config/auth.js' import { setToken } from '@/config/auth.js'
const vConsole = new VConsole() // const vConsole = new VConsole()
export default { export default {
onLaunch: function (options) { onLaunch: function (options) {
// //

View File

@ -1,10 +1,11 @@
<template> <template>
<view class="special-share-page"> <view ref="specialSharePage" class="special-share-page">
<!-- 特殊场景背景图片 --> <!-- 特殊场景背景图片 -->
<image <image
class="share-bg-image" class="share-bg-image"
:src="specialBackgroundImage" :src="specialBackgroundImage"
mode="scaleToFill" mode="scaleToFill"
crossorigin="anonymous"
@load="onBackgroundImageLoad" @load="onBackgroundImageLoad"
@error="onBackgroundImageError" @error="onBackgroundImageError"
v-if="specialBackgroundImage" v-if="specialBackgroundImage"
@ -37,6 +38,7 @@
<view class="loader"></view> <view class="loader"></view>
</view> </view>
</view> </view>
<text <text
class="member-code-text special-member-code-style" class="member-code-text special-member-code-style"
style=" style="
@ -53,6 +55,7 @@
</template> </template>
<script> <script>
import { snapdom } from '@zumer/snapdom'
import html2canvas from 'html2canvas' import html2canvas from 'html2canvas'
import { getSharedImg } from '@/config/login' import { getSharedImg } from '@/config/login'
export default { export default {
@ -90,7 +93,7 @@ export default {
isLoadingBackground: false, // isLoadingBackground: false, //
} }
}, },
mounted() { created() {
this.getBackgroundImage() this.getBackgroundImage()
}, },
methods: { methods: {
@ -124,57 +127,13 @@ export default {
this.$emit('background-image-error', e) this.$emit('background-image-error', e)
} }
}, },
//
retryGetBackgroundImage() {
console.log('手动重试获取背景图')
this.specialBackgroundImage = ''
this.backgroundImageLoaded = false
this.imageLoadRetryCount = 0
this.isLoadingBackground = false
this.getBackgroundImage()
},
//
getStatusInfo() {
return {
specialBackgroundImage: this.specialBackgroundImage
? this.specialBackgroundImage.length + ' bytes'
: '',
qrCodeImage: this.qrCodeImage
? this.qrCodeImage.substring(0, 50) + '...'
: '',
backgroundImageLoaded: this.backgroundImageLoaded,
isLoadingBackground: this.isLoadingBackground,
imageLoadRetryCount: this.imageLoadRetryCount,
isReady:
this.specialBackgroundImage &&
this.qrCodeImage &&
!this.isLoadingBackground,
}
},
// //
handleShareGenerated(dataUrl) { handleShareGenerated(dataUrl) {
console.log( uni.showToast({
'特殊场景分享图片生成成功,图片大小:', title: '图片生成成功,请长按保存',
Math.round(dataUrl.length / 1024) + 'KB' icon: 'success',
) duration: 2000,
})
if (this.isWechat) {
//
uni.showToast({
title: '图片生成成功,请长按保存',
icon: 'success',
duration: 2000,
})
} else {
uni.showToast({
title: '图片生成成功,请长按保存',
icon: 'success',
duration: 2000,
})
}
}, },
// //
@ -201,23 +160,11 @@ export default {
} else { } else {
this.backgroundImageLoaded = false this.backgroundImageLoaded = false
this.imageLoadRetryCount++ this.imageLoadRetryCount++
if (this.imageLoadRetryCount < this.maxRetryCount) {
console.log(
`背景图片加载失败,重试中 (${this.imageLoadRetryCount}/${this.maxRetryCount})`
)
this.$emit('background-image-retry', this.imageLoadRetryCount)
} else {
this.$emit('background-image-error', '背景图片加载失败')
}
} }
} catch (error) { } catch (error) {
console.error('获取特殊场景背景图片失败:', error)
this.backgroundImageLoaded = false this.backgroundImageLoaded = false
this.imageLoadRetryCount++ this.imageLoadRetryCount++
if (this.imageLoadRetryCount < this.maxRetryCount) { if (this.imageLoadRetryCount < this.maxRetryCount) {
console.log(
`背景图片加载失败,重试中 (${this.imageLoadRetryCount}/${this.maxRetryCount})`
)
this.$emit('background-image-retry', this.imageLoadRetryCount) this.$emit('background-image-retry', this.imageLoadRetryCount)
} else { } else {
this.$emit('background-image-error', error) this.$emit('background-image-error', error)
@ -229,18 +176,104 @@ export default {
}, },
async generateShareImage() { async generateShareImage() {
await this.getBackgroundImage() // await this.getBackgroundImage()
try { try {
await this.$nextTick() this.$nextTick(() => {
await this.capturePageWithHtml2Canvas() const info = uni.getSystemInfoSync()
console.log(info.platform, '.....info')
if (info.platform === 'ios') {
console.log('iOS detected, using html2canvas for capturing.')
this.capturePageWithHtml2Canvas()
} else {
console.log('Non-iOS detected, using snapdom for capturing.')
this.capturePageWithSnapDom()
}
})
} catch (error) { } catch (error) {
uni.hideLoading() uni.hideLoading()
uni.showToast({ title: '图片生成失败,请稍后重试', icon: 'none' }) uni.showToast({ title: '图片生成失败,请稍后重试', icon: 'none' })
} }
}, },
// 使html2canvas // 使html2canvas (For iOS)
async capturePageWithHtml2Canvas() { async capturePageWithHtml2Canvas() {
return new Promise(async (resolve, reject) => {
//
const waitForImages = async () => {
//
if (this.isLoadingBackground) {
console.log('等待背景图片接口调用完成...')
setTimeout(waitForImages, 100)
return
}
//
if (!this.backgroundImageLoaded) {
console.log('等待背景图片加载完成...')
setTimeout(waitForImages, 100)
return
}
//
if (!this.qrCodeImage) {
console.log('等待二维码生成...')
setTimeout(waitForImages, 100)
return
}
//
if (!this.specialBackgroundImage) {
console.log('等待特殊背景图片参数...')
setTimeout(waitForImages, 100)
return
}
console.log('所有图片准备就绪开始使用html2canvas截图...')
//
setTimeout(() => {
const element = this.$el
if (!element) {
uni.hideLoading()
reject(new Error('找不到组件容器'))
return
}
html2canvas(element, {
useCORS: true,
allowTaint: true,
backgroundColor: null,
scale: 2, //
dpi: 300,
width: element.offsetWidth,
height: element.offsetHeight,
})
.then(canvas => {
const dataUrl = canvas.toDataURL('image/jpeg', 1.0)
uni.hideLoading()
this.handleShareGenerated(dataUrl)
this.$emit('share-generated', dataUrl)
resolve()
})
.catch(err => {
uni.hideLoading()
console.error('html2canvas截取失败:', err)
uni.showToast({
title: '图片生成失败,请稍后重试',
icon: 'none',
})
reject(err)
})
}, 1000)
}
//
waitForImages()
})
},
// 使snapDOM
async capturePageWithSnapDom() {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
// //
const waitForImages = async () => { const waitForImages = async () => {
@ -275,57 +308,74 @@ export default {
console.log('所有图片准备就绪,开始截图...') console.log('所有图片准备就绪,开始截图...')
// //
setTimeout(() => { setTimeout(async () => {
const element = this.$el const element = this.$el
if (!element) { if (!element) {
reject(new Error('找不到组件容器')) reject(new Error('找不到组件容器'))
return return
} }
console.log( try {
'开始html2canvas截取特殊场景容器尺寸:', //
element.offsetWidth, const rect = element.getBoundingClientRect()
'x', const offsetWidth = element.offsetWidth
element.offsetHeight const offsetHeight = element.offsetHeight
) const scrollWidth = element.scrollWidth
const scrollHeight = element.scrollHeight
const clientWidth = element.clientWidth
const clientHeight = element.clientHeight
html2canvas(element, { //
useCORS: true, const elementWidth = Math.max(
allowTaint: false, offsetWidth,
backgroundColor: null, scrollWidth,
scale: window.devicePixelRatio, clientWidth,
logging: true, rect.width
width: element.offsetWidth, )
height: element.offsetHeight, const elementHeight = Math.max(
windowWidth: element.offsetWidth, offsetHeight,
windowHeight: element.offsetHeight, scrollHeight,
scrollX: 0, clientHeight,
scrollY: 0, rect.height
x: 0, )
y: 0,
imageTimeout: 10000,
proxy: undefined,
foreignObjectRendering: false,
})
.then(canvas => {
uni.hideLoading()
const dataUrl = canvas.toDataURL('image/jpeg', 1)
// setTimeout(async () => {
this.handleShareGenerated(dataUrl) try {
// const result = await snapdom(element, {
this.$emit('share-generated', dataUrl) width: elementWidth,
resolve() height: elementHeight,
}) quality: 1,
.catch(err => { compress: false,
uni.hideLoading()
console.error('html2canvas截取失败:', err) // useProxy: true,
uni.showToast({ })
title: '图片生成失败,请稍后重试', const canvas = await result.toCanvas()
icon: 'none',
}) const dataUrl = canvas.toDataURL('image/jpeg')
reject(err)
uni.hideLoading()
this.handleShareGenerated(dataUrl)
this.$emit('share-generated', dataUrl)
resolve()
} catch (err) {
uni.hideLoading()
console.error('snapDOM截取失败:', err)
uni.showToast({
title: '图片生成失败,请稍后重试',
icon: 'none',
})
reject(err)
}
}, 500)
} catch (err) {
uni.hideLoading()
console.error('snapDOM初始化失败:', err)
uni.showToast({
title: '图片生成失败,请稍后重试',
icon: 'none',
}) })
reject(err)
}
}, 1000) }, 1000)
} }
@ -407,10 +457,7 @@ export default {
.qr-code-outer { .qr-code-outer {
width: 400rpx; width: 400rpx;
height: 400rpx; height: 400rpx;
background: rgba(255, 255, 255, 0.98);
border-radius: 20rpx; border-radius: 20rpx;
box-shadow: 0px 8rpx 20rpx rgba(50, 50, 90, 0.06);
border: 1px solid #f0f0f0;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -418,12 +465,6 @@ export default {
box-sizing: border-box; box-sizing: border-box;
} }
/* 特殊场景下的二维码样式 */
.special-qr-style {
/* 用户可以在这里自定义特殊场景下的二维码样式 */
/* 例如:不同的位置、大小、边框等 */
}
.qr-code, .qr-code,
.qr-code-placeholder { .qr-code-placeholder {
width: 100%; width: 100%;

7
package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@zumer/snapdom": "^1.9.5",
"axios": "^1.5.0", "axios": "^1.5.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"echarts": "^4.9.0", "echarts": "^4.9.0",
@ -768,6 +769,12 @@
"dev": true, "dev": true,
"peer": true "peer": true
}, },
"node_modules/@zumer/snapdom": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/@zumer/snapdom/-/snapdom-1.9.5.tgz",
"integrity": "sha512-p+9C86evbQceSlDBhRA2+BJSotBPO7YFh6b7LeUDPowt/iAx5kzZomO3Fme7mYqfpn990TDKFHn0XascDvUODw==",
"license": "MIT"
},
"node_modules/acorn": { "node_modules/acorn": {
"version": "6.4.2", "version": "6.4.2",
"resolved": "https://mirrors.cloud.tencent.com/npm/acorn/-/acorn-6.4.2.tgz", "resolved": "https://mirrors.cloud.tencent.com/npm/acorn/-/acorn-6.4.2.tgz",

View File

@ -4,6 +4,7 @@
"description": "h5", "description": "h5",
"main": "main.js", "main": "main.js",
"dependencies": { "dependencies": {
"@zumer/snapdom": "^1.9.5",
"axios": "^1.5.0", "axios": "^1.5.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"echarts": "^4.9.0", "echarts": "^4.9.0",

View File

@ -17,7 +17,7 @@
<!-- 特殊场景 --> <!-- 特殊场景 -->
<SpecialSharePage <SpecialSharePage
v-if="isSpecialScene" v-if="isSpecialScene && sourceVisible"
:qrCodeImage="qrCodeImage" :qrCodeImage="qrCodeImage"
:specialBackgroundImage="userInfo.sharePosterImage" :specialBackgroundImage="userInfo.sharePosterImage"
:userInfo="userInfo" :userInfo="userInfo"
@ -70,6 +70,7 @@ export default {
isWechat: false, isWechat: false,
generatedImageUrl: '', generatedImageUrl: '',
userInfo: uni.getStorageSync('User'), userInfo: uni.getStorageSync('User'),
sourceVisible: true,
} }
}, },
computed: { computed: {
@ -146,7 +147,9 @@ export default {
// //
handleShareGenerated(dataUrl) { handleShareGenerated(dataUrl) {
console.log('handleShareGenerated', dataUrl) this.$nextTick(() => {
this.sourceVisible = false
})
uni.hideLoading() uni.hideLoading()
if (this.isWechat) { if (this.isWechat) {
@ -157,13 +160,6 @@ export default {
if (this.isSpecialScene) { if (this.isSpecialScene) {
// //
this.generatedImageUrl = dataUrl this.generatedImageUrl = dataUrl
} else {
//
this.downloadImage(dataUrl)
uni.showToast({
title: '图片已开始下载',
icon: 'success',
})
} }
} }
}, },