不知道大家看过 Apple iOS 的视差壁纸吗?只需在 iOS 的墙纸设置中启用“视角缩放”,主屏幕就会根据设备的倾斜/位置进行视觉响应。虽然只是简单的视觉效果,但效果很好。
下图是本文要构建的示例。
注意:本文所有图像都只在 FAIR USE 内使用,不会外传。
开始
我们使用 Flutter 的官方传感器插件获取设备的倾斜/旋转。添加下列依赖项,在项目文件夹中运行“pub get”就能完成安装。
sensors: ^0.4.2+6
注意:本文使用的是上述版本,请大家使用最新版本。
我们需要两张图片,一张星空背景图片,一张行星图片。要确保行星的图片是透明的,因为要将行星图片堆叠在背景图片的上层。
我们用 Stack 微件来放置图片,用 Positioned 微件把图片放置在堆栈内。
另外,我们用 Accelerometer Events 获取 x 和 y 轴上的加速度,来检测设备的倾斜/旋转角度。
编码
class PerspectiveZoomDemo extends StatefulWidget {
PerspectiveZoomDemo({
Key key,
}) : super(key: key);
@override
_PerspectiveZoomDemoState createState() => _PerspectiveZoomDemoState();
}
class _PerspectiveZoomDemoState extends State<PerspectiveZoomDemo> {
AccelerometerEvent acceleration;
StreamSubscription<AccelerometerEvent> _streamSubscription;
int planetMotionSensitivity = 4;
int bgMotionSensitivity = 2;
@override
void initState() {
_streamSubscription = accelerometerEvents.listen((AccelerometerEvent event) {
setState(() {
acceleration = event;
});
});
super.initState();
}
@override
void dispose() {
_streamSubscription.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Stack(
children: <Widget>[
Positioned(
top: acceleration.y * bgMotionSensitivity,
bottom: acceleration.y * -bgMotionSensitivity,
right: acceleration.x * -bgMotionSensitivity,
left: acceleration.x * bgMotionSensitivity,
child: Align(
child: Image.asset(
"assets/images/bg.jpg",
height: 1920,
fit: BoxFit.fitHeight,
),
),
),
Positioned(
top: acceleration.y * planetMotionSensitivity,
bottom: acceleration.y * -planetMotionSensitivity,
right: acceleration.x * -planetMotionSensitivity,
left: acceleration.x * planetMotionSensitivity,
child: Align(
child: Image.asset(
"assets/images/earth_2.png",
width: 250,
),
),
),
],
),
),
);
}
}
说明
我们订阅了设备的加速计事件,并基于 bgMotionSensitivity 和 planetMotionSensitivity 相乘所得的积来更改背景和行星图片的位置。
问:为什么 bgMotionSensitivity 低于 planetMotionSensitivity?
答:因为这就是视差效果的工作原理,举例较远的对象要比距离较近的对象移速慢。
结果
行星和背景对设备运动的响应很及时,但是…
试运行后,发现运行状态非常不稳定,且呈现断断续续的状态。
那么,是哪里出问题了呢?
问题和解决方案
问题是加速计的事件过快,并且数值变化也很快,所以,每次数值突然变化时,动画就会卡顿。
所以,我们需要在快速变化的值之间添加一些转场延迟,而 flutter 的 AnimatedPositioned 微件可以在 Stack 微件中使用。
解决方案
AnimatedPositioned(
duration: Duration(milliseconds: 250),
top: acceleration.y * bgMotionSensitivity,
bottom: acceleration.y * -bgMotionSensitivity,
right: acceleration.x * -bgMotionSensitivity,
left: acceleration.x * bgMotionSensitivity,
child: Align(
child: Image.asset(
"assets/images/bg.jpg",
height: 1920,
fit: BoxFit.fitHeight,
),
),
),
AnimatedPositioned(
duration: Duration(milliseconds: 250),
top: acceleration.y * planetMotionSensitivity,
bottom: acceleration.y * -planetMotionSensitivity,
right: acceleration.x * -planetMotionSensitivity,
left: acceleration.x * planetMotionSensitivity,
child: Align(
child: Image.asset(
"assets/images/earth_2.png",
width: 250,
),
),
),
说明
我们用 AnimatedPositioned 替换了 Positioned,并设置了一些延迟(以毫秒为单位)来创建平滑的转场效果。
结果
我们最后获得的视差效果非常流畅,大家可以尝试登录或介绍页面使用这样的视差效果。
原文作者:Abhi Tripathi
原文链接:https://abhimortal6.medium.com/flutter-smooth-parallax-effect-for-login-and-intro-screens-10bf42073741