共计 6521 个字符,预计需要花费 17 分钟才能阅读完成。
Flutter 实现背景 Parallax 动画
原文 https://arkapp.medium.com/bac…
前言
咱们将创立咱们的 Flutter 我的项目惊人的 Parallax 动画。
在本文中,咱们将实现一个简略的实用工具 widget,它将在任何 widget 之上增加 Parallax 成果。
注释
创立 Base Widget
让咱们创立咱们的根底 widget,咱们将增加 Parallax 动画。在 BaseWidget 中,咱们将从 Asset 目录增加一个图像。稍后,咱们将增加 Parallax 成果到这个图像。
import 'package:flutter/material.dart';
class BaseWidget extends StatelessWidget {const BaseWidget({super.key});
@override
Widget build(BuildContext context) {
///We will add parallax effect to this image
return Image.asset(
'assets/moon.webp',
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
);
}
}
创立 Parallax widget
当初咱们将创立一个实用工具 widget,它将为下面的 BaseWidget 增加 Parallax 成果。这将是采纳子窗口 widget 作为结构函数参数的状态窗口 widget。
import 'package:flutter/material.dart';
class ParallaxAnimationWidget extends StatefulWidget {
final Widget child;
const ParallaxAnimationWidget({required this.child, super.key});
@override
State<ParallaxAnimationWidget> createState() => _WidgetState();
}
class _WidgetState extends State<ParallaxAnimationWidget> {
@override
Widget build(BuildContext context) {return Container();
}
}
当初咱们将减少子窗口 widget 的宽度。为此,咱们将首先确定子 widget 的宽度。咱们将把 GlobalKeyto 增加到子 widget 中,应用该键将获取 widget 的宽度。
import 'package:flutter/material.dart';
class ParallaxAnimationWidget extends StatefulWidget {
final Widget child;
const ParallaxAnimationWidget({required this.child, super.key});
@override
State<ParallaxAnimationWidget> createState() => _WidgetState();
}
class _WidgetState extends State<ParallaxAnimationWidget> {final childKey = GlobalKey();
late Widget childWithKey;
double? childBaseWidth;
@override
void initState() {super.initState();
childWithKey = SizedBox(key: childKey, child: widget.child);
fetchChildWidth();}
@override
Widget build(BuildContext context) {
return Stack(
children: [childWithKey,],
);
}
fetchChildWidth() {
WidgetsBinding.instance.addPostFrameCallback((_) {
final RenderBox renderBoxRed =
childKey.currentContext?.findRenderObject() as RenderBox;
final childSize = renderBoxRed.size;
childBaseWidth = childSize.width;
},
);
}
}
这里咱们增加了 StackWidget,而后在其中增加了子 Widget。当初,咱们将减少咱们的子窗口 widget 的宽度,以实现横跨该宽度的 Parallax 成果。
import 'package:flutter/material.dart';
class ParallaxAnimationWidget extends StatefulWidget {
final Widget child;
const ParallaxAnimationWidget({required this.child, super.key});
@override
State<ParallaxAnimationWidget> createState() => _WidgetState();
}
class _WidgetState extends State<ParallaxAnimationWidget> {
///We are increasing the widget width by 20% you can change according
///to your needs.
final parallaxWidthPercent = 20;
final childKey = GlobalKey();
late Widget childWithKey;
double? childBaseWidth;
double? totalAdditionalParallaxWidth;
double? rightPosition;
double maxEndPosition = 0;
double maxStartPosition = 0;
@override
void initState() {super.initState();
childWithKey = SizedBox(key: childKey, child: widget.child);
fetchChildWidth();}
@override
Widget build(BuildContext context) {
return Stack(
children: [
///Validating the width and setting new increased width.
SizedBox(
width:
(childBaseWidth != null && totalAdditionalParallaxWidth != null)
? (childBaseWidth! + totalAdditionalParallaxWidth!)
: null,
child: childWithKey,
),
],
);
}
fetchChildWidth() {
WidgetsBinding.instance.addPostFrameCallback((_) {
final RenderBox renderBoxRed =
childKey.currentContext?.findRenderObject() as RenderBox;
final childSize = renderBoxRed.size;
childBaseWidth = childSize.width;
initChildPosition();},
);
}
initChildPosition() {
totalAdditionalParallaxWidth = childBaseWidth! * parallaxWidthPercent / 100;
rightPosition = -totalAdditionalParallaxWidth! / 2;
maxEndPosition = -childBaseWidth! + totalAdditionalParallaxWidth!;
maxStartPosition = totalAdditionalParallaxWidth!;
setState(() {});
}
}
咱们曾经减少了 20% 的宽度部件,您能够依据您的须要扭转。咱们还应用新的宽度计算了动画地位参数 (right position、maxEndposition、maxStartposition)。这个新参数将在下一步中用于增加动画。
增加动画
咱们将应用 Animatedposition 来创立漂亮的 Parallax 动画。咱们将创立一个定时器,它将一直扭转咱们的子窗口 widget 的地位,以创立 Parallax 成果。
import 'dart:async';
import 'package:flutter/material.dart';
class ParallaxAnimationWidget extends StatefulWidget {
final Widget child;
const ParallaxAnimationWidget({required this.child, super.key});
@override
State<ParallaxAnimationWidget> createState() => _WidgetState();
}
class _WidgetState extends State<ParallaxAnimationWidget> {
///You can change the duration accoridng to your needs
Duration animationDuration = const Duration(seconds: 15);
final initialDelay = Future.delayed(const Duration(seconds: 1));
Timer? animationTimer;
final parallaxWidthPercent = 20;
final childKey = GlobalKey();
late Widget childWithKey;
double? childBaseWidth;
double? totalAdditionalParallaxWidth;
double? rightPosition;
double maxEndPosition = 0;
double maxStartPosition = 0;
@override
void initState() {super.initState();
childWithKey = SizedBox(key: childKey, child: widget.child);
fetchChildWidth();
initTimer();}
@override
Widget build(BuildContext context) {
return Stack(
children: [
///This will animate our widget between edge positions.
AnimatedPositioned(
right: rightPosition,
duration: animationDuration,
child: SizedBox(
width:
(childBaseWidth != null && totalAdditionalParallaxWidth != null)
? (childBaseWidth! + totalAdditionalParallaxWidth!)
: null,
child: childWithKey,
),
),
],
);
}
initTimer() async {animationTimer?.cancel();
await initialDelay;
updateChildPosition();
animationTimer = Timer.periodic(
animationDuration,
(_) => updateChildPosition(),);
}
///This method will animate our widget horizontally
updateChildPosition() async {if (rightPosition == 0) {rightPosition = maxEndPosition;} else if (rightPosition == maxEndPosition) {rightPosition = maxStartPosition;} else if (rightPosition == maxStartPosition) {rightPosition = maxEndPosition;} else {rightPosition = maxEndPosition;}
setState(() {});
}
fetchChildWidth() {
WidgetsBinding.instance.addPostFrameCallback((_) {
final RenderBox renderBoxRed =
childKey.currentContext?.findRenderObject() as RenderBox;
final childSize = renderBoxRed.size;
childBaseWidth = childSize.width;
initChildPosition();},
);
}
initChildPosition() {
totalAdditionalParallaxWidth = childBaseWidth! * parallaxWidthPercent / 100;
rightPosition = -totalAdditionalParallaxWidth! / 2;
maxEndPosition = -childBaseWidth! + totalAdditionalParallaxWidth!;
maxStartPosition = totalAdditionalParallaxWidth!;
setState(() {});
}
@override
void dispose() {super.dispose();
///Closing timer on widget dispose.
animationTimer?.cancel();}
}
应用 Parallax 动画
咱们曾经实现了 ParallaxAnimationWidget,当初咱们只须要将它增加到 BaseWidget 中就能够看到它的神奇之处了。
import 'package:parallax/base_widget.dart';
import 'package:parallax/parallax_animation_widget.dart';
import 'package:flutter/material.dart';
void main() {runApp(const MyApp());
}
class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
///Addding animation to our base widget
home: ParallaxAnimationWidget(child: BaseWidget(),
),
);
}
}
就是这样!您曾经胜利地增加 Parallax 动画到您的 Flutter 我的项目。您能够在我的项目中的任何中央应用此 widget 来创立令人惊叹的 UI。
结束语
如果本文对你有帮忙,请转发让更多的敌人浏览。
兴许这个操作只有你 3 秒钟,对我来说是一个激励,感激。
祝你有一个美妙的一天~
© 猫哥
- 微信 ducafecat
- https://wiki.ducafecat.tech
- https://video.ducafecat.tech
本文由 mdnice 多平台公布