Flutter仿抖音TikTok手势交互(一)
Flutter是Google推出并开源的移动应用开发框架,专注于跨平台、高可靠性和高性能。开发者可以通过Dart语言开发应用程序,一组代码同时运行在iOS和Android平台上。
抖音,英文名为TikTok,是一款风靡全球的短视频应用程序。在玩抖音的日子里,最让我舒服的就是抖音的手势交互。另外最近研究了Flutter,所以就产生了用Flutter来模仿TikTok手势交互的想法。
我们来看看效果:
Gif:giphy.com/gifs/Y0nMQw…
Github地址:github.com/ditclear/ti…
下载 demo GestureDetector And Transform 自从它是手势交互,需要检测手势。 Flutter 提供了 Transform可以在绘制时对其子控件应用矩阵变换(变换)。 Matrix4是一个4D矩阵,通过它我们可以实现各种矩阵运算。 效果如下: Flutter 提供了多种加密变换效果供开发者选择,例如:平移、旋转、缩放。 明白了这两点之后,我们来一点一点分解之前的效果。 首先必须明确,这些交互效果实际上是通过捕获手指的滑动,得到x坐标或者y坐标的偏移量,然后使用Transform进行各种变换来获得的。明白了此时实现这样的效果并不困难。 Gif:user-gold-cdn.xitu.io/2019/4/20/1… 这里的互动都是水平滑动,所以这里主要处理❀ onHorizontalDragXX 相关事件。 然后我们看一下主页的布局: 左:拍摄页面 中:主页右:用户页面 外层是手势整个页面上的 这里我们选择拍摄面(左)来详细说一下。 通过观察可以看到,随着偏移的变化,这里其实有两个变化:1.缩放2.前景色透明度。 缩放可以直接使用上面的 当我们手指水平移动时,会记录总量 通过setState更新偏移量OffsetX后,Flutter会重绘视图,达到上面的效果。 Gif:user-gold-cdn.xitu.io/2019/4/20/1… Flutter提供了英雄动画来实现这样的过渡效果。 Hero指的是可以在轨道(页面)之间“飞行”的小部件。简单来说,英雄动画就是当路线改变时,有一个共享的widget可以在新旧路线之间进行切换。由于共享widget在新旧路由页面上,位置和外观可能不同,所以在路由切换时会有一个逐渐的过渡,从而产生英雄动画。 确保标签一致。 Gif:user-gold-cdn.xitu.io/2019/4/20/1… 和首页的思路一样,只不过这里的手势是垂直的。 的布局也是 使用动画根据手指离开屏幕时的偏移量进行调整。 一般来说,这些交互依赖于手势的检测。与Android相比,Flutter有一切都是widget的理念, Github地址:github.com/ditclear/ti… 作者:ditclearGestureDetector
来帮助开发者,并提供了多个回调来处理手势。属性/回调 描述 onTapDown 每次用户与屏幕交互时调用onTap 短暂触摸屏幕 onTapCancel时触发当用户触摸时触发 onDoubleTap 用户在短时间内 ❀ onLongPress 当用户触摸屏幕超过 500ms 时触发 onVerticalDragDown 当一个接触点开始与屏幕交互并垂直方向移动时触发此回调 onVerticalDragEnd当用户停止时认为完成,此回调被触发 onVerticalDragCancel 当用户突然停止拖动时触发 opHorizontalDragDown 并移动触摸屏与触摸方向交互 onHorizontalDragStart 当触摸点开始水平方向移动时触发 onHorizontalDragUpdate 每次触摸点位置在屏幕上发生变化时触发,该回调被触发En Horizontal 水平拖动结束时触发 onHorizontalDragCancel onHorizontalDragDown 在未成功结束时触发 onPanDown onPanStart 当触摸点开始移动时触发 onPanUpdate 每次触摸点位置在屏幕上发生变化时触发。当平移操作完成时,会触发此回调 onPanEnd 。当触摸点开始与屏幕交互并在 1.0 处建立焦点时,会触发 onScaleStart 。 onScaleUpdate 当触摸点与屏幕交互时触发。同时标记一个新焦点 onScaleEnd 触摸点将不再跟随屏幕屏幕上出现了所有的交互,也意味着缩放手势已经准备好了。 手势检测器
不会侦听上述所有手势。它仅侦听传入回调不为空的情况。所以,如果你想禁用某个手势,可以传入 null 给对应的回调。 这篇文章主要关注画线,比如 Onpanxx
, ❙OnHorizontaldragxx,, , Onverticaldragxx等等。 Container(
color: Colors.black,
child: new Transform(
alignment: Alignment.topRight, //相对于坐标系原点的对齐方式
transform: new Matrix4.skewY(0.3), //沿Y轴倾斜0.3弧度
child: new Container(
padding: const EdgeInsets.all(8.0),
color: Colors.deepOrange,
child: const Text('Apartment for rent!'),
),
),
);
复制代码
交互分解
首页互动
Gesture Detector,里面用的它是一个
stack
,类似于Android中的FrameLayout
。它包含 3 个Transform
子小部件。 Transform.scale
,前景色可以使用ForegroundDecoration
通过改变颜色来改变效果。看一下实现: /// 左侧Widget
///
/// 通过 [Transform.scale] 进行根据 [offsetX] 缩放
/// 最小 0.88 最大为 1
Transform buildLeftPage(double screenWidth) {
return Transform.scale(
scale: 0.88 + 0.12 * offsetX / screenWidth < 0.88 ? 0.88 : 0.88 + 0.12 * offsetX / screenWidth,
child: Container(
child: Image.asset(
"assets/left.png",
fit: BoxFit.fill,
),
foregroundDecoration: BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 1 - (offsetX / screenWidth)),
),
),
);
}
复制代码
offsetX
,然后通过setState进行更新。 onHorizontalDragUpdate: (details) {
// 控制 offsetX 的值在 -screenWidth 到 screenWidth 之间
if (offsetX + details.delta.dx >= screenWidth) {
setState(() {
offsetX = screenWidth;
});
} else if (offsetX + details.delta.dx <= -screenWidth) {
setState(() {
offsetX = -screenWidth;
});
} else {
setState(() {
offsetX += details.delta.dx;
});
}
}
复制代码
英雄动画
/// tiktok_page.dart
Widget build(BuildContext context) {
return Hero(
tag: "detail",
//child
)
)
/// detail_page.dart
Widget build(BuildContext context) {
return Hero(
tag: "detail",
// child
)
}
复制代码
详情页互动
GestureDetector
加上Stack
然后加上变换。
Hero(
tag: "detail",
child: GestureDetector(
onVerticalDragUpdate: (details){
// dy 不超过 -screenHeight * 0.6
dy += details.delta.dy;
if ((dy < 0 && dy.abs() > screenHeight * 0.6)) {
dy = -screenHeight * 0.6;
} else {
setState(() {});
}
},
child: Stack(
children: <Widget>[
Image.asset(
"assets/detail.png",
fit: BoxFit.fitWidth,
width: screenWidth,
height: screenHeight,
),
Transform.translate(
offset: Offset(0, dy + screenHeight),
child: Container(
height: screenHeight * 0.6,
child: GestureDetector(
onTap: () {},
child: Image.asset(
"assets/comment.png",),
)
),
),
],
),
),
);
复制代码
onVerticalDragEnd: (_){
// 滑动截止时,根据 dy 判断是展开还是回缩
if (dy < 0) {
if (!isCommentShow && dy.abs() > screenHeight * 0.2) {
if (dy.abs() > screenHeight * 0.2) {
animateToTop(screenHeight);
} else {
animateToBottom(screenHeight);
}
} else {
if (dy.abs() > screenHeight * 0.4) {
animateToTop(screenHeight);
} else {
animateToBottom(screenHeight);
}
}
}
},
复制代码
写在最后
Gesture Detector
和Hero
都是widget,并提供了很多回调函数。再加上数据驱动的UI和Flutter优秀的渲染机制,降低了开发者的石头交互难度。
链接:https://juejin.im/post/5cbadb71f265da037c7c7ce19fets:版权归作者所有。如需商业转载,请联系求作者授权。非商业转载请来源。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。