• 简体中文
  • BootLoop

    有东西正在启动,而没人知道要多久。

    属于 @aora-bot/surfaces,不在 @aora-bot/react 里:pnpm add @aora-bot/surfaces @aora-bot/react,并在 React 样式之后引入 @aora-bot/surfaces/styles.css。

    正在启动工作区正在把一切接上线。
    已循环 0 次
    import { useState } from 'react';
    import { BootLoop } from '@aora-bot/surfaces';
    
    export default function Demo() {
      const [cycles, setCycles] = useState(0);
      return (
        <div className="site-demo site-demo--column">
          <BootLoop
            title="正在启动工作区"
            body={cycles >= 3 ? '比平时慢一些。' : '正在把一切接上线。'}
            durationMs={1800}
            onCycle={setCycles}
          />
          <span className="site-demo__label">已循环 {cycles} 次</span>
        </div>
      );
    }

    展示活动,而不是进度

    不确定的等待是进度条唯一会说谎的场合,所以这个场景改为展示活动:角色在 working 和 thinking 之间交替——底层是一条旋涡腰带换成三颗游走的珠子——同时一条扫描线从屏幕后方划过。durationMs 是每个状态停留的时间;一对状态是一个循环。

    onCycle 的存在是为了让宿主能升级提示。两个循环只是启动慢;十二个循环就是值得说点什么的问题,而只有宿主知道该说哪句话。上面的示例在三次之后换了文案。

    inFront 让角色站在屏幕前面而不是后面,适合边框只是布景、而不是正在启动的东西本身的布局。

    LoadingMark

    即将到来的那个角色形状的占位符。

    加载中
    加载中
    加载中
    import { useEffect, useState } from 'react';
    import { LoadingMark } from '@aora-bot/surfaces';
    
    export default function Demo() {
      const [ready, setReady] = useState(false);
      useEffect(() => {
        const timer = setInterval(() => setReady((r) => !r), 2200);
        return () => clearInterval(timer);
      }, []);
    
      return (
        <div className="site-demo">
          {(['hex', 'cloud', 'star'] as const).map((shape) => (
            <div className="site-demo__cell" key={shape}>
              <LoadingMark shape={shape} color="violet" ready={ready} sizePx={48} />
              <span className="site-demo__label">{ready ? '就绪' : '加载中'}</span>
            </div>
          ))}
        </div>
      );
    }

    六边形将要落下的地方放一个灰色圆形,就是一次待发生的布局抖动;头像槽里放一个转圈,则是在页面已有的动作之外再添一种动作。这里用的是轮廓本身——同一个形状生成器,做成 CSS mask——上面加一层闪光,于是占位符和角色占据完全相同的轮廓,ready 时的切换是交叉淡入而不是替换。

    淡入过程中两层都保持挂载。在 ready 时卸载占位符,会让角色的第一帧对着空白显示一帧,而那正是这个组件要避免的闪烁。

    API

    属性类型默认值说明
    bodystring——
    classNamestring——
    color"black" | "brown" | "red" | "orange" | "yellow" | "green" | "cyan" | "blue" | "violet" | "magenta" | "pink" | "gray"——
    durationMsnumber2600Time on each state. The pair makes one full cycle.
    face"cute" | "eyes" | "line"——
    inFrontbooleanfalseThe character stands in front of the screen rather than behind it.
    lineWeightnumber——
    nose"slant" | "angular" | "hook" | "long" | "curve" | "bow" | null——
    onCycle((cycle: number) => void)—Called at the end of every cycle, for a host counting how long this has gone on.
    shape"mochi" | "circle" | "blob" | "pebble" | "egg" | "bun" | "tofu" | "pill" | "hex" | "onigiri" | "gem" | "cloud" | "drop" | "leaf" | "flower" | "clover" | "sprout" | "mushroom" | "acorn" | "cactus" | "pear" | "apple" | "strawberry" | "cherry" | "star" | "heart" | "flame" | "bell" | "ghost" | "bot" | "crescent" | "boba" | "fish" | "octopus" | "cat" | "bunny" | "bear" | "chick" | "penguin" | "pig" | "fox" | "bob" | "topknot" | "spiky" | "curly" | "twinbuns"——
    sizePxnumber——
    styleCSSProperties——
    titlestring——
    widthPxnumber280—

    LoadingMark

    属性类型默认值说明
    classNamestring——
    color"black" | "brown" | "red" | "orange" | "yellow" | "green" | "cyan" | "blue" | "violet" | "magenta" | "pink" | "gray"——
    face"cute" | "eyes" | "line"——
    lineWeightnumber——
    nose"slant" | "angular" | "hook" | "long" | "curve" | "bow" | null——
    onSettle((from: "idle" | "sleeping" | "drowsy" | "bored" | "waking" | "powering-down" | "listening" | "thinking" | "searching" | "reading" | "writing" | "working" | "loading" | "progress" | "radar" | "orbit" | "dictating" | "uploading" | "sending" | "receiving" | "messaging" | "waiting" | "spawning" | "composing" | "dragging" | "happy" | "excited" | "laughing" | "playful" | "proud" | "celebrate" | "bouncing" | "shy" | "sad" | "angry" | "confused" | "suspicious" | "surprised" | "scared" | "curious" | "humming" | "alerting" | "notifying" | "refusing", to: "idle" | "sleeping" | "drowsy" | "bored" | "waking" | "powering-down" | "listening" | "thinking" | "searching" | "reading" | "writing" | "working" | "loading" | "progress" | "radar" | "orbit" | "dictating" | "uploading" | "sending" | "receiving" | "messaging" | "waiting" | "spawning" | "composing" | "dragging" | "happy" | "excited" | "laughing" | "playful" | "proud" | "celebrate" | "bouncing" | "shy" | "sad" | "angry" | "confused" | "suspicious" | "surprised" | "scared" | "curious" | "humming" | "alerting" | "notifying" | "refusing") => void)—Fires when a finite `state` (`spawning`, `celebrate`, …) has resolved to its settle target. The inner mark is paused until `ready`, so the clock starts at the cross-fade, not at mount.
    readybooleanfalseSwap the placeholder for the live character.
    shape"mochi" | "circle" | "blob" | "pebble" | "egg" | "bun" | "tofu" | "pill" | "hex" | "onigiri" | "gem" | "cloud" | "drop" | "leaf" | "flower" | "clover" | "sprout" | "mushroom" | "acorn" | "cactus" | "pear" | "apple" | "strawberry" | "cherry" | "star" | "heart" | "flame" | "bell" | "ghost" | "bot" | "crescent" | "boba" | "fish" | "octopus" | "cat" | "bunny" | "bear" | "chick" | "penguin" | "pig" | "fox" | "bob" | "topknot" | "spiky" | "curly" | "twinbuns"'blob'—
    sizePxnumber28—
    state"idle" | "sleeping" | "drowsy" | "bored" | "waking" | "powering-down" | "listening" | "thinking" | "searching" | "reading" | "writing" | "working" | "loading" | "progress" | "radar" | "orbit" | "dictating" | "uploading" | "sending" | "receiving" | "messaging" | "waiting" | "spawning" | "composing" | "dragging" | "happy" | "excited" | "laughing" | "playful" | "proud" | "celebrate" | "bouncing" | "shy" | "sad" | "angry" | "confused" | "suspicious" | "surprised" | "scared" | "curious" | "humming" | "alerting" | "notifying" | "refusing"——
    styleCSSProperties——

    接下来