HTML5 2D 游戏开发 设置舞台(5)当窗口获得焦点时解冻游戏
 
- UID
- 1066743
|

HTML5 2D 游戏开发 设置舞台(5)当窗口获得焦点时解冻游戏
当窗口获得焦点时解冻游戏当游戏恢复时,玩家会喜欢平稳地过渡到操作,为他们提供一些时间来重新获得控制。在这段时间里,在恢复游戏之前提供有关剩余时间量的反馈,这会是一个好主意。Snail Bait 通过在 toast 中显示倒计时来实现该反馈,所以我从 toast 的概述开始这个讨论。
Toasttoast 是游戏暂时向玩家显示的某些消息,像图 2 中的 Good Luck! toast:
图 2. toast 像 Snail Bait 本身,Snail Bait toast 是使用 HTML、CSS 和 JavaScript 的组合来实现的,如接下来的三个清单所示。
清单 7 显示一个 toast 的 HTML 代码:
清单 7. toast:HTML1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| <!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<div id='wrapper'>
<!-- Toast...................................................-->
<div id='toast'></div>
...
</div>
...
</body>
</html>
|
实现 Snail Bait 的 Good Luck! toast 的 CSS 如清单 8 所示:
清单 8. toast:CSS1
2
3
4
5
6
7
8
9
10
11
12
13
| #toast {
position: absolute;
...
-webkit-transition: opacity 0.5s;
-moz-transition: opacity 0.5s;
-o-transition: opacity 0.5s;
transition: opacity 0.5s;
opacity: 0;
z-index: 1;
display: none;
}
|
清单 9 显示 Good Luck! toast 的 JavaScript:
清单 9. toast:JavaScript1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| var SnailBait = function () {
...
this.toast = document.getElementById('toast'),
this.DEFAULT_TOAST_TIME = 3000, // 3 seconds
...
};
SnailBait.prototype = {
...
start: function () {
...
snailBait.splashToast('Good Luck!');
},
splashToast: function (text, howLong) {
howLong = howLong || this.DEFAULT_TOAST_TIME;
toast.style.display = 'block';
toast.innerHTML = text;
setTimeout( function (e) {
toast.style.opacity = 1.0; // After toast is displayed
}, 50);
setTimeout( function (e) {
toast.style.opacity = 0; // Starts CSS3 transition
setTimeout( function (e) {
toast.style.display = 'none'; // Just before CSS3 animation concludes
}, 480);
}, howLong);
},
...
}
|
正如前面这三个清单中的实现,toast 只是 DIV,您可以在 中看到。事情在 中变得更为有趣,其中列出了 DIV 的 CSS。DIV 的位置是 absolute,这意味着它可以显示在其他 DIV 的上面或下面,而不是前面或后面。toastDIV 的 z-index 值也是 1,这意味着它始终显示在游戏画布的上面,其 z-index 的默认值为 0。最后,toast 元素的 CSS 定义一个绑定到 opacity 属性的 0.5 秒的过渡,当更改该属性时,CSS 用 0.5 秒时间将 DIV 从之前的不透明度平滑过渡到新的值。
在 的 splashToast() 方法中,事情变得更加有趣,toast 会在一段指定的时间内显示。当 Snail Bait 调用 splashToast() 时,默认的显示时间为 3 秒,toast 淡入 0.5 秒,短暂地显示 2.5 秒,然后淡出 0.5 秒。下面是它的工作原理:
splashToast() 方法首先将 toastDIV 的 display 属性设置为 block,这通常会使得 DIV 变得可见。但是,因为它的 opacity 属性的初始值为 0,所以 toastDIV 仍保持不可见。然后 splashToast() 将 toastDIV 的内部 HTML 设置为您传递给方法的文本,但不透明度设置保持不变,所以设置文本也不会使得 DIV 可见。
为了使得 toastDIV 可见,我将它的不透明度设置为 1.0。该设置触发因我在 中指定的过渡而产生的 CSS3 动画,但是,仅当不透明度设置稍后(在本例中是 50 毫秒)产生看起来很奇怪的 setTimeout() 的结果时,才会修改不透明度设置,在该函数中它是封闭的。这是因为:
只能对有中间状态的元素属性指定 CSS3 过渡。例如,如果您将不透明度从 0.2 修改为 0.3(随机选择的两个数字),中间不透明度为 0.21、0.22 等。
过渡需要中间状态,这是有一定道理的;如果没有中间状态,就没有一个明确的方法来指定过渡的动画。例如,这就是为什么您不能为 display 属性指定一个过渡的原因,它没有中间状态。不仅如此,如果您修改了 display 属性,那么 CSS3 将不再接受您为其他任何属性指定的任何过渡。这也是有一定道理的,因为您在让 CSS3 执行互相冲突的两件事:例如,通过修改 display 属性让元素立即可见,但又使用 opacity 属性的过渡使其慢慢地淡入视图。CSS3 不能两件事同时做,所以它选择了修改 display 属性。
半透明的 和事件经过前面关于 splashToast() 的讨论之后,您可能想知道为什么该方法要这么麻烦地操作 toastDIV 的 display 属性。为什么不直接操纵的 DIV 的不透明度使其变得可见或不可见呢?答案是,除非您明确地有意这样做,让不可见的 DIV 悬停在那里,否则这并不是一个好主意,因为它们很可能以其他令人惊讶的方式(如拦截事件)来显示它们的存在。
如果 splashToast() 同时设置 toastDIV 的 display 和 opacity 属性,CSS3 会忽略不透明度的过渡,因此,在设置 display 属性之后,该方法会将不透明度设置为 1.0,更确切地说,在大约 50ms 之后会执行该操作。
最后,当所需的显示时间过去后,splashToast() 会将 toastDIV 的 opacity 属性重新设置为 0,这会再次触发一个 0.5 秒的 CSS3 动画。在 CSS3 动画开始两秒钟后之后,splashToast() 方法会将 display 属性重新设置为 0。
解冻 Snail Bait当 Snail Bait 在暂停后恢复播放时,它通过三秒钟的倒计时让玩家有时间做好准备,如图 3 所示:
图 3. 解冻过程中的倒计时 清单 10 显示了倒计时的 JavaScript:
清单 10. 倒计时:JavaScript1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| var SnailBait = function (canvasId) {
...
this.toast = document.getElementById('toast'),
};
window.onblur = function (e) { // Pause if unpaused
if (!snailBait.paused) {
snailBait.togglePaused();
}
};
window.onfocus = function (e) { // unpause if paused
var originalFont = snailBait.toast.style.fontSize;
if (snailBait.paused) {
snailBait.toast.style.font = '128px fantasy';
snailBait.splashToast('3', 500); // Display 3 for one half second
setTimeout(function (e) {
snailBait.splashToast('2', 500); // Display 2 for one half second
setTimeout(function (e) {
snailBait.splashToast('1', 500); // Display 1 for one half second
setTimeout(function (e) {
snailBait.togglePaused();
setTimeout(function (e) { // Wait for '1' to disappear
snailBait.toast.style.fontSize = originalFont;
}, 2000);
}, 1000);
}, 1000);
}, 1000);
}
};
|
当 Snail Bait 窗口重新获得焦点时,它会使用 splashToast() 方法启动倒计时。每个数字淡入 0.5 秒,然后淡出 0.5 秒。一旦倒计时为零,onfocus 处理器就会重新启动游戏。
然而,如果玩家在倒计时过程中激活了另一个窗口或选项卡,清单 10 中的代码就会无法正常工作,因为无论窗口是否获得焦点,游戏都会在倒计时结束时重新开始。这很容易解决,利用一个 windowHasFocus 标实即可,如清单 11 所示:
清单 11. 在倒计时期间失去焦点的处理1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
| var SnailBait = function (canvasId) {
...
this.windowHasFocus = true,
...
};
...
SnailBait.prototype = {
...
splashToast: function (text, howLong) {
howLong = howLong || this.DEFAULT_TOAST_TIME;
toast.style.display = 'block';
toast.innerHTML = text;
setTimeout( function (e) {
if (snailBait.windowHasFocus) {
toast.style.opacity = 1.0; // After toast is displayed
}
}, 50);
setTimeout( function (e) {
if (snailBait.windowHasFocus) {
toast.style.opacity = 0; // Starts CSS3 transition
}
setTimeout( function (e) {
if (snailBait.windowHasFocus) {
toast.style.display = 'none';
}
}, 480);
}, howLong);
},
...
};
...
window.onblur = function (e) { // pause if unpaused
snailBait.windowHasFocus = false;
if (!snailBait.paused) {
snailBait.togglePaused();
}
};
window.onfocus = function (e) { // unpause if paused
var originalFont = snailBait.toast.style.fontSize;
snailBait.windowHasFocus = true;
if (snailBait.paused) {
snailBait.toast.style.font = '128px fantasy';
snailBait.splashToast('3', 500); // Display 3 for one half second
setTimeout(function (e) {
snailBait.splashToast('2', 500); // Display 2 for one half second
setTimeout(function (e) {
snailBait.splashToast('1', 500); // Display 1 for one half second
setTimeout(function (e) {
if ( snailBait.windowHasFocus) {
snailBait.togglePaused();
}
setTimeout(function (e) { // Wait for '1' to disappear
snailBait.toast.style.fontSize = originalFont;
}, 2000);
}, 1000);
}, 1000);
}, 1000);
}
};
|
|
|
|
|
|
|