☰
Streamlit `:shimmer[]` Markdown 指令:为 AI 应用文本添加流光加载动画
2026/10/10 5:26:10 网站建设 项目流程
  • 数据可视化
  • 后端
  • 前端

【免费下载链接】streamlit

Streamlit — A faster way to build and share data apps.

项目地址:https://gitcode.com/gh_mirrors/st/streamlit
点击查看免费下载

导读

本文围绕 Streamlit 的:shimmer[text]Markdown 指令展开,介绍其设计动机、语法用法、动画行为与无障碍特性,并结合当前仓库的前端源码与测试,剖析该指令从 Markdown 解析到 CSS 动画渲染的完整实现链路。读完本文,你将掌握在st.markdown、st.write、组件标签、聊天消息等任意渲染 Markdown 的位置使用流光加载动画,并理解其与 Streamlit 主题系统和prefers-reduced-motion偏好集成的底层原理。

背景:AI 应用为什么需要「流光文字」

AI 驱动的应用在生成响应时存在不可避免的处理等待期。一个细微的、持续流动的高光效果(shimmer)已成为 ChatGPT、Claude、Copilot 等 AI 界面中广泛采用的视觉范式:它传达「正在思考」或「正在加载」的状态,同时避免闪烁、旋转加载圈等强刺激元素对用户的干扰。

在引入该指令之前,Streamlit 用户要实现这一效果只能通过st.html()注入自定义 CSS。这种做法存在三个明显短板:

  • 繁琐:需要手写@keyframes动画、渐变与background-clip等一堆 CSS 细节;
  • 无法融入主题系统:自定义 CSS 不会随 Streamlit 的明暗主题自动适配颜色;
  • 门槛高:要求用户理解 CSS 动画与浏览器兼容性。

