微信小游戏排行榜

子域index.js代码如下:

import * as Consts from './consts'

const PAGE_SIZE = 6;
const ITEM_HEIGHT = 125;

const dataSorter = (gameDatas, field = Consts.OpenDataKeys.Grade) => {
    return gameDatas.sort((a, b) => {
        const kvDataA = a.KVDataList.find(kvData => kvData.key === Consts.OpenDataKeys.Grade);
        const kvDataB = b.KVDataList.find(kvData => kvData.key === Consts.OpenDataKeys.Grade);
        const gradeA = kvDataA ? parseInt(kvDataA.value || 0) : 0;
        const gradeB = kvDataB ? parseInt(kvDataB.value || 0) : 0;
        return gradeA > gradeB ? -1 : gradeA < gradeB ? 1 : 0;
    });
}

class RankListRenderer
{
    constructor()
    {
        this.totalPage = 0;
        this.currPage = 0;
        this.gameDatas = [];    //https://developers.weixin.qq.com/minigame/dev/document/open-api/data/UserGameData.html
        this.init();
    }

    init()
    {
        this.canvas = wx.getSharedCanvas();
        this.ctx = this.canvas.getContext('2d');
        this.ctx.imageSmoothingEnabled = true;
        this.ctx.imageSmoothingQuality = "high";
    }

    listen()
    {
        //msg -> {action, data}
        wx.onMessage(msg => {
            console.log("ranklist wx.onMessage", msg);
            switch(msg.action)
            {
                case Consts.DomainAction.FetchFriend:
                    this.fetchFriendData();
                    break;

                case Consts.DomainAction.FetchGroup:
                    if(!msg.data)
                    {
                        return;
                    }
                    this.fetchGroupData(msg.data);
                    break;

                case Consts.DomainAction.Paging:
                    if(!this.gameDatas.length)
                    {
                        return;
                    }
                    const delta = msg.data;
                    const newPage = this.currPage + delta;
                    if(newPage < 0)
                    {
                        console.log("已经是第一页了");
                        return;
                    }
                    if(newPage + 1 > this.totalPage)
                    {
                        console.log("没有更多了");
                        return;
                    }
                    this.currPage = newPage;
                    this.showPagedRanks(newPage);
                    break;

                default:
                    console.log(`未知消息类型:msg.action=${msg.action}`);
                    break;
            }
        });
    }

    fetchGroupData(shareTicket)
    {
        //取出群同玩成员数据
        wx.getGroupCloudStorage({
            shareTicket,
            keyList:[
                Consts.OpenDataKeys.Grade,
            ],
            success:res => {
                console.log("wx.getGroupCloudStorage success", res);
                const dataLen = res.data.length;
                this.gameDatas = dataSorter(res.data);
                this.currPage = 0;
                this.totalPage = Math.ceil(dataLen / PAGE_SIZE);
                if(dataLen)
                {
                    this.showPagedRanks(0);
                }
            },
            fail:res => {
                console.log("wx.getGroupCloudStorage fail", res);
            },
        });
    }

    fetchFriendData()
    {
        //取出所有好友数据
        wx.getFriendCloudStorage({
            keyList:[
                Consts.OpenDataKeys.Grade,
            ],
            success:res => {
                console.log("wx.getFriendCloudStorage success", res);
                const dataLen = res.data.length;
                this.gameDatas = dataSorter(res.data);
                this.currPage = 0;
                this.totalPage = Math.ceil(dataLen / PAGE_SIZE);
                if(dataLen)
                {
                    this.showPagedRanks(0);
                }
            },
            fail:res => {
                console.log("wx.getFriendCloudStorage fail", res);
            },
        });
    }

    showPagedRanks(page)
    {
        const pageStart = page * PAGE_SIZE;
        const pagedData = this.gameDatas.slice(pageStart, pageStart + PAGE_SIZE);
        const pageLen = pagedData.length;

        this.ctx.clearRect(0, 0, 1000, 1000);
        for(let i = 0, len = pagedData.length; i < len; i++)
        {
            this.drawRankItem(this.ctx, i, pageStart + i + 1, pagedData[i], pageLen);
        }
    }

