微信小程序- 用canvas生成排行榜
设计功能不是很复杂,也不想用插件,最终出现现在版本,主要用到微信小程序 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,},});},
生成后的效果: