## 目录

1. [引言:动画的"手感"从何而来](#1-引言动画的手感从何而来)
2. [Curve 缓动曲线详解](#2-curve-缓动曲线详解)
   - [2.1 什么是缓动曲线](#21-什么是缓动曲线)
   - [2.2 Curve 枚举一览](#22-curve-枚举一览)
   - [2.3 各曲线的运动特征对比](#23-各曲线的运动特征对比)
3. [Swiper 的 curve() 属性](#3-swiper-的-curve-属性)
4. [实战:5 种曲线可切换的轮播](#4-实战5-种曲线可切换的轮播)
5. [代码逐段解析](#5-代码逐段解析)
   - [5.1 数据模型:CurvePreset 与 CardData](#51-数据模型curvepreset-与-carddata)
   - [5.2 CardView:紧凑型卡片渲染](#52-cardview紧凑型卡片渲染)
   - [5.3 CurveSelector:曲线切换按钮组](#53-curveselector曲线切换按钮组)
   - [5.4 CurveInfo:曲线说明卡](#54-curveinfo曲线说明卡)
   - [5.5 build():Swiper + curve 核心绑定](#55-buildswiper--curve-核心绑定)
6. [5 种曲线的实际用户体验](#6-5-种曲线的实际用户体验)
7. [换一种写法:将 Curve 枚举替换为自定义 cubicBezier](#7-换一种写法将-curve-枚举替换为自定义-cubicbezier)
8. [常见问题与解决方案](#8-常见问题与解决方案)
9. [本系列五篇全览](#9-本系列五篇全览)
10. [总结](#10-总结)

---

## 1. 引言:动画的"手感"从何而来

在移动端应用中,同样一个滑动操作——比如轮播图的翻页——在不同的应用中给人完全不同的"手感":

| 应用 | 翻页手感 | 用户感受 |
|------|---------|---------|
| **微信** 朋友圈 | 跟随手指,松手即停 | 干脆、可控 |
| **抖音** 视频切换 | 快速滑过,惯性减速 | 爽快、流畅 |
| **Apple Music** 专辑翻页 | 缓慢跟随,弹性过界 | 精致、优雅 |
| **淘宝** Banner | 匀速滚动,自动播放 | 平稳、不打扰 |

这种"手感"的差异,本质上是由**动画的缓动曲线(Easing Curve)**决定的。缓动曲线定义了动画属性(位置、透明度、缩放等)随时间变化的速率——它是快还是慢?是先快后慢还是先慢后快?是否会有回弹?

HarmonyOS NEXT 的 `Curve` 枚举提供了 10+ 种预设缓动曲线,通过 Swiper 的 `.curve()` 属性即可轻松切换轮播动画的"手感"。本文将通过一个可交互的示例,带你逐一体验每种曲线的不同感受。

---

## 2. Curve 缓动曲线详解

### 2.1 什么是缓动曲线

**缓动曲线**(Easing Curve)是一个函数,它描述了动画完成度(Y 轴)随时间(X 轴)的变化关系。

```
动画完成度
  ↑ 1.0 ┤      ╲
        │       ╲
        │        ╲     ← 曲线越陡 = 速度越快
        │         ╲
        │          ╲
        │           ╲
  ↑ 0.0 ┤────────────╲──→
        └─────────────────→ 时间
```

- **X 轴**:时间,从 0 到 1(动画开始到结束)
- **Y 轴**:动画完成度,从 0 到 1(0% 到 100%)
- **斜率**:速度。斜率越大 = 速度越快

**匀速动画**(`Curve.Linear`)是一条对角线:每单位时间移动相同距离,机械、生硬。

**缓入缓出**(`Curve.EaseInOut`)是一条 S 形曲线:开始慢 → 中间快 → 结束慢,符合物理世界的运动规律。

### 2.2 Curve 枚举一览

HarmonyOS NEXT 的 `Curve` 枚举提供了以下预设值:

| Curve 值 | 曲线形态 | 速度特征 |
|---------|---------|---------|
| `Linear` | 直线对角线 | 全程匀速 |
| `Ease` | S 形(默认) | 慢→快→慢 |
| `EaseIn` | 上凸抛物线 | 慢→快(加速) |
| `EaseOut` | 下凸抛物线 | 快→慢(减速) |
| `EaseInOut` | 对称 S 形 | 慢→快→慢(更平缓) |
| `FastOutSlowIn` | 先陡后平 | 先快→后慢(Material Design 标准) |
| `FastOutLinearIn` | 先陡后直线 | 先快→后匀速 |
| `LinearOutSlowIn` | 先直线后平 | 先匀速→后慢 |
| `SpringMotion` | 超越终点后回弹 | 弹性回弹 |
| `Friction` | 快速衰减 | 摩擦减速 |

### 2.3 各曲线的运动特征对比

**FastOutSlowIn**(⭐ 最常用)

这是 Material Design 的推荐曲线,也是大多数 Android 应用的默认手感。它的特点是:

- 动画开始时非常快(几乎瞬间启动)
- 接近终点时缓慢减速
- 给用户"干脆利落、不拖沓"的感觉

用一句话描述:**"啪的一下就过去了,最后轻轻停住"**。

**EaseOut**

与 FastOutSlowIn 类似但更柔和:

- 起始速度较快(但不如 FastOutSlowIn 快)
- 接近终点时平滑减速
- 适合"物体到达目的地"的场景

**EaseIn**

与 EaseOut 相反:

- 起始缓慢
- 越到终点速度越快
- 适合"物体离开"的场景(如卡片飞走)

**EaseInOut**

对称的 S 形曲线:

- 起止缓慢,中间快速
- 最符合自然界的运动规律
- 适合"页面入场→展示→退场"的完整过程

**Friction**

模拟摩擦力效果:

- 起始速度快
- 迅速减速到停止
- 适合"被推动后因摩擦而停止"的物理模拟

---

## 3. Swiper 的 curve() 属性

Swiper 组件通过 `.curve()` 属性设置滑动动画的缓动曲线:

```ets
Swiper() {
  // 子页面
}
.curve(Curve.FastOutSlowIn)     // 设置缓动曲线
.duration(400)                   // 动画时长(毫秒)
```

**`.curve()` 的工作机制**:

1. 用户手指滑动 Swiper 或 autoPlay 触发切换
2. Swiper 计算当前页到目标页的**起始状态**和**结束状态**
3. Swiper 使用 `.curve()` 指定的缓动曲线,在 `.duration()` 时间内完成插值
4. 每一帧的插值结果驱动页面位置变化,形成动画

**关键认知**:`.curve()` 只影响 Swiper 的自动滑动动画和松手后的惯性滑动。手指触摸拖动期间,Swiper 完全跟随手指位置,不受 curve 影响。这是所有 Swiper 组件的一致行为——触摸时直接映射,松手后缓动归位。

---

## 4. 实战:5 种曲线可切换的轮播

### 4.1 设计目标

创建一个轮播应用,用户可以通过点击按钮实时切换 5 种不同的缓动曲线,立即感受每种曲线的动画差异。

### 4.2 特性列表

1. **Swiper 轮播**:5 张渐变背景卡片,自动轮播
2. **曲线切换**:底部 5 个按钮,对应 FastOutSlowIn / EaseOut / EaseIn / EaseInOut / Friction
3. **实时生效**:点击按钮后,下一次滑动立即使用新曲线
4. **曲线说明**:底部信息卡同步显示当前曲线的名称和运动特征
5. **自动演示**:autoPlay + loop 自动轮播,方便对比不同曲线

### 4.3 界面预览

```
┌──────────────────────────────────┐
│  🌀 Swiper + 缓动曲线             │
├──────────────────────────────────┤
│  ┌──────────────────────────┐    │
│  │          🚀               │    │
│  │         急速               │    │
│  │   FastOutSlowIn 风格       │    │
│  │                          │    │
│  │   Curve.FastOutSlowIn    │    │
│  │   先快后慢·标准入场        │    │
│  └──────────────────────────┘    │
├──────────────────────────────────┤
│  🎯 选择动画曲线                 │
│  [🚀] [🎈] [⚡] [🍃] [💎]       │
├──────────────────────────────────┤
│  📈 FastOutSlowIn                │
│  ● Curve.FastOutSlowIn           │
│  ● 先快后慢·标准入场              │
│  ● 先加速到高速,再缓慢减速...    │
│  ● Swiper .curve() 属性应用      │
└──────────────────────────────────┘
```

---

## 5. 代码逐段解析

### 5.1 数据模型:CurvePreset 与 CardData

```ets
interface CurvePreset {
  name: string;           // 曲线名称,如 "FastOutSlowIn"
  curveType: Curve;       // Curve 枚举值,如 Curve.FastOutSlowIn
  description: string;    // 中文描述
  gradientStart: string;  // 卡片渐变色起点
  gradientEnd: string;    // 卡片渐变色终点
  emoji: string;          // 按钮图标
}

interface CardData {
  emoji: string;  // 卡片大图标
  title: string;  // 卡片标题
  desc: string;   // 卡片描述
}
```

**CurvePreset 的设计意图**:

`curveType` 字段存储 `Curve` 枚举值,这是 `Curve` 类型的属性,而非字符串。在数据定义时直接赋值:

```ets
{ name: 'FastOutSlowIn', curveType: Curve.FastOutSlowIn, ... }
```

使用时通过 `.curve()` 直接传递:

```ets
.curve(this.presets[this.selectedCurveIndex].curveType)
```

这种设计避免了字符串到枚举的映射转换,类型安全且性能更好。

**5 条预设数据**:

```ets
private readonly presets: CurvePreset[] = [
  { name: 'FastOutSlowIn', curveType: Curve.FastOutSlowIn, description: '先快后慢·标准入场', gradientStart: '#FF6B6B', gradientEnd: '#EE5A24', emoji: '🚀' },
  { name: 'EaseOut',       curveType: Curve.EaseOut,       description: '快→慢·缓出停止',   gradientStart: '#A29BFE', gradientEnd: '#6C5CE7', emoji: '🎈' },
  { name: 'EaseIn',        curveType: Curve.EaseIn,        description: '慢→快·加速进入',   gradientStart: '#55EFC4', gradientEnd: '#00B894', emoji: '⚡' },
  { name: 'EaseInOut',     curveType: Curve.EaseInOut,     description: '慢快慢·自然平缓',   gradientStart: '#74B9FF', gradientEnd: '#0984E3', emoji: '🍃' },
  { name: 'Friction',      curveType: Curve.Friction,      description: '摩擦减速·平稳停止', gradientStart: '#FD79A8', gradientEnd: '#E84393', emoji: '💎' },
];
```

### 5.2 CardView:紧凑型卡片渲染

```ets
@Builder
private CardView(card: CardData, idx: number) {
  Stack() {
    Column()
      .width('100%').height('100%').borderRadius(24)
      .linearGradient({ direction: GradientDirection.Bottom,
        colors: [[this.presets[idx % this.presets.length].gradientStart, 0],
                 [this.presets[idx % this.presets.length].gradientEnd, 1]] })
    Column()
      .width('100%').height('100%').borderRadius(24)
      .backgroundColor('#FFFFFF').opacity(0.08)
    Column() {
      Text(card.emoji).fontSize(72)
      Text(card.title).fontSize(28).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
      Text(card.desc).fontSize(15).fontColor('#FFFFFF').opacity(0.85)
    }
    .position({ y: 50 })
    Column() {
      Text('Curve.' + this.presets[idx % this.presets.length].name)
        .fontSize(13).fontWeight(FontWeight.Bold).fontColor('#FFFFFF').fontFamily('monospace')
      Text(this.presets[idx % this.presets.length].description)
        .fontSize(12).fontColor('#FFFFFF').opacity(0.7)
    }
    .position({ y: 220 })
  }
  .width('100%').height('100%').clip(true)
}
```

**紧凑写法的动机**:

最终编译通过的代码采用了每行串联多个 `.method()` 的紧凑风格。这并非风格偏好,而是因为在之前 的编译尝试中,换行缩进的版本因复杂的嵌套结构产生了多次编译错误(括号匹配、`let` 声明等)。紧凑写法减少了换行和缩进层级,让编译器更容易解析。

**idx % presets.length 的作用**:

卡片有 5 张,曲线预设也有 5 条,`idx % 5` 建立了卡片与曲线的一一映射:

| 卡片索引 | 曲线预设 |
|---------|---------|
| 0 🚀 急速 | FastOutSlowIn |
| 1 🎈 轻盈 | EaseOut |
| 2 ⚡ 脉冲 | EaseIn |
| 3 🍃 平滑 | EaseInOut |
| 4 💎 优雅 | Friction |

### 5.3 CurveSelector:曲线切换按钮组

```ets
@Builder
private CurveSelector() {
  Column() {
    Text('🎯 选择动画曲线').fontSize(16)
    Row() {
      ForEach(this.presets, (p: CurvePreset, i: number) => {
        Column() {
          Text(p.emoji).fontSize(24)
          Text(p.name).fontSize(11)
            .fontColor(i === this.selectedCurveIndex ? '#007AFF' : '#666666')
        }
        .width(64).height(64)
        .backgroundColor(i === this.selectedCurveIndex ? '#EBF2FF' : '#FFFFFF')
        .border({ width: i === this.selectedCurveIndex ? 2 : 1,
                  color: i === this.selectedCurveIndex ? '#007AFF' : '#E8E8E8' })
        .onClick(() => { this.selectedCurveIndex = i; })
      })
    }
    .justifyContent(FlexAlign.SpaceEvenly)
  }
}
```

**选中态 vs 未选中态的视觉区分**:

| 属性 | 选中态 | 未选中态 |
|------|--------|---------|
| 文字颜色 | `#007AFF` 蓝色 | `#666666` 灰色 |
| 背景色 | `#EBF2FF` 浅蓝 | `#FFFFFF` 白色 |
| 边框宽度 | `2px` | `1px` |
| 边框颜色 | `#007AFF` 蓝色 | `#E8E8E8` 浅灰 |

**点击切换的逻辑**:

```ets
.onClick(() => { this.selectedCurveIndex = i; })
```

这行代码是"声明式状态驱动"的精髓——只需要更新状态变量,Swiper 自动在下次滑动时使用新曲线。无需手动"应用"或"刷新"。

### 5.4 CurveInfo:曲线说明卡

```ets
@Builder
private CurveInfo() {
  Column() {
    // 标题行:曲线名称
    Row() { Text('📈 ' + this.presets[this.selectedCurveIndex].name) }
    Divider()
    Column() {
      // 第 1 行:Curve.xxx
      Row() { Circle().fill(gradientStart); Text('Curve.' + name) }
      // 第 2 行:中文描述
      Row() { Circle(); Text(description) }
      // 第 3 行:运动特征(条件文本)
      Row() {
        Circle()
        if (this.selectedCurveIndex === 0) { Text('先加速到高速,再缓慢减速到终点') }
        else if (this.selectedCurveIndex === 1) { Text('快速接近终点,缓慢停止') }
        else if (...) { ... }
      }
      // 第 4 行:Swiper 属性说明
      Row() { Circle(); Text('Swiper .curve() 属性应用此曲线') }
    }
  }
}
```

**条件文本的 ArkTS 实现**:

在 ArkTS 的 @Builder 中使用 `if/else if/else` 实现条件文本渲染:

```ets
if (this.selectedCurveIndex === 0) {
  Text('先加速到高速,再缓慢减速到终点').fontSize(14).fontColor('#666666').margin({ left: 8 }).lineHeight(20)
} else if (this.selectedCurveIndex === 1) {
  Text('快速接近终点,缓慢停止').fontSize(14).fontColor('#666666').margin({ left: 8 }).lineHeight(20)
}
```

注意:每个 `Text()` 的样式链(`.fontSize().fontColor().margin()`)必须写在每个分支内部。不能写在 `if/else` 块之外,因为 `.fontSize()` 不能链式调用在 `if` 语句上——`if` 不是组件,没有属性方法。

### 5.5 build():Swiper + curve 核心绑定

```ets
build() {
  Scroll() {
    Column() {
      Text('🌀 Swiper + 缓动曲线')

      Swiper() {
        ForEach(this.cards, (card: CardData, idx: number) => {
          Stack() { this.CardView(card, idx) }
            .width('100%').height('100%').padding({ left: 16, right: 16 })
        }, (card: CardData, idx: number): string => idx.toString())
      }
      .width('100%').height(310).index(this.currentIndex)
      .curve(this.presets[this.selectedCurveIndex].curveType)   // ← 核心绑定
      .autoPlay(true).interval(2000).loop(true).duration(800).indicator(false)
      .onChange((index: number) => { this.currentIndex = index; })

      this.CurveSelector()
      Stack() { this.CurveInfo() }.margin({ top: 12 })
    }
  }
  .width('100%').height('100%').backgroundColor('#F2F3F8')
}
```

**`.curve()` 的绑定机制详解**:

```ets
.curve(this.presets[this.selectedCurveIndex].curveType)
```

这行代码完成了以下工作:

1. `this.selectedCurveIndex` 是一个 `@State` 变量
2. 当用户点击曲线按钮时,`onClick` 修改 `selectedCurveIndex`
3. ArkTS 框架检测到 `@State` 变化,重新执行 `build()` 方法
4. `this.presets[this.selectedCurveIndex].curveType` 返回新的 `Curve` 枚举值
5. Swiper 的 `.curve()` 属性接收到新值
6. 下一次滑动动画使用新的缓动曲线

**duration: 800 的原因**:

默认的 `duration` 是 400ms。本示例设为 800ms,目的是**让曲线效果更明显**。400ms 的动画太快,FastOutSlowIn 和 EaseInOut 的差异不易被感知。800ms 给了足够的时间让每种曲线的速度变化充分展现。

---

## 6. 5 种曲线的实际用户体验

在模拟器或真机上运行后,点击切换不同曲线,可以通过 Swiper 自动轮播观察以下差异:

| 曲线 | 视觉感受 | 速度变化 | 适合场景 |
|------|---------|---------|---------|
| **FastOutSlowIn** | "唰"一下滑过去,轻轻停住 | 启动极快→末端减速 | 页面切换、卡片滑动 |
| **EaseOut** | 匀速滑出,自然停止 | 较快→缓慢停止 | 列表滚动、菜单弹出 |
| **EaseIn** | 缓慢起步,越来越快 | 慢→持续加速 | 物体飞出屏幕 |
| **EaseInOut** | 从容滑过,起止平缓 | 慢→快→慢 | 引导页、全屏转场 |
| **Friction** | 像被推了一下然后摩擦停止 | 快→迅速减速 | 通知面板收起 |

**推荐尝试顺序**:

```
FastOutSlowIn → EaseOut → EaseInOut → EaseIn → Friction
```

从最常用的 FastOutSlowIn 开始,然后逐步体验差异。最后切换到 Friction,感受"摩擦刹车"的独特手感。

---

## 7. 换一种写法:将 Curve 枚举替换为自定义 cubicBezier

本示例使用了 `Curve` 枚举的预设值。如果预设值不能满足你的需求——比如你想要一个"先轻微回弹再向前"的效果——可以使用 `Curve.cubicBezier()` 方法创建自定义曲线。

> **注意**:在 HarmonyOS NEXT SDK 6.1.1 中,`Curve.cubicBezier` 方法可能不可用或已改名。以下代码为概念示例,实际可用的 API 请参考最新 SDK 文档。

```ets
// 概念代码:自定义贝塞尔曲线
let customCurve: Curve = Curve.cubicBezier(0.25, 0.1, 0.25, 1.0);

Swiper() { ... }
.curve(customCurve)
.duration(600)
```

**cubicBezier 参数对照表**:

| cx1 | cy1 | cx2 | cy2 | 效果 | 对应预设 |
|-----|-----|-----|-----|------|---------|
| 0.4 | 0.0 | 0.2 | 1.0 | 先快后慢 | ≈ FastOutSlowIn |
| 0.0 | 0.0 | 0.58 | 1.0 | 快→慢 | ≈ EaseOut |
| 0.42 | 0.0 | 1.0 | 1.0 | 慢→快 | ≈ EaseIn |
| 0.42 | 0.0 | 0.58 | 1.0 | 慢→快→慢 | ≈ EaseInOut |
| 0.34 | 1.56 | 0.64 | 1.0 | 弹性回弹 | SpringMotion |

如果 `Curve.cubicBezier` 在你的 SDK 版本中不可用,直接使用预设值是更稳妥的方案。

---

## 8. 常见问题与解决方案

### 8.1 curve() 修改后没有立即生效

**现象**:切换曲线后,手动滑动时手感没有变化。

**原因**:Swiper 的 `.curve()` 动画仅在**自动播放**或**手指松手后的惯性滑动**时生效。触摸拖拽期间,Swiper 完全跟随手指位置。

**验证方法**:开启 autoPlay,观察自动轮播时不同曲线的切换节奏差异。

### 8.2 曲线差异不明显

**现象**:切换不同曲线后,感觉滑动动画"都一样"。

**原因**:`duration` 太短(如 200ms)时,曲线差异难以感知。

**解决**:将 `duration` 增加到 600~1000ms:

```ets
.duration(800)  // 建议 600~1000ms 用于对比测试
```

生产环境建议恢复为 300~400ms。

### 8.3 curve() 不支持自定义 cubicBezier

**现象**:`Curve.cubicBezier(0.4, 0, 0.2, 1)` 编译报错。

**原因**:不同 SDK 版本的 API 差异。SDK 6.1.1 中 `Curve` 可能只支持预设枚举值。

**解决方案**:使用预设值。如果确实需要自定义曲线,查阅当前 SDK 版本中关于 `CubicBezierCurve` 类的文档。

### 8.4 @Builder 中 if/else 导致样式丢失

**现象**:条件 Text 的 fontSize 不生效。

**原因**:`.fontSize()` 被链式调用在 `if/else` 块上,而不是 `Text()` 上。

```ets
// ❌ 错误:fontSize 链式调用在 if 上
if (cond) { Text('A') }
.fontSize(14)

// ✅ 正确:fontSize 写在每个 Text 上
if (cond) { Text('A').fontSize(14) }
else { Text('B').fontSize(14) }
```

---

## 9. 本系列五篇全览

本文是"鸿蒙原生 ArkTS 布局实战"系列的第五篇。以下是全系列概览:

| # | 标题 | 组件 | 核心属性 | 文件大小 | 编译 |
|---|------|------|---------|---------|------|
| 1 | Tabs + animateTo 切换动画 | Tabs | `animateTo`, `getUIContext()?.animateTo()` | 325 行 | ✅ |
| 2 | Swiper 轮播图 | Swiper | `autoPlay`, `interval` | 397 行 | ✅ |
| 3 | Tabs + vertical 侧边栏 | Tabs | `vertical: true`, `barPosition.Start` | 402 行 | ✅ |
| 4 | Swiper + loop 无限循环 | Swiper | `loop: true` | 483 行 | ✅ |
| **5** | **Swiper + Curve 缓动曲线** | **Swiper** | **`curve(Curve.xxx)`** | **177 行** | ✅ |

**演进脉络**:

- 第 1~3 篇:分别介绍了 Tabs 和 Swiper 两大容器的基础用法
- 第 4 篇:深入 Swiper 的 loop 原理
- **第 5 篇(本篇)**:深入 Swiper 的动画控制——curve 属性

从"能用"到"用好",这个系列逐步深入到 HarmonyOS NEXT 布局组件的细节。

---

## 10. 总结

### 10.1 核心知识点

| 知识点 | 掌握程度 |
|--------|---------|
| Curve 枚举的 5 种常用预设值 | ✅ FastOutSlowIn / EaseOut / EaseIn / EaseInOut / Friction |
| Swiper 的 `.curve()` 属性 | ✅ 绑定 Curve 枚举控制滑动动画节奏 |
| 曲线切换的"声明式"模式 | ✅ 修改 @State → 框架自动应用新曲线 |
| 紧凑型 @Builder 写法 | ✅ 避免楼层嵌套和 let 声明 |
| @Builder 内的条件文本 | ✅ if/else 分支中分别设置样式 |
| duration 对曲线感知的影响 | ✅ 800ms 对比测试 vs 400ms 生产环境 |

### 10.2 关于"动画在手感上的价值"

缓动曲线是一个容易被忽视但至关重要的 UI 细节。一个 400ms 的动画,配上合适的曲线,用户完全不会注意到它的存在——但会感觉到"这个应用用起来很顺手"。

反之,一个不合适的曲线(例如用 `Linear` 做页面切换),用户可能会觉得"有点生硬",但说不出具体原因。

**好的动画曲线 = 好的用户体验中最容易被忽视的那 10%。**

### 10.3 推荐阅读

- [HarmonyOS NEXT 开发文档 — Curve 枚举](https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-animator-curve-V5)
- [Material Design — 动效指南](https://m2.material.io/design/motion/speed.html)
- [Easing Functions Cheat Sheet](https://easings.net/) — 可视化对比各种缓动曲线

---

> **本文配套完整代码**:`entry/src/main/ets/pages/Index.ets`,177 行,已通过编译验证。  
> **编译命令**:`hvigorw assembleApp --no-daemon`  
> **运行方式**:在 DevEco Studio 中打开项目,连接鸿蒙 NEXT 模拟器或真机运行。
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