    //canvas原点在左上角
    drawRankItem(ctx, index, rank, data, pageLen)
    {
        const avatarUrl = data.avatarUrl.substr(0, data.avatarUrl.length - 1) + "132";
        const nick = data.nickname.length <= 10 ? data.nickname : data.nickname.substr(0, 10) + "...";
        const kvData = data.KVDataList.find(kvData => kvData.key === Consts.OpenDataKeys.Grade);
        const grade = kvData ? kvData.value : 0;
        const itemGapY = ITEM_HEIGHT * index;
        //名次
        ctx.fillStyle = "#D8AD51";
        ctx.textAlign = "right";
        ctx.baseLine = "middle";
        ctx.font = "70px Helvetica";
        ctx.fillText(`${rank}`, 90, 80 + itemGapY);

        //头像
        const avatarImg = wx.createImage();
        avatarImg.src = avatarUrl;
        avatarImg.onload = () => {
            if(index + 1 > pageLen)
            {
                return;
            }
            ctx.drawImage(avatarImg, 120, 10 + itemGapY, 100, 100);
        };

        //名字
        ctx.fillStyle = "#777063";
        ctx.textAlign = "left";
        ctx.baseLine = "middle";
        ctx.font = "30px Helvetica";
        ctx.fillText(nick, 235, 80 + itemGapY);

        //分数
        ctx.fillStyle = "#777063";
        ctx.textAlign = "left";
        ctx.baseLine = "middle";
        ctx.font = "30px Helvetica";
        ctx.fillText(`${grade}分`, 620, 80 + itemGapY);

        //分隔线
        const lineImg = wx.createImage();
        lineImg.src = 'subdomain/images/llk_x.png';
        lineImg.onload = () => {
            if(index + 1 > pageLen)
            {
                return;
            }
            ctx.drawImage(lineImg, 14, 120 + itemGapY, 720, 1);
        };
    }
}

const rankList = new RankListRenderer();
rankList.listen();

主域:

import {appdata} from './appdata'
import {POP_UI_BASE} from './common/ui/pop_ui_base'
import {pop_mgr, UI_CONFIG} from "./common/ui/pop_mgr"
import {TimerMgr} from "./common/timer/timer_mgr"
import * as utils from "./common/util"
import * as wxapi from "./common/wxapi"
import * as Audio from "./common/audio/audioplayer"

const {ccclass, property} = cc._decorator;
@ccclass
export class RankView2 extends POP_UI_BASE {

    @property(cc.Sprite)
    img_head: cc.Sprite = null;

    @property(cc.Label)
    txt_name: cc.Label = null;

    @property(cc.Sprite)
    img_rank: cc.Sprite = null;

    timerId:number;
    isDirty:boolean;
    rankTexture:cc.Texture2D;
    rankSpriteFrame:cc.SpriteFrame;

    on_show(...params)
    {
        const appUserInfo = appdata.appUserInfo;
        const wxUserInfo = appdata.wxUserInfo;
        const avatarUrl = wxUserInfo.avatarUrl132;
        if(avatarUrl.length > 0)
        {
            this.txt_name.string = wxUserInfo.nickName;
            utils.load_external_img(this.img_head, avatarUrl, "png");
        }

        //只能在主域设置大小, 且要先于赋值到sprite才起作用
        const sharedCanvas = wxapi.wxOpenData.wxGetSharedCanvas();
        sharedCanvas.width = this.img_rank.node.width;
        sharedCanvas.height = this.img_rank.node.height;

        this.rankTexture = new cc.Texture2D();
        this.rankSpriteFrame = new cc.SpriteFrame();
        
        //拿好友排行榜
        this.isDirty = true;
        wxapi.wxOpenData.wxPostMessageToSubDomain({
            action:wxapi.WxDomainAction.FetchFriend,
        });
        this.timerId = TimerMgr.getInst().loop(0.1, utils.gen_handler(this.updateRankList, this));
    }

