LoadingOverlay 加载遮罩

盖在一块区域上的加载指示,稍作等待再显示。

本周新增

收藏 128 次,评论 32 条,新粉丝 12 位。

'use client'

import { useState } from 'react'
import { Button, Card, LoadingOverlay, Stack, Text } from '@hina-ui/react'

export default function Demo() {
  const [loading, setLoading] = useState(false)

  async function reload() {
    setLoading(true)
    await new Promise(resolve => setTimeout(resolve, 1800))
    setLoading(false)
  }

  return (
    <Stack gap="sm" align="start">
      <Card className="relative w-96">
        <Stack gap="xs">
          <Text weight="medium">本周新增</Text>
          <Text tone="muted" size="sm">
            收藏 128 次,评论 32 条,新粉丝 12 位。
          </Text>
        </Stack>
        <LoadingOverlay visible={loading} text="正在刷新" />
      </Card>
      <Button variant="outline" tone="neutral" disabled={loading} onClick={reload}>
        刷新
      </Button>
    </Stack>
  )
}
tsx

用法

import { LoadingOverlay } from '@hina-ui/react'
ts

加载遮罩铺满最近的定位容器,把内容压淡并放上加载指示;把它放进带 relative 的容器里,用 visible 控制显隐。它一出现就挡住下面的交互,但要过一小段时间才真正显示出来,很快结束的请求不会闪一下。

这块内容在加载期间被压淡,也不能点击。

'use client'

import { useState } from 'react'
import { Card, LoadingOverlay, Stack, Switch, Text } from '@hina-ui/react'

export default function Demo() {
  const [loading, setLoading] = useState(true)

  return (
    <Stack gap="md" align="start">
      <Switch checked={loading} onCheckedChange={setLoading}>
        显示遮罩
      </Switch>
      <Card className="relative w-96">
        <Text>这块内容在加载期间被压淡,也不能点击。</Text>
        <LoadingOverlay visible={loading} />
      </Card>
    </Stack>
  )
}
tsx

示例

文字

text 在加载指示下方显示一行说明,同时作为指示的无障碍名称。

正在把草稿同步到云端。

import { Card, LoadingOverlay, Text } from '@hina-ui/react'

export default function Demo() {
  return (
    <Card className="relative h-40 w-96">
      <Text>正在把草稿同步到云端。</Text>
      <LoadingOverlay visible text="正在同步" size="lg" />
    </Card>
  )
}
tsx

延时与最短停留

delay 是 visible 变为真之后等多久再淡入,默认 300 毫秒,这段时间内撤掉它什么都不会出现;设为 0 立即显示,适合用户主动触发、明知要等的操作。minVisible 是显示之后至少停留多久,默认 300 毫秒,避免刚出现就消失的闪动;设为 0 则请求一结束就淡出。

默认延时下快请求看不到遮罩;打开「立即显示」后两种请求都会看到。

'use client'

import { useState } from 'react'
import { Button, Card, Inline, LoadingOverlay, Stack, Switch, Text } from '@hina-ui/react'

export default function Demo() {
  const [loading, setLoading] = useState(false)
  const [immediate, setImmediate] = useState(false)
  const [result, setResult] = useState('')

  async function request(ms: number) {
    setLoading(true)
    setResult('')
    await new Promise(resolve => setTimeout(resolve, ms))
    setLoading(false)
    setResult(`${ms} 毫秒后返回`)
  }

  return (
    <Stack gap="md" align="start">
      <Inline gap="md" align="center">
        <Button variant="outline" tone="neutral" disabled={loading} onClick={() => request(150)}>
          快请求
        </Button>
        <Button variant="outline" tone="neutral" disabled={loading} onClick={() => request(1500)}>
          慢请求
        </Button>
        <Switch checked={immediate} onCheckedChange={setImmediate}>
          立即显示
        </Switch>
      </Inline>
      <Card className="relative w-96">
        <Text>{result || '默认延时下快请求看不到遮罩;打开「立即显示」后两种请求都会看到。'}</Text>
        <LoadingOverlay visible={loading} delay={immediate ? 0 : undefined} />
      </Card>
    </Stack>
  )
}
tsx

自定义内容

children 替换薄面里的加载指示与文字,可以放品牌形象、进度说明等自己的内容;薄面、延时与挡住交互的行为不变。自定义内容里应当有 role="status" 或者等价的文字,辅助技术才知道这里在等待。

书架里的内容正在整理。

星见书音抱着书跑过来

书音正在整理书架,稍等一下。

import { Card, Image, LoadingOverlay, Stack, Text } from '@hina-ui/react'

export default function Demo() {
  return (
    <Card className="relative h-56 w-96">
      <Text>书架里的内容正在整理。</Text>
      <LoadingOverlay visible delay={0}>
        <Stack gap="xs" align="center" role="status">
          <Image
            src="/mascot/run.gif"
            alt="星见书音抱着书跑过来"
            ratio={1}
            fit="contain"
            skeleton={false}
            eager
            lazy={false}
            className="size-28"
          />
          <Text size="sm" tone="muted">
            书音正在整理书架,稍等一下。
          </Text>
        </Stack>
      </LoadingOverlay>
    </Card>
  )
}
tsx

覆盖整个视口

fixed 让遮罩覆盖整个视口,用于整页级别的等待。

'use client'

import { useState } from 'react'
import { Button, Image, LoadingOverlay, Stack, Text } from '@hina-ui/react'

export default function Demo() {
  const [loading, setLoading] = useState(false)

  async function reload() {
    setLoading(true)
    await new Promise(resolve => setTimeout(resolve, 2000))
    setLoading(false)
  }

  return (
    <>
      <Button variant="outline" tone="neutral" onClick={reload}>
        覆盖整页两秒
      </Button>
      <LoadingOverlay visible={loading} fixed>
        <Stack gap="xs" align="center" role="status">
          <Image
            src="/mascot/run.gif"
            alt="星见书音抱着书跑过来"
            ratio={1}
            fit="contain"
            skeleton={false}
            eager
            lazy={false}
            className="size-32"
          />
          <Text size="sm" tone="muted">
            正在切换账号
          </Text>
        </Stack>
      </LoadingOverlay>
    </>
  )
}
tsx

行为

  • visible 变为真时立即挡住区域内的交互,等待 delay 后淡入;变为假时淡出并移除,显示未满 minVisible 则补足后再淡出。
  • 延时内撤掉不会闪现。
  • 遮罩不停止页面滚动,也不接管焦点;需要打断整页任务时用 Dialog 的锁定。

无障碍

  • 加载指示是 role="status",名称取自 text,没有时取语言包的「加载中」。
  • 显示的文字对辅助技术隐藏,避免与指示的名称重复播报。

API

Props

属性
类型
默认值
说明
visible
boolean
false
是否显示
text
string
—
指示下方的说明
fixed
boolean
false
是否覆盖整个视口
size
'sm' | 'md' | 'lg'
'md'
加载指示的尺寸
delay
number
300
显示前等待的毫秒数,0 立即显示
minVisible
number
300
显示后至少停留的毫秒数
className
string
—
追加至遮罩的类名

内容属性

属性
说明
children
薄面里的内容,默认是加载指示与 text