feat(specialSharePage): 分享页自定义背景图
This commit is contained in:
		
							parent
							
								
									848ccce4d2
								
							
						
					
					
						commit
						eb3089544e
					
				
							
								
								
									
										4
									
								
								App.vue
								
								
								
								
							
							
						
						
									
										4
									
								
								App.vue
								
								
								
								
							| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
    // 分享注册
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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%;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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',
 | 
			
		||||
          })
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue