记录--Loading 用户体验 - 加载时避免闪烁
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
在切换详情页中有这么一个场景,点击上一条,会显示上一条的详情页,同理,点击下一条,会显示下一条的详情页。

伪代码如下所示:
我们定义了一个 switcher 模版, 用户点击上一条、下一条时调用 goToPreOrNext 方法。该页面通过 loadingDone 状态判断是否展示加载效果。
// html
<thy-loading [thyDone]="loadingDone"></thy-loading>
<ng-container *ngIf="loadingDone">
<styx-pivot-detail>
...
<thy-arrow-switcher
...
(thyPrevious)="goToPreOrNext($event)"
(thyNext)="goToPreOrNext($event)"
></thy-arrow-switcher>
...
</styx-pivot-detail>
</ng-container>
在 goToPreOrNext 方法中,当调用该方法时,通过 goToPreOrNextResolve 接口返回下一条的详情 id,通过该 id 请求详情数据。
// 请求下一条 id
fetchPreOrNext(event: ThyArrowSwitcherEvent) {
...
this.goToPreOrNextResolve(event.index, topicId).subscribe(
(id: string) => {
this.getDetail(id);
}
...
)
} // 请求详情数据
getDetail(postId: string) {
this.loadingDone = false;
this.store
.fetchPost(postId)
.pipe(
finalize(() => {
this.loadingDone = true;
})
)
.subscribe();
}
这看起来好像没有什么问题,应该一般都是这么干的,我们运行看看。

如何切换时候不闪?
与最上面的相比,有没有发现每次切换时,都会闪一下,用户体验很不好,有没有办法可以解决它?
这个问题就是 loadingDone 的状态切换导致的,我们把 loadingDone 干掉是不是就可以了?
如下代码所示:
// 请求详情数据
getDetail(postId: string) {
// 注释掉这一行
// this.loadingDone = false;
this.store
.fetchPost(postId)
.pipe(
finalize(() => {
this.loadingDone = true;
})
)
.subscribe();
}

好像方案可行?
但是把网络调成低速 3G 后,会发现,我们的加载效果没了,页面像卡住了一样,这当然不行。

有没有更好的方案?
setTimeout 方案
把先前 loadingDone 状态不放到 getDetail 方法中,而是单独拿出来紧跟 this.getDetail(id) 后面。
代码如下:
// 定义一个 timer
**private timer = null;** // 请求下一条 id
fetchPreOrNext(event: ThyArrowSwitcherEvent) {
...
this.goToPreOrNextResolve(event.index, topicId).subscribe(
(id: string) => {
this.getDetail(id); **this.timer = setTimeout(() => {
this.loadingDone = false;
}, 500);** }
...
)
} // 请求详情数据
getDetail(postId: string) {
// 删除掉该行loadingDone 代码
**// this.loadingDone = false;**
this.store
.fetchPost(postId)
.pipe(
finalize(() => {
this.loadingDone = true;
// 记得清除
**clearTimeout(this.timer);**
})
)
.subscribe();
}
这么做的含义就是,我们给到 loadingDone 500ms 的缓冲时间,如果 500ms 内返回数据了,则没有 loading 的效果,如果没有加载回来,在会显示加载中。
一般情况如下所示:

低速网络下的效果:

