滾動(dòng)前行的輪子 — Flutter 交錯(cuò)動(dòng)畫(huà)應(yīng)用實(shí)例

前言

之前一篇我們講了 Flutter組合動(dòng)畫(huà)實(shí)現(xiàn)的方式 —— 交錯(cuò)動(dòng)畫(huà),如需了解原理的可以查看下面這篇:用 Flutter 做出 GIF 圖片的效果 。借助 GIF 和繪圖技巧是可以做到類(lèi)似 GIF 那種效果的。本篇我們來(lái)一個(gè)應(yīng)用實(shí)例,我們讓輪子在草地滾動(dòng)著前進(jìn),而且還能粘上“綠色的草”,運(yùn)行效果如下動(dòng)畫(huà)所示。

滾動(dòng)的輪子.gif

動(dòng)畫(huà)解析

上面實(shí)現(xiàn)的效果實(shí)際上有三個(gè)動(dòng)畫(huà)組成:

  • 輪子前進(jìn)的動(dòng)畫(huà)
  • 輪子滾動(dòng)
  • 輪子的邊緣顏色漸變(由黑色變成綠色)

這三個(gè)動(dòng)畫(huà)是同時(shí)進(jìn)行的,因此需要使用到交錯(cuò)動(dòng)畫(huà),即使用一個(gè) AnimationController來(lái)控制三個(gè) Tween 對(duì)象實(shí)現(xiàn)上述的動(dòng)畫(huà)組合。

編碼實(shí)現(xiàn)

首先是輪子組件的定義,為了讓輪子轉(zhuǎn)動(dòng)的效果能夠看到,我們給輪子填充了線性的漸變色,然后輪子的尺寸、旋轉(zhuǎn)速度(time)和邊框顏色由上級(jí)組件來(lái)控制。整個(gè)實(shí)現(xiàn)很簡(jiǎn)單,就是一個(gè)加了裝飾的 Container 而已。

class Wheel extends StatelessWidget {
  final double size;
  final Color color;
  final double time;
  const Wheel({
    Key? key,
    required this.size,
    required this.time,
    required this.color,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: size,
      height: size,
      transform: Matrix4.identity()..rotateZ(2 * pi * time),
      transformAlignment: Alignment.center,
      decoration: BoxDecoration(
        border: Border.all(color: color, width: 10.0),
        borderRadius: BorderRadius.circular(size / 2),
        gradient: LinearGradient(
          colors: [
            Colors.white,
            Colors.orange[100]!,
            Colors.orange[400]!,
          ],
        ),
      ),
    );
  }
}

然后是整個(gè)頁(yè)面布局,整個(gè)頁(yè)面布局其實(shí)就是一個(gè) Stack,然后底部是綠色的 Container再加兩個(gè)輪子,都是使用 Positioned 來(lái)確定各自的位置。然后就是通過(guò)受控的Tween 對(duì)象控制輪子的旋轉(zhuǎn)速度,輪子外邊沿顏色和移動(dòng)的距離,代碼如下,其中輪子移動(dòng)距離通過(guò)控制邊距實(shí)現(xiàn)。

Widget build(BuildContext context) {
  final bottomHeight = MediaQuery.of(context).size.height / 3;
  return Scaffold(
    appBar: AppBar(
      title: const Text('交錯(cuò)動(dòng)畫(huà)'),
    ),
    body: Stack(children: [
      Positioned(
        child: Container(
          width: double.infinity,
          height: bottomHeight,
          color: Colors.green[400],
        ),
        bottom: 0,
        left: 0,
        right: 0,
      ),
      Positioned(
          child: Wheel(
            size: wheelSize,
            color: _color.value!,
            time: _time.value,
          ),
          left: _offset.value * MediaQuery.of(context).size.width,
          bottom: bottomHeight),
      Positioned(
          child: Wheel(
            size: wheelSize,
            color: _color.value!,
            time: -_time.value,
          ),
          right: _offset.value * MediaQuery.of(context).size.width,
          bottom: bottomHeight)
    ]),
    floatingActionButton: FloatingActionButton(
      child: Icon(Icons.play_arrow),
      onPressed: () {
        if (_controller.isCompleted) {
          _controller.reverse();
        } else if (!_controller.isAnimating) {
          _controller.forward();
        }
      },
    ),
  );
}

