当前位置: 首页 > news >正文

记录--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 用户体验 - 加载时避免闪烁

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 在切换详情页中有这么一个场景&#xff0c;点击上一条&#xff0c;会显示上一条的详情页&#xff0c;同理&#xff0c;点击下一条&#xff0c;会显示下一条的详情页。 伪代码如下所示&#xff1a; 我们…...

系统架构设计专业技能 · 软件工程之软件测试与维护(六)【系统架构设计师】

系列文章目录 系统架构设计专业技能 网络规划与设计&#xff08;三&#xff09;【系统架构设计师】 系统架构设计专业技能 系统安全分析与设计&#xff08;四&#xff09;【系统架构设计师】 系统架构设计高级技能 软件架构设计&#xff08;一&#xff09;【系统架构设计师…...

基于亚奈奎斯特采样和SOMP算法的平板脉冲响应空间插值matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ...................................................................... %fine regular gr…...

柏睿向量数据库Rapids VectorDB赋能企业级大模型构建及智能应用

ChatGPT的问世,在为沉寂已久的人工智能重新注入活力的同时,也把长期默默无闻的向量数据库推上舞台。今年4月以来,全球已有4家知名向量数据库公司先后获得融资,更加印证了向量数据库在AI大模型时代的价值。 什么是向量数据库? 在认识向量数据库前,先来了解一下最常见的关…...

装备合成(牛客)

登录—专业IT笔试面试备考平台_牛客网 题目&#xff1a; 牛牛有x件材料a和y件材料b&#xff0c;用2件材料a和3件材料b可以合成一件装备&#xff0c;用4件材料a和1件材料b也可以合成一件装备。牛牛想要最大化合成的装备的数量&#xff0c;于是牛牛找来了你帮忙。 分析&#xff…...

C语言学习之一级指针二级指针

一级指针&#xff1a;内存中每个字节都有一个编号&#xff0c;这个编号就是指针&#xff0c;也称作地址&#xff1b;专门用来存储地址的变量&#xff0c;就是指针变量&#xff1b;定义一级指针变量并初始化&#xff1a; 数据类型 *指针变量名 &普通变量名; 数据类型 *指针…...

【腾讯云 Cloud Studio 实战训练营】使用 Cloud Studio 快速构建 Vue + Vite 完成律师 H5 页面

【腾讯云 Cloud Studio 实战训练营】使用 Cloud Studio 快速构建 Vue Vite 完成律师 H5 页面 前言一、基本介绍1.应用场景2.产品优势 二、准备工作1.注册 Cloud Studio2.进入 Vue 预置开发环境 三、使用 Cloud Studio 快速构建 Vue Vite 完成律师 H5 页面1.安装相关依赖包2.主…...

Vim常用指令

Vim常用指令 Vim是一个强大的文本编辑器&#xff0c;它在命令行界面下工作&#xff0c;拥有丰富的功能和快捷键。本文将介绍一些常用的Vim指令&#xff0c;帮助您更高效地使用Vim编辑器。 基本操作 以下是一些基本的Vim操作指令&#xff1a; i&#xff1a;进入插入模式&…...

24届近3年青岛理工大学自动化考研院校分析

今天给大家带来的是青岛理工大学控制考研分析 满满干货&#xff5e;还不快快点赞收藏 一、青岛理工大学 学校简介 青岛理工大学是一所以工为主&#xff0c;土木建筑、机械制造、环境能源学科特色鲜明&#xff0c;理工经管文法艺等学科协调发展的多科性大学。是国家首批地方…...

进入现代云技术的世界-APIGateway、ServiceMesh、OpenStack、异步化框架、云原生框架、命令式API与声明式API

目录 APIGateway Service Mesh OpenStack 异步化框架 云原生框架 命令式API与声明式API APIGateway API网关&#xff08;API Gateway&#xff09;是一个服务器——充当了客户端和内部服务之间的中间层。API网关负责处理API请求&#xff0c;将客户端的请求路由到相应的后端…...

Macbook 终端 git 命令补全和提示

Mac OS自带的终端&#xff0c;用起来虽然有些不太方便&#xff0c;界面也不够友好&#xff0c;关键是在windows上用习惯了自动补全功能&#xff0c;在Mac上一个个的拼写单词是真的难受&#xff0c;逼着我记英文单词。 经过一天的磨合&#xff0c;我实在忍不了&#xff0c;在网上…...

2024考研408-计算机网络 第六章-应用层学习笔记

