Forráskód Böngészése

优化海报生成

wkw 3 hete
szülő
commit
e4926bfa83
6 módosított fájl, 166 hozzáadás és 134 törlés
  1. 35 126
      pages/index/index.js
  2. 2 1
      pages/index/index.json
  3. 14 4
      pages/index/index.wxml
  4. 14 3
      pages/index/index.wxss
  5. 5 0
      utils/request.js
  6. 96 0
      utils/util.js

+ 35 - 126
pages/index/index.js

@@ -1,7 +1,7 @@
-import {miniProgramConfig,sharePoster} from '../../api/other';
+import {miniProgramConfig} from '../../api/other';
 import {BASE_URL} from '../../utils/request';
 import {isLoggedIn,doLogin} from '../../utils/auth';
-import { REPORT_BEHAVIOR } from '../../utils/util.js';
+import { REPORT_BEHAVIOR,DRAW_POSTER } from '../../utils/util.js';
 const app = getApp();
 Page({
   data: {
@@ -17,36 +17,21 @@ Page({
     showShare: false,
     showPoster:false,
     posterImg:'',//生成的海报
-  },
-  onLoad(){
-    // this.getminiProgramConfig();
+    posterLoading:false,
+    imageReady: false
   },
   // 获取配置信息
   async getminiProgramConfig(){
     const res = await miniProgramConfig();
     wx.setStorageSync('programConfig', res.data);
     app.globalData.programConfig = res.data;
-    this.setData({
-      banners:res.data.carousels,
-      introduction:res.data.marathon_event.introduction,
-      start_time:res.data.marathon_event.start_time,
-      end_time:res.data.marathon_event.end_time,
-      ad_img:res.data.ad_img,
-      share_img:res.data.share_img
-    })
+    this.updateData(res.data);
   },
   onShow() {
     if(!app.globalData.programConfig){
       this.getminiProgramConfig();
     }else{
-      this.setData({
-        banners:app.globalData.programConfig.carousels,
-        introduction:app.globalData.programConfig.marathon_event.introduction,
-        start_time:app.globalData.programConfig.marathon_event.start_time,
-        end_time:app.globalData.programConfig.marathon_event.end_time,
-        ad_img:app.globalData.programConfig.ad_img,
-        share_img:app.globalData.programConfig.share_img
-      })
+      this.updateData(app.globalData.programConfig);
     }
     if (typeof this.getTabBar === 'function' && this.getTabBar()) {
       this.getTabBar().setData({
@@ -57,6 +42,16 @@ Page({
       })
     }
   },
+  updateData(data){
+    this.setData({
+      banners:data.carousels,
+      introduction:data.marathon_event.introduction,
+      start_time:data.marathon_event.start_time,
+      end_time:data.marathon_event.end_time,
+      ad_img:data.ad_img,
+      share_img:data.share_img
+    })
+  },
   goPage(e){
     const index = e.currentTarget.dataset.index;
     if(index == 0){
@@ -121,112 +116,26 @@ Page({
     const programConfig = app.globalData.programConfig || wx.getStorageSync('programConfig');
     const posterKey = `posterImg_${userInfo.avatar}_${userInfo.nickname}_${programConfig.share_img}_${programConfig.share_qrcode}`;
     const cachedData = wx.getStorageSync('posterCache') || {};
-    if (cachedData.key === posterKey && cachedData.path) {
-      this.setData({ posterImg: cachedData.path, showPoster: true });
-    } else {
-      this.setData({ showPoster: true }, async () => {
-        const path = await this.drawPoster();
-        wx.setStorageSync('posterCache', { key: posterKey, path });
-      });
-    }
-  },
-
-  async drawPoster() {
-    const userInfo = app.globalData.userInfo || wx.getStorageSync('userInfo');
-    const programConfig = app.globalData.programConfig || wx.getStorageSync('programConfig');
-    const ctx = wx.createCanvasContext('posterCanvas', this);
-    const canvasWidth = 300;
-    const canvasHeight = 600;
-    const bottomHeight = 80;
-    const radius = 16; // 圆角半径
-  
-    ctx.save();
-    ctx.setFillStyle('#fff');
-    this.drawRoundRect(ctx, 0, 0, canvasWidth, canvasHeight, radius);
-  
-    // 背景图
-    const bgUrl = programConfig.share_img;
-    if (bgUrl) {
-      const bgPath = bgUrl.startsWith('http') ? await this.downloadImage(bgUrl) : bgUrl;
-      ctx.drawImage(bgPath, 0, 0, canvasWidth, canvasHeight - bottomHeight);
-    }
-  
-    // 底部白色区域
-    ctx.setFillStyle('#fff');
-    ctx.fillRect(0, canvasHeight - bottomHeight, canvasWidth, bottomHeight);
-  
-    // 用户头像(圆形)
-    const avatarUrl = userInfo.avatar;
-    if (avatarUrl) {
-      const avatarPath = avatarUrl.startsWith('http') ? await this.downloadImage(avatarUrl) : avatarUrl;
-      const avatarSize = 40;
-      const avatarX = 20;
-      const avatarY = canvasHeight - bottomHeight + (bottomHeight - avatarSize) / 2;
-      ctx.save();
-      ctx.beginPath();
-      ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, Math.PI * 2);
-      ctx.clip();
-      ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize);
-      ctx.restore();
-    }
-  
-    // 昵称
-    const nickName = userInfo.nickname || '游客';
-    ctx.setFontSize(16);
-    ctx.setFillStyle('#000');
-    const textX = 20 + 40 + 12;
-    const textY = canvasHeight - bottomHeight + bottomHeight / 2 + 6;
-    ctx.fillText(nickName, textX, textY);
-  
-    // 二维码
-    const qrUrl = programConfig.share_qrcode;
-    if (qrUrl) {
-      const qrPath = qrUrl.startsWith('http') ? await this.downloadImage(qrUrl) : qrUrl;
-      const qrSize = 50;
-      const qrX = canvasWidth - qrSize - 20;
-      const qrY = canvasHeight - bottomHeight + (bottomHeight - qrSize) / 2;
-      ctx.drawImage(qrPath, qrX, qrY, qrSize, qrSize);
-    }
-    ctx.restore(); // 恢复裁剪
-    return new Promise((resolve, reject) => {
-      ctx.draw(true, () => {
-        wx.canvasToTempFilePath({
-          canvasId: 'posterCanvas',
-          success: res => resolve(res.tempFilePath),
-          fail: err => {
-            console.error('生成临时文件失败:', err);
-            reject(err);
-          }
-        }, this);
-      });
-    });
-  },
-  
-  // 下载网络图片到临时路径
-  downloadImage(url) {
-    return new Promise((resolve, reject) => {
-      wx.downloadFile({
-        url,
-        success(res) {
-          if (res.statusCode === 200) resolve(res.tempFilePath);
-          else reject(new Error('下载失败'));
-        },
-        fail(err) { reject(err); }
-      });
+    const needGenerate = !cachedData.path || cachedData.key !== posterKey;
+    this.setData({ 
+      showPoster: true, 
+      posterLoading: needGenerate,
+      posterImg: needGenerate ? '' : cachedData.path,
+      imageReady: false
     });
+    if (!needGenerate) return;
+    try {
+      const path = await DRAW_POSTER(); 
+      wx.setStorageSync('posterCache', { key: posterKey, path });
+      this.setData({ posterImg: path });
+    } catch (err) {
+      this.setData({ posterLoading: false });
+      wx.showToast({ title: '生成海报失败', icon: 'none' });
+    }
   },
-  // 绘制圆角矩形函数
-  drawRoundRect(ctx, x, y, w, h, r) {
-    ctx.beginPath();
-    ctx.moveTo(x + r, y);
-    ctx.arcTo(x + w, y, x + w, y + h, r);
-    ctx.arcTo(x + w, y + h, x, y + h, r);
-    ctx.arcTo(x, y + h, x, y, r);
-    ctx.arcTo(x, y, x + w, y, r);
-    ctx.closePath();
-    ctx.fill();
-    ctx.clip(); // 关键:裁剪出圆角
-  },
+  onImageLoad() {
+    this.setData({ posterLoading: false, imageReady: true });
+  },     
   closePoster(){
     this.setData({ showPoster: false});
   },
@@ -249,4 +158,4 @@ Page({
       fail: console.error,
     });
   }
-})
+})

+ 2 - 1
pages/index/index.json

@@ -1,7 +1,8 @@
 {
   "usingComponents": {
     "countdown": "/components/Countdown/Countdown",
-    "van-popup": "@vant/weapp/popup/index"
+    "van-popup": "@vant/weapp/popup/index",
+    "van-loading": "@vant/weapp/loading/index"
   },
   "navigationStyle": "custom"
 }

+ 14 - 4
pages/index/index.wxml

@@ -61,6 +61,7 @@
       </view>
     </view>
   </view>
+  <!-- 弹窗 -->
   <van-popup show="{{showShare}}" position="bottom" round>
     <view class="share-container">
       <view class="share-options">
@@ -79,12 +80,21 @@
       </view>
     </view>
   </van-popup>
+  <!-- 海报 -->
   <view class="poster-container" wx:if="{{showPoster}}" bindtap="closePoster">
     <view class="poster-content">
-      <image wx:if="{{posterImg}}" src="{{posterImg}}" mode="aspectFit" class="poster-img" />
-      <canvas wx:if="{{!posterImg}}" canvas-id="posterCanvas" width="300" height="600" style="height:600px;"></canvas>
-
-      <view class="poster-actions">
+      <van-loading wx:if="{{posterLoading}}" color="#FFA100" class="poster-loading">海报生成中...</van-loading>
+      <canvas wx:if="{{posterLoading}}" canvas-id="posterCanvas"
+        style="width:300px;height:600px;position:absolute;top:-9999px;"></canvas>
+      <image 
+        wx:if="{{posterImg}}" 
+        src="{{posterImg}}" 
+        mode="aspectFit" 
+        class="poster-img"
+        style="opacity: {{imageReady ? 1 : 0}}; transition: opacity 0.3s;"
+        bindload="onImageLoad"
+      />
+      <view class="poster-actions" wx:if="{{posterImg && imageReady}}">
         <button class="poster-lf" bindtap="savePoster">保存到相册</button>
         <button class="poster-ri" bindtap="sendImg" data-type="poster">发送朋友</button>
       </view>

+ 14 - 3
pages/index/index.wxss

@@ -168,6 +168,16 @@
   width: 100%;
   background-color: rgba(0,0,0,0.7);
 }
+.poster-loading{
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+  
+}
+.van-loading__text{
+  color: #FFA100 !important;
+}
 .poster-content{
   position: absolute;
   top: 10%;
@@ -178,9 +188,10 @@
   margin: 0 auto;
 }
 .poster-img{
-  width: 100%;
-  height: 100%;
-  border-radius: 20rpx;
+  width: 300px;
+  height: 600px;
+  display: block;
+  /* border-radius: 20rpx; */
 }
 .poster-actions{
   display: flex;

+ 5 - 0
utils/request.js

@@ -30,6 +30,11 @@ function request({ loading = true, url, method = 'GET', data = {}, header = {} }
           });
           wx.removeStorageSync('token');
           wx.removeStorageSync('expires_in');
+          wx.removeStorageSync('posterCache');
+          wx.removeStorageSync('userInfo');
+          wx.removeStorageSync('programConfig');
+          getApp().globalData.userInfo = null;
+          getApp().globalData.programConfig = null;
           setTimeout(() => {
             wx.reLaunch({
               url: '/pages/index/index'

+ 96 - 0
utils/util.js

@@ -39,3 +39,99 @@ export async function REPORT_BEHAVIOR(eventName = '', extra = {}) {
     return null;
   }
 }
+// 生成海报
+export async function DRAW_POSTER() {
+  const userInfo = getApp().globalData.userInfo || wx.getStorageSync('userInfo');
+  const programConfig = getApp().globalData.programConfig || wx.getStorageSync('programConfig');
+  const ctx = wx.createCanvasContext('posterCanvas', this);
+  const canvasWidth = 300;
+  const canvasHeight = 600;
+  const bottomHeight = 80;
+  const radius = 16; // 圆角半径
+
+  ctx.save();
+  ctx.setFillStyle('#fff');
+  drawRoundRect(ctx, 0, 0, canvasWidth, canvasHeight, radius);
+
+  // 背景图
+  const bgUrl = programConfig.share_img;
+  if (bgUrl) {
+    const bgPath = bgUrl.startsWith('http') ? await downloadImage(bgUrl) : bgUrl;
+    ctx.drawImage(bgPath, 0, 0, canvasWidth, canvasHeight - bottomHeight);
+  }
+
+  // 底部白色区域
+  ctx.setFillStyle('#fff');
+  ctx.fillRect(0, canvasHeight - bottomHeight, canvasWidth, bottomHeight);
+
+  // 用户头像(圆形)
+  const avatarUrl = userInfo.avatar;
+  if (avatarUrl) {
+    const avatarPath = avatarUrl.startsWith('http') ? await downloadImage(avatarUrl) : avatarUrl;
+    const avatarSize = 40;
+    const avatarX = 20;
+    const avatarY = canvasHeight - bottomHeight + (bottomHeight - avatarSize) / 2;
+    ctx.save();
+    ctx.beginPath();
+    ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, Math.PI * 2);
+    ctx.clip();
+    ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize);
+    ctx.restore();
+  }
+
+  // 昵称
+  const nickName = userInfo.nickname || '游客';
+  ctx.setFontSize(16);
+  ctx.setFillStyle('#000');
+  const textX = 20 + 40 + 12;
+  const textY = canvasHeight - bottomHeight + bottomHeight / 2 + 6;
+  ctx.fillText(nickName, textX, textY);
+
+  // 二维码
+  const qrUrl = programConfig.share_qrcode;
+  if (qrUrl) {
+    const qrPath = qrUrl.startsWith('http') ? await downloadImage(qrUrl) : qrUrl;
+    const qrSize = 50;
+    const qrX = canvasWidth - qrSize - 20;
+    const qrY = canvasHeight - bottomHeight + (bottomHeight - qrSize) / 2;
+    ctx.drawImage(qrPath, qrX, qrY, qrSize, qrSize);
+  }
+  ctx.restore(); // 恢复裁剪
+  return new Promise((resolve, reject) => {
+    ctx.draw(true, () => {
+      wx.canvasToTempFilePath({
+        canvasId: 'posterCanvas',
+        success: res => resolve(res.tempFilePath),
+        fail: err => {
+          console.error('生成临时文件失败:', err);
+          reject(err);
+        }
+      }, this);
+    });
+  });
+}
+// 下载网络图片到临时路径
+function downloadImage(url) {
+  return new Promise((resolve, reject) => {
+    wx.downloadFile({
+      url,
+      success(res) {
+        if (res.statusCode === 200) resolve(res.tempFilePath);
+        else reject(new Error('下载失败'));
+      },
+      fail(err) { reject(err); }
+    });
+  });
+}
+// 绘制圆角矩形函数
+function drawRoundRect(ctx, x, y, w, h, r) {
+  ctx.beginPath();
+  ctx.moveTo(x + r, y);
+  ctx.arcTo(x + w, y, x + w, y + h, r);
+  ctx.arcTo(x + w, y + h, x, y + h, r);
+  ctx.arcTo(x, y + h, x, y, r);
+  ctx.arcTo(x, y, x + w, y, r);
+  ctx.closePath();
+  ctx.fill();
+  ctx.clip(); // 关键:裁剪出圆角
+}