feat: use wx.createUserInfoButton to get weixin's avarta
This commit is contained in:
+129
-6
@@ -97,6 +97,24 @@ const MenuScene = {
|
||||
enter() {
|
||||
this._pressedIndex = -1;
|
||||
this._tankAnim = 0;
|
||||
this._avatarImg = null;
|
||||
|
||||
// Load avatar image if profile has one
|
||||
const profile = GameGlobal.playerProfile;
|
||||
if (profile && profile.avatarUrl) {
|
||||
this._loadAvatarImage(profile.avatarUrl);
|
||||
}
|
||||
|
||||
// Listen for profile updates (avatar may arrive after initial render)
|
||||
this._profileHandler = (data) => {
|
||||
if (data && data.avatarUrl && !this._avatarImg) {
|
||||
this._loadAvatarImage(data.avatarUrl);
|
||||
}
|
||||
};
|
||||
const bus = GameGlobal.eventBus;
|
||||
if (bus && typeof bus.on === 'function') {
|
||||
bus.on('profile:updated', this._profileHandler);
|
||||
}
|
||||
|
||||
// Kick off nickname acquisition as early as possible so that later
|
||||
// network messages (CREATE_TEAM, SOLO_MATCH, ...) can carry it.
|
||||
@@ -120,6 +138,18 @@ const MenuScene = {
|
||||
|
||||
exit() {
|
||||
this._pressedIndex = -1;
|
||||
// Destroy UserInfoButton overlay when leaving the menu
|
||||
const profile = GameGlobal.playerProfile;
|
||||
if (profile && typeof profile.destroyUserInfoButton === 'function') {
|
||||
profile.destroyUserInfoButton();
|
||||
}
|
||||
// Remove profile update listener
|
||||
const bus = GameGlobal.eventBus;
|
||||
if (bus && typeof bus.off === 'function' && this._profileHandler) {
|
||||
bus.off('profile:updated', this._profileHandler);
|
||||
}
|
||||
this._profileHandler = null;
|
||||
this._avatarImg = null;
|
||||
},
|
||||
|
||||
update(dt) {
|
||||
@@ -151,6 +181,55 @@ const MenuScene = {
|
||||
ctx.fillStyle = accentGrad;
|
||||
ctx.fillRect(0, 0, SCREEN_WIDTH, 3);
|
||||
|
||||
// ---- Player Avatar & Nickname (top-left) ----
|
||||
const profile = GameGlobal.playerProfile;
|
||||
const avatarSize = 28;
|
||||
const avatarX = 10;
|
||||
const avatarY = 10;
|
||||
const avatarR = avatarSize / 2;
|
||||
|
||||
// Avatar circle background
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(avatarX + avatarR, avatarY + avatarR, avatarR, 0, Math.PI * 2);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = 'rgba(30,48,84,0.7)';
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = 'rgba(255,215,0,0.4)';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.stroke();
|
||||
|
||||
// Avatar image or default icon
|
||||
if (profile && profile.avatarUrl && this._avatarImg && this._avatarImg.complete) {
|
||||
ctx.clip();
|
||||
ctx.drawImage(this._avatarImg, avatarX, avatarY, avatarSize, avatarSize);
|
||||
} else {
|
||||
// Default user icon (simple silhouette)
|
||||
ctx.fillStyle = 'rgba(255,215,0,0.5)';
|
||||
ctx.beginPath();
|
||||
ctx.arc(avatarX + avatarR, avatarY + avatarR - 2, avatarR * 0.35, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(avatarX + avatarR, avatarY + avatarR + avatarR * 0.55, avatarR * 0.55, avatarR * 0.3, 0, Math.PI, 0);
|
||||
ctx.fill();
|
||||
}
|
||||
ctx.restore();
|
||||
|
||||
// Nickname
|
||||
const displayName = profile ? profile.getDisplayName() : 'Tanker';
|
||||
ctx.font = 'bold 11px Arial';
|
||||
ctx.fillStyle = MC.GOLD;
|
||||
ctx.textAlign = 'left';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(displayName, avatarX + avatarSize + 6, avatarY + avatarR - 5);
|
||||
|
||||
// Hint text (only if not yet granted)
|
||||
if (profile && !profile.granted) {
|
||||
ctx.font = '9px Arial';
|
||||
ctx.fillStyle = MC.SUBTITLE;
|
||||
ctx.fillText(t('menu.tapToAuth') || 'Tap to authorize', avatarX + avatarSize + 6, avatarY + avatarR + 8);
|
||||
}
|
||||
|
||||
// ---- Gold Balance (top-right pill) ----
|
||||
const gold = GameGlobal.currencyManager ? GameGlobal.currencyManager.getGold() : 0;
|
||||
const goldText = `🪙 ${gold}`;
|
||||
@@ -469,19 +548,63 @@ const MenuScene = {
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* Kick off profile acquisition on menu enter. Since WeChat 2022-10 there
|
||||
* is NO silent way to get the real nickname — we draw a canvas button and
|
||||
* call `wx.getUserProfile` directly from its touchend handler.
|
||||
* Load avatar image from URL for Canvas rendering.
|
||||
* @param {string} url
|
||||
* @private
|
||||
*/
|
||||
_loadAvatarImage(url) {
|
||||
if (!url || this._avatarImg) return;
|
||||
try {
|
||||
const img = wx.createImage();
|
||||
img.onload = () => {
|
||||
this._avatarImg = img;
|
||||
};
|
||||
img.onerror = () => {
|
||||
console.warn('[MenuScene] Failed to load avatar image');
|
||||
};
|
||||
img.src = url;
|
||||
} catch (e) {
|
||||
console.warn('[MenuScene] _loadAvatarImage error:', e && e.message);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Kick off profile acquisition on menu enter.
|
||||
*
|
||||
* Since WeChat 2022-10, `wx.getUserInfo` returns "scope unauthorized" —
|
||||
* the ONLY way to get the real nickname + avatar is `wx.createUserInfoButton`.
|
||||
* We create a transparent overlay button that covers the avatar area in the
|
||||
* top-left corner. When the user taps their avatar, WeChat returns the real
|
||||
* profile data.
|
||||
* @private
|
||||
*/
|
||||
_initPlayerProfile() {
|
||||
const profile = GameGlobal.playerProfile;
|
||||
if (!profile) return;
|
||||
|
||||
// Best-effort placeholder fetch (used only to pre-fill _nickname with
|
||||
// "微信用户" on older devices; does not mark granted).
|
||||
// If already granted (from a previous session's cache), no button needed
|
||||
if (profile.granted) return;
|
||||
|
||||
// Layout must match the avatar area drawn in render():
|
||||
// avatarX=10, avatarY=10, avatarSize=28 + nickname text area
|
||||
const avatarLayout = {
|
||||
x: 8,
|
||||
y: 8,
|
||||
width: 120,
|
||||
height: 32,
|
||||
};
|
||||
|
||||
// Create the UserInfoButton — visible style covering avatar + nickname area
|
||||
if (typeof profile.fetchSilent === 'function') {
|
||||
profile.fetchSilent().catch(() => { /* ignore */ });
|
||||
profile.fetchSilent(avatarLayout).then((ok) => {
|
||||
console.log('[MenuScene] fetchSilent completed, granted=', ok);
|
||||
if (ok) {
|
||||
// Button was tapped and profile was granted — destroy it
|
||||
profile.destroyUserInfoButton();
|
||||
}
|
||||
}).catch((e) => {
|
||||
console.warn('[MenuScene] fetchSilent error:', e);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user