Flutterカスタムで不思議な効果を実現するカード切替ビュー

11802 ワード

前言
この間、Flutterの勢いはますます強くなり、Androidプログラム猿として、私も自然と急いで試してみたいと思っています.アニメのこの部分を勉強した後、Flutterアニメーションの実現に対する理解を深めるために、前に書いたカード切替効果のオープンソースの小さな項目を、Flutterで「翻訳」することにしました.
くだらないことは言わないで、まず効果を見てみましょう.
Android
iOS
Githubアドレス:github.com/BakerJQ/Flu…
構想
まず、カードの積層効果について、元Androidプロジェクトでは、Scale差異やTranslationYによって体現されており、Flutterはこの方式を引き続き採用することができる.
次に、カスタムカードの内容については、元AndroidプロジェクトがAdapterで実現され、FlutterについてはIndexedWidgetBuilderで実現される.
最後に、カスタマイズの効果の実現であり、元Androidプロジェクトは0から1のValueAnimatorを通じてアニメーションの展示過程を定義し、Flutterには、それに対応するAnimationとAnimationControllerがあり、アニメーションの過程で具体的なビューの展示方式を直接カスタマイズすることができます.
コンポーネント一覧
カードビューはアニメーションの状況に応じてレンダリングする必要があるため、明らかにStatefulWidgetです.
また、3つの基本的なアニメーションモードを示します.
enum AnimType {
  TO_FRONT,//                 ,             
  SWITCH,//               ,        
  TO_END,//                ,            
}

すべてのアニメーションロジックをHelperとControllerで処理
ここでControllerは構築方法によって入力される
InfiniteCards({
  @required this.controller,
  this.width,
  this.height,
  this.background,
});

HelperはinitStateで構築され、初期化され、同時にHelperをControllerにバインドします.
@override
void initState() {
  ...
  _helper = AnimHelper(
      controller: widget.controller,
      //        ,     setState      
      listenerForSetState: () {
        setState(() {});
      });
  _helper.init(this, context);
  if (widget.controller != null) {
      widget.controller.animHelper = _helper;
  }
}

buildプロセスではHelperによって特定のWidgetリストが返され,Stackは積層効果を実現するためである.
Widget build(BuildContext context) {
  ...
  return Container(
    ...
    child: Stack(
      children: _helper.getCardList(_width, _height),
    ),
  );
}

これで、基本的な初期化などの操作は完了します.次に、ControllerとHelperがどのように働いているかを見てみましょう.
Controller
まず、Controllerに含まれる内容を見てみましょう.
class InfiniteCardsController {
  //     
  IndexedWidgetBuilder _itemBuilder;
  //    
  int _itemCount;
  //    
  Duration _animDuration;
  //            
  bool _clickItemToSwitch;
  //  Transform
  AnimTransform _transformToFront,_transformToBack,...;
  //  Transform
  ZIndexTransform _zIndexTransformCommon,...;
  //    
  AnimType _animType;
  //    ( Android   )
  Curve _curve;
  //helper
  AnimHelper _animHelper;
  ...
  void anim(int index) {
    _animHelper.anim(index);
  }
  void reset(...) {
    ...
    //     
    setControllerParams();
    _animHelper.reset(); 
    ...
  }
}

このことから,Controllerは基本的にパラメータコンフィギュレータとHelperのメソッドエージェントとして存在することが分かる.これにより、子供靴たちは、動効のカスタマイズや動効のトリガなどの操作がControllerで行われていることを知っているに違いありません.demoは以下のようにします.
//  Controller
_controller = InfiniteCardsController(
  itemBuilder: _renderItem,
  itemCount: 5,
  animType: AnimType.SWITCH,
);
//  reset
_controller.reset(
  itemCount: 4,
  animType: AnimType.TO_FRONT,
  transformToBack: _customToBackTransform,
);
//           
_controller.reset(animType: AnimType.TO_END);
_controller.next();

具体的なカスタマイズについては、後で話しましょう.まずHelperを見てみましょう.
Helper
Helperはアニメーション効果全体を実現するコアクラスです.まず、コアメンバーをいくつか見てみましょう.
class AnimHelper {
  final InfiniteCardsController controller;
  //    
  AnimationController _animationController;
  Animation<double> _animation;
  //    
  List _cardList = new List();
  //         ,          
  CardItem _cardToBack, _cardToFront;
  //           ,            
  int _positionToBack, _positionToFront;
}