    on_hide() 
    {
        this.rankTexture = null;
        this.rankSpriteFrame = null;
        this.isDirty = false;
        TimerMgr.getInst().remove(this.timerId);
    }

    updateRankList()
    {
        if(!this.isDirty)
        {
            return;
        }
        const sharedCanvas = wxapi.wxOpenData.wxGetSharedCanvas();
        this.rankTexture.initWithElement(sharedCanvas);
        this.rankTexture.handleLoadedTexture();
        this.rankSpriteFrame.setTexture(this.rankTexture);
        this.img_rank.spriteFrame = this.rankSpriteFrame;
    }

    onTouchPageBtn(event, delta)
    {
        this.isDirty = true;
        wxapi.wxOpenData.wxPostMessageToSubDomain({
            action:wxapi.WxDomainAction.Paging,
            data:parseInt(delta),
        });
    }

    onTouchGroupRank()
    {
        wxapi.wxShare.share({
            title:"分享文本", 
            imageUrl:"shareimg/bg02.png",
            query:`openID=${appdata.appUserInfo.openid}`,
        }, utils.gen_handler((shareTickets:string[]) => {
            if(!shareTickets || !shareTickets.length)
            {
                console.log('本次分享无shareTicket');
                return;
            }
            const shareTicket = shareTickets[0];
            this.isDirty = true;
            wxapi.wxOpenData.wxPostMessageToSubDomain({
                action:wxapi.WxDomainAction.FetchGroup,
                data:shareTicket,
            });
        }));
    }
}

排行榜滑动和翻页

主域:

import * as Consts from "./consts"
import {appdata} from './appdata'
import {POP_UI_BASE} from './common/ui/pop_ui_base'
import {pop_mgr, UI_CONFIG} from "./common/ui/pop_mgr"
import {TimerMgr} from "./common/timer/timer_mgr"
import * as utils from "./common/util"
import * as wxapi from "./common/wxapi"
import * as Audio from "./common/audio/audioplayer"

const {ccclass, property} = cc._decorator;
@ccclass
export class RankView2 extends POP_UI_BASE {
    @property(cc.Sprite)
    img_rank: cc.Sprite = null;

    timerId:number;
    isDirty:boolean;
    rankTexture:cc.Texture2D;
    rankSpriteFrame:cc.SpriteFrame;

    on_show(...params)
    {
        //只能在主域设置大小, 且要先于赋值到sprite才起作用
        const sharedCanvas = wxapi.wxOpenData.wxGetSharedCanvas();
        sharedCanvas.width = this.img_rank.node.width;
        sharedCanvas.height = this.img_rank.node.height;

        this.rankTexture = new cc.Texture2D();
        this.rankSpriteFrame = new cc.SpriteFrame();
        
        //拿好友排行榜
        this.isDirty = true;
        wxapi.wxOpenData.wxPostMessageToSubDomain({
            action:wxapi.WxDomainAction.FetchFriend,
        });
        this.timerId = TimerMgr.getInst().loop(0.1, utils.gen_handler(this.updateRankList, this));
        this.img_rank.node.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
    }

