【Day24】反馈元件 - Spin

元件介绍

Spin 是一个载入状态元件,当页面正在处理非同步行为,或需要让用户等待的作业时,用来显示以缓解用户等待的焦虑。

参考设计 & 属性分析

一个简单的 loading 状态,只需要拿一个适合的 icon 来旋转就可以了

Spin 元件虽然相对单纯,但是也是有一些做法可以让我们使用起来更为方便。

内容加载中

Antd 让 Spin 也能够拥有 children 元件,这样的做法可以让载入的同时也能够看到内容。

<Spin spinning={this.state.spinning}>
  {children}
</Spin>

假设一个情境是我们需要编辑某个资料 table 或是留言板,每做一个小动作就要打一次 API ,每次打 API 就会整页白掉变成 loading 状态,载入完之後再重新显示资料,如果这些操作很频繁的话,那这样的使用者体验必定很不好。

因此若这一页的内容正在载入中,但是又不想要整个区块换成载入状态,或许用这种内容加载的方式也是一种选择。

组建大小

透过一个 props 来决定 Spin 的大小,也是很基本却很重要的性质之一,毕竟把 Spin 放在一颗按钮里面,跟把 Spin 放在整个页面的中间,所需要的 size 很可能会不同。Antd 这边只提供可选的选项,分别是 small, default, large

indicator

随着网站的不同,为了配合设计师的设计,我们也需要能够随心所欲的更改 Spin 的 icon。当然一个网站应该不太会设计成每一页的 Spin 都长不一样,不过 Antd 是为了让广大的开发者能够使用,因此会有这个需求,如果是自己的网站要使用,应该一到两种应该就很够用了。

介面设计

属性 说明 类型 默认值
className 客制化样式 string
isLoading 是否载入中 boolean false
indicator 自定义载入符号 ReactNode, string <CircularProgress />
children 内容 ReactNode, string

元件实作

Spin 最简单的原理就是根据 是否加载中 这个 boolean 来判断是否要显示载入符号,其他的部分就是根据不同情境来调整样式。

那今天我们要实作的情境有两种,一种是 Spin 直接当作加载元件,一种是 Spin 会成为一个加载容器,所以我的想法如下:

const Spin = ({
  indicator, isLoading, children, ...props
}) => {

  if (!children) {
    return indicator;
  }
  return (
    <SpinContainer {...props}>
      {children}
      {isLoading && indicator}
    </SpinContainer>
  );
};

主要核心的想法到目前为止就已经差不多了,其他的部分就是样式上的调整。

当我们要直接 show 出一个 Spin:

<Spin />

我预设的样式就会是这样:

Custom Indicator

当然这个样式我偷懒是直接拿 MUI 的 <CircularProgress /> 来使用。

如果我们要客制化样式也是可以的,假设我们今天拿到一个 SVG 档,我把它转换成 JS:

export const SpinnerIcon = (props) => (
  <svg
    aria-hidden="true"
    focusable="false"
    data-prefix="fas"
    data-icon="spinner"
    role="img"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 512 512"
    className="svg-inline--fa fa-spinner fa-w-16 fa-3x"
    {...props}
  >
    <path fill="currentColor" d="M304 48c0 ...." className="" />
  </svg>
);

然後我就能够自己做一个 indicator,当作 props 传入,或是直接做进 Spin 元件当作预设的 indicator,我这边以 props 传入为例:

import styled, { keyframes } from 'styled-components';

const rotateAnimation = keyframes`
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
`;

const RotateContainer = styled.div`
  width: 40px;
  height: 40px;
  animation: ${rotateAnimation} 1000ms ease-in-out infinite;
`;

<Spin
  indicator={(
    <RotateContainer>
      <FaSpinner />
    </RotateContainer>
  )}
/>

就会是这样:

Spin As Container

再来就是我们把 Spin 当作一个容器,让他的 children 有载入状态,使用起来如下:

<Spin isLoading>
  <Content />
</Spin>

我的作法提供给大家参考:

<SpinContainer {...props}>
  {children}
  {isLoading && (
    <>
      <Mask />
      <Indicator
        ref={indicatorRef}
        className="spin__indicator"
        $indicatorSize={indicatorSize}
      >
        {indicator}
      </Indicator>
    </>
  )}
</SpinContainer>

主要的想法就是 <SpinContainer /> 这一层设为 position: relative;,然後里面的 <Indicator /> 设为 position: absolute; 让他能够盖在内容上面,并做置中的定位。

然後可以看到我这边多给一个 <Mask /> 元件,主要是如果直接 show 出 indicator 叠在内容上的话,会显得有点缭乱,所以我想要弱化载入中时内容的显眼度,来凸显加载中的样式,以下就是今天的成果展示:


Spin 元件原始码:
Source code

Storybook:
Spin


<<:  [Java Day26] 6.3. super

>>:  【Day22】人力资源篇-Recruitment

Day_08 : 让 Vite 来开启你的Vue 之 Vite 核心 HMR

Hi Dai Gei Ho~ 我是Winnie~ 今天是第八天,中秋连假到数结束第二天~ 在开始说明...

Day 27 - ROS 树莓派光达履带小车实作 (1)

终於进入小车实作啦~~~~上个连假笔者本来就要来写的,结果拖到这个连假才有空来玩车车XD 首先介绍笔...

Day 27 : 快速排序法 Quick Sort

今天要来实作的快速排序法Quick Sort,虽然不是最佳的(以前学习的时候看到他的名字以为它会是最...

番外篇(2)一起来做 To Do List!- 实作篇(1)

上一篇先介绍运用的知识点,这篇会着重在实作时的心路历程...不是啦,是怎麽把这个网页写出来的。先上成...

JS [笔记] overflow 致 滑动卡顿不顺畅(IOS)

参阅: https://codertw.com/%E5%89%8D%E7%AB%AF%E9%96%8...