导言

最近看到 自如团队 发布的 自如客APP裸眼3D效果的完结,这个布局确实做得很风趣,越玩越上瘾,感谢自如团队的共享。随即按照自己的思路用 Flutter 完结一遍,来看看毕竟效果。

banner 样式 全屏样式
IMG_0020.gif IMG_0021.gif

本文会偏重介绍我在完结过程中的思路和规划,所以无论你是前端 /iOS/Android/Flutter 都可以参看同样的路子去完结。假设有任何问题,也欢迎讨论。


一、整体构思

从效果上可以看出,跟着我们设备的旋转,有的部分顺着倾斜方向滑动,有的朝着相反方向,而有的则不动。所以图片上的元素必定分为不同的图层,旋转设备让图层产生移动即可达到效果。

将图片分为了前、中、后三层,跟着手机角度的旋转,中层坚持不动,上层顺着旋转方向移动,下层与上层相逆。

拿去吧你!Flutter 仿自如 App 裸眼 3D 效果| 8月更文挑战
(图片来自自如共享)

所以在图片分层之后,这个效果就变成了两步:

1、获取手机的旋转信息

2、依据旋转信息移动不同的图层


二、获取手机的旋转信息

Flutter 中有这样一个插件 sensors_plus ,运用它可以帮忙我们获取两个传感器的信息:Accelerometer(加速度传感器)、Gyroscope(陀螺仪)。

拿去吧你!Flutter 仿自如 App 裸眼 3D 效果| 8月更文挑战

每个传感器供应了一个 Stream ,其发送的事情包括 X、Y、Z 标明手机不同方向的改动的速度。通过对 Stream 的监听,我们便可实时获取相关传感器数据。

这个仓库中也附带了一个别感贪吃蛇的 demo,倾斜设备,小蛇便朝着倾斜方向前进。

贪吃蛇.gif

插件的更多介绍可以检查视频: Flutter Widgets 介绍合集 —— 103. Sensors_plus

我们完结的效果需求依据手机旋转移动图层,天然运用陀螺仪传感器即可:

gyroscopeEvents.listen(
    (GyroscopeEvent event) {
// event.x  event.y  event.z
  
  },
),

回调的 GyroscopeEvent 包括三个特色,x、y、z,分别对应下图三个方向所检测到的旋转速度(单位:弧度/秒)

拿去吧你!Flutter 仿自如 App 裸眼 3D 效果| 8月更文挑战

结合需求来看,我们只需运用 Y 轴(对应图画在水平方向的移动)和 X 轴(对应图画在竖直方向的移动)的数据即可。


三、依据旋转信息移动图层

在网上找了一个 psd 文件,导出图片之后整体长这样:

拿去吧你!Flutter 仿自如 App 裸眼 3D 效果| 8月更文挑战

我在 psd 文件中导出 3 个图层,需求留心图片格式要为 .png,这样上一个图层的通明区域不会被填充为白色而遮挡住下一个图层,之后直接运用 Image widget 展现图片即可:

前景 中景(白色的文字,所以看不见) 布景
拿去吧你!Flutter 仿自如 App 裸眼 3D 效果| 8月更文挑战 拿去吧你!Flutter 仿自如 App 裸眼 3D 效果| 8月更文挑战 拿去吧你!Flutter 仿自如 App 裸眼 3D 效果| 8月更文挑战

1、让图层动起来

图片分为三层,我们天然想到运用 Stack 作为容器,顺次放入三个图层(Widget)

// 布景图层
Widget? backgroundWidget;
// 中景图层
Widget? middleWidget;
// 前景图层
Widget? foregroundWidget;

图层移动其实很简单,就是去批改每一个图层的偏移量。再调查这个完结效果,会发现跟着我们的旋转,图层中的内容好像 出来相同。

拿去吧你!Flutter 仿自如 App 裸眼 3D 效果| 8月更文挑战