对应地,该功能由用户请求驱动(仓库 issue [#13247]),并明确覆盖了四类典型用例:

  • AI 响应生成:等待 LLM 返回时显示「Thinking...」;
  • 流式输出指示:内容开始流式传输前先显示 shimmer;
  • 渐进揭示:文本逐段可用时以动画呈现;
  • 通用加载态:任何渲染 Markdown 的上下文中的处理中提示。

语法与使用场景

:shimmer[text]遵循 Streamlit 既有的 Markdown 指令模式——与:red[text]、:small[text]、:color-badge[text]等扩展一脉相承。语法极其简单:

:shimmer[Some text content]

该指令在 Streamlit 渲染 Markdown 的任何位置均可使用。产品规格文档给出的完整用法覆盖了常见入口:

import streamlit as st # 在 st.markdown 中 st.markdown(":shimmer[Thinking...]") # 在 st.write 中 st.write(":shimmer[Processing your request...]") # 在组件标签中 st.button(":shimmer[Loading...]", disabled=True) # 在聊天消息中 with st.chat_message("assistant"): st.markdown(":shimmer[Generating response...]") # 与其他文本组合 st.markdown("Status: :shimmer[Analyzing data...]")

后端文档字符串(见 lib/streamlit/elements/markdown.py)对这一能力有正式描述:「Shimmer effect for loading or in-progress text, using the syntax:shimmer[text to shimmer]. The text fades in and out to indicate ongoing activity. This respects the user's reduced motion preferences.」——即文本以淡入淡出的方式指示进行中的活动,并尊重用户的减弱动态效果偏好。

此外,st.markdown的help参数提示文本同样支持该指令,因此你可以在组件帮助气泡中复用 shimmer 语法。e2e 测试应用(e2e_playwright/mega_tester_app.py)也将其与:small[]、LaTeX 等其他指令混排使用,验证了多指令共存场景:

":small[small] :shimmer[shimmer] $$a = b$$" st.markdown("Shimmer status: :shimmer[Loading generated summary...]")

动画行为与视觉设计

动画规格

:shimmer[]的效果是一道平滑、连续的「高光带」扫过文本,具体规格如下:

  • 无限循环:动画持续循环,直到元素被移除或替换;
  • 8 秒周期:采用偏慢的 8 秒循环时长,营造柔和、不分散注意力的观感;
  • 线性渐变扫光:一个线性渐变形成微妙的「亮光」扫过效果。

产品规格的最初设计描述是使用 CSSbackground-clip: text配合动画渐变实现;从当前仓库的实际实现看(见下文源码剖析),最终落地采用了等价的mask-image蒙版动画方案:通过动画化蒙版位置,让文字的透明度在 55% 与 100% 之间往复,从而在视觉上形成亮光扫过、文字忽隐忽现的效果——这与规格文档中「text fades in and out」(文字淡入淡出)的描述完全吻合。

主题自动适配

shimmer 渐变无需任何配置即可在两种主题下表现自然:

  • 浅色主题:从文字色渐变到更浅的高光再回到文字色;
  • 深色主题:从文字色渐变到更亮的高光再回到文字色。

需要指出的是,最终实现并非在 CSS 中硬编码两套渐变色,而是让元素color: inherit继承周围文本颜色、蒙版只控制透明度(详见下文源码剖析),从而天然随主题和上下文(如:red[]指令)变色。

无障碍:尊重减弱动态效果偏好

动画尊重用户的prefers-reduced-motion偏好。当用户系统开启减弱动态效果时,shimmer 动画被禁用,文字以静态方式显示,避免前庭障碍(vestibular disorders)用户产生不适。这不仅是产品规格的明确要求,也在源码样式与后端文档字符串中均有落实。

与其他指令的组合

shimmer 可以与其他 Markdown 指令自由嵌套:

# 与颜色指令组合 st.markdown(":red[:shimmer[Error loading...]]") # 与加粗组合 st.markdown("**:shimmer[Important update loading...]**") # 用于链接文字(shimmer 作用于链接文本) st.markdown(":shimmer[[Click here](https://example.com)]")

嵌套时存在明确的视觉层级规则::shimmer[:red[text]]内层颜色胜出(显示红色);:red[:shimmer[text]]则 shimmer 继承外层的红色。这一规则在源码注释与前端单测中均有记录(见下文)。

源码级实现剖析

一、解析层:remark 文本指令插件

:shimmer[]的解析实现在前端 Markdown 渲染组件中。核心逻辑位于 StreamlitMarkdown.tsx 的createRemarkColoringAndSmall工厂函数——该函数基于 remark 的 AST 遍历(visit(tree, "textDirective", ...))处理所有文本指令:

// Handle shimmer text directive (:shimmer[]) if (nodeName === "shimmer") { const data = node.data || (node.data = {}) data.hName = "span" data.hProperties = data.hProperties || {} data.hProperties.className = ["stMarkdownShimmer"] return }

它把:shimmer[]节点转换为一个带stMarkdownShimmer类名的<span>,动画样式全部由类名驱动。这种「解析只做标记、样式交给 CSS」的分层设计,使得指令渲染零新增运行时依赖——正如产品规格清单中所确认的「No new dependencies — uses existing remark plugin infrastructure」(复用既有 remark 插件基础设施)。插件的动态加载与错误处理机制可进一步参考 utils.ts。

二、渲染层:蒙版渐变 + 关键帧动画

动画样式定义在 styled-components.ts,通过 Emotion 的keyframes声明了一个蒙版位置动画:

const shimmerAnimation = keyframes` 0% { mask-position: 600% center; -webkit-mask-position: 600% center; } 100% { mask-position: -600% center; -webkit-mask-position: -600% center; } `

mask-position从 600% 扫到 -600%,即蒙版从右向左扫过文本。对应的span.stMarkdownShimmer样式(同文件 L442-L471)揭示了三个关键细节:

  1. 颜色继承:color: inherit,元素颜色继承周围文本(源码注释明确说明:元素颜色是蒙版能到达的最亮值,因此继承周围文本而非固定某个淡色,否则整个扫光会停留在偏暗区间);
  2. 透明度蒙版:蒙版渐变在 90 度方向从rgba(0,0,0,0.55)(55% 透明度)经rgba(0,0,0,1)(峰值全亮)再回到 0.55,配合mask-size: 200% 100%让高光带横跨文本;
  3. 动画与无障碍:animation: ${shimmerAnimation} 8s linear infinite实现 8 秒线性无限循环;并在@media (prefers-reduced-motion: reduce)下关闭动画并移除蒙版,文字完全静态显示。

三、验证层:单元测试与 e2e 覆盖

前端单元测试(StreamlitMarkdown.test.tsx)覆盖了三个关键行为:

  • :shimmer[Loading...]渲染为带stMarkdownShimmer类的<span>;
  • 普通文本不会被误加 shimmer 类;
  • :red[:shimmer[Loading...]]嵌套时,内层元素持有stMarkdownShimmer类、外层父元素持有stMarkdownColoredText类并应用了颜色样式——从 DOM 结构层面验证了「shimmer 继承外层颜色」的组合规则。

e2e 层同样有覆盖:e2e_playwright/markdown_features.py 将":shimmer[Loading...]"纳入特性矩阵,配合 st_markdown_test.py 进行端到端断言。

完整实战示例

示例一:聊天中的「思考中」占位

这是最常见的 AI 聊天场景:先显示 shimmer 占位,模拟 API 调用后替换为真实回复:

import streamlit as st import time if prompt := st.chat_input("Ask me anything"): st.chat_message("user").write(prompt) with st.chat_message("assistant"): placeholder = st.empty() placeholder.markdown(":shimmer[Thinking...]") time.sleep(2) # 模拟 API 调用 placeholder.markdown("Here's my response!")

示例二:配合流式输出

在内容逐块到达时,用 shimmer 填充尚未生成的部分,最后移除:

import streamlit as st with st.chat_message("assistant"): placeholder = st.empty() placeholder.markdown(":shimmer[Generating...]") # 开始流式输出 response = "" for chunk in generate_response(): response += chunk placeholder.markdown(response + ":shimmer[...]") # 最终完整回复(不再带 shimmer) placeholder.markdown(response)

示例三:状态指示器

在仪表盘中并排展示不同服务的状态:

import streamlit as st col1, col2 = st.columns(2) with col1: st.markdown("**Database:** :green[Connected]") with col2: st.markdown("**AI Model:** :shimmer[Loading...]")

设计边界与未来规划

产品规格明确划定了初版的范围边界,值得使用者了解:

暂不提供定制参数。初版实现没有任何配置项——单一、带合理默认值的动画,追求极简与跨应用视觉一致性。speed、intensity、color等参数留待后续依据用户反馈扩展。

st.shimmer上下文管理器属于未来工作(issue [#14266])。规格设想的形态类似st.spinner:

with st.shimmer("Thinking..."): result = expensive_operation()

它会自动围绕代码块显示/隐藏 shimmer。Markdown 指令提供了基础动画能力,上下文管理器将是其上的便利封装。

落地检查清单

产品规格末尾的核对表可作为功能验收依据,也印证了该指令的低侵入性:

检查项结论
是否适用于 SiS、Cloud 等环境✅ 纯 CSS 动画,无任何服务端依赖
是否有破坏性 API 变更✅ 新增指令,纯增量变更
是否引入新依赖✅ 复用既有 remark 插件基础设施
是否收集指标✅ 不适用——Markdown 指令无独立指标
是否有安全/法律影响✅ 无
是否需要文档更新✅ 已纳入st.markdown指令文档

一句话总结::shimmer[]是 Streamlit 为 AI 应用补齐的一块关键拼图——零依赖、主题自适配、无障碍友好,让「思考中」「加载中」的视觉反馈从一项 CSS 手艺活变成一行 Markdown。

[issue-13247]: 仓库用户请求「将文本流光动画作为 Markdown 指令加入」 [issue-14266]: 仓库用户请求「提供 st.shimmer 上下文管理器」

  • 数据可视化
  • 后端
  • 前端

【免费下载链接】streamlit

Streamlit — A faster way to build and share data apps.

项目地址:https://gitcode.com/gh_mirrors/st/streamlit
点击查看免费下载

相关推荐

上一篇:让DVA应用加载速度提升50%:WebP/AVIF图片优化全攻略
下一篇:RxJS v4 `thenDo` 操作符全解析:用 Join Patterns 把多个 Observable 组合成可匹配的 Plan

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询