Android自如客APP裸眼3D效果的实现

2286次阅读  |  发布于2年以前

背景

移动端界面设计如此火热的今天,各类大厂的设计规范和设计语言已经非常的成熟,我们想做一些在这套成熟的设计规范之外的尝试和创新,所以有别于传统的banner交互形式成为了我们的发力点。

设计理念

由于app版面空间有限,除了功能导向、阅读习惯和设计美观外,自如想在既定的框下,做一下不同的设计尝试,哪怕这种尝试只能提升用户1%的观感。可能租了几年自如的房子,用了几年自如客app,你可能也不会注意到一些小的细节。但如果哪天,作为用户的你突然发现了这个隐藏的“彩蛋”,看到了自如在这些小细节上的用心,我相信那天你将会对自如这个品牌有更深层次的认识和理解。

裸眼3D技术一般都是应用在裸眼3D大屏、全息投影等等比较常见的场景中,在APP的banner上应用,的确也是一次全新的尝试。我们通过借助移动设备上的传感器、以及自身的屏显清晰度、画面呈现,将2D影像转化为景深效果,以呈现出不用"3D"眼镜就可看到的3D效果。

实现方式

以下以Android为例,介绍一下该效果的实现方式。

分层

自如客app的banner其实一直在创新当中,有专门注意过的同学可能知道,在裸眼3D效果之前,自如客app其实就已经实现了分层,当时为了实现更加自然和精致的切换效果:在每个banner滑入滑出的时候,底部其实会在原地进行渐显渐隐,内容会跟随手势滑入滑出。此次为了实现3D效果,我们在以前分层的基础上加了一层中景,将原有的前景拆分为前景和中景。


上图的sl_bg为背景,pv_middle为中景,sl为前景

由于切换的交互,实际上banner使用了两个viewpager进行了联动。背景在最底层的viewpager里面,中景和前景在另外一个viewpager里。

跟手位移

打开自如客app后,用户操作设备可以明显感受到画面的错位移动,造成视觉上的景深效果。这种错位移动其实就是借助设备本身的传感器来实现的,具体实现方式是我们让中景始终保持不动,同时从设备传感器获取当前设备对应的倾斜角,根据倾斜角计算出背景和前景的移动距离,然后执行背景和前景移动的动作。如下图所示:


为了使用的方便,我们封装了一个SensorLayout,专门用于根据设备的倾斜角执行内容的位移;SensorLayout内部的主要实现:

注册对应的传感器

mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
// 重力传感器
mAcceleSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// 地磁场传感器
mMagneticSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);

mSensorManager.registerListener(this, mAcceleSensor, SensorManager.SENSOR_DELAY_GAME);
mSensorManager.registerListener(this, mMagneticSensor, SensorManager.SENSOR_DELAY_GAME);

计算偏转角度

if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
    mAcceleValues = event.values;
}
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
    mMageneticValues = event.values;
}

float[] values = new float[3];
float[] R = new float[9];
SensorManager.getRotationMatrix(R, null, mAcceleValues, mMageneticValues);
SensorManager.getOrientation(R, values);
// x轴的偏转角度
values[1] = (float) Math.toDegrees(values[1]);
// y轴的偏转角度
values[2] = (float) Math.toDegrees(values[2]);

通过重力传感器和地磁场传感器,获取设备的偏转角度 根据偏转角度执行滑动

if (mDegreeY <= 0 && mDegreeY > mDegreeYMin) {
    hasChangeX = true;
    scrollX = (int) (mDegreeY / Math.abs(mDegreeYMin) * mXMoveDistance*mDirection);
} else if (mDegreeY > 0 && mDegreeY < mDegreeYMax) {
    hasChangeX = true;
    scrollX = (int) (mDegreeY / Math.abs(mDegreeYMax) * mXMoveDistance*mDirection);
}
if (mDegreeX <= 0 && mDegreeX > mDegreeXMin) {
    hasChangeY = true;
    scrollY = (int) (mDegreeX / Math.abs(mDegreeXMin) * mYMoveDistance*mDirection);
} else if (mDegreeX > 0 && mDegreeX < mDegreeXMax) {
    hasChangeY = true;
    scrollY = (int) (mDegreeX / Math.abs(mDegreeXMax) * mYMoveDistance*mDirection);
}
smoothScrollTo(hasChangeX ? scrollX : mScroller.getFinalX(), hasChangeY ? scrollY : mScroller.getFinalY());

mDegreeX即为第二部中获取的偏转角度,mDegreeXMinmDegreeXMax为X轴可发生偏转位移的角度最大值和最小值,mYMoveDistance即为Y轴上的最大偏移距离(围绕X轴发生旋转,视图会沿Y轴上发生位移);Y轴上的偏转同理;就算好X轴和Y轴的偏移距离后,使用scroller进行滑动;

实现总结

读到这里,相信大家已经基本了解了这套banner的实现方案。Android端在布局上进行了分层,中景位置不变,借助重力传感器和地磁场传感器获取偏转角度,根据角度使背景和前景进行错位移动。iOS端的实现原理也基本一致,不再赘述。

Copyright© 2013-2019

京ICP备2023019179号-2