这确实是一种方案,但是总感觉哪里怪怪的。
这里是个定时任务并且 500ms 后触发。试想一种结果,当我快速点击下一条并且在 300ms 获取到了数据并把 loadingDone 状态置为 true, 但 500ms时,loadingDone 状态置为 false,造成假死的情况,显然这不是我们想要的。
那这该如何解决?
RxJS 大法
抛去使用 setTimeout 的方案,我们对 getDetail 代码改成如下的形式。
大致的思路是,将请求的 loading 状态与数据获取的状态分离,并定义了两个流 result$ 和 showLoadingIndicator$。
result$ 流请求到数据之后,之后之后的一些操作, showLoadingIndicator$ 流则负责 loading 状态的推送。
来看看怎么一步一步实现的:
首先我们定义一个请求的流。
const fetchPost$ = () => this.store.fetchPost(postId);
然后分别定义了两个流 result$ 和 showLoadingIndicator$。这里的 share() 函数是因为会有两个订阅它的地方。
const result$ = fetchPost$().pipe(share()); const showLoadingIndicator$;
然后我们来处理 showLoadingIndicator$ 流。
我们期望在 500ms 内请求到的数据,则不应该展示 loading,否则,应该展示 loading 状态。
const showLoadingIndicator$ = timer(500).pipe(mapTo(true), takeUntil(result$))
如果在 500ms 后很快请求到了数据,为了避免闪屏,我们需要让 loading 至少显示 1s。然后使用 merge() 合并这两种结果。
const showLoadingIndicator$ = merge(
timer(500).pipe(mapTo(true), takeUntil(result$)),
combineLatest(result$, timer(1000)).pipe(mapTo(false))
).pipe(startWith(false), distinctUntilChanged());
最后订阅它们。
result$.subscribe(
result => {
// 请求到结果后的操作
},
error => {
// TODO
}
); showLoadingIndicator$.subscribe(isLoading => {
// 更新 loadingDone 状态
this.loadingDone = !isLoading;
});
完整的代码如下:
// 请求下一条 id
fetchPreOrNext(event: ThyArrowSwitcherEvent) {
...
this.goToPreOrNextResolve(event.index, topicId).subscribe(
(id: string) => {
this.getDetail(id);
}
...
)
} // 请求详情数据
getDetail(postId: string) { const fetchPost$ = () => this.store.fetchPost(postId); const result$ = fetchPost$().pipe(share()); const showLoadingIndicator$ = merge(
timer(500).pipe(mapTo(true), takeUntil(result$)),
combineLatest(result$, timer(1000)).pipe(mapTo(false))
).pipe(startWith(false), distinctUntilChanged()); result$.subscribe(
result => {
// TODO
},
error => {
// TODO
}
); showLoadingIndicator$.subscribe(isLoading => {
this.loadingDone = !isLoading;
});
}
如果想更细致知道如何实现的,参考下面这篇文档:
Loading indication with a delay and anti-flickering in RxJS

本文转载于:
https://juejin.cn/post/7176943529057321017
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

