Code前端首页关于Code前端联系我们

Flutter模仿抖音TikTok手势交互(二)

terry 2年前 (2023-09-23) 阅读数 69 #移动小程序

在之前的手势交互文章中,我们学习了GestureDetector、Transform和Hero动画,并在TikTok中实现了几种手势交互效果。

我们来看看这个实现的效果:Flutter仿写抖音TikTok手势交互(二)

Gif:user-gold-cdn.xitu.io/2019/4/25/1…

Github地址:github.com/ditclear/十…

下载体验

Flutter仿写抖音TikTok手势交互(二)

交互分解

这次主要包括两个下拉交互:下拉刷新和下拉返回。

  • 向下滑动刷新

Flutter仿写抖音TikTok手势交互(二)

Gif:user-gold-cdn.xitu.io/2019/4/25/1…

手指向下滑动时,offsetY发生变化。这里有两个变化:

  1. Transparency
  2. y轴方向偏移

Transparency 这里可以使用Flutter提供的Opacity组件,专门用来改变透明度,使用也很简单。

Opacity(
  opacity: currentOpacity,
  child:childWidget
)  
复制代码

只需传入不透明度即可。我们需要做的就是用offsetY 来表示不透明度的值。

值得注意的是,这里有两个透明度变化:顶部导航栏下拉更新内容文本

而且两者不会同时出现。当你向下拖动时,顶部导航栏会逐渐变成透明,当达到一定距离(可以认为是最大下拉距离的一半)时,就会隐藏。 向下拖动 文字更新内容后才会出现,其透明度逐渐变得不透明。

我们设置下拉菜单的最大滑动距离为40,那么临界点就是20。

从这里我们可以推导出顶部导航栏透明度变化的公式下拉距离。

opacity = 1 - offsetY / 20

因为offsetY最大可以为40,并且不透明度必须在0到1之间。因此,最终的公式为:

opacity = max offset(Y, / 1) 20)

具体实现:Flutter仿写抖音TikTok手势交互(二)

向下拖动时,记录总偏移量offsetY,检查是否大于0且不超过最大距离。

onVerticalDragUpdate: (details) {
        final tempY = offsetY + details.delta.dy / 2;
        if (currentIndex == 0) {
          //最大下拉距离不超过40
          if (tempY > 0) {
            if (tempY < 40) {
              setState(() {
                offsetY = tempY;
              });
            } else if (offsetY != 40) {
              setState(() {
                setState(() {
                  offsetY = 40;
                });
              });
              // 当下拉到最大距离时,触发震动效果
              vibrate();
            }
          }
        } else {
          offsetY = 0;
        }
      }

复制代码

当向下拖动到最大距离时,可以使用震动产生震动效果。

当手指离开屏幕时,执行另一个动画,将OffsetY设置为0,并通过setState告诉UI渲染。

// 下拉结束
onVerticalDragEnd: (_) {
    if (offsetY != 0) {
       animateToTop();
     }
}

/// 滑动到顶部
///
/// [offsetY] to 0.0
void animateToTop() {
  animationControllerY =
      AnimationController(duration: Duration(milliseconds: offsetY.abs() * 1000 ~/ 40), vsync: this);
  final curve = CurvedAnimation(parent: animationControllerY, curve: Curves.easeOutCubic);
  animationY = Tween(begin: offsetY, end: 0.0).animate(curve)
    ..addListener(() {
      setState(() {
        offsetY = animationY.value;
      });
    });
  animationControllerY.forward();
}
复制代码
  • 下拉返回

Flutter仿写抖音TikTok手势交互(二)

Gif:user-gold-cdn.xitu.io/2019/4/25/1…

简单来说,下拉的时候整个页面Y方向平移-轴方向,当超过指定距离时,直接退出本页面。

// 滑动截止时
onPanEnd: (_) {
  if (offsetY > 100) {
    // 下拉距离超过100,即退出页面
    Navigator.pop(context);
  } else if (offsetY > 0) {
    // 下拉距离小于100,恢复原样
    animateToBottom(screenHeight);
  } else if (offsetY < 0) {
    // 上拉根据是否已经显示评论框 [isCommentShow]和offsetY来判断是展开还是收缩
    if (!isCommentShow && offsetY.abs() > screenHeight * 0.2) {
      if (offsetY.abs() > screenHeight * 0.2) {
        animateToTop(screenHeight);
      } else {
        animateToBottom(screenHeight);
      }
    } else {
      if (offsetY.abs() > screenHeight * 0.4) {
        animateToTop(screenHeight);
      } else {
        animateToBottom(screenHeight);
      }
    }
  }
},
复制代码

如果页面有偏移,请在最外层添加一层Transform.translate。注意,offset必须大于0。

Transform.translate(
  offset: Offset(0, max(0, offsetY)),
  child: childWidget
  )
复制代码

当你完成了申诉的基本逻辑后,运行后你会发现和你预想的还是有一些差别。 Flutter仿写抖音TikTok手势交互(二)

背景是不透明的。当时我以为是黑色背景,需要设置背景颜色的透明度。然而,我在ThemeData中尝试了各种可疑的颜色,发现没有效果。后来我想是PageRoute的原因。 。

当我们调用Navigator进行推送操作时,我们必须发送一个PageRoute,比如常用的Material❀路线或CupertinoPageRoute ,这是PageRoute 中的opaque 表示不透明,并且始终为真。

@override
bool get opaque => true;
复制代码

为了解决上述问题,这里复制一份MaterialPageRoute的源代码,然后将opaque更改为false。

/// copy 一份 MaterialPageRoute,修改opaque
class TransparentPage<T> extends PageRoute<T> {
  //...
  /// false 代表背景透明
  @override
  bool get opaque => false;
   
  //...  
}
复制代码

最后,在上一篇文章中,有同学问如何将列表中的Hero动画化。我还在代码中进行了模拟,以确保英雄标签是相同的。在新旧路由之间切换时,Flutter 将为相同的标签设置动画。 Hero 小部件是动画的。

/// detail_page.dart
child: Hero(
  tag: "detail_$currentIndex",
  child: GestureDetector(
      child:PageView(
                onPageChanged: (index) {
                  setState(() {
                    currentIndex = index;
                  });
                },
          //..
         ),
      ),
    )
    
/// right_page.dart
child: Hero(
      tag: "detail_0",
      child: Image.asset(
             assets/detail.png",
             fit: BoxFit.fill,
       ),
   ),
复制代码

写在最后

有了手势交互的经验,实现以上效果并不难。

需要花点时间的问题是背景颜色,但是Flutter是开源的,注释和用例都写得很详细。如果Flutter框架不能满足您的需求,您可以完全修改底层Flutter源码来实现。期望的效果。

至此,本文的内容就结束了。还剩下一步来处理手势冲突。这个内容会放在下一篇文章中。关注我,获取最新文章。

Github地址:github.com/ditclear/ti…

作者:ditclear
来源:掘金

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门