次に、切り替えアニメーションをトリガーする場合、これらのメンバーがどのように協力しているかを見てみましょう.
1枚のカードを選択して切り替えると、このカードは前に切り替える必要があるカード(ToFront)であり、1枚目のカードは、後に切り替える必要があるカード(ToBack)である.
void _cardAnim(int index, CardItem card) {
  //        
  _cardToFront = card;
  _cardToBack = _cardList[0];
  _positionToBack = 0;
  _positionToFront = index;
  //    
  _animationController.forward(from: 0.0);
}

AnimationListenerが設定されているため、アニメーション中にsetStateが呼び出され、Widgetのbuildがトリガーされ、HelperのgetCardListメソッドがトリガーされます.アニメーションを切り替える過程で、カードWidgetリストに戻る方法を見てみましょう.
List getCardList(double width, double height) {
  for (int i = 0; i < controller.itemCount; i++) {
    ...
    if (_isSwitchAnim) {
      //      
      _switchTransform(width, height, i);
    }
    ...
  }
  //  zIndex      
  List copy = List.from(_cardList);
  copy.sort((card1, card2) {
    return card1.zIndex < card2.zIndex ? 1 : -1;
  });
  return copy.map((card) {
    return card.transformWidget;
  }).toList();
}

上記のコードに示すように、アニメーション処理を先に行い、zIndexに従ってソートします.前の後レンダリングを保証するためです.
アニメーションはどのように処理されているのか、前のカードに切り替える例を示します.
void _toFrontTransform(double width, double height, int fromPosition, int toPosition) {
    CardItem cardItem = _cardList[fromPosition];
    controller.zIndexTransformToFront(
        cardItem, _animation.value,
        _getCurveValue(_animation.value),
        width, height, fromPosition, toPosition);
    cardItem.transformWidget = controller.transformToFront(
        cardItem.widget, _animation.value,
        _getCurveValue(_animation.value),
        width, height, fromPosition, toPosition);
  }

もともと、このステップでは、HelperがControllerで設定したカスタムアニメーション方式で、カードのWidgetを得た.
これにより、アニメーション表示の基本的な流れが説明され、次に最も重要な部分であるアニメーションをカスタマイズする方法について説明します.
カスタムアニメーション
一般的なアニメーションを例に、カスタムアニメーションの主な流れを見てみましょう.
まず、AnimTransformは、以下の方法の定義である.
typedef AnimTransform = Transform Function(
    Widget item,//    Widget
    double fraction,//       
    double curveFraction,//        
    double cardHeight,//    
    double cardWidth,//    
    int fromPosition,//      
    int toPosition);//         

この方法は,ビュー変換を処理するためのWidgetに特化したTransformを返し,我々が行うべきことは,伝達されたパラメータに基づいて対応する係数の下のWidgetを構築することである.DefaultCommonTransformの例:
Transform _defaultCommonTransform(Widget item, 
    double fraction, double curveFraction, double cardHeight, double cardWidth, int fromPosition, int toPosition) 
  //         {
  int positionCount = fromPosition - toPosition;
  // 0.8          ,       0.1
  //(0.8 - 0.1 * fromPosition) =          
  //(0.1 * fraction * positionCount) =                
  double scale = (0.8 - 0.1 * fromPosition) + (0.1 * fraction * positionCount);
  // Y      ,     ,         0.02
  //-cardHeight * (0.8 - scale) * 0.5           
  double translationY = -cardHeight * (0.8 - scale) * 0.5 -
      cardHeight * (0.02 * fromPosition - 0.02 * fraction * positionCount);
  //     ,  Y     Widget
  return Transform.translate(
    offset: Offset(0, translationY),
    child: Transform.scale(
      scale: scale,
      child: item,
    ),
  );
}

1位に移動する選択カードについても同様であり、そのカードに対応する変換器によってカスタムアニメーションの変換が行われるにすぎない.
最後の効果は、プレゼンテーション図で最初にクリックし、画像を前に1位に反転させる効果のようなものです.
まとめ
Flutterは宣言的なビュー構築方式を採用しているため,符号化初期には原生符号化方式の思考の影響を多少受けることがあり,つらいと感じている.しかし、慣れると、アニメーションや補間器の概念など、共通の考えがたくさんあることがわかります.
また、ソースコードの読み取りは、Androidで直接ScrollViewをanimateTo操作するよりも、FlutterでScrollControllerでanimateTo操作する必要がある点で、FlutterでInfiniteCards効果を実現する方法を見つけました.
もっと具体的なDemoはGithubのFlutter-InfiniteCards Repoに行ってください.starとティissueを歓迎します.
もう一度Githubアドレスを貼ります:github.com/BakerJQ/Flu…
転載先:https://juejin.im/post/5ca375f3e51d451a18362e2a