Loading 用户体验 - 加载时避免闪烁

news2025/6/8 13:27:57

🍓 前言

在切换详情页中有这么一个场景,点击上一条,会显示上一条的详情页,同理,点击下一条,会显示下一条的详情页。

伪代码如下所示:

我们定义了一个 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

这样,就可以实现丝滑的切换详情页了,希望对你有帮助~

如果你有其他的想法,欢迎到评论区一起讨论。

最后

整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享

部分文档展示:



文章篇幅有限,后面的内容就不一一展示了

有需要的小伙伴,可以点下方卡片免费领取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/152074.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TensorRT部署YOLOv5(03)-TensorRT介绍

TensorRT是本专栏中最重要的内容,绝大多数内容将围绕TensorRT来展开,本文对TensorRT进行一个基本的介绍,让不熟悉TensorRT的读者能够对TensorRT是什么,如何使用它有一个较为全面的认识 Nvidia TensorRT是一个用于Nvidia GPU上高性能机器学习推理的SDK,对开发者屏蔽了模型…

到底什么样的 REST 才是最佳 REST?

说起 REST API&#xff0c;小伙伴们多多少少都有听说过&#xff0c;但是如果让你详细介绍一下什么是 REST&#xff0c;估计会有很多人讲不出来&#xff0c;或者只讲出来其中一部分。 今天松哥就来和大家一起来聊一聊到底什么是 REST&#xff0c;顺便再来看下 Spring HATEOAS 的…

[算法与数据结构]——并查集

目录 1. 概论 定义&#xff1a; 主要构成&#xff1a; 作用&#xff1a; 2. 并查集的现实意义 故事引入&#xff1a; 数据结构的角度来看&#xff1a; 3. find( )函数的定义与实现 故事引入&#xff1a; 实现&#xff1a; 4. join( )函数的定义与实现 故事引入&#xff1a; 实现…

c++11 标准模板(STL)(std::forward_list)(三)

