# 如何通过为位置添加动画来移动组件

> 使用 PositionAnimation（以像素为单位的线性插值）将组件从一个点滑动到另一个点。

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

# 如何通过为位置添加动画来移动组件

**目的。** 为组件的**像素位置**添加动画（例如：将卡片从右侧滑动到中央）。`PositionAnimation` 进行线性插值，且**不会**被夹紧到 [0,1] —— 非常适合坐标。

## 使用的 API
- `new PositionAnimation(float start, float end, float duration [, float fps])` —— **线性**插值 `start → end`，**不夹紧**；`duration` 以秒计；`fps` 可选（默认 60）。
- `PositionAnimation.onUpdate(Runnable)` / `onComplete(Runnable)` / `getValue()` / `start()` / `stop()` / `cancel()` —— 与 `AlphaAnimation` 属于同一族。
- `Component.setX(int)` / `Component.setY(int)` —— 重新定位组件（返回 `Component`）。替代方案：当同时改变尺寸时使用 `setBounds(int x, int y, int w, int h)`。
- `Component.getY()` / `getWidth()` / `getHeight()` —— 用于保留未被添加动画的轴。
- `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
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Card card = new Card();
      add(card);

      int fromX = Screen.width;          // 从屏幕外右侧开始
      int toX   = (Screen.width - card.width) / 2;   // 水平居中

      PositionAnimation slide = new PositionAnimation(fromX, toX, 0.4f);   // 400 ms
      slide.onUpdate(() ->
         {
            card.x = (int) slide.getValue();     // 只移动 X 轴；Y 保持不变（x 是公开字段）
            Component.repaint();                 // 没有它帧不会重绘
         })
         .onComplete(() -> Component.repaint())
         .start();
   }
}
```

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

## 最小片段
```java
// 已在源码核实：PositionAnimation 线性无 clamp；Component.setX(int)/setY(int)；repaint() 静态
Card card = new Card();
add(card);

int fromX = Screen.width;          // 从屏幕外右侧开始
int toX   = (Screen.width - card.getWidth()) / 2;   // 水平居中

PositionAnimation slide = new PositionAnimation(fromX, toX, 0.4f);   // 400 ms
slide.onUpdate(() ->
   {
      card.setX((int) slide.getValue());   // 只移动 X 轴；Y 保持不变
      Component.repaint();                 // 没有它帧不会重绘
   })
   .onComplete(() -> Component.repaint())
   .start();
```

细节：

- 如果需要同时在 X **和** Y 上移动，请使用**两个** `PositionAnimation`（每个轴一个），或在 `onUpdate` 中计算两者并应用 `setBounds(x, y, w, h)`。
- `setX`/`setY` 会改变在父级中的**绝对**位置；在采用自动布局的屏幕上，优先为你直接控制其 bounds 的组件添加动画（避免与 `Layout` 冲突）。
- 与 `AlphaAnimation` 不同，这里**没有**缓动：运动是匀速的。要实现平滑运动，请在你自己的函数中串接一个缓动，或与 `AlphaAnimation` 组合以实现不透明度效果。

## 深入了解
- [值动画](/docs/ui/animations/value-animations)
- 姊妹配方：[组件的淡入淡出](/docs/cookbook/anim/fade) · [循环脉冲](/docs/cookbook/anim/pulso-loop)
- 相关：[打开和关闭屏幕](/docs/cookbook/nav/abrir-tela)
