优化动画效果

This commit is contained in:
yuanjunyao 2023-11-30 10:16:09 +08:00
parent e24be85f04
commit d950901497
6 changed files with 214 additions and 103 deletions

View File

@ -66,6 +66,7 @@ class FirstTimeDialog extends StatelessWidget {
left: 0, left: 0,
right: 0, right: 0,
child: AnimatedVisibilityWidget( child: AnimatedVisibilityWidget(
animationWidgetBuilder: AnimatedVisibilityWidget.fadeAnimationWidgetBuilder,
isVisible: isTextShown.value, isVisible: isTextShown.value,
child: const Center( child: const Center(
child: TouchHintWidget(), child: TouchHintWidget(),

View File

@ -68,6 +68,15 @@ class SelectController extends GetxController {
occupations.add(Occupation( occupations.add(Occupation(
id: 4, id: 4,
gender: 2, gender: 2,
name: '运动员',
enName: 'athletes',
vImage: 'btn_male_pic_athletes',
hImage: 'pic_male_athletes',
selected: false,
));
occupations.add(Occupation(
id: 5,
gender: 2,
name: '画家', name: '画家',
enName: 'artist', enName: 'artist',
vImage: 'btn_male_pic_artist', vImage: 'btn_male_pic_artist',
@ -75,16 +84,6 @@ class SelectController extends GetxController {
selected: true, selected: true,
enable: true, enable: true,
)); ));
occupations.add(Occupation(
id: 5,
gender: 2,
name: '运动员',
enName: 'athletes',
vImage: 'btn_male_pic_athletes',
hImage: 'pic_male_athletes',
selected: false,
));
occupations.add(Occupation( occupations.add(Occupation(
id: 6, id: 6,
gender: 2, gender: 2,

View File

@ -168,6 +168,10 @@ class PartnerWidget extends HookWidget {
return HookBuilder(builder: (context) { return HookBuilder(builder: (context) {
final isDialogShown = useState(false); final isDialogShown = useState(false);
final isTextShown = useState(false); final isTextShown = useState(false);
final fadeOutAnimationController = useAnimationController(
initialValue: 1.0,
duration: const Duration(milliseconds: 500),
);
return Stack( return Stack(
children: [ children: [
Positioned.fill( Positioned.fill(
@ -180,82 +184,94 @@ class PartnerWidget extends HookWidget {
), ),
), ),
), ),
Positioned(top: 42.h, left: 149.w, child: Images.ip), Positioned(
top: 42.h,
left: 149.w,
child: FadeTransition(
opacity: fadeOutAnimationController,
child: Images.ip,
),
),
Positioned( Positioned(
top: 76.h, top: 76.h,
left: 451.w, left: 451.w,
child: AnimatedVisibilityWidget( child: FadeTransition(
isVisible: true, opacity: fadeOutAnimationController,
duration: const Duration(milliseconds: 500), child: AnimatedVisibilityWidget(
animationWidgetBuilder: isVisible: true,
AnimatedVisibilityWidget.fadeAnimationWidgetBuilder, duration: const Duration(milliseconds: 500),
isInitAnimated: true, animationWidgetBuilder:
onDone: (visible) { AnimatedVisibilityWidget.fadeAnimationWidgetBuilder,
if (visible) { isInitAnimated: true,
isDialogShown.value = true; onDone: (visible) {
} if (visible) {
}, isDialogShown.value = true;
child: Container( }
width: 464.w, },
height: 270.h, child: Container(
decoration: const BoxDecoration( width: 464.w,
image: DecorationImage( height: 270.h,
image: Images.questionDialog, decoration: const BoxDecoration(
fit: BoxFit.fill, image: DecorationImage(
image: Images.questionDialog,
fit: BoxFit.fill,
),
), ),
), padding: REdgeInsets.only(top: 45.0, left: 45.0, right: 45.0),
padding: REdgeInsets.only(top: 45.0, left: 45.0, right: 45.0), child: Column(
child: Column( mainAxisAlignment: MainAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, children: [
children: [ Expanded(
Expanded( child: SizedBox(
child: SizedBox( width: double.infinity,
width: double.infinity, child: Visibility(
child: Visibility( visible: isDialogShown.value,
visible: isDialogShown.value, child: AnimatedTextKit(
child: AnimatedTextKit( totalRepeatCount: 1,
totalRepeatCount: 1, pause: Duration.zero,
animatedTexts: [ animatedTexts: [
TypewriterAnimatedText( TypewriterAnimatedText(
'每个来到建木之境的探梦者,都需要先选定自己的【梦想职业】,才能继续探索哦。准备好了吗?', '每个来到建木之境的探梦者,都需要先选定自己的【梦想职业】,才能继续探索哦。准备好了吗?',
textStyle: TextStyles.mediumWhiteShadow18_034, textStyle: TextStyles.mediumWhiteShadow18_034,
textAlign: TextAlign.start, textAlign: TextAlign.start,
speed: const Duration(milliseconds: 150), speed: const Duration(milliseconds: 150),
cursor: '', cursor: '',
), ),
], ],
onFinished: () { onFinished: () {
isTextShown.value = true; isTextShown.value = true;
}, },
),
), ),
), ),
), ),
), const RSizedBox(
const RSizedBox( height: 36.0,
height: 36.0,
),
AnimatedVisibilityWidget(
isVisible: isTextShown.value,
duration: const Duration(milliseconds: 500),
animationWidgetBuilder:
AnimatedVisibilityWidget.fadeAnimationWidgetBuilder,
child: ImageTxtButton(
text: '准备好了',
imgName: 'question/btn_icon_begin',
textStyle: TextStyles.mediumWhite20_014,
width: 186.0,
height: 54.0,
onPressed: () async {
await controller.confirm();
await Get.offAllNamed(Routes.SELECT);
},
), ),
), AnimatedVisibilityWidget(
const RSizedBox( isVisible: isTextShown.value,
height: 64.0, duration: const Duration(milliseconds: 500),
), animationWidgetBuilder:
], AnimatedVisibilityWidget.fadeAnimationWidgetBuilder,
child: ImageTxtButton(
text: '准备好了',
imgName: 'question/btn_icon_begin',
textStyle: TextStyles.mediumWhite20_014,
width: 186.0,
height: 54.0,
onPressed: () async {
await controller.confirm();
await fadeOutAnimationController.reverse();
await Get.offAllNamed(Routes.SELECT);
},
),
),
const RSizedBox(
height: 64.0,
),
],
),
), ),
), ),
), ),

View File

@ -41,6 +41,7 @@ class WelcomeView extends GetView<WelcomeController> {
: AnimatedVisibilityWidget( : AnimatedVisibilityWidget(
isVisible: !finishInput.value, isVisible: !finishInput.value,
duration: const Duration(milliseconds: 800), duration: const Duration(milliseconds: 800),
curve: Curves.fastOutSlowIn,
animationWidgetBuilder: AnimatedVisibilityWidget animationWidgetBuilder: AnimatedVisibilityWidget
.fadeAnimationWidgetBuilder, .fadeAnimationWidgetBuilder,
onDone: (visible) { onDone: (visible) {

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../utils/image_utils.dart'; import '../utils/image_utils.dart';
@ -17,10 +18,13 @@ class Images {
width: 35.w, width: 35.w,
); );
static Widget up = LoadAssetImage( static Widget up = _UpAndDown(
'btn_icon_up', child: LoadAssetImage(
height: 47.w, 'btn_icon_up',
width: 47.w, fit: BoxFit.contain,
height: 47.w,
width: 47.w,
),
); );
static Widget ip = LoadAssetImage( static Widget ip = LoadAssetImage(
@ -43,16 +47,20 @@ class Images {
fit: BoxFit.fill, fit: BoxFit.fill,
); );
static Widget splashBegin = LoadAssetImage( static Widget splashBegin = _FadeInAndOut(
'splash/btn_icon_begin', child: LoadAssetImage(
height: 105.h, 'splash/btn_icon_begin',
width: 378.w, height: 105.h,
width: 378.w,
),
); );
static Widget welcomeBegin = LoadAssetImage( static Widget welcomeBegin = _FadeInAndOut(
'welcome/btn_label_bg_up', child: LoadAssetImage(
height: 114.h, 'welcome/btn_label_bg_up',
width: 270.w, height: 114.h,
width: 270.w,
),
); );
static Widget pullDown = LoadAssetImage( static Widget pullDown = LoadAssetImage(
@ -61,10 +69,12 @@ class Images {
width: 21.w, width: 21.w,
); );
static Widget pullUp = LoadAssetImage( static Widget pullUp = _UpAndDown(
'welcome/btn_icon_up', child: LoadAssetImage(
height: 21.w, 'welcome/btn_icon_up',
width: 21.w, height: 21.w,
width: 21.w,
),
); );
static Widget selectPre = LoadAssetImage( static Widget selectPre = LoadAssetImage(
@ -79,16 +89,20 @@ class Images {
width: 24.w, width: 24.w,
); );
static Widget questionRecommended = LoadAssetImage( static Widget questionRecommended = _FadeInAndOut(
'question/label_recommended', child: LoadAssetImage(
height: 105.h, 'question/label_recommended',
width: 390.w, height: 105.h,
width: 390.w,
),
); );
static Widget homeCreate = LoadAssetImage( static Widget homeCreate = _FadeInAndOut(
'home/label_label_create', child: LoadAssetImage(
height: 105.h, 'home/label_label_create',
width: 378.w, height: 105.h,
width: 378.w,
),
); );
static Widget homeReset = LoadAssetImage( static Widget homeReset = LoadAssetImage(
@ -319,3 +333,83 @@ class Images {
static const AssetImage avatarUser = static const AssetImage avatarUser =
AssetImage("assets/images/label_avatar_user.png"); AssetImage("assets/images/label_avatar_user.png");
} }
class _FadeInAndOut extends HookWidget {
const _FadeInAndOut({
super.key,
required this.child,
});
final Widget child;
@override
Widget build(BuildContext context) {
final controller = useAnimationController(
duration: const Duration(milliseconds: 300),
reverseDuration: const Duration(milliseconds: 500),
);
useEffect(() {
void onStateChange(AnimationStatus state) {
if (state == AnimationStatus.completed) {
controller.reverse();
} else if (state == AnimationStatus.dismissed) {
controller.forward();
}
}
controller.addStatusListener(onStateChange);
controller.forward();
return () {
controller.removeStatusListener(onStateChange);
};
}, [controller]);
final opacityTween = Tween(
begin: 0.7,
end: 1.0,
);
return FadeTransition(
opacity: opacityTween.animate(controller),
child: child,
);
}
}
class _UpAndDown extends HookWidget {
const _UpAndDown({
super.key,
required this.child,
});
final Widget child;
@override
Widget build(BuildContext context) {
final controller = useAnimationController(
duration: const Duration(milliseconds: 300),
reverseDuration: const Duration(milliseconds: 500),
);
useEffect(() {
void onStateChange(AnimationStatus state) {
if (state == AnimationStatus.completed) {
controller.reverse();
} else if (state == AnimationStatus.dismissed) {
controller.forward();
}
}
controller.addStatusListener(onStateChange);
controller.forward();
return () {
controller.removeStatusListener(onStateChange);
};
}, [controller]);
final offsetTween = Tween(
begin: const Offset(0, 0.2),
end: const Offset(0, 0),
);
return SlideTransition(
position: offsetTween.animate(controller),
child: child,
);
}
}

View File

@ -182,7 +182,7 @@ class SwipeNextPageHandler extends StatelessWidget {
} }
}, },
onPanCancel: () { onPanCancel: () {
controller.animateTo(controller.lowerBound, curve: Curves.easeIn);
}, },
onPanUpdate: (details) { onPanUpdate: (details) {
controller.value -= details.delta.dy; controller.value -= details.delta.dy;