最后就是構(gòu)建受AnimationController 控制的 Tween 對(duì)象了,這個(gè)在 用 Flutter 做出 GIF 圖片的效果 已經(jīng)介紹過(guò)了,代碼如下:

late AnimationController _controller;
late Animation<double> _time;
late Animation<double> _offset;
late Animation<Color?> _color;

final wheelSize = 80.0;

@override
void initState() {
  _controller =
      AnimationController(duration: Duration(seconds: 4), vsync: this)
        ..addListener(() {
          setState(() {});
        });

  _time = Tween<double>(begin: 0, end: 8.0).animate(
    CurvedAnimation(
      parent: _controller,
      curve: Interval(
        0.0,
        1.0,
        curve: Curves.linear,
      ),
    ),
  );
  _offset = Tween<double>(begin: 0, end: 1.0).animate(
    CurvedAnimation(
      parent: _controller,
      curve: Interval(
        0.0,
        1.0,
        curve: Curves.easeInCubic,
      ),
    ),
  );
  _color = ColorTween(begin: Colors.black87, end: Colors.green).animate(
    CurvedAnimation(
      parent: _controller,
      curve: Interval(
        0.0,
        0.8,
        curve: Curves.easeIn,
      ),
    ),
  );
  super.initState();
}

就這樣,一對(duì)奔向?qū)Ψ降妮喿觿?dòng)畫(huà)效果就完成了!源碼已上傳至:動(dòng)畫(huà)相關(guān)源碼。

總結(jié)

交錯(cuò)動(dòng)畫(huà)實(shí)際上可以實(shí)現(xiàn)非常有創(chuàng)意的動(dòng)效,只是這樣會(huì)需要很高的繪圖技巧,比如使用 CustomPaint 來(lái)做。接下來(lái)的幾篇我們來(lái)介紹一下 CustomPaint相關(guān)的內(nèi)容。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 交錯(cuò)動(dòng)畫(huà) 你將學(xué)習(xí)到什么: 交錯(cuò)動(dòng)畫(huà)由序列或重疊的動(dòng)畫(huà)組成。 要?jiǎng)?chuàng)建交錯(cuò)動(dòng)畫(huà),使用多個(gè)動(dòng)畫(huà)對(duì)象。 一個(gè)Animat...
    文vane閱讀 1,346評(píng)論 0 1
  • Flutter動(dòng)畫(huà)學(xué)習(xí)效果直接貼代碼 運(yùn)行看吧,更改push入口查看不同動(dòng)畫(huà)效果?;A(chǔ)動(dòng)畫(huà):顏色漸變、控件大小更改...
    GA_閱讀 2,520評(píng)論 0 8
  • 該文已授權(quán)公眾號(hào) 「碼個(gè)蛋」,轉(zhuǎn)載請(qǐng)指明出處 在 Flutter 中,自帶手勢(shì)監(jiān)聽(tīng)的目前為止好像只有按鈕部件和一些...
    Kuky_xs閱讀 1,814評(píng)論 2 3
  • 簡(jiǎn)介 動(dòng)畫(huà)在日常開(kāi)發(fā)中,經(jīng)常會(huì)遇到,在一些需求中添加上動(dòng)畫(huà)會(huì)讓體驗(yàn)好很多。選擇一個(gè)合適的動(dòng)畫(huà)方案,能夠讓我們的效率...
    Zzzzzzzzzzzzzz閱讀 867評(píng)論 0 0
  • 概述 動(dòng)畫(huà)API認(rèn)識(shí) 動(dòng)畫(huà)案例練習(xí) 其它動(dòng)畫(huà)補(bǔ)充 一、動(dòng)畫(huà)API認(rèn)識(shí) 動(dòng)畫(huà)實(shí)際上是我們通過(guò)某些方式(某種對(duì)象,An...
    IIronMan閱讀 476評(píng)論 1 3

友情鏈接更多精彩內(nèi)容