    on_hide() 
    {
        this.rankTexture = null;
        this.rankSpriteFrame = null;
        this.isDirty = false;
        TimerMgr.getInst().remove(this.timerId);
        this.img_rank.node.off(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
    }

    onTouchMove(event:cc.Event.EventTouch)
    {
        const deltaY = event.getDeltaY();
        // console.log("rank touchmove:", deltaY);
        this.isDirty = true;
        wxapi.wxOpenData.wxPostMessageToSubDomain({
            action:wxapi.WxDomainAction.Paging,
            data:deltaY,
        });
    }

    updateRankList()
    {
        if(!this.isDirty)
        {
            return;
        }
        const sharedCanvas = wxapi.wxOpenData.wxGetSharedCanvas();
        this.rankTexture.initWithElement(sharedCanvas);
        this.rankTexture.handleLoadedTexture();
        this.rankSpriteFrame.setTexture(this.rankTexture);
        this.img_rank.spriteFrame = this.rankSpriteFrame;
    }

    onTouchGroupRank()
    {
        wxapi.wxShare.share({
            title:"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 
            imageUrl:"shareimg/bg02.png",
            query:`openID=${appdata.appUserInfo.openid}`,
        }, utils.gen_handler((shareTickets:string[]) => {
            if(!shareTickets || !shareTickets.length)
            {
                console.log('本次分享无shareTicket');
                return;
            }
            const shareTicket = shareTickets[0];
            this.isDirty = true;
            wxapi.wxOpenData.wxPostMessageToSubDomain({
                action:wxapi.WxDomainAction.FetchGroup,
                data:shareTicket,
            });
        }));
    }
}

子域:

import * as Consts from './consts'

const PAGE_SIZE = 8;
const ITEM_WIDTH = 750;
const ITEM_HEIGHT = 125;

const dataSorter = (gameDatas, field = Consts.OpenDataKeys.Grade) => {
    return gameDatas.sort((a, b) => {
        const kvDataA = a.KVDataList.find(kvData => kvData.key === Consts.OpenDataKeys.Grade);
        const kvDataB = b.KVDataList.find(kvData => kvData.key === Consts.OpenDataKeys.Grade);
        const gradeA = kvDataA ? parseInt(kvDataA.value || 0) : 0;
        const gradeB = kvDataB ? parseInt(kvDataB.value || 0) : 0;
        return gradeA > gradeB ? -1 : gradeA < gradeB ? 1 : 0;
    });
}

class RankListRenderer {
    constructor() {
        this.offsetY = 0;
        this.maxOffsetY = 0;
        this.gameDatas = [];    //https://developers.weixin.qq.com/minigame/dev/document/open-api/data/UserGameData.html
        this.init();
    }

    init() {
        this.canvas = wx.getSharedCanvas();
        this.ctx = this.canvas.getContext('2d');
        this.ctx.imageSmoothingEnabled = true;
        this.ctx.imageSmoothingQuality = "high";
    }

    listen() {
        //msg -> {action, data}
        wx.onMessage(msg => {
            //console.log("ranklist wx.onMessage", msg);
            switch (msg.action) {
                case Consts.DomainAction.FetchFriend:
                    this.fetchFriendData();
                    break;

                case Consts.DomainAction.FetchGroup:
                    if (!msg.data) {
                        return;
                    }
                    this.fetchGroupData(msg.data);
                    break;

                case Consts.DomainAction.Paging:
                    if (!this.gameDatas.length) {
                        return;
                    }
                    const deltaY = msg.data;
                    const newOffsetY = this.offsetY + deltaY;
                    if (newOffsetY < 0) {
                        // console.log("前面没有更多了");
                        return;
                    }
                    if (newOffsetY + PAGE_SIZE * ITEM_HEIGHT > this.maxOffsetY) {
                        // console.log("后面没有更多了");
                        return;
                    }
                    this.offsetY = newOffsetY;
                    this.showRanks(newOffsetY);
                    break;

                default:
                    console.log(`未知消息类型:msg.action=${msg.action}`);
                    break;
            }
        });
    }

    fetchGroupData(shareTicket) {
        //取出群同玩成员数据
        wx.getGroupCloudStorage({
            shareTicket,
            keyList: [
                Consts.OpenDataKeys.Grade,
            ],
            success: res => {
                console.log("wx.getGroupCloudStorage success", res);
                const dataLen = res.data.length;
                this.gameDatas = dataSorter(res.data);
                this.offsetY = 0;
                this.maxOffsetY = dataLen * ITEM_HEIGHT;
                if (dataLen) {
                    this.showRanks(0);
                }
            },
            fail: res => {
                console.log("wx.getGroupCloudStorage fail", res);
            },
        });
    }

