Futterがドトーンポイントの絶賛効果を実現します。


効果図は以下の通りです

分析効果
1.コントロール全体をクリックして、コントロールは画面上に敷き詰められ、効果はサブウィジェットの上にあります。
2.クリックが可能です
3.連撃の場合、ハートのアニメ効果が現れ、ハートが重なる
4.動画の位置は指のクリック位置によって変わります。
考え方
1.ジェスチャーをGestureDetectorで監視し、指を上げた時に時間ミリ秒の値を記録し、指を再度押した時に、ミリ秒の値と前の時間ミリ秒の値との差を取得することで、クリックするか、ハート動画効果が現れるかを判断します。
2.Stockコンポーネントを使用して、ビューの階層を決定します。例えば、ビデオビューでポイント効果を達成するためには、ビデオビューコンポーネントはサブコンポーネントとして、下の階に置かれます。
3.ハートのアニメーション効果は一つ一つ実行するのではなく、指のクリック位置と時間によって実行されます。どうやってこのような需要を実現しますか?リストを使って指でクリックした位置を記憶し、巡回してリストの位置を取得し、対応する位置でアニメーションを実行しながらリストから削除します。
アニメーションについては、Futterが提供する動画ウィジェットで実現できます。
実現する
1.外部公開のパラメータ
Sttefule Widgetを継承することにより、カスタマイズ部品を実現します。

 :LikeGestureWidget

  const LikeGestureWidget({
    Key? key,
    required this.child,
    this.onAddFavorite,
    this.onSingleTap,
  }) : super(key: key);

  final Function? onAddFavorite;//       
  final Function? onSingleTap;//     
  final Widget child;//   
2.必要なオブジェクトを宣言する
_にありますLikeGesture Widget Stateで必要なオブジェクトを宣言します。

 :_LikeGestureWidgetState

  GlobalKey _key = GlobalKey();//       
  List<Offset> icons = [];//      
  int lastMilliSeconds = -1;//              
3.ジェスチャーの処理
私たちはGesture Detectorを通して指の押しと持ち上げのイベントを監督します。
lastMilliSecondsを使って持ち上げた時の時間ミリ秒値を記録し、onTapDownで戻した時に現在の時間ミリ秒値を取得し、持ち上げることと押すことによる時間差を設定します。ここで、差分値が500ミリ秒未満であれば、リストにこの時の指のクリック位置を追加し、500ミリ秒以上であれば、イベントをクリックしながらUIを再構築します。
Gesture Detectorは一番外側の部品で、ジェスチャーのポイント効果をコントロールしながらレイアウトをコントロールします。StockをGesture Detectorのサブパーツとして使い、Stockは積層レイアウトであり、外部の部品を小ハート動画ビューの下に置く。

 :_LikeGestureWidgetState

 @override
  Widget build(BuildContext context) {

    return GestureDetector(
      key: _key,
      onTapDown: (detail) {

        setState(() {
          //          
          int currentMilliSeconds = DateTime.now().millisecondsSinceEpoch;
          //                   
          int diff = currentMilliSeconds - lastMilliSeconds;
          //      500  ,                ,    500        
          if(diff < 500){
            icons.add(_convertPosition(detail.globalPosition));
            widget.onAddFavorite?.call();
          }else{
            widget.onSingleTap?.call();
          }
        });
      },
      onTapUp: (detail) {
        //           
        lastMilliSeconds = DateTime.now().millisecondsSinceEpoch;
      },

      child: Stack(
        children: <Widget>[
          //     ,       
          widget.child,
          //       
          _getIconStack(),
        ],
      ),
    );
   }
  //                               。
  Offset _convertPosition(Offset p) {
    RenderBox getBox = _key.currentContext!.findRenderObject() as RenderBox;
    return getBox.globalToLocal(p);
  }
