游戏添加
This commit is contained in:
parent
d4e41b1423
commit
f2eb343cf7
@ -5,6 +5,7 @@
|
||||
var scriptCache = {};
|
||||
var pagInitCache = {};
|
||||
var videoLayoutCache = {};
|
||||
var svgaVideoItemCache = {};
|
||||
var MIN_SPLIT_MATCH_FRAMES = 3;
|
||||
var MIN_USEFUL_FRAME_BRIGHTNESS = 0.035;
|
||||
var MIN_SPLIT_SATURATION_GAP = 0.08;
|
||||
@ -41,6 +42,23 @@
|
||||
return scriptCache[url];
|
||||
}
|
||||
|
||||
function loadSVGAItemOnce(sourceURL, canvas, options) {
|
||||
if (svgaVideoItemCache[sourceURL]) return svgaVideoItemCache[sourceURL];
|
||||
svgaVideoItemCache[sourceURL] = loadScriptOnce(options.svgaScript)
|
||||
.then(function () {
|
||||
if (!window.SVGA) throw new Error('svga_unavailable');
|
||||
return new Promise(function (resolve, reject) {
|
||||
var parser = new window.SVGA.Parser(canvas);
|
||||
parser.load(sourceURL, resolve, reject);
|
||||
});
|
||||
})
|
||||
.catch(function (error) {
|
||||
delete svgaVideoItemCache[sourceURL];
|
||||
throw error;
|
||||
});
|
||||
return svgaVideoItemCache[sourceURL];
|
||||
}
|
||||
|
||||
function waitForWindowLoad() {
|
||||
if (document.readyState === 'complete') return Promise.resolve();
|
||||
return new Promise(function (resolve) {
|
||||
@ -49,7 +67,9 @@
|
||||
}
|
||||
|
||||
function extensionFromURL(url) {
|
||||
var clean = String(url || '').split('?')[0].split('#')[0];
|
||||
var clean = String(url || '')
|
||||
.split('?')[0]
|
||||
.split('#')[0];
|
||||
var match = clean.match(/\.([a-z0-9]+)$/i);
|
||||
return match ? match[1].toLowerCase() : '';
|
||||
}
|
||||
@ -87,10 +107,7 @@
|
||||
var separator = base.indexOf('?') >= 0 ? '&' : '?';
|
||||
return base + separator + 'url=' + encodeURIComponent(url);
|
||||
}
|
||||
if (
|
||||
window.HyAppAPI &&
|
||||
typeof window.HyAppAPI.buildURL === 'function'
|
||||
) {
|
||||
if (window.HyAppAPI && typeof window.HyAppAPI.buildURL === 'function') {
|
||||
return window.HyAppAPI.buildURL('/api/v1/media/effects/proxy', {
|
||||
url: url,
|
||||
});
|
||||
@ -456,8 +473,7 @@
|
||||
) {
|
||||
return;
|
||||
}
|
||||
var alphaStats =
|
||||
first.saturation < second.saturation ? first : second;
|
||||
var alphaStats = first.saturation < second.saturation ? first : second;
|
||||
var saturationGap = Math.abs(second.saturation - first.saturation);
|
||||
if (
|
||||
saturationGap < MIN_SPLIT_SATURATION_GAP ||
|
||||
@ -505,9 +521,7 @@
|
||||
var width = Math.max(1, video.videoWidth || 1);
|
||||
var height = Math.max(1, video.videoHeight || 1);
|
||||
return {
|
||||
kind: isHorizontal
|
||||
? 'splitAlphaHorizontal'
|
||||
: 'splitAlphaVertical',
|
||||
kind: isHorizontal ? 'splitAlphaHorizontal' : 'splitAlphaVertical',
|
||||
hasAlphaMask: true,
|
||||
hasNativeAlpha: false,
|
||||
contentAspectRatio: isHorizontal
|
||||
@ -584,14 +598,14 @@
|
||||
}
|
||||
return (
|
||||
detectPackedAlphaTopRight(packedFrames) ||
|
||||
sampleVideoFrames(video, 'split', maxWidth).then(function (
|
||||
splitFrames
|
||||
) {
|
||||
return (
|
||||
detectSplitAlpha(splitFrames, video) ||
|
||||
fullFrameLayout(video, 'normal', false)
|
||||
);
|
||||
})
|
||||
sampleVideoFrames(video, 'split', maxWidth).then(
|
||||
function (splitFrames) {
|
||||
return (
|
||||
detectSplitAlpha(splitFrames, video) ||
|
||||
fullFrameLayout(video, 'normal', false)
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
})
|
||||
.then(function (layout) {
|
||||
@ -603,7 +617,11 @@
|
||||
function rectToPixels(rect, width, height) {
|
||||
var x = clamp(Math.round(rect.x * width), 0, width - 1);
|
||||
var y = clamp(Math.round(rect.y * height), 0, height - 1);
|
||||
var right = clamp(Math.round((rect.x + rect.width) * width), x + 1, width);
|
||||
var right = clamp(
|
||||
Math.round((rect.x + rect.width) * width),
|
||||
x + 1,
|
||||
width
|
||||
);
|
||||
var bottom = clamp(
|
||||
Math.round((rect.y + rect.height) * height),
|
||||
y + 1,
|
||||
@ -793,7 +811,12 @@
|
||||
return video.play().catch(function () {});
|
||||
};
|
||||
|
||||
EffectPlayer.prototype._playCanvasVideo = function (url, options, video, layout) {
|
||||
EffectPlayer.prototype._playCanvasVideo = function (
|
||||
url,
|
||||
options,
|
||||
video,
|
||||
layout
|
||||
) {
|
||||
var self = this;
|
||||
if (!this._isCurrent(url)) return Promise.resolve();
|
||||
var size = layoutCanvasSize(video, layout, options);
|
||||
@ -831,10 +854,9 @@
|
||||
video.load();
|
||||
};
|
||||
draw();
|
||||
return seekVideo(video, 0)
|
||||
.then(function () {
|
||||
return video.play().catch(function () {});
|
||||
});
|
||||
return seekVideo(video, 0).then(function () {
|
||||
return video.play().catch(function () {});
|
||||
});
|
||||
};
|
||||
|
||||
EffectPlayer.prototype._playVideo = function (url, options) {
|
||||
@ -860,39 +882,27 @@
|
||||
|
||||
EffectPlayer.prototype._playSVGA = function (url, options) {
|
||||
var self = this;
|
||||
return loadScriptOnce(options.svgaScript).then(function () {
|
||||
if (!self._isCurrent(url)) return undefined;
|
||||
if (!window.SVGA) throw new Error('svga_unavailable');
|
||||
var sourceURL = proxyBinaryURL(url, 'svga', options);
|
||||
var size = mediaSize(self.container, options);
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = size.width;
|
||||
canvas.height = size.height;
|
||||
styleNode(canvas, options);
|
||||
self._mount(canvas);
|
||||
var player = new window.SVGA.Player(canvas);
|
||||
var parser = new window.SVGA.Parser(canvas);
|
||||
self.cleanup = function () {
|
||||
if (player.stopAnimation) player.stopAnimation();
|
||||
if (player.clear) player.clear();
|
||||
};
|
||||
return new Promise(function (resolve, reject) {
|
||||
parser.load(
|
||||
sourceURL,
|
||||
function (videoItem) {
|
||||
if (!self._isCurrent(url)) {
|
||||
if (player.stopAnimation) player.stopAnimation();
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
player.setVideoItem(videoItem);
|
||||
player.startAnimation();
|
||||
resolve();
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
});
|
||||
var sourceURL = proxyBinaryURL(url, 'svga', options);
|
||||
var size = mediaSize(self.container, options);
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = size.width;
|
||||
canvas.height = size.height;
|
||||
styleNode(canvas, options);
|
||||
self._mount(canvas);
|
||||
return loadSVGAItemOnce(sourceURL, canvas, options).then(
|
||||
function (videoItem) {
|
||||
if (!self._isCurrent(url)) return undefined;
|
||||
if (!window.SVGA) throw new Error('svga_unavailable');
|
||||
var player = new window.SVGA.Player(canvas);
|
||||
self.cleanup = function () {
|
||||
if (player.stopAnimation) player.stopAnimation();
|
||||
if (player.clear) player.clear();
|
||||
};
|
||||
player.setVideoItem(videoItem);
|
||||
player.startAnimation();
|
||||
return undefined;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
EffectPlayer.prototype._playPAG = function (url, options) {
|
||||
@ -991,6 +1001,20 @@
|
||||
create: function (target, options) {
|
||||
return new EffectPlayer(target, options);
|
||||
},
|
||||
preload: function (url, options) {
|
||||
var normalized = extend(
|
||||
{
|
||||
svgaScript: DEFAULT_SVGA_SCRIPT,
|
||||
proxy: true,
|
||||
},
|
||||
options || {}
|
||||
);
|
||||
var type = inferType(url, normalized.type);
|
||||
if (type !== 'svga') return Promise.resolve();
|
||||
var canvas = document.createElement('canvas');
|
||||
var sourceURL = proxyBinaryURL(url, 'svga', normalized);
|
||||
return loadSVGAItemOnce(sourceURL, canvas, normalized);
|
||||
},
|
||||
play: function (target, url, options) {
|
||||
var player = new EffectPlayer(target, options);
|
||||
return player.play(url).then(function () {
|
||||
|
||||
255
common/game-preloader.js
Normal file
255
common/game-preloader.js
Normal file
@ -0,0 +1,255 @@
|
||||
(function () {
|
||||
var imageCache = {};
|
||||
var binaryCache = {};
|
||||
var imagePattern = /\.(png|jpe?g|gif|webp|svg|avif)(\?|#|$)/i;
|
||||
var svgaPattern = /\.svga(\?|#|$)/i;
|
||||
|
||||
function each(nodes, iteratee) {
|
||||
Array.prototype.forEach.call(nodes || [], iteratee);
|
||||
}
|
||||
|
||||
function normalizeURL(url, baseURL) {
|
||||
var value = String(url || '').trim();
|
||||
if (
|
||||
!value ||
|
||||
value.indexOf('data:') === 0 ||
|
||||
value.indexOf('blob:') === 0
|
||||
) {
|
||||
return '';
|
||||
}
|
||||
try {
|
||||
return new URL(value, baseURL || window.location.href).href;
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function addURL(list, seen, url, baseURL) {
|
||||
var normalized = normalizeURL(url, baseURL);
|
||||
if (!normalized || seen[normalized]) return;
|
||||
seen[normalized] = true;
|
||||
list.push(normalized);
|
||||
}
|
||||
|
||||
function collectFromCSSText(list, seen, text) {
|
||||
var pattern = /url\((['"]?)(.*?)\1\)/g;
|
||||
var match;
|
||||
while ((match = pattern.exec(text || ''))) {
|
||||
addURL(list, seen, match[2]);
|
||||
}
|
||||
}
|
||||
|
||||
function collectSrcSet(list, seen, value) {
|
||||
String(value || '')
|
||||
.split(',')
|
||||
.forEach(function (item) {
|
||||
addURL(list, seen, item.trim().split(/\s+/)[0]);
|
||||
});
|
||||
}
|
||||
|
||||
function collectDocumentURLs(list, seen) {
|
||||
each(document.querySelectorAll('img[src]'), function (img) {
|
||||
addURL(list, seen, img.getAttribute('src'));
|
||||
});
|
||||
each(document.querySelectorAll('source[srcset]'), function (source) {
|
||||
collectSrcSet(list, seen, source.getAttribute('srcset'));
|
||||
});
|
||||
each(document.querySelectorAll('style'), function (node) {
|
||||
collectFromCSSText(list, seen, node.textContent || '');
|
||||
});
|
||||
each(document.querySelectorAll('[style]'), function (node) {
|
||||
collectFromCSSText(list, seen, node.getAttribute('style') || '');
|
||||
});
|
||||
each(document.styleSheets, function (sheet) {
|
||||
try {
|
||||
each(sheet.cssRules || [], function (rule) {
|
||||
collectFromCSSText(list, seen, rule.cssText || '');
|
||||
});
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
|
||||
function collectResourceURLs(assetURLs) {
|
||||
var list = [];
|
||||
var seen = {};
|
||||
(assetURLs || []).forEach(function (url) {
|
||||
addURL(list, seen, url);
|
||||
});
|
||||
collectDocumentURLs(list, seen);
|
||||
return list;
|
||||
}
|
||||
|
||||
function isImageURL(url) {
|
||||
return imagePattern.test(String(url || ''));
|
||||
}
|
||||
|
||||
function isSVGAURL(url) {
|
||||
return svgaPattern.test(String(url || ''));
|
||||
}
|
||||
|
||||
function preloadImage(url) {
|
||||
var normalized = normalizeURL(url);
|
||||
if (!normalized) return Promise.resolve(false);
|
||||
if (imageCache[normalized]) return imageCache[normalized];
|
||||
imageCache[normalized] = new Promise(function (resolve) {
|
||||
var image = new Image();
|
||||
image.onload = function () {
|
||||
resolve(true);
|
||||
};
|
||||
image.onerror = function () {
|
||||
resolve(false);
|
||||
};
|
||||
image.decoding = 'async';
|
||||
image.src = normalized;
|
||||
if (image.complete) resolve(true);
|
||||
});
|
||||
return imageCache[normalized];
|
||||
}
|
||||
|
||||
function preloadBinary(url) {
|
||||
var normalized = normalizeURL(url);
|
||||
if (!normalized) return Promise.resolve(false);
|
||||
if (binaryCache[normalized]) return binaryCache[normalized];
|
||||
binaryCache[normalized] = window
|
||||
.fetch(normalized, { cache: 'force-cache' })
|
||||
.then(function (response) {
|
||||
return response.ok;
|
||||
})
|
||||
.catch(function () {
|
||||
return false;
|
||||
});
|
||||
return binaryCache[normalized];
|
||||
}
|
||||
|
||||
function preloadSVGA(url, options) {
|
||||
options = options || {};
|
||||
if (
|
||||
window.HyAppEffectPlayer &&
|
||||
typeof window.HyAppEffectPlayer.preload === 'function'
|
||||
) {
|
||||
return window.HyAppEffectPlayer.preload(url, {
|
||||
type: 'svga',
|
||||
svgaScript: options.svgaScript,
|
||||
waitForWindowLoad: options.waitForWindowLoad === true,
|
||||
})
|
||||
.then(function () {
|
||||
return true;
|
||||
})
|
||||
.catch(function () {
|
||||
return preloadBinary(url);
|
||||
});
|
||||
}
|
||||
return preloadBinary(url);
|
||||
}
|
||||
|
||||
function preloadResource(url, options) {
|
||||
if (isSVGAURL(url)) return preloadSVGA(url, options && options.svga);
|
||||
if (isImageURL(url)) return preloadImage(url);
|
||||
return preloadBinary(url);
|
||||
}
|
||||
|
||||
function setProgress(progress) {
|
||||
var options =
|
||||
typeof progress === 'number'
|
||||
? { progress: progress }
|
||||
: progress || {};
|
||||
var root = options.root || document.documentElement;
|
||||
var safeProgress = Math.min(
|
||||
Math.max(Number(options.progress) || 0, 0),
|
||||
1
|
||||
);
|
||||
var progressNode =
|
||||
options.progressNode ||
|
||||
document.querySelector('[data-role="loading-progress"]');
|
||||
root.style.setProperty('--loading-progress', safeProgress.toFixed(4));
|
||||
if (progressNode) {
|
||||
progressNode.setAttribute(
|
||||
'aria-valuenow',
|
||||
String(Math.round(safeProgress * 100))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function runTasks(tasks, onProgress) {
|
||||
var completed = 0;
|
||||
var total = Math.max((tasks && tasks.length) || 0, 1);
|
||||
var update =
|
||||
typeof onProgress === 'function'
|
||||
? onProgress
|
||||
: function (value) {
|
||||
setProgress(value);
|
||||
};
|
||||
update(0);
|
||||
if (!tasks || !tasks.length) {
|
||||
update(1);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
// 资源预加载是体验优化,单个资源失败不应该卡死大厅;失败项会返回 false,进度仍然推进。
|
||||
return Promise.all(
|
||||
tasks.map(function (task) {
|
||||
return Promise.resolve()
|
||||
.then(task)
|
||||
.catch(function () {
|
||||
return false;
|
||||
})
|
||||
.then(function (value) {
|
||||
completed += 1;
|
||||
update(completed / total);
|
||||
return value;
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function complete(options) {
|
||||
options = options || {};
|
||||
var loadingScreen =
|
||||
options.loadingScreen ||
|
||||
document.querySelector('[data-role="loading-screen"]');
|
||||
setProgress({
|
||||
root: options.root,
|
||||
progress: 1,
|
||||
progressNode: options.progressNode,
|
||||
});
|
||||
if (loadingScreen) {
|
||||
loadingScreen.classList.add('is-complete');
|
||||
loadingScreen.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
if (options.frame) {
|
||||
options.frame.classList.remove('is-preloading');
|
||||
}
|
||||
}
|
||||
|
||||
function once(task) {
|
||||
var promise = null;
|
||||
return function () {
|
||||
if (!promise) {
|
||||
promise = Promise.resolve().then(task);
|
||||
}
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise(function (resolve) {
|
||||
window.setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
window.HyAppGamePreloader = {
|
||||
addURL: addURL,
|
||||
collectResourceURLs: collectResourceURLs,
|
||||
complete: complete,
|
||||
delay: delay,
|
||||
isImageURL: isImageURL,
|
||||
isSVGAURL: isSVGAURL,
|
||||
normalizeURL: normalizeURL,
|
||||
once: once,
|
||||
preloadBinary: preloadBinary,
|
||||
preloadImage: preloadImage,
|
||||
preloadResource: preloadResource,
|
||||
preloadSVGA: preloadSVGA,
|
||||
runTasks: runTasks,
|
||||
setProgress: setProgress,
|
||||
};
|
||||
})();
|
||||
@ -24,11 +24,13 @@
|
||||
"rockGame.start": "ابدأ",
|
||||
"rockGame.matching": "جارٍ البحث...",
|
||||
"rockGame.matched": "تم العثور على خصم",
|
||||
"rockGame.chooseGesture": "اختر الإشارة التي تريد لعبها",
|
||||
"rockGame.winnerReceives": "الفائز يحصل على",
|
||||
"rockGame.rock": "حجر",
|
||||
"rockGame.paper": "ورقة",
|
||||
"rockGame.scissors": "مقص",
|
||||
"rockGame.win": "فوز",
|
||||
"rockGame.youWin": "فزت",
|
||||
"rockGame.lose": "خسارة",
|
||||
"rockGame.draw": "تعادل",
|
||||
"rockGame.coins": "عملات",
|
||||
@ -36,6 +38,7 @@
|
||||
"rockGame.playAgain": "العب مرة أخرى",
|
||||
"rockGame.changeLanguage": "تغيير اللغة",
|
||||
"rockGame.selectStake": "اختر قيمة الرهان",
|
||||
"rockGame.configFeeNote": "الرسوم {fee}%",
|
||||
"rockGame.apiUnavailable": "واجهة RPS غير متاحة",
|
||||
"rockGame.refundNote": "إذا لم ينضم أحد إلى لعبة حجر ورقة مقص، سيتم رد العملات خلال 10 دقائق.",
|
||||
"roomReward.pageTitle": "فعالية مكافآت الغرفة",
|
||||
|
||||
@ -24,11 +24,13 @@
|
||||
"rockGame.start": "START",
|
||||
"rockGame.matching": "Matching...",
|
||||
"rockGame.matched": "Matched",
|
||||
"rockGame.chooseGesture": "Choose your gesture",
|
||||
"rockGame.winnerReceives": "The winner receives",
|
||||
"rockGame.rock": "Rock",
|
||||
"rockGame.paper": "Paper",
|
||||
"rockGame.scissors": "Scissors",
|
||||
"rockGame.win": "Win",
|
||||
"rockGame.youWin": "YOU WIN",
|
||||
"rockGame.lose": "Lose",
|
||||
"rockGame.draw": "Draw",
|
||||
"rockGame.coins": "Coins",
|
||||
@ -36,6 +38,7 @@
|
||||
"rockGame.playAgain": "Play again",
|
||||
"rockGame.changeLanguage": "Change language",
|
||||
"rockGame.selectStake": "Select a stake",
|
||||
"rockGame.configFeeNote": "Fee {fee}%",
|
||||
"rockGame.apiUnavailable": "RPS API unavailable",
|
||||
"rockGame.refundNote": "If no one joins the rock-paper-scissors game, the coins will be refunded in 10 minutes.",
|
||||
"roomReward.pageTitle": "Room Reward Event",
|
||||
|
||||
@ -24,11 +24,13 @@
|
||||
"rockGame.start": "INICIAR",
|
||||
"rockGame.matching": "Emparejando...",
|
||||
"rockGame.matched": "Emparejado",
|
||||
"rockGame.chooseGesture": "Elige tu gesto",
|
||||
"rockGame.winnerReceives": "El ganador recibe",
|
||||
"rockGame.rock": "Piedra",
|
||||
"rockGame.paper": "Papel",
|
||||
"rockGame.scissors": "Tijeras",
|
||||
"rockGame.win": "Victoria",
|
||||
"rockGame.youWin": "GANASTE",
|
||||
"rockGame.lose": "Derrota",
|
||||
"rockGame.draw": "Empate",
|
||||
"rockGame.coins": "Monedas",
|
||||
@ -36,6 +38,7 @@
|
||||
"rockGame.playAgain": "Jugar otra vez",
|
||||
"rockGame.changeLanguage": "Cambiar idioma",
|
||||
"rockGame.selectStake": "Selecciona una apuesta",
|
||||
"rockGame.configFeeNote": "Comisión {fee}%",
|
||||
"rockGame.apiUnavailable": "API de RPS no disponible",
|
||||
"rockGame.refundNote": "Si nadie se une al juego de piedra, papel o tijeras, las monedas se reembolsarán en 10 minutos.",
|
||||
"roomReward.pageTitle": "Evento de recompensas de sala",
|
||||
|
||||
69
game/common/game-loading.css
Normal file
69
game/common/game-loading.css
Normal file
@ -0,0 +1,69 @@
|
||||
.loading-screen {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 200;
|
||||
overflow: hidden;
|
||||
background: #030623;
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
transition:
|
||||
opacity 320ms ease,
|
||||
visibility 0s linear 0s;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.loading-screen.is-complete {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition:
|
||||
opacity 320ms ease,
|
||||
visibility 0s linear 320ms;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.loading-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center center;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.loading-progress {
|
||||
position: absolute;
|
||||
top: var(--loading-progress-top, 692px);
|
||||
left: var(--loading-progress-left, 61px);
|
||||
width: var(--loading-progress-width, 253px);
|
||||
height: var(--loading-progress-height, 8px);
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255, 213, 139, 0.76);
|
||||
border-radius: 999px;
|
||||
background:
|
||||
linear-gradient(
|
||||
180deg,
|
||||
rgba(12, 10, 32, 0.8) 0%,
|
||||
rgba(2, 4, 18, 0.92) 100%
|
||||
),
|
||||
rgba(5, 7, 25, 0.86);
|
||||
box-shadow:
|
||||
0 0 12px rgba(255, 164, 58, 0.32),
|
||||
inset 0 0 7px rgba(0, 0, 0, 0.78);
|
||||
}
|
||||
|
||||
.loading-progress-bar {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(90deg, #ff612f 0%, #ffc957 52%, #3ca4ff 100%);
|
||||
box-shadow:
|
||||
0 0 12px rgba(255, 214, 87, 0.84),
|
||||
inset 0 1px 1px rgba(255, 255, 255, 0.45);
|
||||
transform: scaleX(var(--loading-progress, 0));
|
||||
transform-origin: left center;
|
||||
transition: transform 180ms ease;
|
||||
}
|
||||
BIN
game/rock/assets/figma/loading-bg.jpg
Normal file
BIN
game/rock/assets/figma/loading-bg.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 220 KiB |
1118
game/rock/index.html
1118
game/rock/index.html
File diff suppressed because it is too large
Load Diff
BIN
game/touzi/assets/figma/loading-bg.jpg
Normal file
BIN
game/touzi/assets/figma/loading-bg.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
@ -9,6 +9,7 @@
|
||||
<title data-i18n="diceMatch.pageTitle">Dice Match</title>
|
||||
<link rel="stylesheet" href="../../common/theme.css" />
|
||||
<link rel="stylesheet" href="../common/game-match.css" />
|
||||
<link rel="stylesheet" href="../common/game-loading.css" />
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
@ -623,14 +624,16 @@
|
||||
.stake-coin {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex: 0 0 16px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
flex: 0 0 14px;
|
||||
}
|
||||
|
||||
.stake-value {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.match-button {
|
||||
@ -1708,7 +1711,37 @@
|
||||
aria-label="Dice match lobby"
|
||||
data-i18n-aria="diceMatch.pageLabel"
|
||||
>
|
||||
<main class="dice-frame" data-node-id="466:61" data-name="dice">
|
||||
<main
|
||||
class="dice-frame is-preloading"
|
||||
data-node-id="466:61"
|
||||
data-name="dice"
|
||||
>
|
||||
<div
|
||||
class="loading-screen"
|
||||
data-role="loading-screen"
|
||||
aria-label="Loading"
|
||||
role="status"
|
||||
>
|
||||
<img
|
||||
class="loading-bg"
|
||||
src="assets/figma/loading-bg.jpg"
|
||||
alt=""
|
||||
/>
|
||||
<div
|
||||
class="loading-progress"
|
||||
data-role="loading-progress"
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
aria-valuenow="0"
|
||||
>
|
||||
<span
|
||||
class="loading-progress-bar"
|
||||
data-role="loading-progress-bar"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="layer"
|
||||
style="left: -47px; top: 0; width: 470px; height: 834px"
|
||||
@ -2500,7 +2533,8 @@
|
||||
window.HyAppI18nSupported = ['en', 'ar', 'es'];
|
||||
</script>
|
||||
<script src="../../common/i18n.js?v=dice-lang-20260611"></script>
|
||||
<script src="../../common/effect-player.js"></script>
|
||||
<script src="../../common/effect-player.js?v=dice-preload-20260611"></script>
|
||||
<script src="../../common/game-preloader.js?v=game-preloader-20260611"></script>
|
||||
<script>
|
||||
(function () {
|
||||
var DESIGN_WIDTH = 375;
|
||||
@ -2508,6 +2542,64 @@
|
||||
var MAX_STAGE_WIDTH = 768;
|
||||
var SETTLEMENT_AUTO_CLOSE_MS = 30000;
|
||||
var DICE_ROLL_EFFECT_URL = 'assets/figma/dice-roll.svga';
|
||||
var DICE_PRELOAD_ASSET_URLS = [
|
||||
'assets/figma/loading-bg.jpg',
|
||||
'assets/figma/amount-fill.png',
|
||||
'assets/figma/back-button.png',
|
||||
'assets/figma/badge-help.png',
|
||||
'assets/figma/bg-arena.png',
|
||||
'assets/figma/blue-dice.png',
|
||||
'assets/figma/blue-flare.png',
|
||||
'assets/figma/blue-ring.png',
|
||||
'assets/figma/coin.png',
|
||||
'assets/figma/dice-blue-1.png',
|
||||
'assets/figma/dice-blue-2.png',
|
||||
'assets/figma/dice-blue-3.png',
|
||||
'assets/figma/dice-blue-4.png',
|
||||
'assets/figma/dice-blue-5.png',
|
||||
'assets/figma/dice-blue-6.png',
|
||||
'assets/figma/dice-red-1.png',
|
||||
'assets/figma/dice-red-2.png',
|
||||
'assets/figma/dice-red-3.png',
|
||||
'assets/figma/dice-red-4.png',
|
||||
'assets/figma/dice-red-5.png',
|
||||
'assets/figma/dice-red-6.png',
|
||||
DICE_ROLL_EFFECT_URL,
|
||||
'assets/figma/game-icon.png',
|
||||
'assets/figma/home-indicator.svg',
|
||||
'assets/figma/mask-bg.svg',
|
||||
'assets/figma/match-button.png',
|
||||
'assets/figma/matching-center-bg.svg',
|
||||
'assets/figma/matching-center.svg',
|
||||
'assets/figma/matching-left.svg',
|
||||
'assets/figma/matching-right.svg',
|
||||
'assets/figma/opponent-avatar-glow.svg',
|
||||
'assets/figma/opponent-card.png',
|
||||
'assets/figma/opponent-question.png',
|
||||
'assets/figma/player-avatar-glow.png',
|
||||
'assets/figma/player-card.png',
|
||||
'assets/figma/prize-panel.svg',
|
||||
'assets/figma/red-dice.png',
|
||||
'assets/figma/red-flare.png',
|
||||
'assets/figma/red-ring.png',
|
||||
'assets/figma/search-icon.svg',
|
||||
'assets/figma/stake-fill.png',
|
||||
'assets/figma/stake-mask-left.svg',
|
||||
'assets/figma/stake-mask-right.svg',
|
||||
'assets/figma/stake-outline.svg',
|
||||
'assets/figma/status-battery.svg',
|
||||
'assets/figma/status-cellular.svg',
|
||||
'assets/figma/status-frame.svg',
|
||||
'assets/figma/status-wifi.svg',
|
||||
'assets/figma/vs.png',
|
||||
'assets/figma/win-avatar-glow.png',
|
||||
'assets/figma/win-play-again-button.png',
|
||||
'assets/figma/win-player-card.png',
|
||||
'assets/figma/win-rematch-button.png',
|
||||
'assets/figma/win-reward-panel.png',
|
||||
'assets/figma/win-victory-light.png',
|
||||
'assets/figma/winner-text-fill.png',
|
||||
];
|
||||
var root = document.documentElement;
|
||||
var transparentPixel =
|
||||
'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%221%22 height=%221%22%2F%3E';
|
||||
@ -2523,6 +2615,7 @@
|
||||
opponent: null,
|
||||
};
|
||||
var state = {
|
||||
preloading: true,
|
||||
profile: {},
|
||||
appearance: {},
|
||||
balanceCoin: 0,
|
||||
@ -2586,6 +2679,88 @@
|
||||
postBridge('recharge', payload);
|
||||
}
|
||||
|
||||
function isInsufficientBalanceError(error) {
|
||||
var code = String(
|
||||
(error && error.code) || ''
|
||||
).toUpperCase();
|
||||
var message = String(
|
||||
(error && error.message) || ''
|
||||
).toLowerCase();
|
||||
return (
|
||||
code === 'INSUFFICIENT_BALANCE' ||
|
||||
message === 'insufficient balance'
|
||||
);
|
||||
}
|
||||
|
||||
function handleInsufficientBalance() {
|
||||
// 余额不足需要马上让用户补币:先给固定英文提示,再拉起 Flutter 支付页桥接。
|
||||
showMessage('insufficient balance');
|
||||
openRecharge();
|
||||
}
|
||||
|
||||
function setLoadingProgress(progress) {
|
||||
window.HyAppGamePreloader.setProgress({
|
||||
root: root,
|
||||
progress: progress,
|
||||
});
|
||||
}
|
||||
|
||||
function completeLoadingScreen() {
|
||||
state.preloading = false;
|
||||
setMatchButtonBusy(false, matchText());
|
||||
window.HyAppGamePreloader.complete({
|
||||
root: root,
|
||||
frame: frame,
|
||||
loadingScreen: role('loading-screen'),
|
||||
progressNode: role('loading-progress'),
|
||||
});
|
||||
}
|
||||
|
||||
function collectDicePreloadURLs() {
|
||||
return window.HyAppGamePreloader.collectResourceURLs(
|
||||
DICE_PRELOAD_ASSET_URLS
|
||||
);
|
||||
}
|
||||
|
||||
function preloadResourceOnce(url) {
|
||||
return window.HyAppGamePreloader.preloadResource(url, {
|
||||
svga: {
|
||||
svgaScript: '../../common/vendor/svga.min.js',
|
||||
waitForWindowLoad: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function preloadLobbyDataImages(lobbyData) {
|
||||
var urls = [];
|
||||
var seen = {};
|
||||
var profile = (lobbyData && lobbyData.profile) || {};
|
||||
var appearance = (lobbyData && lobbyData.appearance) || {};
|
||||
window.HyAppGamePreloader.addURL(
|
||||
urls,
|
||||
seen,
|
||||
avatarURL(profile)
|
||||
);
|
||||
window.HyAppGamePreloader.addURL(
|
||||
urls,
|
||||
seen,
|
||||
frameURL(appearance.avatar_frame || {})
|
||||
);
|
||||
if (!urls.length) return Promise.resolve();
|
||||
return Promise.all(
|
||||
urls.map(function (url) {
|
||||
return window.HyAppGamePreloader.preloadImage(url);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function runPreloadTasks(tasks) {
|
||||
return window.HyAppGamePreloader.runTasks(
|
||||
tasks,
|
||||
setLoadingProgress
|
||||
);
|
||||
}
|
||||
|
||||
function each(nodes, iteratee) {
|
||||
Array.prototype.forEach.call(nodes, iteratee);
|
||||
}
|
||||
@ -3607,6 +3782,11 @@
|
||||
})
|
||||
.catch(function (error) {
|
||||
stopRollEffects();
|
||||
if (isInsufficientBalanceError(error)) {
|
||||
resetToLobby(false);
|
||||
handleInsufficientBalance(error);
|
||||
return;
|
||||
}
|
||||
showMessage(error.message);
|
||||
resetToLobby(false);
|
||||
});
|
||||
@ -3785,6 +3965,11 @@
|
||||
success: false,
|
||||
error_code: error && error.message,
|
||||
});
|
||||
if (isInsufficientBalanceError(error)) {
|
||||
resetToLobby(true);
|
||||
handleInsufficientBalance(error);
|
||||
return;
|
||||
}
|
||||
showMessage(error.message);
|
||||
resetToLobby(true);
|
||||
});
|
||||
@ -3858,13 +4043,33 @@
|
||||
setMatchButtonBusy(true, matchingText());
|
||||
return;
|
||||
}
|
||||
if (state.preloading) {
|
||||
setMatchButtonBusy(
|
||||
true,
|
||||
i18nText('diceMatch.loading', 'Loading')
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!state.busy) {
|
||||
setActionText(matchingText());
|
||||
setMatchButtonBusy(false, matchText());
|
||||
}
|
||||
}
|
||||
|
||||
function loadLobby() {
|
||||
function requestLobbyData() {
|
||||
var lobbyData = {
|
||||
profile: {},
|
||||
appearance: {},
|
||||
balance: null,
|
||||
configResponse: null,
|
||||
};
|
||||
function onceTask(task) {
|
||||
var promise = null;
|
||||
return function () {
|
||||
if (!promise) promise = task();
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
if (!window.HyAppAPI || !diceAPI()) {
|
||||
showMessage(
|
||||
i18nText(
|
||||
@ -3872,32 +4077,61 @@
|
||||
'Dice API unavailable'
|
||||
)
|
||||
);
|
||||
return;
|
||||
return {
|
||||
lobbyData: lobbyData,
|
||||
tasks: [
|
||||
function () {
|
||||
return Promise.resolve();
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
setMatchButtonBusy(
|
||||
true,
|
||||
i18nText('diceMatch.loading', 'Loading')
|
||||
);
|
||||
var configStartedAt = performance.now();
|
||||
Promise.all([
|
||||
api()
|
||||
var profileTask = function () {
|
||||
return api()
|
||||
.user.me()
|
||||
.then(function (data) {
|
||||
lobbyData.profile = data || {};
|
||||
return data;
|
||||
})
|
||||
.catch(function () {
|
||||
lobbyData.profile = {};
|
||||
return null;
|
||||
}),
|
||||
api()
|
||||
});
|
||||
};
|
||||
var appearanceTask = function () {
|
||||
return api()
|
||||
.user.appearance()
|
||||
.then(function (data) {
|
||||
lobbyData.appearance = data || {};
|
||||
return data;
|
||||
})
|
||||
.catch(function () {
|
||||
lobbyData.appearance = {};
|
||||
return null;
|
||||
}),
|
||||
api()
|
||||
});
|
||||
};
|
||||
var balanceTask = function () {
|
||||
return api()
|
||||
.wallet.balances('COIN')
|
||||
.then(function (data) {
|
||||
lobbyData.balance = data || null;
|
||||
return data;
|
||||
})
|
||||
.catch(function () {
|
||||
lobbyData.balance = null;
|
||||
return null;
|
||||
}),
|
||||
diceAPI()
|
||||
});
|
||||
};
|
||||
var configTask = function () {
|
||||
return diceAPI()
|
||||
.config('dice')
|
||||
.then(function (data) {
|
||||
lobbyData.configResponse = data || null;
|
||||
trackSelfGameEvent('config_load_success', {
|
||||
duration_ms: Math.round(
|
||||
performance.now() - configStartedAt
|
||||
@ -3906,6 +4140,7 @@
|
||||
return data;
|
||||
})
|
||||
.catch(function () {
|
||||
lobbyData.configResponse = null;
|
||||
trackSelfGameEvent('config_load_fail', {
|
||||
duration_ms: Math.round(
|
||||
performance.now() - configStartedAt
|
||||
@ -3914,20 +4149,87 @@
|
||||
error_code: 'config_failed',
|
||||
});
|
||||
return null;
|
||||
}),
|
||||
]).then(function (results) {
|
||||
state.profile = results[0] || {};
|
||||
state.appearance = results[1] || {};
|
||||
state.config =
|
||||
(results[3] && results[3].config) || null;
|
||||
if (results[2])
|
||||
setCoin(balanceFromResponse(results[2]));
|
||||
renderSelfProfile();
|
||||
renderStakeOptions(state.config);
|
||||
setMatchButtonBusy(false, matchText());
|
||||
});
|
||||
};
|
||||
return {
|
||||
lobbyData: lobbyData,
|
||||
tasks: [
|
||||
onceTask(profileTask),
|
||||
onceTask(appearanceTask),
|
||||
onceTask(balanceTask),
|
||||
onceTask(configTask),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function applyLobbyData(lobbyData) {
|
||||
lobbyData = lobbyData || {};
|
||||
state.profile = lobbyData.profile || {};
|
||||
state.appearance = lobbyData.appearance || {};
|
||||
state.config =
|
||||
(lobbyData.configResponse &&
|
||||
lobbyData.configResponse.config) ||
|
||||
null;
|
||||
if (lobbyData.balance) {
|
||||
setCoin(balanceFromResponse(lobbyData.balance));
|
||||
}
|
||||
renderSelfProfile();
|
||||
renderStakeOptions(state.config);
|
||||
setMatchButtonBusy(
|
||||
state.preloading,
|
||||
state.preloading
|
||||
? i18nText('diceMatch.loading', 'Loading')
|
||||
: matchText()
|
||||
);
|
||||
}
|
||||
|
||||
function loadLobby() {
|
||||
var lobby = requestLobbyData();
|
||||
return runPreloadTasks(lobby.tasks).then(function () {
|
||||
return preloadLobbyDataImages(lobby.lobbyData).then(
|
||||
function () {
|
||||
applyLobbyData(lobby.lobbyData);
|
||||
return lobby.lobbyData;
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function loadStartup() {
|
||||
var lobby = requestLobbyData();
|
||||
var tasks = lobby.tasks.slice();
|
||||
collectDicePreloadURLs().forEach(function (url) {
|
||||
tasks.push(function () {
|
||||
return preloadResourceOnce(url);
|
||||
});
|
||||
});
|
||||
tasks.push(function () {
|
||||
return Promise.all([
|
||||
lobby.tasks[0](),
|
||||
lobby.tasks[1](),
|
||||
]).then(function () {
|
||||
return preloadLobbyDataImages(lobby.lobbyData);
|
||||
});
|
||||
});
|
||||
// 启动期必须等用户、余额、配置、骰子 SVGA、六面骰子和页面图片都进入缓存后,才允许用户进入大厅操作。
|
||||
return runPreloadTasks(tasks)
|
||||
.then(function () {
|
||||
applyLobbyData(lobby.lobbyData);
|
||||
return new Promise(function (resolve) {
|
||||
window.setTimeout(resolve, 180);
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
completeLoadingScreen();
|
||||
trackSelfGameEvent('preload_complete', {
|
||||
duration_ms: Math.round(
|
||||
performance.now() - pageStartedAt
|
||||
),
|
||||
asset_count: tasks.length,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initActions() {
|
||||
document.addEventListener('click', function (event) {
|
||||
var target = event.target.closest('[data-action]');
|
||||
@ -3943,6 +4245,7 @@
|
||||
openRecharge();
|
||||
return;
|
||||
}
|
||||
if (state.preloading) return;
|
||||
if (action === 'stake') {
|
||||
if (state.busy) return;
|
||||
selectStake(target);
|
||||
@ -3996,7 +4299,11 @@
|
||||
'hyapp:i18n-ready',
|
||||
refreshLocalizedRuntimeText
|
||||
);
|
||||
loadLobby();
|
||||
loadStartup().catch(function (error) {
|
||||
showMessage(error && error.message);
|
||||
completeLoadingScreen();
|
||||
setMatchButtonBusy(false, matchText());
|
||||
});
|
||||
window.addEventListener('resize', updateScale);
|
||||
window.addEventListener('orientationchange', updateScale);
|
||||
if (window.HyAppBridge && window.HyAppBridge.ready) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user