    fetchFriendData() {
        //取出所有好友数据
        wx.getFriendCloudStorage({
            keyList: [
                Consts.OpenDataKeys.Grade,
            ],
            success: res => {
                console.log("wx.getFriendCloudStorage success", res);
                const dataLen = res.data.length;
                this.gameDatas = dataSorter(res.data);
                this.offsetY = 0;
                this.maxOffsetY = dataLen * ITEM_HEIGHT;
                if (dataLen) {
                    this.showRanks(0);
                }
            },
            fail: res => {
                console.log("wx.getFriendCloudStorage fail", res);
            },
        });
    }

    showRanks(offsetY) {
        const startY = offsetY % ITEM_HEIGHT;
        const startIndex = Math.floor(offsetY / ITEM_HEIGHT);
        const stopIndex = startIndex + PAGE_SIZE + (startY == 0 ? 0 : 1);
        const datas = this.gameDatas.slice(startIndex, stopIndex);

        this.ctx.clearRect(0, 0, 1000, 1000);
        for (let i = 0, len = datas.length; i < len; i++) {
            this.drawRankItem(this.ctx, i, startIndex + i + 1, datas[i], startY, this.offsetY);
        }
    }

    drawAvatar(ctx, avatarUrl, x, y, w, h, cb) {
        avatarUrl = avatarUrl.substr(0, avatarUrl.lastIndexOf('/')) + "/132";
        ctx.fillStyle = "#ffffff";
        ctx.fillRect(x - 5, y - 5, w + 10, h + 10);

        const avatarImg = wx.createImage();
        avatarImg.src = avatarUrl;
        avatarImg.onload = () => {
            cb(avatarImg);
        };
    }

    //canvas原点在左上角
    drawRankItem(ctx, index, rank, data, startY, prevOffsetY) {
        const nick = data.nickname.length <= 10 ? data.nickname : data.nickname.substr(0, 10) + "...";
        const kvData = data.KVDataList.find(kvData => kvData.key === Consts.OpenDataKeys.Grade);
        const grade = kvData ? kvData.value : 0;
        const itemGapY = ITEM_HEIGHT * index - startY;

        //背景颜色
        if (rank % 2 == 1) {
            ctx.fillStyle = "#FBF7E4";
            ctx.fillRect(0, itemGapY, ITEM_WIDTH, ITEM_HEIGHT);
        }

        //名次
        if (rank < 4) {
            const rankImg = wx.createImage();
            rankImg.src = `subdomain/images/llk_phb_icon${rank}.png`;
            rankImg.onload = () => {
                if(prevOffsetY == this.offsetY) {
                    ctx.drawImage(rankImg, 55, 30 + itemGapY, 78, 82);
                }
            };
        } else {
            ctx.fillStyle = "#BDBDBD";
            ctx.textAlign = "right";
            ctx.baseLine = "middle";
            ctx.font = "50px Helvetica";
            ctx.fillText(`${rank}`, 100, 80 + itemGapY);
        }

        //头像
        const avatarX = 125;
        const avatarY = 25 + itemGapY;
        const avatarW = 80;
        const avatarH = 80;
        this.drawAvatar(ctx, data.avatarUrl, avatarX, avatarY, avatarW, avatarH, (avatarImg) => {
            if(prevOffsetY == this.offsetY) {
                ctx.drawImage(avatarImg, avatarX, avatarY, avatarW, avatarH);
            }
        })

        //名字
        ctx.fillStyle = "#777063";
        ctx.textAlign = "left";
        ctx.baseLine = "middle";
        ctx.font = "30px Helvetica";
        ctx.fillText(nick, 220, 80 + itemGapY);

        //分数
        ctx.fillStyle = "#777063";
        ctx.textAlign = "left";
        ctx.baseLine = "middle";
        ctx.font = "30px Helvetica";
        ctx.fillText(`${grade}分`, 620, 80 + itemGapY);
    }
}

const rankList = new RankListRenderer();
rankList.listen();

上面都是在论坛上看到的,最近自己也是再写排行榜,所以整理一下,防止自己忘记

你可能感兴趣的:(微信小游戏排行榜)