记录--Loading 用户体验 - 加载时避免闪烁的更多相关文章
- WinForm用户自定义控件,在主窗体加载时出现闪烁;调用用户控件出现闪烁,需要鼠标才能够显示
转载自:http://www.dotblogs.com.tw/rainmaker/archive/2012/02/22/69811.aspx 解决方案: 在调用用户控件的窗体里面添加一下代码: pro ...
- cocos2d-html5如何更改预加载时的默认的logo图片和删除loading小圈圈
找到框架目录(2.1.4)里的cocos2d目录里有个CCLoader.js文件,找到LoaderScene的构造方法ctor,可以看到有一行: this._logoTexture.src= &quo ...
- WebApi 数据保护操作未成功。这可能是由于未为当前线程的用户上下文加载用户配置文件导致的。当线程执行模拟时,可能会出现此情况。","ExceptionType":"System.Security.Cryptography.CryptographicException","StackTrace
在调用System.Security.Cryptography.ProtectedData.Protect方法来保护私密信息时,IIS可能会报以下错误:CryptographicException: ...
- 页面数据加载完成时,显示loading页面.数据加载完,loading隐藏.
一,引入三个文件 jQuery版本使用 jQuery v1.7.1 jquery-easyui文件中,引入easyui-lang-zh_CN.js的js 做数据加载时使用jquery.blockui. ...
- 寻找丢失的微服务-HAProxy热加载问题的发现与分析 原创: 单既喜 一点大数据技术团队 4月8日 在一点资讯的容器计算平台中,我们通过HAProxy进行Marathon服务发现。本文记录HAProxy服务热加载后某微服务50%概率失效的问题。设计3组对比实验,验证了陈旧配置的HAProxy在Reload时没有退出进而导致微服务丢失,并给出了解决方案. Keywords:HAProxy热加
寻找丢失的微服务-HAProxy热加载问题的发现与分析 原创: 单既喜 一点大数据技术团队 4月8日 在一点资讯的容器计算平台中,我们通过HAProxy进行Marathon服务发现.本文记录HAPro ...
- 页面加载时loading效果
页面加载时loading效果: <!DOCTYPE html> <html lang="en"> <head> <meta charset ...
- ionic js 加载动画 ionSpinner 提供了许多种旋转加载的动画图标。当你的界面加载时,你就可以呈现给用户相应的加载图标。 该图标采用的是SVG
ionic 加载动画 ion-spinner ionSpinner 提供了许多种旋转加载的动画图标.当你的界面加载时,你就可以呈现给用户相应的加载图标. 该图标采用的是SVG. 用法 <ion- ...
- JqueryEasyUI 解决IE下加载时页面错乱的问题 分类: JavaScript JqueryEasyUI 2014-09-20 09:50 545人阅读 评论(1) 收藏
问题描述: 一直觉得jqueryeasyui在IE下的渲染效果不大好,尤其刚进入页面时的加载,页面会出现布局错乱,虽然是一闪而过,但是给用户的体验不好: 可以通过在页面onload时,增加一个遮罩层, ...
- jquery mobile 的loading提示“正在加载...”在不同版本中的不同实现方式
在jquery mobile开发中,在页面的切换.或者ajax获取数据时由于网速慢等其他原因,会有一个加载的时间,如果能在这段时间给一个“正在加载...”的提示,用户体验会更好.下面来简单的介绍一下在 ...
- 你所不知道的SQL Server数据库启动过程(用户数据库加载过程的疑难杂症)
前言 本篇主要是上一篇文章的补充篇,上一篇我们介绍了SQL Server服务启动过程所遇到的一些问题和解决方法,可点击查看,我们此篇主要介绍的是SQL Server启动过程中关于用户数据库加载的流程, ...
随机推荐
- JS leetcode 有多少小于当前数字的数字 解题分析,你应该了解的桶排序
壹 ❀ 引 刷题其实一直没断,只是这两天懒得整理题目...那么今天来记录一道前天做的题,题目本身不难,不过拓展起来还是有些东西可以讲,题目来自leetcode有多少小于当前数字的数字,题目描述如下: ...
- RocketMQ—RocketMQ消息重复消费问题
RocketMQ-RocketMQ消息重复消费问题 重复消费问题的描述 什么情况下会发生重复消费的问题: 生产者多次投递消息:如果生产者发送消息时,连接有延迟,MQ还没收到消息,生产者又发送了一次消息 ...
- Rollup的基本使用
Rollup的基本使用 rollup.js是一个模块打包工具,可以使项目从一个入口文件开始,将所有使用到的模块文件都打包到一个最终的发布文件中,Rollup极其适合构建一个工具库,Vue.js源码就是 ...
- Laravel入坑指南(9)——数据迁移与填充
当我们开发完成一个(小)项目,发布到线上时,我们需要将本地数据库迁移到服务器上,并且填充初始化数据.而Laravel框架规定了一套完善的数据迁移与填充机制. 在官网中分别介绍了以下四个命令: php ...
- 解决etcd集群空间占用问题 mvcc: database space exceeded
一.查看状态 ./etcdctl --write-out=table endpoint status 可以看到空间占用(DB SIZE).本次是清理过了.原本达到了2G(为默认上限) 二.查看当前版本 ...
- New Questions
1. C++/Qt 实现一个信号槽 #include <functional> #include <iostream> #include <vector> clas ...
- win32-制作mini dump文件
一个完整的用户模式dump是基本的用户模式转储文件. 此转储文件包括进程的整个内存空间,程序的可执行映像本身,句柄表以及其他信息,这些信息对于调试器在重建转储发生时正在使用的内存中很有用. 可以将完整 ...
- Lucene介绍与使用
Lucene介绍与使用 原文链接:https://blog.csdn.net/weixin_42633131/article/details/82873731 不选择使用Lucene的6大原因? 原文 ...
- 硬件开发笔记(六): 硬件开发基本流程,制作一个USB转RS232的模块(五):创建USB封装库并关联原理图元器件
前言 有了原理图,可以设计硬件PCB,在设计PCB之间还有一个协同优先动作,就是映射封装,原理图库的元器件我们是自己设计的.为了更好的表述封装设计过程,本文描述了一个创建USB封装,创建DIP焊盘 ...
- vscode添加gitbash终端方法
1.打开vscode 2.点击文件,ctrl+, 3.搜索shell windows { ... // 添加如下代码 "terminal.integrated.profiles.window ...
