最详细代码讲解微信开放数据域绘制排行榜(翻页和滑动列表)

果断要mark

楼主你好,按你的项目mark了下,出现了一个问题,就是滑动的时候,头像图片绘制会闪一下闪一下的

1赞

:one:已解决

1赞

ios不会,安卓机滚动会卡啊?

cocos子域会有性能问题, 严重掉帧

1赞

所以不要每帧都去更新。

什么原因?怎么解决的?

这样是不是就不用创建两个工程?

mark 学习

是的啊

是的啊

楼主,请教一下,这个方法可以在子域里面使用使用UI的Prefab来显示吗。

mark

绘制的排名闪一下就不见了,这个什么情况呢?怎样可以不用子域工程呢?

怎么解决的?

楼主你好,你创建的是creator工程么?我直接打开打不开

我怎么编辑帖子啊,现在帖的有点bug,我想修改下

帖子内容写的有点bug,滑动效率太低。因为我修改了排行榜实现。在子域中先将排行榜绘制到离屏canvas中,滑动的时候根据滑动距离将离屏canvas相关内容绘制到sharedCanvas,再将sharedCanvas绘制到主域中。这样解决了原版实现滑动卡顿的问题。
主域代码如下

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().addUpdater(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);
    }

    private lastDeltaY:number;
    onTouchMove(event:cc.Event.EventTouch)
    {
        const deltaY = event.getDeltaY();
        if(this.lastDeltaY == deltaY)
        {
            return;
        }
        this.lastDeltaY = deltaY;
        // 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()
    {
        report.getShareInfo(res => {
            wxapi.wxShare.share(
                {
                    title:res.text,
                    imageUrl:res.image,
                    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,
                    });
                }, this),
            );
        }, this);
        Audio.AudioPlayer.getInst().play_sound(Audio.AUDIO_CONFIG.Audio_Btn);
    }

    onTouchPlayBtn()
    {
        this.hide();
        if(wxapi.wxSession.get())
        {
            pop_mgr.get_inst().show(UI_CONFIG.level);
        }
    }
}

子域代码如下:

import * as Consts from './consts'

const ITEM_HEIGHT = 125;
const PAGE_SIZE = 10;
const RANK_PAGE_HEIGHT = ITEM_HEIGHT * PAGE_SIZE;

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;
	});
}

//"openDataContext": "subdomain",
class RankListRenderer {
	constructor() {
    }
    
    init() {
        if(this.isInited) {
            return;
        }
        this.isInited = true;
        // console.log("RanklListRenderer init");

        this.offsetY = 0;
        this.contentHeight = 0;
        this.gameDatas = [];    //https://developers.weixin.qq.com/minigame/dev/document/open-api/data/UserGameData.html
        
        const sharedCanvas = wx.getSharedCanvas();
        this.sharedCtx = sharedCanvas.getContext('2d');
        this.sharedCanvasWidth = sharedCanvas.width;
        this.sharedCanvasHeight = sharedCanvas.height;
        
        this.rankCanvasList = [];
        this.rankImgLoadingCnt = [];
    }

	listen() {
		//msg -> {action, data}
		wx.onMessage(msg => {
			//console.log("ranklist wx.onMessage", msg);
            this.init();
			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 + this.sharedCanvasHeight > this.contentHeight) {
						// console.log("后面没有更多了");
						return;
					}
					this.offsetY = newOffsetY;
                    this.drawSharedCanvas(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.contentHeight = dataLen * ITEM_HEIGHT;
				if (dataLen) {
					this.rankCanvasList.length = 0;
                    this.drawSharedCanvas(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.contentHeight = dataLen * ITEM_HEIGHT;
				if (dataLen) {
					this.rankCanvasList.length = 0;
                    this.drawSharedCanvas(0);
				}
			},
			fail: res => {
				console.log("wx.getFriendCloudStorage fail", res);
			},
		});
	}
    
    drawSharedCanvas(offsetY) {
		//清除sharedCanvas旧内容
        const sharedWidth = this.sharedCanvasWidth;
        const sharedHeight = this.sharedCanvasHeight;
        this.sharedCtx.clearRect(0, 0, sharedWidth, sharedHeight);

        //分页,绘制多个rankCanvas,再将rankCanvas内容绘制到sharedCanvas
        const pageY = offsetY % RANK_PAGE_HEIGHT;
        const pageIndex = Math.floor(offsetY / RANK_PAGE_HEIGHT);
		const isOverOnePage = pageY + sharedHeight > RANK_PAGE_HEIGHT;

		let rankCanvas = this.getPagedRankCanvas(pageIndex);
		if(!isOverOnePage) {
			this.sharedCtx.drawImage(rankCanvas, 0, pageY, sharedWidth, sharedHeight, 0, 0, sharedWidth, sharedHeight);
		} else {
			//绘制当前页后半部分
			const partialHeight = RANK_PAGE_HEIGHT - pageY;
			this.sharedCtx.drawImage(rankCanvas, 0, pageY, sharedWidth, partialHeight, 0, 0, sharedWidth, partialHeight);

			//绘制下一页前半部分
			rankCanvas = this.getPagedRankCanvas(pageIndex + 1);
			this.sharedCtx.drawImage(rankCanvas, 0, 0, sharedWidth, sharedHeight - partialHeight, 0, partialHeight, sharedWidth, sharedHeight - partialHeight);
		}
    }

	getPagedRankCanvas(pageIndex) {
		let canvas = this.rankCanvasList[pageIndex];
		if(!canvas) {
			canvas = wx.createCanvas();
			canvas.width = this.sharedCanvasWidth;
			canvas.height = RANK_PAGE_HEIGHT;
			this.rankCanvasList[pageIndex] = canvas;
			
			this.rankImgLoadingCnt[pageIndex] = 0;
			const ctx = canvas.getContext('2d');
			this.drawPagedRanks(ctx, pageIndex, () => {
				if(this.rankImgLoadingCnt[pageIndex] == 0) {
					this.drawSharedCanvas(this.offsetY);
				}
			});
		}
		return canvas;
	}

    drawPagedRanks(ctx, pageIndex, cb) {
        for(let i = 0; i < PAGE_SIZE; i++) {
			const pageOffset = pageIndex * PAGE_SIZE;
			const data = this.gameDatas[pageOffset + i];
			if(!data) continue;
            this.drawRankItem(ctx, pageIndex, i, pageOffset + i + 1, data, cb);
        }
    }

    //canvas原点在左上角
	drawRankItem(ctx, pageIndex, dataIndex, rank, data, cb) {
		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 * dataIndex;

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

		//名次
		if (rank < 4) {
			const rankImg = wx.createImage();
            rankImg.src = `subdomain/images/llk_phb_icon${rank}.png`;
            this.rankImgLoadingCnt[pageIndex]++;
			rankImg.onload = () => {
                ctx.drawImage(rankImg, 55, 30 + itemGapY, 78, 82);
                this.rankImgLoadingCnt[pageIndex]--;
                cb && cb();
			};
		} else {
			ctx.fillStyle = "#777063";
			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, pageIndex, cb);

		//名字
		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);
	}

	drawAvatar(ctx, avatarUrl, x, y, w, h, pageIndex, 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;
        this.rankImgLoadingCnt[pageIndex]++;
		avatarImg.onload = () => {
            ctx.drawImage(avatarImg, x, y, w, h);
            this.rankImgLoadingCnt[pageIndex]--;
            cb && cb();
		};
	}
}

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

我改了实现,我下面楼层我贴的最新代码

我改了实现,我下面楼层我贴的最新代码22222