文章目录 前言一、网络应用模型1.1、认识应用层功能和特点1.2、网络应用层模型&#xff1a;1.2.1、客户/服务器&#xff08;C/S&#xff09;模型1.2.2、P2P模型 二、DNS系统2.1、认识DNS与IP地址的关系2.2、DNS解析的大致流程2.3、域名的分类2.4、域名服务器的分类2.5、域名解析…...

使用阿里云服务器部署和使用GitLab

本文阿里云百科分享使用阿里云服务器部署和使用GitLab&#xff0c;GitLab是Ruby开发的自托管的Git项目仓库&#xff0c;可通过Web界面访问公开的或者私人的项目。本教程介绍如何部署和使用GitLab。 目录 准备工作 部署GitLab环境 使用GitLab 登录GitLab 生成密钥对文件并…...

React入门学习笔记3

事件处理 通过onXxx属性指定事件处理函数(注意大小写) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件——为了更好的兼容性 eg&#xff1a;οnclick》onClickReact中的事件是通过事件委托方式处理的(委托给组件最外层的元素)——为了更高效通过event.target得到发生…...

从零开始理解Linux中断架构(25)中断运行全景实例

前面我们基本理解了软中断处理的基本框架,为了对中断调用有一个全景的直观感受,我们在网卡驱动程序的中断函数dump_stack,观看一下各种情况下的软中断调用call Stack的情况。 (1)ksoftirqd处理软中断的情况 有线以太网卡NAPI轮询的调用栈 [ 106.374117] Hardware name: K…...

go-zero 是如何实现计数器限流的?

原文链接&#xff1a; 如何实现计数器限流&#xff1f; 上一篇文章 go-zero 是如何做路由管理的&#xff1f; 介绍了路由管理&#xff0c;这篇文章来说说限流&#xff0c;主要介绍计数器限流算法&#xff0c;具体的代码实现&#xff0c;我们还是来分析微服务框架 go-zero 的源…...

【考研复习】24王道数据结构课后习题代码|第3章栈与队列

文章目录 3.1 栈3.2 队列3.3 栈和队列的应用 3.1 栈 int symmetry(linklist L,int n){char s[n/2];lnode *pL->next;int i;for(i0;i<n/2;i){s[i]p->data;pp->next;}i--;if(n%21) pp->next;while(p&&s[i]p->data){i--;pp->next;}if(i-1) return 1;…...

java中excel文件下载

1、System.getProperty(user.dir) 获取的是启动项目的容器位置 2、 Files.copy(sourceFile.toPath(), destinationFile.toPath(), StandardCopyOption.REPLACE_EXISTING); StandardCopyOption.REPLACE_EXISTING 来忽略文件已经存在的异常&#xff0c;如果存在就去覆盖掉它Sta…...

29 | 广州美食店铺数据分析

广州美食店铺数据分析 一、数据分析项目MVP加/价值主张宣言 随着经济的快速发展以及新媒体的兴起,美食攻略、美食探店等一系列东西进入大众的眼球,而人们也会在各大平台中查找美食推荐,因此本项目做的美食店铺数据分析也是带有可行性的。首先通过对广东省的各市美食店铺数量…...

fastApi基础

1、fastApi简介 官方文档&#xff1a;https://fastapi.tiangolo.com/ 源码&#xff1a; https://github.com/tiangolo/fastapi 2、环境准备 安装python 安装pycharm 安装fastAPI 安装 uvicorn 查看已经安装的第三方库&#xff1a;pip list 查看pip 配置信息&#xff1a;pip co…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...

tauri项目,如何在rust端读取电脑环境变量

如果想在前端通过调用来获取环境变量的值&#xff0c;可以通过标准的依赖&#xff1a; std::env::var(name).ok() 想在前端通过调用来获取&#xff0c;可以写一个command函数&#xff1a; #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...

网页端 js 读取发票里的二维码信息(图片和PDF格式)

起因 为了实现在报销流程中&#xff0c;发票不能重用的限制&#xff0c;发票上传后&#xff0c;希望能读出发票号&#xff0c;并记录发票号已用&#xff0c;下次不再可用于报销。 基于上面的需求&#xff0c;研究了OCR 的方式和读PDF的方式&#xff0c;实际是可行的&#xff…...

深度解析云存储:概念、架构与应用实践

在数据爆炸式增长的时代&#xff0c;传统本地存储因容量限制、管理复杂等问题&#xff0c;已难以满足企业和个人的需求。云存储凭借灵活扩展、便捷访问等特性&#xff0c;成为数据存储领域的主流解决方案。从个人照片备份到企业核心数据管理&#xff0c;云存储正重塑数据存储与…...