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>
import VConsole from 'vconsole'
// import VConsole from 'vconsole'
import { setToken } from '@/config/auth.js'
const vConsole = new VConsole()
// const vConsole = new VConsole()
export default {
onLaunch: function (options) {
//

View File

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

7
package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@zumer/snapdom": "^1.9.5",
"axios": "^1.5.0",
"dayjs": "^1.11.13",
"echarts": "^4.9.0",
@ -768,6 +769,12 @@
"dev": 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": {
"version": "6.4.2",
"resolved": "https://mirrors.cloud.tencent.com/npm/acorn/-/acorn-6.4.2.tgz",

View File

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

View File

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