# 如何创建持续脉冲（循环）

> 使用 LoopAnimation 以无限循环为某个值添加动画，适用于加载指示器和发光效果。

Source: https://crossfly.dev/zh/docs/cookbook/anim/pulso-loop — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 如何创建持续脉冲（循环）

**目的。** 让某个元素无限地脉动/闪烁（"加载中"指示器、发光、按钮的呼吸效果）。`LoopAnimation` 反复地将一个值从 0 → 1 循环，可带或不带缓动，**没有** `onComplete`（它是无限的——你用 `stop()` 停止它）。

## 使用的 API
- `new LoopAnimation(float duration [, float fps, boolean easeInOut])` —— 每个循环持续 `duration` 秒并自动重新开始。`fps` 默认 60；`easeInOut` 默认 `false`（线性）。当 `easeInOut=true` 时，每个循环应用三次 ease-in-out 缓动。
- `LoopAnimation.onUpdate(Runnable)` —— 每帧的回调；读取 `getValue()`（0–1，当前循环的进度）。
- `LoopAnimation.getValue()` —— 当前循环的进度。
- `LoopAnimation.start()` / `stop()` —— 启动 / 中断循环。**没有 `onComplete` 也没有 `cancel()`** —— 它本质上是无限的。
- `Component.alpha(float)` —— 应用不透明度（用于"呼吸"效果）。
- `Component.repaint()` —— 静态方法；在 `onUpdate` 中必须调用。

## 完整示例（复制、粘贴并运行）
```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.animation.*;

/** 可执行的主界面：循环脉动一个「●」；关闭界面时停止循环。 */
public class MainWindow extends Window
{
   private LoopAnimation pulse;

   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Label dot = new Label("●");
      add(dot);

      // 1 秒周期，60 fps，带缓动（柔和脉动）
      pulse = new LoopAnimation(1f, 60f, true);
      pulse.onUpdate(() ->
         {
            // 值从 0 → 1 并重启；映射到不透明度 0.3 → 1.0
            float v = 0.3f + 0.7f * pulse.getValue();
            dot.alpha(v);
            Component.repaint();
         })
         .start();

      // 结束脉动的按钮（例如加载完成时）
      add(new Button("Parar").onAction(e -> pulse.stop()));
   }

   @Override
   public void onRemove()
   {
      // 离开界面时停止，以免留下孤立循环在屏幕外动画。
      if (pulse != null) pulse.stop();
      super.onRemove();
   }
}
```

> 下面的代码片段仅展示**核心部分**。要运行它，请将其放入上面的 `MainWindow` 骨架中。

## 最小片段
```java
// 已在源码核实：LoopAnimation(duration, fps, easeInOut)；无 onComplete；用 stop() 停止
Label dot = new Label("●");
add(dot);

// 1 秒周期，60 fps，带缓动（柔和脉动）
LoopAnimation pulse = new LoopAnimation(1f, 60f, true);
pulse.onUpdate(() ->
   {
      // 值从 0 → 1 并重启；映射到不透明度 0.3 → 1.0
      float v = 0.3f + 0.7f * pulse.getValue();
      dot.alpha(v);
      Component.repaint();
   })
   .start();

// 稍后，用于结束脉动（例如加载完成时）：
pulse.stop();
```

重要注意事项：

- **保留引用**（`pulse`）以便能够调用 `stop()`。没有 `stop()` 的 `LoopAnimation` 会永远运行并持续消耗 CPU/GPU（每帧都调用 `repaint()`）。
- **离开屏幕时停止它**：当 `Window` 关闭时（`onRemove`/`hide`）调用 `pulse.stop()`，以免留下一个在屏幕外持续播放动画的孤儿循环。
- 每个循环开始时 `getValue()` 会回到 0。要实现"来回"脉冲（0→1→0），请对 `getValue()` 应用三角函数或正弦函数进行映射。

## 深入了解
- [值动画](/docs/ui/animations/value-animations) · [按压与反馈](/docs/ui/animations/press-and-feedback)
- 姊妹配方：[组件的淡入淡出](/docs/cookbook/anim/fade) · [移动组件](/docs/cookbook/anim/mover-componente)
