flutter - 在动画上创建弹性效果
flutter - create an elastic effect on animation
我在高度为 100.0
的 Stack
小部件中有一个容器。它使用 Positioned
小部件位于中心,如下所示
Container(
width:100.0,
height:100.0,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Positioned(
top: 40.0,
child: Container(
width: 20.0,
height: 20.0,
color: Colors.red,
),
)
],
)
)
我想以这样的方式设置红色容器的动画,使其在单击时到达父容器的底部,在再次单击时弹回顶部然后返回中心。
我尝试使用 Curves.elasticOut
,但这对我来说不够弹跳。
我如何实现这种效果
试试这个代码,让我知道我是否正确理解你的动画
已编辑:
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
import 'dart:math';
const BOX_COLOR = Colors.cyan;
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Spring Box",
theme: ThemeData(
primaryColor: Colors.red,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Padding(
child: PhysicsBox(),
padding: EdgeInsets.only(
left: 20.0,
right: 20.0,
top: 20.0,
bottom: 20.0,
),
),
));
}
}
class PhysicsBox extends StatefulWidget {
@override
BoxState createState() => BoxState();
}
class BoxState extends State<PhysicsBox> with TickerProviderStateMixin {
AnimationController controller;
AnimationController controller2;
Animation<double> animation;
SpringSimulation simulation;
double _position;
@override
void initState() {
super.initState();
simulation = SpringSimulation(
SpringDescription(
mass: 1.0,
stiffness: 100.0,
damping: 5.0,
),
400.0,
208.0,
-4000.0,
);
controller2 = AnimationController(vsync: this,duration: Duration(milliseconds: 70));
animation = Tween(begin: 200.0, end: 400.0).animate(controller2)
..addListener((){
if(controller2.status == AnimationStatus.completed){controller.reset();}
setState(() {
_position = animation.value;
});
});
controller = AnimationController(vsync: this,duration: Duration(milliseconds: 700))..forward()
..addListener(() {
if(controller.status == AnimationStatus.completed){controller2.reset();}
setState(() {
_position = simulation.x(controller.value);
});
print('${simulation.x(controller.value)}');
});
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
width:500.0,
height:500.0,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Positioned(
top: _position,
child: GestureDetector(
onTap: (){
if (controller.status == AnimationStatus.completed) {
controller2.forward();//controller.reset();
}else{
controller.forward();}
},
child: Container(
width: 100.0,
height: 100.0,
color: Colors.red,
),
),
)
],
)
);
}
}
对 Flutter 使用 TweenMax:https://pub.dartlang.org/packages/tweenmax
用 GestureDetector 包装容器以使用点击手势,然后用 TweenContainer:
替换红色容器
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
import 'package:tweenmax/tweenmax.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Spring Box",
theme: ThemeData(
primaryColor: Colors.red,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
HomePageState createState() => HomePageState();
}
class HomePageState extends State<HomePage> {
bool isClicked = false;
@override
Widget build(BuildContext context) {
TweenContainer redContainer = TweenContainer(
data: TweenData(
top: 40,
width: 20.0,
height: 20.0,
color: Colors.red,
),
);
return Scaffold(
body: GestureDetector(
child: Container(
width: 100.0,
height: 100.0,
margin: EdgeInsets.only(top: 100, left: 100),
color: Colors.yellow,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
redContainer
],
)
),
onTap: (){
// click first time, animate "redContainer" to bottom:
if(!isClicked){
TweenMax.to(
redContainer,
duration: 0.3,
ease: Curves.ease,
data: TweenData(
top: 80
)
);
} else { // click second time, animate "redContainer" to top:
TweenMax.to(
redContainer,
duration: 0.2,
ease: Curves.easeIn,
data: TweenData(
top: 0
),
onComplete: (redContainer){
// animate it back to center position:
TweenMax.to(
redContainer,
duration: 0.8,
ease: ElasticOutCurve(0.3),
data: TweenData(
top: 40
)
);
}
);
}
isClicked = !isClicked;
},
)
);
}
}
观看此视频:https://drive.google.com/file/d/1i3BgxWiVna6kQMRKVgUEQDTW2QTlSXWo/view?usp=sharing
我在高度为 100.0
的 Stack
小部件中有一个容器。它使用 Positioned
小部件位于中心,如下所示
Container(
width:100.0,
height:100.0,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Positioned(
top: 40.0,
child: Container(
width: 20.0,
height: 20.0,
color: Colors.red,
),
)
],
)
)
我想以这样的方式设置红色容器的动画,使其在单击时到达父容器的底部,在再次单击时弹回顶部然后返回中心。
我尝试使用 Curves.elasticOut
,但这对我来说不够弹跳。
我如何实现这种效果
试试这个代码,让我知道我是否正确理解你的动画
已编辑:
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
import 'dart:math';
const BOX_COLOR = Colors.cyan;
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Spring Box",
theme: ThemeData(
primaryColor: Colors.red,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Padding(
child: PhysicsBox(),
padding: EdgeInsets.only(
left: 20.0,
right: 20.0,
top: 20.0,
bottom: 20.0,
),
),
));
}
}
class PhysicsBox extends StatefulWidget {
@override
BoxState createState() => BoxState();
}
class BoxState extends State<PhysicsBox> with TickerProviderStateMixin {
AnimationController controller;
AnimationController controller2;
Animation<double> animation;
SpringSimulation simulation;
double _position;
@override
void initState() {
super.initState();
simulation = SpringSimulation(
SpringDescription(
mass: 1.0,
stiffness: 100.0,
damping: 5.0,
),
400.0,
208.0,
-4000.0,
);
controller2 = AnimationController(vsync: this,duration: Duration(milliseconds: 70));
animation = Tween(begin: 200.0, end: 400.0).animate(controller2)
..addListener((){
if(controller2.status == AnimationStatus.completed){controller.reset();}
setState(() {
_position = animation.value;
});
});
controller = AnimationController(vsync: this,duration: Duration(milliseconds: 700))..forward()
..addListener(() {
if(controller.status == AnimationStatus.completed){controller2.reset();}
setState(() {
_position = simulation.x(controller.value);
});
print('${simulation.x(controller.value)}');
});
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
width:500.0,
height:500.0,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Positioned(
top: _position,
child: GestureDetector(
onTap: (){
if (controller.status == AnimationStatus.completed) {
controller2.forward();//controller.reset();
}else{
controller.forward();}
},
child: Container(
width: 100.0,
height: 100.0,
color: Colors.red,
),
),
)
],
)
);
}
}
对 Flutter 使用 TweenMax:https://pub.dartlang.org/packages/tweenmax
用 GestureDetector 包装容器以使用点击手势,然后用 TweenContainer:
替换红色容器import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
import 'package:tweenmax/tweenmax.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Spring Box",
theme: ThemeData(
primaryColor: Colors.red,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
HomePageState createState() => HomePageState();
}
class HomePageState extends State<HomePage> {
bool isClicked = false;
@override
Widget build(BuildContext context) {
TweenContainer redContainer = TweenContainer(
data: TweenData(
top: 40,
width: 20.0,
height: 20.0,
color: Colors.red,
),
);
return Scaffold(
body: GestureDetector(
child: Container(
width: 100.0,
height: 100.0,
margin: EdgeInsets.only(top: 100, left: 100),
color: Colors.yellow,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
redContainer
],
)
),
onTap: (){
// click first time, animate "redContainer" to bottom:
if(!isClicked){
TweenMax.to(
redContainer,
duration: 0.3,
ease: Curves.ease,
data: TweenData(
top: 80
)
);
} else { // click second time, animate "redContainer" to top:
TweenMax.to(
redContainer,
duration: 0.2,
ease: Curves.easeIn,
data: TweenData(
top: 0
),
onComplete: (redContainer){
// animate it back to center position:
TweenMax.to(
redContainer,
duration: 0.8,
ease: ElasticOutCurve(0.3),
data: TweenData(
top: 40
)
);
}
);
}
isClicked = !isClicked;
},
)
);
}
}
观看此视频:https://drive.google.com/file/d/1i3BgxWiVna6kQMRKVgUEQDTW2QTlSXWo/view?usp=sharing