文章目标

把分散在页面里的动画改造成统一动效系统,解决三类问题:

  1. 动效时长不统一。
  2. 低性能设备卡顿无法控制。
  3. 无法按业务需要启停或调速。

步骤 1:明确动效规范并映射为代码规则

先把需求翻译成“可执行约束”:

  1. 页面动效 200-300ms。
  2. 组件交互 100-200ms。
  3. 状态动效有统一节奏,并且可降级到静态。

对应实现放在 MotionController

Duration pageDuration() => _scaledDuration(260, minMs: 200, maxMs: 300);
Duration componentDuration() => _scaledDuration(150, minMs: 100, maxMs: 200);
Duration stateDuration() => _scaledDuration(180, minMs: 120, maxMs: 220);

代码说明:

  1. baseMs 作为默认体验节奏。
  2. clamp 保证业务调速后也不越界。
  3. 页面无需写 magic number,统一从控制器拿时长。
  Duration _scaledDuration(
    int baseMs, {
    required int minMs,
    required int maxMs,
  }) {
    final raw = (baseMs / _speed).round();
    final safeMs = raw.clamp(minMs, maxMs);
    return Duration(milliseconds: safeMs);
  }

步骤 2:建立全局动效控制中心

新增 lib/motion/motion_controller.dart,核心状态包括:

  1. enabled:总开关。
  2. speed:速度倍率。
  3. autoDegradeEnabled:自动降级开关。
  4. autoDegraded:当前是否降级。
  5. lastFps:实时帧率近似值。
  6. reset():恢复默认动效状态。

核心代码:

void setSpeed(double value) {
  final clamped = value.clamp(0.5, 1.5).toDouble();
  if ((_speed - clamped).abs() < 0.001) return;
  _speed = clamped;
  notifyListeners();
}

代码说明:

  1. 速度控制有边界。
  2. 用微小差值判断避免高频无效刷新。
  3. 修改后统一 notifyListeners() 驱动全局生效。
void setEnabled(bool value) {
    if (_enabled == value) {
      return;
    }
    _enabled = value;
    notifyListeners();
  }

  void setAutoDegradeEnabled(bool value) {
    if (_autoDegradeEnabled == value) {
      return;
    }
    _autoDegradeEnabled = value;
    if (!_autoDegradeEnabled) {
      _autoDegraded = false;
      _lowFpsStreak = 0;
    }
    notifyListeners();
  }

  void setSpeed(double value) {
    final clamped = value.clamp(0.5, 1.5).toDouble();
    if ((_speed - clamped).abs() < 0.001) {
      return;
    }
    _speed = clamped;
    notifyListeners();
  }

  void reset() {
    _enabled = true;
    _speed = 1.0;
    _autoDegradeEnabled = true;
    _autoDegraded = false;
    _lastFps = 60;
    _lowFpsStreak = 0;
    notifyListeners();
  }

步骤 3:接入运行期性能监控与自动降级

通过 Flutter 帧回调做轻量性能感知:

SchedulerBinding.instance.addTimingsCallback(_onFrameTimings);

降级逻辑核心:

if (fps < 30) {
  _lowFpsStreak += timings.length;
} else {
  _lowFpsStreak = math.max(0, _lowFpsStreak - timings.length);
}
final shouldDegrade = _lowFpsStreak >= 20;

代码说明:

  1. 不是单帧触发,避免误判。
  2. 连续低帧触发降级,恢复后可解除。
  3. 通过状态变化通知页面做统一降级。
    在这里插入图片描述

步骤 4:抽出统一动效判断 helper

新增 lib/motion/motion_helpers.dart

bool motionEnabledNow(BuildContext context) {
  final motion = Provider.of<MotionController>(context, listen: false);
  final systemDisabled = MediaQuery.maybeOf(context)?.disableAnimations ?? false;
  return motion.enabled && !motion.reduceForPerformance && !systemDisabled;
}

Duration motionDuration(BuildContext context, Duration fallback) {
  return motionEnabledNow(context) ? fallback : Duration.zero;
}

代码说明:

  1. 把“系统减少动画”也纳入策略。
  2. 所有页面统一调用,避免策略分叉。
  3. 降级或关停时自动回退静态展示。
    在这里插入图片描述

步骤 5:在应用入口注册全局控制器

lib/main.dartMultiProvider 中注入:

providers: [
  ChangeNotifierProvider(create: (_) => UserProvider()),
  ChangeNotifierProvider(create: (_) => MotionController()),
],

代码说明:

  1. 保持与现有状态管理模式一致。
  2. 全应用任何页面都可消费动效策略。
  3. 后续扩展统计和埋点也更方便。

步骤 6:给出可视化控制面板

lib/pages/profile/profile_page.dart 新增动效设置卡片:

  1. 启用动效开关。
  2. 自动降级开关。
  3. 速度滑杆。
  4. 当前 FPS 展示。
  5. 重置按钮。

关键代码:

SwitchListTile(
  title: const Text('启用动效'),
  value: motion.enabled,
  onChanged: motion.setEnabled,
)

在这里插入图片描述

本文总结

这次改造的关键不在“动画写得多漂亮”,而在“是否可治理”。通过控制中心、统一 helper、自动降级和可视化面板,动效从散点代码升级为系统能力。后续不管新页面还是新同学接入,都能保持一致行为和可控风险。

示例地址,点击跳转到gitcode

欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