自訂緩動曲線


在〈隱式動畫 Widget〉中談過,在 Curves 文件,你可以看到各種不同的變化曲線,其中也有標示各曲線大致的效果展示。

這邊留意到曲線的橫軸為 t,縱軸為 xt 是從 0.0 到 1.0,也就是特性起始值與目標值間的差距被標準化為 0.0 到 1.0,而對應的 x 會作為後續插值的依據(根據後續文件中會談到的 Tween)。

如果 Curves 文件 中內建的曲線都不符合你的需求,而想要自訂曲線,將是根據 t 建立對應的 x 值,方式是繼承 Curve 類別,並重新定義其 transformInternal 方法,例如 Curves.linear 的實現,就僅僅是單純地將傳入的 t 值傳回罷了:

class _Linear extends Curve {
  const _Linear._();

  @override
  double transformInternal(double t) => t;
}

transformInternal 收到的 t 必然是在 0.0 與 1.0 之間,因為 Curvetransform 就這麼限制了,super.transform(t) 中會呼叫 transformInternal

@immutable
abstract class Curve extends ParametricCurve<double> {
  ...
  @override
  double transform(double t) {
    if (t == 0.0 || t == 1.0) {
      return t;
    }
    return super.transform(t);
  }

  ...
}

因此若要定義正弦曲線,可以如下:

class SineCurve extends Curve {
  double period = 1;

  SineCurve({this.period});

  @override
  double transformInternal(double t) {
    return sin(period * 2 * pi * t);
  }
}

這個正弦曲線可以定義週期,如果想套用在位移上,為了目標值剛好是在曲線的正峰值(不然最後 t 為 1.0 時會有劇烈變化),可以選擇週期為 1.5,例如:

import 'dart:math';

import 'package:flutter/material.dart';

void main() => runApp(
  MaterialApp(
    home: Caterpillar(),
  )
);

class Caterpillar extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _CaterpillarState();
}

class _CaterpillarState extends State<Caterpillar> {
  var forward = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('openhome.cc'),
      ),
      body: Center(
        child: AnimatedContainer(
          transform: Matrix4.translationValues(forward ? -150: 150, 0, 0),
          width: 150,
          child: Image.network('https://openhome.cc/Gossip/images/caterpillar.jpg'),
          duration: Duration(seconds: 1),
          curve: SineCurve(period: 1.25),
        )
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() {
          forward = !forward;
        }),  // 註冊 onPressed 處理器
        tooltip: forward ? 'forward' : 'back',
        child: Icon(forward ? Icons.arrow_forward : Icons.arrow_back),
      ),
    );
  }
}

class SineCurve extends Curve {
  double period = 1;

  SineCurve({this.period});

  @override
  double transformInternal(double t) {
    return sin(period * 2 * pi * t);
  }
}

曲線在 t 為 0.0 時,通常 x 也是 0.0,t 為 1.0 時通常 x 也就是 1.0,當然,實際上還是要看你想要什麼效果,來看一下操作的成果:

自訂緩動曲線