如何使用 Flutter 更好的平滑视差效果?


Here is an example of what we’re gonna build today.
你看过Apple iOS的视差壁纸吗?如果你在iOS的墙纸设置中启用“透视缩放”,你会发现你的主屏幕将根据设备的倾斜/位置做出视觉响应。这虽然是简单的视觉上的把戏,却令人印象深刻。
以下是我们今天要构建的示例。
%E6%9C%AA%E5%91%BD%E5%90%8D_%E5%89%AF%E6%9C%AC %E6%9C%AA%E5%91%BD%E5%90%8D_%E5%89%AF%E6%9C%AC1

注意:本文中所有图像都在FAIR USE内使用,并且实际文件不外传。

开始啦…
我们将使用Flutter的官方传感器插件来获取设备的倾斜/旋转。
只需将以下内容作为你的依赖项,然后在项目文件夹中运行“pub get”就能完成简单的安装了。

sensors: ^0.4.2+6

注意:需检查并使用最新版本。在写本文时,我使用了以上叙述的版本。

现在我们需要两张图像,一张用作星空背景,另一张用于行星。我们要确保行星的图像是透明的,因为我们要将它堆叠在背景图像的顶部。

我们将用Stack小部件来放置图像。将小部件放在堆栈里的图像中。

对于设备的倾斜/旋转检测,我们将使用运动事件来获取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,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

说明:
我们已经订阅了设备的accelerometer events,并且正在乘数bgMotionSensitivity和planetMotionSensitivity的基础上更改背景和行星图像的位置。

为什么bgMotionSensitivity低于planetMotionSensitivity?
答:这是因为视差效果的工作原理,即离你较远的对象要比离你近的对象移动得慢。

结果:
行星和背景对设备运动的响应及时,但是…
%E6%9C%AA%E5%91%BD%E5%90%8D_%E5%89%AF%E6%9C%AC

如果对它进行试运行,你就会发现它非常不稳定并且断断续续的。
那我们在哪里做错了呢?

问题及解决方案:
这里的问题是加速度计的事件太快了,并且值变化得也很快,因此每当值突然变化时,我们的动画就会停顿。

我们需要添加某种过渡延迟来填补这些快速变化的值。幸运的是,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替换了Positioning,并设置了一些延迟(以毫秒为单位)以创建平滑的过渡效果。

结果:

%E6%9C%AA%E5%91%BD%E5%90%8D_%E5%89%AF%E6%9C%AC

它就像漂浮在屏幕中一样流畅。
这可以用作登录或介绍屏幕的良好背景。

原文作者 Abhi Tripathi
原文链接 https://abhimortal6.medium.com/flutter-smooth-parallax-effect-for-login-and-intro-screens-10bf42073741

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