Fra*_*len 5 javascript firebase-realtime-database
我有一个应用程序,其中一个用户主持一个游戏,然后其他用户可以对主持人提出的问题进行投票。从主持人发布问题的那一刻起,玩家有 20 秒的时间进行投票。
如何在所有玩家的屏幕上显示倒数计时器并使它们与主机同步?
许多开发人员被困在这个问题上,因为他们试图在所有用户之间同步倒计时本身。这很难保持同步,而且容易出错。然而,有一种更简单的方法,我在许多项目中都使用过它。
每个客户端需要显示其倒数计时器的所有内容都是相当静态的信息:
我们将使用数据库的服务器时间作为第一个值,第二个值来自主机代码,相对偏移量是 Firebase 为我们提供的值。
下面的代码示例是用 JavaScript 为网络编写的,但使用相同的方法(和非常相似的代码)并应用于 iOS、Android 和大多数其他实现实时侦听器的 Firebase SDK。
让我们首先将开始时间和间隔写入数据库。忽略安全规则和验证,这可以很简单:
const database = firebase.database();
const ref = database.ref("countdown");
ref.set({
startAt: ServerValue.TIMESTAMP,
seconds: 20
});
Run Code Online (Sandbox Code Playgroud)
当我们执行上面的代码时,它会将当前时间写入数据库,这是一个 20 秒的倒计时。由于我们用 来写时间ServerValue.TIMESTAMP,数据库会把时间写在服务器上,所以它不会受到主机本地时间(或偏移量)的影响。
现在让我们看看其他用户是如何读取这些数据的。与 Firebase 一样,我们将使用on()侦听器,这意味着我们的代码正在积极侦听数据何时被写入:
ref.on("value", (snapshot) => {
...
});
Run Code Online (Sandbox Code Playgroud)
当此ref.on(...代码执行时,它会立即从数据库中读取当前值并运行回调。但它也会继续监听对数据库的更改,并在发生另一次写入时再次运行代码。
因此,让我们假设我们正在收到一个新的数据快照,用于刚刚开始的倒计时。我们如何在所有屏幕上显示准确的倒数计时器?
我们将首先从数据库中获取值:
ref.on("value", (snapshot) => {
const seconds = snapshot.val().seconds;
const startAt = snapshot.val().startAt;
...
});
Run Code Online (Sandbox Code Playgroud)
我们还需要估计本地客户端和服务器上的时间之间有多少时间。Firebase SDK 在它第一次连接到服务器时估计这个时间,我们可以从.info/serverTimeOffset客户端读取它:
const serverTimeOffset = 0;
database.ref(".info/serverTimeOffset").on("value", (snapshot) => { serverTimeOffset = snapshot.val() });
ref.on("value", (snapshot) => {
const seconds = snapshot.val().seconds;
const startAt = snapshot.val().startAt;
});
Run Code Online (Sandbox Code Playgroud)
在一个运行良好的系统中,serverTimeOffset是一个正值,表示我们到服务器的延迟(以毫秒为单位)。但如果我们的本地时钟有偏移,它也可能是一个负值。无论哪种方式,我们都可以使用此值来显示更准确的倒数计时器。
接下来我们将启动一个间隔计时器,它每 100 毫秒左右调用一次:
const serverTimeOffset = 0;
database.ref(".info/serverTimeOffset").on("value", (snapshot) => { serverTimeOffset = snapshot.val() });
ref.on("value", (snapshot) => {
const seconds = snapshot.val().seconds;
const startAt = snapshot.val().startAt;
const interval = setInterval(() => {
...
}, 100)
});
Run Code Online (Sandbox Code Playgroud)
然后我们间隔的每个计时器到期,我们将计算剩余的时间:
const serverTimeOffset = 0;
database.ref(".info/serverTimeOffset").on("value", (snapshot) => { serverTimeOffset = snapshot.val() });
ref.on("value", (snapshot) => {
const seconds = snapshot.val().seconds;
const startAt = snapshot.val().startAt;
const interval = setInterval(() => {
const timeLeft = (seconds * 1000) - (Date.now() - startAt - serverTimeOffset);
...
}, 100)
});
Run Code Online (Sandbox Code Playgroud)
最后,我们以合理的格式记录剩余时间,并在计时器到期时停止计时器:
const serverTimeOffset = 0;
database.ref(".info/serverTimeOffset").on("value", (snapshot) => { serverTimeOffset = snapshot.val() });
ref.on("value", (snapshot) => {
const seconds = snapshot.val().seconds;
const startAt = snapshot.val().startAt;
const interval = setInterval(() => {
const timeLeft = (seconds * 1000) - (Date.now() - startAt - serverTimeOffset);
if (timeLeft < 0) {
clearInterval(interval);
console.log("0.0 left)";
}
else {
console.log(`${Math.floor(timeLeft/1000)}.${timeLeft % 1000}`);
}
}, 100)
});
Run Code Online (Sandbox Code Playgroud)
在上面的代码中肯定还有一些清理工作要做,例如当一个新的倒计时开始而一个仍在进行中时,但整体方法运行良好并且可以轻松扩展到数千个用户。
| 归档时间: |
|
| 查看次数: |
407 次 |
| 最近记录: |