《精通react/vue组件设计》之5分钟教你实现一个极具创意的加载(Loading)组件

前言

本文是笔者写组件设计的第八篇文章, 今天带大家用5分钟实现一个极具创意的加载(loading)组件.涉及的核心知识点主要是css3相关特性, 如果大家非常熟悉,可直接跳过介绍直接看正文.

时刻问自己:是否具备创造力?

[笔记]前端组件的一般分类:

  • 通用型组件: 比如Button, Icon等.
  • 布局型组件: 比如Grid, Layout布局等.
  • 导航型组件: 比如面包屑Breadcrumb, 下拉菜单Dropdown, 菜单Menu等.
  • 数据录入型组件: 比如form表单, Switch开关, Upload文件上传等.
  • 数据展示型组件: 比如Avator头像, Table表格, List列表等.
  • 反馈型组件: 比如Progress进度条, Drawer抽屉, Modal对话框等.
  • 其他业务类型

所以我们在设计组件系统的时候可以参考如上分类去设计,该分类也是antd, element, zend等主流UI库的分类方式.

如果对于react/vue组件设计原理不熟悉的,可以参考我的之前写的组件设计系列文章:

笔者已经将组件库发布到npm上了, 大家可以通过npm安装的方式体验组件.

正文

在开始组件设计之前希望大家对css3和js有一定的基础,并了解基本的react/vue语法.我们先看看实现后的组件效果: 因为动图体积太大,就不给大家传gif了,接下来我们具体分析一下该组件的特点.

1. 组件设计思路

按照之前笔者总结的组件设计原则,我们第一步是要确认需求. 首先我们设计的不是后台管理系统专用的加载动画,而是作为一个C端产品的功用型加载动画.我们都知道加载动画的作用是:在用户等待网页时能看到有用的信息,比如网站介绍,引导, 公司信息等,缓解用户焦虑. 作为一名产品经理或者用户体验师, 这种个性化的加载体验效果往往是更好的.

而加载动画一般会分为策略型加载动画和通用加载动画,通用加载动画我就不说了,大家平时做的系统大部分应该都是通用型加载动画. 我这里介绍一下策略型加载动画: 策略型加载动画往往用在C端产品或者系统中,用来为用户提供更多的引导信息, 当用户首次访问系统或者网站时, 由于某种主动型引导(网站在加载时或者切换页面时故意给用户看到的加载信息)或者环境原因(网络,带宽限制导致的加载过慢,此时出现加载动画), 这些加载信息往往带有某种用途,比如对于个人博客网站, 这个加载动画可以是博主的介绍,博主的宣传信息,github地址等, 对于企业来说,可能是某个新功能的介绍, 网站服务信息的介绍,联系方式等.

在了解完以上背景后, 我们来看看组件设计的线框图: 对于react选手来说,如果没用typescript,建议大家都用PropTypes, 它是react内置的类型检测工具,我们可以直接在项目中导入. vue有自带的属性检测方式,这里就不一一介绍了.

通过以上需求分析, 其实一个加载动画非常简单, 不会涉及到太多功能, 主要在于css3动画的使用. 具体属性有:

  • 加载动画出现时的加载文本
  • 控制加载状态的state

接下来我们就来看看具体实现.

2. 基于react实现一个Loading组件

因为该组件不会涉及到太多的js代码,主要是html和css,所以我们直接先构建组件的结构:

1/** 2 * 骨架屏组件(SEO) 3 * @param {isLoading} bool 加载状态 4 * @param {loadingText} string 加载时的加载文本 5 */ 6export default function Skeleton(props) { 7 let { isLoading = true, loadingText = '正在为您疯狂加载...' } = props 8 return isLoading ? <div className={styles.skeletonWrap}> 9 <div className={styles.skeletonContent} data-loadingText={loadingText}> 10 自定义的引导内容 11 </div> 12 </div> : null 13}

自定义的引导内容这里我就不介绍了, 主要根据不同的网站性质灵活配置.我主要介绍加载动画部分, 其实原理也很简单, 我们在skeletonContent元素上使用一个::after伪对象来实现窗帘动画即可.

在实现动画前大家最好对关键帧动画有所了解,我相信大家都比较了解. 这种关窗帘动画一种实现方式就是通过控制元素宽度, 从0到100%, 然后添加适当的要是优化即可. 动画的代码如下:

1@keyframes spread { 2 0% { 3 width: 0; 4 } 5 100% { 6 width: 100%; 7 } 8}

我们只需要在::after里直接这样使用就好了:

1&::after { 2 animation: spread 18s 3s infinite; 3}

这样动画已经做完了, 但是为了让动画更完整,我们还要考虑一个事实, 如果窗帘宽度从0慢慢变化的过程中, 加载动画的文字一直保持一个颜色会很生硬, 如下图: 所以说作为一个好的交互设计来说, 要让交互体验更顺畅,这里提供一种方式,就是加载的文本在窗帘宽度变化的同时,文字的透明度从0变化到1,这样就会柔和很多, 所以动画可以这么改:

