設計功能不是很復雜,也不想用插件,最終出現現在版本,主要用到微信小程序?wx.canvasToTempFilePath方法
// 直接調用改方法
createQRCode() {const qrCodeCanvasId = "qrcodeCanvas";drawQrcode({width: 200,height: 200,canvasId: "qrcodeCanvas",text: `https://www.dabanban.cn/face?rotateId=${this.data.rotateId}`,});setTimeout(() => {this.convertCanvasToImage(qrCodeCanvasId);}, 500);},//把 Canvas 轉換成圖片convertCanvasToImage(canvasId) {wx.canvasToTempFilePath({canvasId: canvasId,success: (res) => {// 傳給 painter 組件繪制this.generatePoster(res.tempFilePath);},fail: (err) => {console.error("canvas 轉圖片失敗:", err);},});},generatePoster(qrCodeUrl) {const { rankList, maleIcon, femaleIcon, rotateInfo } = this.data;const bgColors = ["#FFECEC", "#EAF4FF", "#FFF8E1", "#F1F1F1"];const spacing = 20;const baseTop = 750;const defaultAvatar = '/images/default-avatar.png'; // 添加默認頭像路徑// 計算每個排名項的基礎高度const getItemHeight = (playerCount) => 80 + (60 * playerCount);// 計算垂直偏移量const getVerticalOffset = (index, prevPlayersCount) => {let totalHeight = 0;for (let i = 0; i < index; i++) {const players = Array.isArray(rankList[i].user) ? rankList[i].user : [rankList[i]];totalHeight += getItemHeight(players.length) + spacing;}return baseTop + totalHeight;};const views = [// 標題{type: "text",text: "看看",css: {top: "40rpx",left: "50%",fontSize: "40rpx",align: "center",color: "#333",fontWeight: "bold",},},// 排行榜背景{type: "image",url: "https://www.dabanban.cn/resource/dabanban/miniImages/rank.png",css: {width: "628rpx",height: "442rpx",top: "132rpx",left: "50%",align: "center",},},// 輪轉比賽背景框{type: "rect",css: {width: "668rpx",height: "280rpx",top: "450rpx",left: "50%",align: "center",borderRadius: "20rpx",color: "#FFFFFF",borderWidth: "2rpx",borderColor: "#E0E0E0",},},// 輪轉比賽標題{type: "text",text: rotateInfo.title,css: {top: "483rpx",left: "72rpx",fontSize: "30rpx",color: "#333",fontWeight: "bold",maxLines: 1,ellipsis: true,},},// 幾隊{type: "text",text: `${rotateInfo.totalNum}隊`,css: {top: "547rpx",left: "72rpx",fontSize: "26rpx",color: "#666",maxLines: 1,ellipsis: true,},},// 多少人輪轉{type: "text",text: `${rotateInfo.rotateTypeName}`,css: {top: "547rpx",left: "120rpx",fontSize: "26rpx",color: "#666",maxLines: 1,ellipsis: true,},},// 多少分制{type: "text",text: `${rotateInfo.scoreRule}分制`,css: {top: "547rpx",left: "350rpx",fontSize: "26rpx",color: "#666",},},// 表頭背景{type: "rect",css: {width: "668rpx",height: "100rpx",left: "50%",top: "650rpx",align: "center",borderRadius: "10rpx",color: "#F0F0F0",},},// 表頭文本{type: "text",text: "排名",css: {top: "685rpx",left: `80rpx`,fontSize: "26rpx",color: "#666",fontWeight: "bold",},},{type: "text",text: "參賽選手",css: {top: "685rpx",left: `200rpx`,fontSize: "26rpx",color: "#666",fontWeight: "bold",},},{type: "text",text: "勝-負",css: {top: "685rpx",left: `485rpx`,fontSize: "26rpx",color: "#666",fontWeight: "bold",},},{type: "text",text: "凈勝分",css: {top: "685rpx",left: `600rpx`,fontSize: "26rpx",color: "#666",fontWeight: "bold",},},];// 動態生成選手排名rankList.forEach((player, index) => {const players = Array.isArray(player.user) ? player.user : [player];const playerCount = players.length;const itemHeight = getItemHeight(playerCount);const itemTop = getVerticalOffset(index, playerCount);// 排名項背景views.push({type: "rect",css: {width: "668rpx",height: `${itemHeight}rpx`,left: "50%",top: `${itemTop}rpx`,align: "center",borderRadius: "10rpx",color: bgColors[index] || "#F1F1F1",}});// 排名數字views.push({type: "text",text: `${index + 1}`,css: {top: `${itemTop + itemHeight/2 - 15}rpx`, // 垂直居中left: "70rpx",fontSize: "30rpx",color: "#121212",fontWeight: "bold",width: "50rpx",textAlign: "center",}});// 每個玩家的信息players.forEach((p, i) => {const playerTop = itemTop + 25 + (i * 80);// 頭像views.push({type: "image",url: p.imgUrl || defaultAvatar,css: {width: "60rpx",height: "60rpx",top: `${playerTop}rpx`,left: "170rpx",borderRadius: "30rpx",}});// 性別圖標views.push({type: "image",url: p.gender === 1 ? femaleIcon : maleIcon,css: {width: "30rpx",height: "30rpx",top: `${playerTop + 15}rpx`,left: "240rpx",}});// 玩家昵稱views.push({type: "text",text: p.nickName || "匿名玩家",css: {top: `${playerTop + 10}rpx`,left: "280rpx",fontSize: "28rpx",color: "#121212",maxLines: 1,ellipsis: true,width: "180rpx",}});});// 勝負記錄和凈勝分(顯示在第一個玩家行)views.push({type: "text",text: `${player.successNum || 0}-${player.failNum || 0}`,css: {top: `${itemTop + itemHeight/2 - 15}rpx`,left: "490rpx",fontSize: "30rpx",color: "#121212",fontWeight: "bold",}},{type: "text",text: `${player.score || 0}`,css: {top: `${itemTop + itemHeight/2 - 15}rpx`,left: "590rpx",fontSize: "30rpx",color: "#121212",width: "100rpx",textAlign: "center",}});});// 計算二維碼位置const qrTop = getVerticalOffset(rankList.length, 0) + 50;views.push({type: "text",text: "長按掃碼查看詳情",css: {top: `${qrTop - 30}rpx`,left: "50%",fontSize: "22rpx",color: "#333",fontWeight: "bold",align: "center",},},{type: "image",url: qrCodeUrl,css: {width: "150rpx",height: "150rpx",top: `${qrTop + 20}rpx`,left: "50%",align: "center",},},{type: "text",text: "—0ne 看看—",css: {top: `${qrTop + 190}rpx`,left: "50%",fontSize: "26rpx",color: "#333",fontWeight: "bold",align: "center",},});this.setData({posterData: {width: "750rpx",height: `${qrTop + 280}rpx`,background: "#F8F9FB",pixelRatio: 2,views,},});},
生成后的效果: