【Flutter 實戰】動畫核心

来源:https://www.cnblogs.com/mengqd/archive/2020/07/07/13258725.html
-Advertisement-
Play Games

老孟導讀:動畫系統是任何一個UI框架的核心功能,也是開發者學習一個UI框架的重中之重,同時也是比較難掌握的一部分,下麵我們就一層一層的揭開 Flutter 動畫的面紗。 任何程式的動畫原理都是一樣的,即:視覺暫留,視覺暫留又叫視覺暫停,人眼在觀察景物時,光信號傳入大腦神經,需經過一段短暫的時間,光的 ...


老孟導讀:動畫系統是任何一個UI框架的核心功能,也是開發者學習一個UI框架的重中之重,同時也是比較難掌握的一部分,下麵我們就一層一層的揭開 Flutter 動畫的面紗。

任何程式的動畫原理都是一樣的,即:視覺暫留,視覺暫留又叫視覺暫停,人眼在觀察景物時,光信號傳入大腦神經,需經過一段短暫的時間,光的作用結束後,視覺形象並不立即消失,這種殘留的視覺稱“後像”,視覺的這一現象則被稱為“視覺暫留”。

人眼能保留0.1-0.4秒左右的圖像,所以在 1 秒內看到連續的25張圖像,人就會感到畫面流暢,而 1 秒內看到連續的多少張圖像稱為 幀率,即 FPS,理論上 達到 24 FPS 畫面比較流暢,而Flutter,理論上可以達到 60 FPS。

AnimationController

介紹完了動畫系統的基本原理,實現一個藍色盒子大小從 100 變為 200動畫效果:

class AnimationBaseDemo extends StatefulWidget {
  @override
  _AnimationBaseDemoState createState() => _AnimationBaseDemoState();
}

class _AnimationBaseDemoState extends State<AnimationBaseDemo> {
  double _size = 100;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: GestureDetector(
        onTap: () {
          setState(() {
            _size = 200;
          });
        },
        child: Container(
          height: _size,
          width: _size,
          color: Colors.blue,
          alignment: Alignment.center,
          child: Text('點我變大',style: TextStyle(color: Colors.white,fontSize: 18),),
        ),
      ),
    );
  }
}

雖然變大了,但並沒有動畫效果,而是直接變大的,想要使其一點點放大需要引入 AnimationController,它是動畫控制器,控制動畫的啟動、停止,還可以獲取動畫的運行狀態,AnimationController 通常在 initState 方法中初始化:

class _AnimationBaseDemoState extends State<AnimationBaseDemo> with SingleTickerProviderStateMixin{
  double _size = 100;
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this,duration: Duration(milliseconds: 500));
  }
  ...
}

這裡有兩個參數需要設置:

  • vsync:當創建 AnimationController 時,需要傳遞一個vsync參數,存在vsync時會防止屏幕外動畫消耗不必要的資源,單個 AnimationController 的時候使用 SingleTickerProviderStateMixin,多個 AnimationController 使用 TickerProviderStateMixin
  • duration:表示動畫執行的時間。

修改如下:

class _AnimationBaseDemoState extends State<AnimationBaseDemo> with SingleTickerProviderStateMixin{
  double _size = 100;
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this,duration: Duration(milliseconds: 500));
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: GestureDetector(
        onTap: () {
          _controller.forward();
        },
        child: Container(
          height: _size,
          width: _size,
          color: Colors.blue,
          alignment: Alignment.center,
          child: Text('點我變大',style: TextStyle(color: Colors.white,fontSize: 18),),
        ),
      ),
    );
  }
  
  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
  }
}

點擊藍色盒子的時候不再直接更改大小,而是執行動畫_controller.forward()

另外在State dispose 生命周期中釋放 AnimationController。

此時點擊藍色盒子發現並不會變大,StatefulWidget 組件改變外觀需要調用 setState,因此給 AnimationController 添加監聽:

_controller = AnimationController(vsync: this,duration: Duration(milliseconds: 500))
..addListener(() {
  setState(() {
    _size = 100+100*_controller.value;
  });
});

每一幀都會回調addListener ,在此回調中設置藍色盒子大小,藍色的大小是由 100 變到 200,而 AnimationController 的值預設是 0 到 1,所以藍色大小等於 _size = 100+100*_controller.value,運行效果:

這就是 Flutter 中最簡單動畫的實現方式,其中最重要的就是 AnimationController,_controller.value 是當前動畫的值,預設從 0 到 1。也可以通過參數形式設置最大值和最小值:

_controller = AnimationController(vsync: this,duration: Duration(milliseconds: 500),lowerBound: 100,upperBound: 200)
..addListener(() {
  setState(() {
    _size = _controller.value;
  });
})

此時 _controller.value 的值就是從 100變化到 200。

除了使用 addListener 監聽每一幀,還可以監聽動畫狀態的變化:

_controller = AnimationController(
    vsync: this,
    duration: Duration(milliseconds: 500),
    lowerBound: 100,
    upperBound: 200)
  ..addStatusListener((status) {
    print('status:$status');
  })

動畫的狀態分為四種:

  • dismissed:動畫停止在開始處。
  • forward:動畫正在從開始處運行到結束處(正向運行)。
  • reverse:動畫正在從結束處運行到開始處(反向運行)。
  • completed:動畫停止在結束處。

再來看下動畫的控制方法:

  • forward:正向執行動畫。
  • reverse:反向執行動畫。
  • repeat:反覆執行動畫。
  • reset:重置動畫。

forward 和 reverse 方法中都有 from 參數,這個參數的意義是一樣的,表示動畫從此值開始執行,而不再是從lowerBound 到 upperBound。比如上面的例子中 from 參數設置 150,那麼執行動畫時,藍色盒子瞬間變為 150,然後再慢慢變大到200。

讓藍色盒子大小從 100 到 200,然後再變到 100,再到 200,如此反覆:

_controller = AnimationController(
    vsync: this,
    duration: Duration(milliseconds: 500),
    lowerBound: 100,
    upperBound: 200)
  ..addStatusListener((AnimationStatus status) {
    if(status == AnimationStatus.completed){
      _controller.reverse();
    }else if(status == AnimationStatus.dismissed){
      _controller.forward();
    }
  })
  ..addListener(() {
    setState(() {
      _size = _controller.value;
    });
  });

只需監聽動畫狀態變化,在動畫結束後再正向/反向再次執行動畫。

雖然上面講了很多,但只有一個重點 AnimationController

AnimationController 設置的最小/大值類型是 double,如果動畫的變化是顏色要如何處理?

AnimationController 在執行動畫期間返回的值是 0 到 1,顏色從藍色變為紅色方法如下:

_controller =
    AnimationController(vsync: this, duration: Duration(milliseconds: 500))
      ..addListener(() {
        setState(() {
          _color = Color.lerp(_startColor, _endColor, _controller.value);
        });
      });

重點是 Color.lerp 方法,此方法是在兩種顏色之間線性插值。

完整代碼如下:

class TweenDemo extends StatefulWidget {
  @override
  _TweenDemoState createState() => _TweenDemoState();
}

class _TweenDemoState extends State<TweenDemo>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
	Color _startColor = Colors.blue;
  Color _endColor = Colors.red;

  Color _color = Colors.blue;

  @override
  void initState() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: Duration(milliseconds: 500))
          ..addListener(() {
            setState(() {
              _color = Color.lerp(_startColor, _endColor, _controller.value);
            });
          });
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: GestureDetector(
        onTap: () {
          _controller.forward();
        },
        child: Container(
          height: 100,
          width: 100,
          color: _color,
          alignment: Alignment.center,
          child: Text(
            '點我變色',
            style: TextStyle(color: Colors.white, fontSize: 18),
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
  }
}

Flutter 中把這種從 0 -> 1 轉換為 藍色 -> 紅色 行為稱之為 Tween(映射)

使用 Tween 完成動畫 藍色 -> 紅色:

class _TweenDemoState extends State<TweenDemo>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation<Color> _animation;

  @override
  void initState() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: Duration(milliseconds: 500))
          ..addListener(() {
            setState(() {});
          });
    _animation =
        ColorTween(begin: Colors.blue, end: Colors.red).animate(_controller);
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: GestureDetector(
        onTap: () {
          _controller.forward();
        },
        child: Container(
          height: 100,
          width: 100,
          color: _animation.value,
          alignment: Alignment.center,
          child: Text(
            '點我變色',
            style: TextStyle(color: Colors.white, fontSize: 18),
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
  }
}

效果和上面是一樣的。

Tween 僅僅是映射,動畫的控制依然由 AnimationController 控制,因此需要 Tween.animate(_controller) 將控制器傳遞給Tween。

系統提供了大量的 Tween:

基本上常用的屬性都包含了其對應的 Tween,看一下 ColorTween 的源代碼實現:

本質上也是使用 Color.lerp 實現的。

Curve

動畫中還有一個重要的概念就是 Curve,即動畫執行曲線。Curve 的作用和 Android 中的 Interpolator(差值器)是一樣的,負責控制動畫變化的速率,通俗地講就是使動畫的效果能夠以勻速、加速、減速、拋物線等各種速率變化。

藍色盒子大小 100 變大到 200,動畫曲線設置為 bounceIn(彈簧效果)

class CurveDemo extends StatefulWidget {
  @override
  _CurveDemoState createState() => _CurveDemoState();
}

class _CurveDemoState extends State<CurveDemo>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation _animation;

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

    _animation = Tween(begin: 100.0, end: 200.0)
        .chain(CurveTween(curve: Curves.bounceIn))
        .animate(_controller);
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: GestureDetector(
        onTap: () {
          _controller.forward();
        },
        child: Container(
          height: _animation.value,
          width: _animation.value,
          color: Colors.blue,
          alignment: Alignment.center,
          child: Text(
            '點我變大',
            style: TextStyle(color: Colors.white, fontSize: 18),
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
  }
}

動畫加上Curve 後,AnimationController 的最小/大值必須是 [0,1]之間,例如下麵的寫法就是錯誤的:

_controller =
    AnimationController(vsync: this, duration: Duration(milliseconds: 1000),lowerBound: 100.0,upperBound: 200.0)
      ..addListener(() {
        setState(() {});
      });
_animation = CurveTween(curve: Curves.bounceIn).animate(_controller);

拋出如下異常:

正確寫法:

_controller =
        AnimationController(vsync: this, duration: Duration(milliseconds: 1000))
          ..addListener(() {
            setState(() {});
          });

    _animation = Tween(begin: 100.0, end: 200.0)
        .chain(CurveTween(curve: Curves.bounceIn))
        .animate(_controller);

系統已經提供了38種常用到動畫曲線:

linear

decelerate

bounceIn

bounceOut

elasticIn

其餘動畫效果可以官方文檔查看。

通常情況下,這些曲線能夠滿足 99.99% 的需求,很多時候設計也就是告訴你動畫 先快後慢 或者 先慢後快,所以選個類似的就可以了,但有一些 特別 的設計非要一個系統沒有的動畫曲線,要怎麼辦?

那就自定義一個動畫曲線

其實自定義一個動畫曲線難點在 數學 上,怎麼把數學公式用代碼實現才是難點。

下麵是一個 樓梯效果 的動畫曲線:

自定義動畫曲線需要繼承 Curve 重寫 transformInternal 方法即可:

class _StairsCurve extends Curve {

  @override
  double transformInternal(double t) {
    return t;
  }
}

直接返回 t 其實就是線性動畫,即 Curves.linear,實現樓梯效果動畫代碼如下:

class _StairsCurve extends Curve {
  //階梯的數量
  final int num;
  double _perStairY;
  double _perStairX;

  _StairsCurve(this.num) {
    _perStairY = 1.0 / (num - 1);
    _perStairX = 1.0 / num;
  }

  @override
  double transformInternal(double t) {
    return _perStairY * (t / _perStairX).floor();
  }
}

修改開始處的案例,使用此曲線:

_animation = Tween(begin: 100.0, end: 200.0)
    .chain(CurveTween(curve: _StairsCurve(5)))
    .animate(_controller);

總結

動畫系統的核心是 AnimationController,而且是不可或缺的,動畫中必須有 AnimationController,而 Tween 和 Curve 則是對 AnimationController 的補充, Tween 實現了將 AnimationController [0,1]的值映射為其他類型的值,比如顏色、樣式等,Curve 是 AnimationController 動畫執行曲線,預設是線性運行。

將 AnimationController 、 Tween 、Curve 進行關聯的方式:

AnimationController _controller;
Animation _animation;

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

  _animation = Tween(begin: 100.0, end: 200.0)
      .animate(_controller);
}

或者:

_animation = _controller.drive(Tween(begin: 100.0, end: 200.0));

加入 Curve :

_animation = Tween(begin: 100.0, end: 200.0)
    .chain(CurveTween(curve: Curves.linear))
    .animate(_controller);

或者:

_animation = _controller
    .drive(CurveTween(curve: Curves.linear))
    .drive(Tween(begin: 100.0, end: 200.0));

只需要 Curve :

_animation = CurveTween(curve: Curves.linear)
    .animate(_controller);

或者

_animation = _controller.drive(CurveTween(curve: Curves.linear));

一個 AnimationController 可以對應多個 Animation(Tween 或者 Curve),StatefulWidget 組件可以包含多個 AnimationController ,但 SingleTickerProviderStateMixin 需要修改為 TickerProviderStateMixin,改變顏色和大小,由兩個 AnimationController 控制:

class MultiControllerDemo extends StatefulWidget {
  @override
  _MultiControllerDemoState createState() => _MultiControllerDemoState();
}

class _MultiControllerDemoState extends State<MultiControllerDemo>
    with TickerProviderStateMixin {
  AnimationController _sizeController;
  AnimationController _colorController;
  Animation<double> _sizeAnimation;
  Animation<Color> _colorAnimation;

  @override
  void initState() {
    super.initState();
    _sizeController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 2000))
          ..addListener(() {
            setState(() {});
          });

    _sizeAnimation = _sizeController
        .drive(CurveTween(curve: Curves.linear))
        .drive(Tween(begin: 100.0, end: 200.0));

    _colorController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 1000))
          ..addListener(() {
            setState(() {});
          });

    _colorAnimation = _colorController
        .drive(CurveTween(curve: Curves.bounceIn))
        .drive(ColorTween(begin: Colors.blue, end: Colors.red));
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: GestureDetector(
        onTap: () {
          _sizeController.forward();
          _colorController.forward();
        },
        child: Container(
          height: _sizeAnimation.value,
          width: _sizeAnimation.value,
          color: _colorAnimation.value,
          alignment: Alignment.center,
          child: Text(
            '點我變化',
            style: TextStyle(color: Colors.white, fontSize: 18),
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _sizeController.dispose();
    _colorController.dispose();
  }
}

AnimationController 、Tween 、Curve 是整個動畫的基礎,Flutter 系統封裝了大量了動畫組件,但這些組件也是基於此封裝的,因為深入瞭解這三部分比學習使用動畫組件更重要,再次對這3個進行總結:

  • AnimationController:動畫控制器,控制動畫的播放、停止等。繼承自Animation< double >,是一個特殊的Animation對象,預設情況下它會線性的生成一個0.0到1.0的值,類型只能是 double 類型,不設置動畫曲線的情況下,可以設置輸出的最小值和最大值。
  • Curve:動畫曲線,作用和Android中的Interpolator(差值器)類似,負責控制動畫變化的速率,通俗地講就是使動畫的效果能夠以勻速、加速、減速、拋物線等各種速率變化。
  • Tween:將 AnimationController 生成的 [0,1]值映射成其他屬性的值,比如顏色、樣式等。

完成一個動畫效果的過程如下:

  1. 創建 AnimationController
  2. 如果需要 Tween 或者 Curve,將 AnimationController 與其關聯,Tween 和 Curve 並不是必須的,當然大部分情況都需要。
  3. 將動畫值作用於組件,當沒有Tween 和 Curve 時,動畫值來源於AnimationController,如果有 Tween 和 Curve,動畫值來源於 Tween 或者Curve 的 Animation。

如果你發現閱讀完此篇文章還是感覺不會寫動畫,不要灰心,這是正常的,第一次想瞭解這些抽象的概念還是比較困難的,如果你有其他平臺的相關經驗,那會好很多,對於動畫,想要掌握個人認為只有一個方法就是 多寫

後面會介紹動畫組件基礎使用、實現原理、高級動畫以及自定義動畫,把每一個動畫組件的用法都親自手寫一遍(而不是複製黏貼),回過頭來在看這篇文章,會有不一樣的感覺。

交流

老孟Flutter博客地址(330個控制項用法):http://laomengit.com

歡迎加入Flutter交流群(微信:laomengit)、關註公眾號【老孟Flutter】:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 2020年是充滿機遇與挑戰的一年,這一年註定不平凡。隨著5G商用、傳統產業數字化轉型加快,我們完全可以想象到互聯網行業即將迎來自己的“高光時刻”,因此,現在學習web前端正當時。 那麼,對於零基礎小白而言,如何才能快速學習web前端呢?下麵,就為大家分享2020年web前端學習路線。 很多對於web ...
  • 本文將介紹 CSS 中一個非常有意思的屬性 mask 。 顧名思義,mask 譯為遮罩。在 CSS 中,mask 屬性允許使用者通過遮罩或者裁切特定區域的圖片的方式來隱藏一個元素的部分或者全部可見區域。 其實 mask 的出現已經有一段時間了,只是沒有特別多實用的場景,在實戰中使用的非常少,本文將羅 ...
  • 背景 我是一名程式員,從事前端開發,一個月15K,在北京雖是月光族但日子也還可以過下去;女朋友就沒有這麼幸運了,受疫情的影響,公司裁員。。。從4月開始就一直在找工作,但都不順利,不是工資低到可憐就是面試後沒有下文; 身邊的朋友都開玩笑的說,你也可以和你男朋友一樣從事前端開發呀!!! 我天。。。反對。 ...
  • 上次成功搭建了vue + electron的helloworld程式,這次將electron應用打包及自動升級的流程梳理了一下。 1. 應用打包 使用electron builder打包只需要在vue.config.js中配置即可,這裡需要註意的是,預設情況下electron builder打包出來 ...
  • 消息隊列 基本概述 MQ,Message Queue,基於TCP協議構建的簡單協議,區別於具體的通信協議。 基於通信協議定義和抽象的更高層次的通信模型,一般都是生產者和消費者模型,又或者說服務端和客戶端模型。 生產者/消費者模型:一般通過定義生產者和消費者實現消息通信從而屏蔽複雜的底層通信協議。應用 ...
  • 【本文完善中...】 無論是http介面,還是rpc介面,防重覆提交(介面防重)都是繞不過的話題。 重覆提交與冪等,既有區別,又有聯繫。冪等的意思是,對資源的一次請求與多次請求,作用是相同的。HTTP的POST方法是非冪等的。如果程式處理不好,重覆提交會導致非冪等,引起系統數據故障。防重覆提交,要通 ...
  • 1.模型管理 :web線上流程設計器、預覽流程xml、導出xml、部署流程 2.流程管理 :導入導出流程資源文件、查看流程圖、根據流程實例反射出流程模型、激活掛起 、自由跳轉 3.運行中流程:查看流程信息、當前任務節點、當前流程圖、作廢暫停流程、指派待辦人 4.歷史的流程:查看流程信息、流程用時、流 ...
  • 一、sentinel的持久化配置 上一章中我們通過Dashboard來為Sentinel客戶端設置各種各樣的規則,但是這些規則預設是存放在記憶體中,極不穩定,無法用於生成環境,所以需要將其持久化。 DataSource 擴展常見的實現方式有: 拉模式:客戶端主動向某個規則管理中心定期輪詢拉取規則,這個 ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...