Ideas from Reference: throw line was
overall effect
Little man of few words, the direct effect on
This can be found by looking at the animation process is divided into three
- A process: tilt to the bottom
- Process II: turn up
Process III: tilt to the right
Three-dimensional image projected onto a two-dimensional plane
Image about the x-axis, a left side view of the image projected onto the two rotation planes, a three-dimensional view of the right side during rotation.
A process
The image can be divided into two parts, upper half completely did not move, the lower half of the rotation about the x-axis, rotation angle changing process can achieve the effect of a
Process two
Two slightly more complex process, look at the case where a certain frame
Red cocked to the lower half, the upper half is not to tilt, so consider plotted vertically divided into two parts
the second half
- Image rotation of 20 degrees about the z-axis
- Crop images, remove only half of
- Picture rotated 45 degrees around the x-axis
- Image -20 degrees about the z-axis
upper half
- Image rotation of 20 degrees about the z-axis
- Crop images, just take the top half
- Pictures around the x-axis rotation of 0 degrees (Why? To unify processes and other processes, to facilitate coding)
- Image -20 degrees about the z-axis
splice
Stitching together the two parts is in effect during a two frame
Animation process to achieve two
Each frame holding angle about the x-axis is fixed, changing the angle about the z-axis on the two animation process can be achieved.
A process to improve (facilitate coding)
About half of the process
- Image 0 degrees about the z-axis
- Crop images, remove only half of
- Pictures at an angle of rotation about the x axis
- Image 0 degrees about the z-axis
Changing the angle of rotation of the x-axis can be a process can be achieved in the lower half of the animation
An upper half portion of the process
- Image 0 degrees about the z-axis
- Crop images, just take the top half
- Pictures 0 degrees rotation about the x axis
- Image 0 degrees about the z-axis
Process three
Three and a process similar to the process, not repeat them.
The entire animation specific parameters
-
A process:
- Upper half: the rotational angle is 0
- Lower half: the angle of rotation around the z axis is always 0, the rotational angle about the x axis the transition from 0 degrees to -45
-
Process II:
- Upper part: an angle about the z-axis transition from 0 to 270 degrees, the angle of rotation about the x-axis is fixed at 0 degrees
- Lower half: the angle of rotation about the z-axis transition from 0 to 270 degrees, the angle of rotation about the x-axis is fixed at 45 degrees
-
Process three
- Upper part: the angle of rotation around the z axis is always 270 degrees, the rotational angle about the x axis transitions from 0 to 45 degrees
- Lower half: the angle of rotation around the z axis is always 270 degrees, the rotational angle about the x axis is 0 degrees always
Coding
First, define an enum, logo animation to the ongoing process that
enum FlipAnimationSteps { animation_step_1, animation_step_2, animation_step_3 }
复制代码
Animate parameters, monitor the state of animation
class _FlipAnimationApp extends State<FlipAnimationApp>
with SingleTickerProviderStateMixin {
var imageWidget = Image.asset(
'images/mario.jpg',
width: 300.0,
height: 300.0,
);
AnimationController controller;
CurvedAnimation animation;
@override
void initState() {
super.initState();
controller =
AnimationController(duration: const Duration(seconds: 1), vsync: this);
animation = CurvedAnimation(
parent: controller,
curve: Curves.easeInOut,
)..addStatusListener((status) {
if (status == AnimationStatus.completed) {
switch (currentFlipAnimationStep) {
case FlipAnimationSteps.animation_step_1:
currentFlipAnimationStep = FlipAnimationSteps.animation_step_2;
controller.reset();
controller.forward();
break;
case FlipAnimationSteps.animation_step_2:
currentFlipAnimationStep = FlipAnimationSteps.animation_step_3;
controller.reset();
controller.forward();
break;
case FlipAnimationSteps.animation_step_3:
break;
}
}
});
controller.forward();
}
@override
Widget build(BuildContext context) {
return AnimateFlipWidget(
animation: animation,
child: imageWidget,
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
复制代码
Let's look at the core classes AnimateFlipWidget
, animation related to the main logic in it.
class AnimateFlipWidget extends AnimatedWidget {
final Widget child;
double _currentTopRotationXRadian = 0;
double _currentBottomRotationXRadian = 0;
double _currentRotationZRadian = 0;
static final _topRotationXRadianTween =
Tween<double>(begin: 0, end: math.pi / 4);
static final _bottomRotationXRadianTween =
Tween<double>(begin: 0, end: -math.pi / 4);
static final _rotationZRadianTween =
Tween<double>(begin: 0, end: (1 + 1 / 2) * math.pi);
AnimateFlipWidget({Key key, Animation<double> animation, this.child})
: super(key: key, listenable: animation);
@override
Widget build(BuildContext context) {
final Animation<double> animation = listenable;
return Center(
child: Container(
child: Stack(
children: [
Transform(
alignment: Alignment.center,
transform: Matrix4.rotationZ(currentFlipAnimationStep ==
FlipAnimationSteps.animation_step_2
? _rotationZRadianTween.evaluate(animation) * -1
: _currentRotationZRadian * -1),
child: Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.002)
..rotateX(currentFlipAnimationStep ==
FlipAnimationSteps.animation_step_3
? _currentTopRotationXRadian =
_topRotationXRadianTween.evaluate(animation)
: _currentTopRotationXRadian),
alignment: Alignment.center,
child: ClipRect(
clipper: _TopClipper(context),
child: Transform(
alignment: Alignment.center,
transform: Matrix4.rotationZ(currentFlipAnimationStep ==
FlipAnimationSteps.animation_step_2
? _currentRotationZRadian =
_rotationZRadianTween.evaluate(animation)
: _currentRotationZRadian),
child: child,
),
),
),
),
Transform(
alignment: Alignment.center,
transform: Matrix4.rotationZ(currentFlipAnimationStep ==
FlipAnimationSteps.animation_step_2
? _rotationZRadianTween.evaluate(animation) * -1
: _currentRotationZRadian * -1),
child: Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.002)
..rotateX(currentFlipAnimationStep ==
FlipAnimationSteps.animation_step_1
? _currentBottomRotationXRadian =
_bottomRotationXRadianTween.evaluate(animation)
: _currentBottomRotationXRadian),
alignment: Alignment.center,
child: ClipRect(
clipper: _BottomClipper(context),
child: Transform(
alignment: Alignment.center,
transform: Matrix4.rotationZ(currentFlipAnimationStep ==
FlipAnimationSteps.animation_step_2
? _currentRotationZRadian =
_rotationZRadianTween.evaluate(animation)
: _currentRotationZRadian),
child: child,
),
),
),
),
],
),
),
);
}
}
复制代码
This class returns a Stack
layout, the conversion result can be an upper half and a lower half of the superposition (note: can not Column
layout oh), children
two of which Transform
result after the two parts is changed. Can be found in two Transform
it is in line with the foregoing conversion process (around the Z axis - about the axis X> rotation - -> Crop> turn back around the Z axis).
Look at the lower half of the process of tailoring
class _BottomClipper extends CustomClipper<Rect> {
final BuildContext context;
_BottomClipper(this.context);
@override
Rect getClip(Size size) {
return new Rect.fromLTRB(
-size.width, size.height / 2, size.width * 2, size.height * 2);
}
@override
bool shouldReclip(CustomClipper<Rect> oldClipper) {
return true;
}
}
复制代码
Define a class that inherits CustomClipper class, rewrite getClip specify a specific trimming range.
Source
Source point here like it oh star
Reproduced in: https: //juejin.im/post/5d0b4e65e51d45105e0212d6