加载,顾名思义,就是将一些信息,从A载到B,这个过程类似运货,而这个过程不是瞬间发生的,就比如把我从深圳运到广州,用复兴号运我需要10分钟,这个就是加载时间。信息从服务器运到本地,从本地运到服务器也需要一定的时间。
无缝加载,是提升加载体验的一种办法,在加载的过程中,用户可无法做其他事情,而过程又很漫长的情况下用户心情就会很烦躁,比如在高铁上我不能玩任何游戏,也没用书给我阅读,只能静坐着等待到达广州。如果我在这期间开ktv,开斗地主,整个过程就不会无聊了,而且我也不只能静坐等待。
一趟列车从广州开往北京,沿途要经过很多站,如果把列车的窗口门口能看到外界的都封堵上,也没有显示屏告诉你现在开到了哪里,心里一定会很着急,甚至不知道列车有没有开动,开的方向对不对。加载也如此,用户操作后没有反馈,或是久久没有结果,会让用户觉得是不是自己的动作没有生效,是不是自己的电脑卡住了
此时有一个指示,告诉乘客“当前在郑州,还有x站到达北京”,体验上提升了几亿个数量级。如果此时再加上列车速度,更能让乘客感觉到“这车真快”,心理上更加舒服
而程序上,用户按下按钮后,反馈上的交流很重要,运行一段复杂程序时,尽可能把关键步骤告诉用户,比如“正在启动”、“正在初始化”、“正在处理”、“正在保存”。
来源于一次对话
在新游戏《崩坏:星穹铁道》中,每次切后台重进或断网重连时,加载的画面不像崩坏3中叠了一层加载中的layer阻止用户操作,而是塞到了右上角进行加载
而这样的好处就是即使经历了某些不该经历的经历之后,用户依然能够正常操作游戏本身,而不用等待加载完毕。
这就是无缝加载的要点,不影响操作
本期以websocket通信为例子,nodejs作为后端。
这里有一个后端,表示当前端发送websocket消息时,后端回复一个JSON消息:
var express = require('express')
var app = express()
require('express-ws')(app)
app.ws('/load', (ws, req) => {
ws.on('message', (msg) => {
ws.send(new Date().getTime())
setTimeout(() => {
ws.send(JSON.stringify({
status: true,
time: new Date().getTime()
}))
}, 2000)
})
})
app.listen(4003);
setTimeout模拟了加载的动作,表示耗时2s
建立一个websocket连接,并且监听websocket传来的消息
var ws = new WebSocket('ws://localhost:4003/load');
ws.onmessage = function (data) {
console.log(data.data)
}
如果此时我们在控制台操作,得到的结果就是
按下回车后,浏览器并没有任何动作,此时我并不知道是否发送成功,只能静等2s后才能接收到后端发来的信息。
此时加入动画,加载器组件来自Ar-Sr-Na:ai.arsrna.cn 里的所有应用均为此控件
发送事件之前,将该进度条隐藏,发送按钮按下后,显示进度条,成功后再次隐藏
首先,让进度条隐藏
定义发送接收事件,并绑定隐藏事件
var ws = new WebSocket('ws://localhost:4003/load');
ws.onmessage = function (data) {
var msg=JSON.parse(data.data)
console.log(msg)
if(msg.status) $('#logProgress').hide()
}
function fischl() {
$('#logProgress').show()
ws.send('')
}
这样一来,加载过程就清晰了,用户知道自己做的事件有所反馈
如上面所说,崩坏3的加载确实有些差劲,我们把它放到三维视图看看这么差劲的加载是怎么回事
甚至
在加载过程中,应用弹了一个非常阴间的加载动画层,阻止了整个应用的操作,只能静待加载完毕后加载层的消失
而在《崩坏:星穹铁道》中,不是大面积的加载,而是仅把加载动画的位置换到了右上角
从而避免挡住用户移动视角,移动人物
这样看似很简单的一个操作,对用户体验的影响极大,改起来也不复杂,就上面例子而言,如果我们把整个加载中覆盖整个body层,就是崩坏3的暴力加载,如果做到小进度条,就是不影响体验的加载。
由于前端websocket断开后并不会自动重连,而后端也不能主动向前端发起连接,所以一旦断开,这个连接如果不再次连上,就永远失去了连接
但是,websocket对象有一个监听断连事件,一旦检测到断连,就重复进行重连
不过要注意的是,如果这个通信不重要,断开一段时间也不会影响用户在本地进行的操作,重连过程不需要搞那么重大
一个稍微小的提示就好,尽量不要打断用户的操作
例如上面的例子
ws.onclose = function (data) {
$('#lostConn').show();
//尝试重连的callback
reConnect()
}
尝试重连操作的方法,就是当发现断连时,再次连接
function reConnect() {
ws = new WebSocket('ws://localhost:4003/load');
}
但是这样做会出现一个问题,当第二次尝试失败时,将不会继续进行下一次重连,而且间隔很长,所以此时可以使用间隔尝试的方式,一直重连直到成功
function reconnect() {
$('#lostConn').show();
reConnectTimes++
$('#text2').html(`正在尝试找回小姐 第 ${reConnectTimes} 次`);
if (lockReconnect) $('#lostConn').hide();return
$('#lostConn').show();
lockReconnect = true;
setTimeout(function () {
setWebsocket();
lockReconnect = false;
}, 1000);//断开后2s自动重连
}
把连接websocket的事件封装为方法,把上面的代码一同加入
var lockReconnect = false;//websocket连接状态,避免重复连接
var reConnectTimes = 0;
setWebsocket()
function setWebsocket() {
ws = new WebSocket('ws://localhost:4003/load');
ws.onmessage = function (data) {
var msg = JSON.parse(data.data)
console.log(msg)
if (msg.status) $('#logProgress').hide()
}
ws.onclose = function (event) {
console.log("断连");
reconnect();
}
ws.onerror = function (event) {
console.log("错误");
reconnect();
}
}
则总逻辑代码如下:
var lockReconnect = false;//websocket连接状态,避免重复连接
setWebsocket()
function setWebsocket() {
ws = new WebSocket('ws://localhost:4003/load');
ws.onmessage = function (data) {
//消息回调 data.data
}
ws.onclose = function (event) {
//console.log("断连");
reconnect();
}
ws.onerror = function (event) {
//console.log("错误");
reconnect();
}
}
function reconnect() {
if (lockReconnect) return;
lockReconnect = true;
setTimeout(function () {
setWebsocket();
lockReconnect = false;
}, 1000);//断开后1s自动重连
}
正常情况下
服务端主动断开
再次启动效果如正常情况。
对于一些很不重要的消息,在前端展示其重连加载时,应使用后台默默加载或者稍微提示的方式。例如本地计算的项目,不怎么依赖服务器的项目。
对于中等级别的连接,本地可以计算,但是部分来自服务端,可以采用用户提示式的加载。例如原神从后台切回前台时重新从服务器获取信息的场景,此时人物允许移动转视角操作,但是敏感项目例如充值,产生伤害等应先等待服务器做出相应回复。
对于完全依赖服务端的应用,应采取隔断交互的措施,防止用户在客户端进行操作,得不到服务器验证从而造成恶意修改数据的后果。例如充值场景,在服务器那边没收到“钱”之前,一定不能让客户端提前回调。
Powered by Ar-Sr-Na
2023-5-1