所以我们一开始进入时,看到的必定仅仅图片的部分区域。我的主意是给每一个图层设置 scale,将图片进行扩展。闪现窗口是固定的,那么一开始只能看到图片的正中方位。(中层可以不用,由于中层本身是不移动的,所以也不用扩展)

拿去吧你!Flutter 仿自如 App 裸眼 3D 效果| 8月更文挑战

旋转手机批改偏移量,为前景和布景层设置相反的偏移量,便可达到两个图层反向运动的效果。

在核算偏移量的时分还需求考虑两个因素:

1、图层的最大偏移量

图层通过了必定份额的扩展,所以存在一个最大的偏移规划,偏移量不能超过这个规划。

拿去吧你!Flutter 仿自如 App 裸眼 3D 效果| 8月更文挑战

不难看出水平方向上最大偏移核算方法为:(缩放份额-1) * 宽 / 2, 竖直方向同理。

2、前景与布景图层的相对偏移速度

由于前景和布景的缩放份额或许不同,假设两者以 1:1 的相对偏移,或许会呈现以下状况。

拿去吧你!Flutter 仿自如 App 裸眼 3D 效果| 8月更文挑战
假设 前景缩放是 1.4,布景为 1.8,当闪现区域向左移动 2 像素的时分。这时布景层所闪现的区域同样向左移动 2 个像素,前景层相反。但这时前景现已达最大的偏移量,不能再持续移动。而布景其实还有区域未能闪现,所以可以通过两者的缩放比核算对应的偏移比,保证两个图片都能完好的展现出来。

// 通过布景偏移核算前景偏移
Offset getForegroundOffset(Offset backgroundOffset) {
 // 假设前景缩放比是 1.4 布景是 1.8 控件宽度为 10
 // 那么前景最大移动 4 像素,布景最大 8 像素
 double offsetRate = ((widget.foregroundScale ?? 1) - 1) /
    ((widget.backgroundScale ?? 1) - 1);
 // 前景取反
 return -Offset(
   backgroundOffset.dx * offsetRate, backgroundOffset.dy * offsetRate);
}

这儿我通过布景偏移为标准,核算前景偏移,并且在核算布景偏移的之前先考虑了最大偏移规划,这样保证前景和布景都不会产生越界行为。先通过拖拽改动偏移量调用 setState 更新界面,看看图层部分完结的效果:

1627550866693665.gif

布景跟着手指滑动而位移,一同前景朝相反的方向移动,当滑动到图层边界时无法持续,整个过程中层坚持不动。

2、传感器控制偏移

图层位移完结之后,我们只需求将上面由手指滑动触发的偏移改动为由传感器触发即可。

这儿我们来想一个问题,我们设备处于水平状态时,闪现区域居中,而当设备倾斜的时分,闪现区域移动。

拿去吧你!Flutter 仿自如 App 裸眼 3D 效果| 8月更文挑战

那么该旋转多少角度达到最大偏移量呢?

所以这儿我定义了两个变量

  double maxAngleX;
double maxAngleY;

分别标明水平缓笔直方向的最大旋转角度。假设 maxAngleX 为 10,标明当你在水平方向旋转设备 10 度的时分,图画闪现到某一个方向的边际。

有了这个定义我们便可反推出布景层 旋转 1 的偏移量为:

1/maxAngleX * maxBackgroundOffset.dx,笔直方向同理。

思路就是这样,不过我在完结的时分还遇到了一个扎手的问题:

由于 sensors_plus 插件中供应的是各方向的旋转速度(rad/s),我们改怎么核算实践的旋转角度?

其实并不难:旋转弧度 = (旋转速度(rad/s) * 时间),那么这儿时间是多少?

看 sensors_plus 插件的安卓端完结,这个插件通过 SensorManager 注册陀螺仪传感器的回调,通过 chanel 将搜集到的数据直接传递到 Flutter 侧。

sensorManager.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);

