在登录和介绍页面应用 Flutter 的平滑视差效果


不知道大家看过 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
推荐阅读
相关专栏
前端与跨平台
90 文章
本专栏仅用于分享音视频相关的技术文章,与其他开发者和声网 研发团队交流、分享行业前沿技术、资讯。发帖前,请参考「社区发帖指南」,方便您更好的展示所发表的文章和内容。