微信小程序- 用canvas生成排行榜

发布于:2025-06-07 ⋅ 阅读:(11) ⋅ 点赞:(0)

设计功能不是很复杂,也不想用插件,最终出现现在版本,主要用到微信小程序 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,
      },
    });
  },

生成后的效果:


网站公告

今日签到

点亮在社区的每一天
去签到