导言
最近看到 自如团队 发布的 自如客APP裸眼3D效果的完结,这个布局确实做得很风趣,越玩越上瘾,感谢自如团队的共享。随即按照自己的思路用 Flutter 完结一遍,来看看毕竟效果。
banner 样式 | 全屏样式 |
---|---|
本文会偏重介绍我在完结过程中的思路和规划,所以无论你是前端 /iOS/Android/Flutter 都可以参看同样的路子去完结。假设有任何问题,也欢迎讨论。
一、整体构思
从效果上可以看出,跟着我们设备的旋转,有的部分顺着倾斜方向滑动,有的朝着相反方向,而有的则不动。所以图片上的元素必定分为不同的图层,旋转设备让图层产生移动即可达到效果。
将图片分为了前、中、后三层,跟着手机角度的旋转,中层坚持不动,上层顺着旋转方向移动,下层与上层相逆。
所以在图片分层之后,这个效果就变成了两步:
1、获取手机的旋转信息
2、依据旋转信息移动不同的图层
二、获取手机的旋转信息
Flutter 中有这样一个插件 sensors_plus ,运用它可以帮忙我们获取两个传感器的信息:Accelerometer(加速度传感器)、Gyroscope(陀螺仪)。
每个传感器供应了一个 Stream ,其发送的事情包括 X、Y、Z 标明手机不同方向的改动的速度。通过对 Stream 的监听,我们便可实时获取相关传感器数据。
这个仓库中也附带了一个别感贪吃蛇的 demo,倾斜设备,小蛇便朝着倾斜方向前进。
插件的更多介绍可以检查视频: Flutter Widgets 介绍合集 —— 103. Sensors_plus
我们完结的效果需求依据手机旋转移动图层,天然运用陀螺仪传感器即可:
gyroscopeEvents.listen(
(GyroscopeEvent event) {
// event.x event.y event.z
},
),
回调的 GyroscopeEvent 包括三个特色,x、y、z,分别对应下图三个方向所检测到的旋转速度(单位:弧度/秒)
结合需求来看,我们只需运用 Y 轴(对应图画在水平方向的移动)和 X 轴(对应图画在竖直方向的移动)的数据即可。
三、依据旋转信息移动图层
在网上找了一个 psd 文件,导出图片之后整体长这样:
我在 psd 文件中导出 3 个图层,需求留心图片格式要为 .png,这样上一个图层的通明区域不会被填充为白色而遮挡住下一个图层,之后直接运用 Image widget 展现图片即可:
前景 | 中景(白色的文字,所以看不见) | 布景 |
---|---|---|
1、让图层动起来
图片分为三层,我们天然想到运用 Stack
作为容器,顺次放入三个图层(Widget)
// 布景图层
Widget? backgroundWidget;
// 中景图层
Widget? middleWidget;
// 前景图层
Widget? foregroundWidget;
图层移动其实很简单,就是去批改每一个图层的偏移量。再调查这个完结效果,会发现跟着我们的旋转,图层中的内容好像 滑
出来相同。
所以我们一开始进入时,看到的必定仅仅图片的部分区域。我的主意是给每一个图层设置 scale
,将图片进行扩展。闪现窗口是固定的,那么一开始只能看到图片的正中方位。(中层可以不用,由于中层本身是不移动的,所以也不用扩展)
旋转手机批改偏移量,为前景和布景层设置相反的偏移量,便可达到两个图层反向运动的效果。
在核算偏移量的时分还需求考虑两个因素:
1、图层的最大偏移量
图层通过了必定份额的扩展,所以存在一个最大的偏移规划,偏移量不能超过这个规划。
不难看出水平方向上最大偏移核算方法为:(缩放份额-1) * 宽 / 2,
竖直方向同理。
2、前景与布景图层的相对偏移速度
由于前景和布景的缩放份额或许不同,假设两者以 1:1 的相对偏移,或许会呈现以下状况。
假设 前景缩放是 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 更新界面,看看图层部分完结的效果:
布景跟着手指滑动而位移,一同前景朝相反的方向移动,当滑动到图层边界时无法持续,整个过程中层坚持不动。
2、传感器控制偏移
图层位移完结之后,我们只需求将上面由手指滑动触发的偏移改动为由传感器触发即可。
这儿我们来想一个问题,我们设备处于水平状态时,闪现区域居中,而当设备倾斜的时分,闪现区域移动。
那么该旋转多少角度达到最大偏移量呢?
所以这儿我定义了两个变量:
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路由规划与源码解析