在安卓端 SensorManager 的搜集灵敏度分几种

  • SensorManager.SENSOR_DELAY_FASTEST(0微秒):最快。最低推延,一般不是特别敏感的处理不引荐运用,该方式或许在成手机电力许多耗费,由于传递的为原始数据,算法不处理好会影响游戏逻辑和UI的功能
  • SensorManager.SENSOR_DELAY_GAME(20000微秒):游戏。游戏推延,一般绝大多数的实时性较高的游戏都是用该等级
  • SensorManager.SENSOR_DELAY_NORMAL(200000微秒):一般。标准延时,关于一般的益智类或EASY等级的游戏可以运用,但过低的采样率或许对一些赛车类游戏有跳帧现象
  • SensorManager.SENSOR_DELAY_UI(60000微秒):用户界面。一般关于屏幕方向主动旋转运用,相对节省电能和逻辑处理,一般游戏开发中不运用

不同灵敏度的搜集时间不同,sensors_plus 默认是 SENSOR_DELAY_NORMAL 即 0.2S ,实践运用感应推延十分高,不太合适这种需求及时响应的场景。所以我直接 fork 项目下来,将 SENSOR_DELAY_NORMAL 改为了 SENSOR_DELAY_GAME ,即每次搜集时间为 20000微秒(0.02秒)。(假设你有相似需求可以通过 nayuta_sensors: 1.0.0 运用)

换算成角度就是:x * 0.02 * 180 / ,再用角度换算布景偏移量,布景偏移量考虑最大偏移规划之后,核算前景,调用 setState 更新界面即可。关键步骤如下:

gyroscopeEvents.listen((event) {
setState(() {
// 通过搜集的旋转速度核算出布景 delta 偏移
Offset deltaOffset = gyroscopeToOffset(-event.y, -event.x);
// 初始偏移量 + delta 偏移 之后考虑越界
backgroundOffset = considerBoundary(deltaOffset + backgroundOffset);
// 布景偏移依据缩放份额获取前景偏移
foregroundOffset = getForegroundOffset(backgroundOffset);
});
});

四、运用说明

项目依托

仓库已上传至 pub 通过依托:

dependencies:
flutter:
flutter_interactional_widget: 1.0.0

github 现已参加 全家桶:github.com/fluttercand…

构造函数

InteractionalWidget

特色 说明 是否必选
double width 视窗宽度
double height 视窗高度
double maxAngleX 水平方向最大的旋转角度
double maxAngleY 竖直方向最大的旋转角度
double? backgroundScale 布景层缩放比
double? middleScale 中景层缩放比
double? foregroundScale 前景层的缩放比
Widget? backgroundWidget 布景层 widget
Widget? middleWidget 中景层 widget
Widget? foregroundWidget 前景层 widget

三个图层均非必传,所以你也可以只指定 前景/布景 单一图层的位移。你也可以参看 github 中的案例用法:

Widget banner() {
return InteractionalWidget(
width: MediaQuery.of(context).size.width,
height: height,
maxAngleY: 30,
maxAngleX: 40,
middleScale: 1,
foregroundScale: 1.1,
backgroundScale: 1.3,
backgroundWidget: backgroundWidget(),
middleWidget: middleWidget(),
foregroundWidget: foregroundWidget(),
);
}

github 中的 演示程序 (拜访链接下载)可以直接运转,包括一个 banner 样式和一个全屏样式的案例。后边这个仓库还会更新一些交互式的小组件,这么良知的博主给个点赞、重视、 star 不过火吧~


五、最后

本来是方案接着写网络编程,半途看到 自如客APP裸眼3D效果的完结 所以趁着周末急忙完结了一下,再次感谢 自如团队 供应这么妙的创意。下一期,仍是按照之前的方案,通过 广播/组播的方法完结一个根底的局域网多端群聊服务。

假设你有任何疑问可以通过群众号与联络我,假设文章对你有所启示,希望能得到你的点赞、重视和收藏,这是我持续写作的最大动力。Thanks~

群众号:进击的Flutter或者 runflutter 里边收拾搜集了最详细的Flutter进阶与优化攻略,欢迎重视。

往期精彩内容:

Flutter 进阶优化

Flutter中心烘托机制

Flutter路由规划与源码解析