4.アニメのハートのレイアウト
はい、私達は今紅ちゃんの心動効果を手配します。ハートは重ねて表示できるので、小さなハートの父のレイアウトとしてStck部品を使用します。リストiconsを巡回してStckのサブパーツを作成します。

 :_LikeGestureWidgetState

 _getIconStack() {
    return Stack(
      children: icons.map<Widget>(
            (position) => TikTokFavoriteAnimationIcon(
              key: Key(position.toString()),
              position: position,
              onAnimationStart: () {
            icons.remove(position);
          },
        ),
      ).toList(),
    );
  }
5.小紅の心悸効果
ハートの動的効果を個々の部品として抽出しました。構造関数は以下の通りです。

 :TikTokFavoriteAnimationIcon

  final Offset? position;//  
  final double size;//    
  final Function? onAnimationStart;//       

  const TikTokFavoriteAnimationIcon({
    Key? key,
    this.onAnimationStart,
    this.position,
    this.size: 160,
  }) : super(key: key);
_。TikTok FavoriteAnimation Iconstateでは、アニメーションコントローラおよびハートの回転角度を宣言していますが、セットステーションのたびに回転角度がランダムに生成されます。

 :_TikTokFavoriteAnimationIconState

  //     
  AnimationController? _animationController;
  //       ,  
  double rotate = pi / 10.0 * (2 * Random().nextDouble() - 1);
  
    @override
  void initState() {
    _animationController = AnimationController(
      lowerBound: 0,
      upperBound: 1,
      duration: Duration(milliseconds: 1000),
      vsync: this,
    );

    _animationController!.addListener(() {
      setState(() {});
    });
    startAnimation();
    super.initState();
  }
アニメを始める時は、アニメの位置を削除します。

  //    ,     ,              
  startAnimation() async {
    await _animationController!.forward();
    widget.onAnimationStart?.call();
  }
レッドハートのレイアウトの位置をどう表示しますか?以前はStck部品をレイアウトとして使っていましたが、これは簡単です。Position部品を使って、小さなハートの位置を正確に決められます。また、指で位置を押すと赤いハートの位置をコントロールすることもできます。

  @override
  Widget build(BuildContext context) {
    return widget.position == null
        ? Container()
        : Positioned(
            left: widget.position!.dx - widget.size /2,
            top: widget.position!.dy - widget.size ,
            child: _getBody(),
          );
  }
小さなハートの動きは、回転、不透明度、スケーリングの3つの効果と、グラデーションの効果があります。

 _getBody() {
    return Transform.rotate(
      angle: rotate,
      child: Opacity(
        opacity: opacity,
        child: Transform.scale(
          alignment: Alignment.bottomCenter,
          scale: scale,
          child: _getContent(),
        ),
      ),
    );
  }


  //      
  double get value => _animationController!.value;

  //         
  double get opacity {
    if (value < 0.1) {
      return 0.9 / 0.1 * value;
    }
    if (value < 0.8) {
      return 0.9;
    }
    var res = 0.9 - (value - 0.8) / (1 - 0.8);
    return res < 0 ? 0 : res;

  }

  //         
  double get scale {
    if(value <= 0.5){
      return  0.6+value / 0.5 * 0.5;
    }else if(value<=0.8){
      return 1.1 * (1/1.1 + (1.1 -1)/1.1 * (value - 0.8) / 0.25);
    }else {
      return 1 + (value - 0.8)/0.2 * 0.5;
    }
  }

  _getContent() {
    return ShaderMask(
      child: _getChild(),
      blendMode: BlendMode.srcATop,
      shaderCallback: (Rect bounds) => RadialGradient(
        center: Alignment.topLeft.add(Alignment(0.5, 0.5)),
        colors: [
          Color(0xffEF6F6F),
          Color(0xffF03E3E),
        ],
      ).createShader(bounds),
    );
  }
  //  
  _getChild() {
    return Icon(Icons.favorite_rounded,size: widget.size,);
  }
効果を使う

ソース
ソースのプロジェクトでは、クラス名:LikeGesture Widget.
ソースの手ぶれのプロジェクトのソースコードの住所
以上がFutterの手ブレポイントの効果を実現するための詳細です。Futterの手ブレポイントの効果に関する資料は他の関連記事に注目してください。