定义于头文件 <forward_list> template< class T, class Allocator std::allocator<T> > class forward_list;(1)(C11 起)namespace pmr { template <class T> using forward_list std::forward_list<T, std::pmr::polymorphic_…

Apollo本地快速部署

GitHub项目地址 Gitee项目地址 Apollo&#xff08;阿波罗&#xff09;是携程框架部门研发的分布式配置中心&#xff0c;能够集中化管理应用不同环境、不同集群的配置&#xff0c;配置修改后能够实时推送到应用端&#xff0c;并且具备规范的权限、流程治理等特性&#xff0c;适…

【计算机网络-数据链路层】介质访问控制协议(MAC协议)

文章目录1 静态划分信道——信道划分 MAC 协议1.1 频分多路复用&#xff08;FDM&#xff09;——“并行”1.2 时分多路复用&#xff08;TDM&#xff09;——“并发”1.2.1 同步时分多路复用1.2.2 异步时分多路复用1.3 波分多路复用&#xff08;WDM&#xff09;1.4 码分多路复用…

数据结构进阶 AVL树

作者&#xff1a;小萌新 专栏&#xff1a;数据结构进阶 作者简介&#xff1a;大二学生 希望能和大家一起进步&#xff01; 本篇博客简介&#xff1a;介绍高阶数据结构:AVL树 AVL树AVL树的概念AVL树节点类的定义AVL树的插入AVL树的旋转左单旋右单旋左右双旋右左单旋AVL树的验证…

多线程适用接口及常见类

日升时奋斗&#xff0c;日落时自省 目录 1、Callable接口 1.1、Callable方式 1.2、非Callable方式 2、JUC&#xff08;java.util.concurrent&#xff09;的常见类 2.1、ReentrantLock 2.2、信号量Semaphore 2.3、CountDownLatch 3、线程安全的集合类 3.1、多线程使用A…

【SpringMVC】使用SpringMVC处理JSON格式的数据

目录 一、前言 二、ResponseBody 三、RequestBody 四、HttpMessageConverter 相关文章&#xff08;可以关注我的SpringMVC专栏&#xff09; SpingMVC专栏SpingMVC专栏一、前言我们在使用Servlet处理前端请求&#xff0c;使用Json格式的数据&#xff0c;通常引入外部提供的一些…

拉伯证券|利好来了,145万手封涨停!低位+低市值“无主”股揭秘

二三四五或将完毕多年“无主”局势。 周末大消息不断。蚂蚁集团1月7日在官网发布关于持续完善公司管理的公告。公告显现&#xff0c;蚂蚁集团调整首要股东投票权&#xff0c;强化与阿里巴巴集团的隔离&#xff0c;阿里巴巴开创人马云抛弃了对蚂蚁集团的实践操控权&#xff0c;蚂…

JavaScript 原型链

文章目录原型链本质 - 对象间的关联关系instanceof 和 isPrototypeOf__proto__的大致实现委托原型链 原型链就是一系列对象的链接。通常来说&#xff0c;这个链接的作用是&#xff1a;如果在对象上没有找到需要的属性或者方法引用&#xff0c;引擎就会继续在[[Prototype]]关联的…

餐饮业数字化提速,OceanBase助海底捞变身“云上捞”

在海底捞火锅智慧餐厅&#xff0c;你会看到忙得团团转的机械臂和传菜机器人&#xff0c;顾客在智能点餐系统上下单&#xff0c;机械臂和传菜机器人就会着手备菜、传菜、上菜&#xff0c;服务人员则有更多时间专心应答顾客的各种询问。这种新模式&#xff0c;也为海底捞家喻户晓…

Springboot新手开发 基本总结

前言&#xff1a; &#x1f44f;作者简介&#xff1a;我是笑霸final&#xff0c;一名热爱技术的在校学生。 &#x1f4dd;个人主页&#xff1a;个人主页1 || 笑霸final的主页2 &#x1f4d5;系列专栏&#xff1a;后端专栏 &#x1f4e7;如果文章知识点有错误的地方&#xff0c;…

网络实验之HSRP协议

一、HSRP协议简介 HSRP&#xff1a;热备份路由器协议&#xff08;HSRP&#xff1a;Hot Standby Router Protocol&#xff09;&#xff0c;是cisco平台一种特有的技术&#xff0c;是cisco的私有协议。该协议中含有多台路由器&#xff0c;对应一个HSRP组。该组中只有一个路由器承…

mac自定义环境变量,mac自定义python变量,自定义通用变量(任意名字)

文章目录几个常用的命令工作原理知道原理后已经有了环境变量没有该环境变量几个常用的命令 功能命令查看当前环境变量echo $PATH编辑文件&#xff08;注&#xff1a;这里是直接编辑bash_profile&#xff09;vi ~/.bash_profile编辑i退出编辑esc回到命令&#xff08;用命令来保…

Apollo实现cron语句的热配置

GitHub项目地址 Gitee项目地址 Apollo&#xff08;阿波罗&#xff09;是携程框架部门研发的分布式配置中心&#xff0c;能够集中化管理应用不同环境、不同集群的配置&#xff0c;配置修改后能够实时推送到应用端&#xff0c;并且具备规范的权限、流程治理等特性&#xff0c;适…

stl algorithms 算法

所有泛型算法&#xff0c;除了少数例外&#xff0c;前两个参数均为一组iterator&#xff0c;用来标识欲遍历容器元素的范围&#xff0c;范围从第一个iterator所指位置开始&#xff0c;至第二个iterator所指位置&#xff08;并不包括&#xff09;结束 如 int arr[3]{1,2,3} ve…

管理客户信息并非易事

客户信息是企业的重要资产&#xff0c;是企业日积累月的价值沉淀&#xff0c;管理客户信息对于企业来说并不是一件容易的事&#xff0c;只有妥善管理客户信息&#xff0c;才能为企业创造更多价值。前言众所周知&#xff0c;客户信息是企业的重要资产&#xff0c;是企业日积累月…

rtu遥测终端机应用及安装介绍

1、设备介绍 设备集遥测终端机功能和视频录像机功能为一体&#xff0c;融合先进的3G/4G/WIFI通信技术、实现水文/水资源/环保212/TCP Modbus/MQTT等数据的采集、视频、图像存储、显示、控制、报警及传输等智能值守功能。 2、设备应用方向 本设备可广泛适用于带视频监测的水…

Jenkins 构建过程中提示 GPG 错误

错误信息如下&#xff1a;[INFO] --- maven-gpg-plugin:3.0.1:sign (sign-artifacts) rets-io --- gpg: no default secret key: No secret keygpg: signing failed: No secret key这个问题的原因应该是我们最近把我们的项目发布到中央 Maven 仓库中&#xff0c;但是发布项目到…