1&::after { 2 color: rgba(255, 255, 255, 0); 3 animation: spread 18s infinite; 4} 5@keyframes spread { 6 0% { 7 width: 0; 8 color: rgba(255, 255, 255, 0); 9 } 10 100% { 11 width: 100%; 12 color: rgba(255, 255, 255, 1); 13 } 14}

效果如下:

最后我们来实现loadingText. 这块也涉及到一个知识点, 因为加载文本其实主要是用来修饰元素的,并没有太多的语义化场景, 所以我们会放在::after伪对象的content里, 但是一般content是在css里写的,那么如何实现动态文本呢? 我们这里就要采用css的属性内容这个api. content不仅仅可以接收一个字符串,还可以接收attr这个关键字,关键字里面的内容是元素的自定义属性, 比如:

1<div data-tip="loading"></div>

那么我们在css里可以通过这种方式直接使用data-tip属性的值:

1div::after{ 2 content: attr(data-tip) 3}

通过以上的方式我们可以在::after里直接拿到data-tip的内容了, content其实还有更多的功能,比如用纯css实现一个计数器,大家可以研究学习一下.

这样,我们的Loading组件就完成了, 还有一个问题是我代码里的组件命名,为什么叫骨架屏呢?其实我们只要改变内容结构, 它立马就可以变成一个骨架屏,所以命名这块可以按照实际需求来确定.

3. 健壮性支持, 我们采用react提供的propTypes工具:

1import PropTypes from 'prop-types' 2// ... 3Skeleton.propTypes = { 4 isLoading: PropTypes.bool, 5 loadingText: PropTypes.string 6}

组件完整css代码如下:

1.skeletonWrap { 2 position: absolute; 3 left: 0; 4 right: 0; 5 top: 0; 6 bottom: 0; 7 background-color: rgba(0,0,0, .6); 8 .skeletonContent { 9 position: relative; 10 margin: 200px auto 0; 11 padding: 20px; 12 width: 800px; 13 display: flex; 14 align-items: center; 15 border-radius: 8px; 16 overflow: hidden; 17 background-color: #fff; 18 &::after { 19 content: '正在为您疯狂加载...'; 20 position: absolute; 21 top: 0; 22 left: 0; 23 display: flex; 24 align-items: center; 25 justify-content: center; 26 width: 0; 27 height: 100%; 28 border-right: 2px solid #ccc; 29 box-shadow: 0 0 8px #000; 30 background: #096; 31 color: rgba(255, 255, 255, 0); 32 font-size: 24px; 33 white-space: nowrap; 34 animation: spread 18s 3s infinite; 35 } 36 @keyframes spread { 37 0% { 38 width: 0; 39 color: rgba(255, 255, 255, 0); 40 } 41 100% { 42 width: 100%; 43 color: rgba(255, 255, 255, 1); 44 } 45 } 46 47 .imgBox { 48 margin-right: 20px; 49 width: 400px; 50 .img { 51 width: 100%; 52 height: 200px; 53 background-color: #ccc; 54 } 55 img { 56 width: 100%; 57 } 58 } 59 .rightBox { 60 flex: 1; 61 .tit { 62 margin-top: 8px; 63 margin-bottom: 8px; 64 font-size: 22px; 65 } 66 .labelWrap { 67 span { 68 margin: 3px; 69 display: inline-block; 70 font-size: 12px; 71 padding: 2px 6px; 72 border-radius: 3px; 73 color: #fff; 74 background-color: #58bd6b; 75 } 76 } 77 .desc { 78 color: rgb(44, 44, 44); 79 font-size: 14px; 80 } 81 } 82 } 83}

关于代码中的css module和classnames的使用大家可以自己去官网学习,非常简单.如果不懂的可以在评论区提问,笔者看到后会第一时间解答.

4 使用Skeleton组件

我们可以通过如下方式使用它:

1<Skeleton loadingText="玩命加载中..." />

笔者已经将实现过的组件发布到npm上了,大家如果感兴趣可以直接用npm安装后使用,方式如下:

1npm i @alex_xu/xui 2 3// 导入xui 4import { 5 Button, 6 Skeleton, 7 Empty, 8 Progress, 9 Tag, 10 Switch, 11 Drawer, 12 Badge, 13 Alert 14} from '@alex_xu/xui'

该组件库支持按需导入,我们只需要在项目里配置babel-plugin-import即可,具体配置如下:

1// .babelrc 2"plugins": [ 3 ["import", { "libraryName": "@alex_xu/xui", "style": true }] 4]

npm库截图如下:

最后

后续笔者将会继续实现

  • modal(模态窗),
  • badge(徽标),
  • table(表格),
  • tooltip(工具提示条),
  • Skeleton(骨架屏),
  • Message(全局提示),
  • form(form表单),
  • switch(开关),
  • 日期/日历,
  • 二维码识别器组件

等组件, 来复盘笔者多年的组件化之旅.

如果想获取组件设计系列完整源码, 或者想学习更多H5游戏, webpacknodegulpcss3javascriptnodeJScanvas数据可视化等前端知识和实战,欢迎在公号《趣谈前端》加入我们的技术群一起学习讨论,共同探索前端的边界。

更多推荐

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )