首页小e动画

This commit is contained in:
czz
2025-06-07 17:49:19 +08:00
parent 77152e5d81
commit 48b3b06f17
3 changed files with 331 additions and 60 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -0,0 +1,273 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
// class FloatingSvgIcon extends StatefulWidget {
// final String assetPath; // SVG 图标路径
// final double width; // 宽度
// final double height; // 高度
// final Duration duration; // 浮动周期
// final double floatOffset; // 浮动高度(单位:比例)
// final VoidCallback? onTap; // 点击事件
// const FloatingSvgIcon({
// super.key,
// required this.assetPath,
// this.width = 60,
// this.height = 60,
// this.duration = const Duration(milliseconds: 800),
// this.floatOffset = 0.02,
// this.onTap,
// });
// @override
// State<FloatingSvgIcon> createState() => _FloatingSvgIconState();
// }
// class _FloatingSvgIconState extends State<FloatingSvgIcon>
// with SingleTickerProviderStateMixin {
// late final AnimationController _controller;
// late final Animation<Offset> _animation;
// @override
// void initState() {
// super.initState();
// _controller = AnimationController(
// vsync: this,
// duration: widget.duration,
// )..repeat(reverse: true);
// _animation = Tween<Offset>(
// begin: Offset(0, -widget.floatOffset),
// end: Offset(0, widget.floatOffset),
// ).animate(CurvedAnimation(
// parent: _controller,
// curve: Curves.easeInOut,
// ));
// }
// @override
// void dispose() {
// _controller.dispose();
// super.dispose();
// }
// // 计算阴影缩放因子,范围大致从 0.8 ~ 1.2
// double getShadowScale(double dy) {
// return 1 - dy * 0.25; // dy 为负时scale > 1dy 为正时scale < 1
// }
// // 计算阴影透明度,范围大致从 0.3 ~ 0.7
// double getShadowOpacity(double dy) {
// return 0.5 - dy * 0.3;
// }
// @override
// Widget build(BuildContext context) {
// return AnimatedBuilder(
// animation: _controller,
// builder: (context, child) {
// final dy = _animation.value.dy;
// final shadowScale = getShadowScale(dy).clamp(0.8, 1.2);
// final shadowOpacity = getShadowOpacity(dy).clamp(0.3, 0.7);
// return SlideTransition(
// position: _animation,
// child: GestureDetector(
// onTap: widget.onTap,
// child: SizedBox(
// width: widget.width,
// height: widget.height + widget.height * 0.3, // 多留点高度给阴影
// child: Stack(
// alignment: Alignment.topCenter,
// children: [
// // 阴影在底部,椭圆形,随浮动缩放和透明度变化
// Positioned(
// bottom: 0,
// child: Transform.scale(
// scale: shadowScale,
// child: Opacity(
// opacity: shadowOpacity,
// child: Container(
// width: widget.width * 0.6,
// height: widget.height * 0.08,
// decoration: BoxDecoration(
// color: Colors.black54,
// borderRadius:
// BorderRadius.circular(widget.height * 0.075),
// boxShadow: [
// BoxShadow(
// color: Colors.black26,
// blurRadius: 8,
// spreadRadius: 1,
// ),
// ],
// ),
// ),
// ),
// ),
// ),
// // SVG图标
// Positioned(
// top: 0,
// child: SizedBox(
// width: widget.width,
// height: widget.height,
// child: SvgPicture.asset(
// widget.assetPath,
// fit: BoxFit.fill,
// ),
// ),
// ),
// ],
// ),
// ),
// ),
// );
// },
// );
// }
// }
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class FloatingSvgIcon extends StatefulWidget {
final String assetPath; // SVG 图标路径
final double width; // 宽度
final double height; // 高度
final Duration duration; // 浮动周期
final double floatOffset; // 浮动高度(单位:比例)
final VoidCallback? onTap; // 点击事件
const FloatingSvgIcon({
super.key,
required this.assetPath,
this.width = 60,
this.height = 60,
this.duration = const Duration(milliseconds: 800),
this.floatOffset = 0.02,
this.onTap,
});
@override
State<FloatingSvgIcon> createState() => _FloatingSvgIconState();
}
class _FloatingSvgIconState extends State<FloatingSvgIcon>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<Offset> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: widget.duration,
)..repeat(reverse: true);
_animation = Tween<Offset>(
begin: Offset(0, -widget.floatOffset),
end: Offset(0, widget.floatOffset),
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
// // 计算阴影缩放因子,范围大致从 0.8 ~ 1.2
// double getShadowScale(double dy) {
// // dy 是当前偏移,负表示向上,正表示向下
// // 上浮时阴影变小,下落时阴影变大
// return (1 - dy * 5).clamp(0.8, 1.2);
// }
// // 计算阴影透明度,范围大致从 0.3 ~ 0.7
// double getShadowOpacity(double dy) {
// return (0.5 - dy * 1.5).clamp(0.3, 0.7);
// }
double getShadowScale(double dy) {
// dy负时上浮阴影变小正时下落阴影变大
return (1 + dy * 5).clamp(0.8, 1.2);
}
double getShadowOpacity(double dy) {
return (0.5 + dy * 1.5).clamp(0.3, 0.7);
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
final dy = _animation.value.dy;
final shadowScale = getShadowScale(dy);
final shadowOpacity = getShadowOpacity(dy);
return SizedBox(
width: widget.width,
height: widget.height + widget.height * 0.3, // 留出阴影空间
child: Stack(
alignment: Alignment.topCenter,
children: [
// 固定底部投影,大小随动画变化
Positioned(
bottom: 4,
child: Transform.scale(
scale: shadowScale,
child: Opacity(
opacity: shadowOpacity,
child: Container(
width: widget.width * 0.5,
height: widget.height * 0.12,
decoration: BoxDecoration(
color: Colors.black,
borderRadius:
BorderRadius.circular(widget.height * 0.1),
boxShadow: const [
BoxShadow(
color: Colors.black38,
blurRadius: 12,
spreadRadius: 0,
offset: Offset(0, 2),
),
],
),
),
),
),
),
// SVG图标上下浮动
SlideTransition(
position: _animation,
child: GestureDetector(
onTap: widget.onTap,
child: SizedBox(
width: widget.width,
height: widget.height,
child: SvgPicture.asset(
widget.assetPath,
fit: BoxFit.fill,
),
),
),
),
],
),
);
},
);
}
}

View File

@@ -9,7 +9,7 @@ import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/controller/device/body_device_controller.dart';
import 'package:vbvs_app/controller/main_bottom/global_controller.dart';
import 'package:vbvs_app/controller/mh_controller/muser_info_controller.dart';
import 'package:vbvs_app/pages/mh_page/FloatingSvgIcon.dart';
class NewHomePage extends StatefulWidget {
const NewHomePage({super.key});
@@ -96,64 +96,64 @@ class _NewHomePageState extends State<NewHomePage> {
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
iconTheme: IconThemeData(color: themeController.currentColor.sc3),
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
titleSpacing: 0,
title: Row(
children: [
// 左侧头像
SizedBox(width: 40.rpx),
CircleAvatar(
radius: 27.rpx, // 可根据需求调整
backgroundImage: login == 1
? (userInfoController.model.user!.avatar == null ||
userInfoController.model.user!.avatar!.isEmpty
? const AssetImage(
iconTheme:
IconThemeData(color: themeController.currentColor.sc3),
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
titleSpacing: 0,
title: Container(
height: 180.rpx,
child: Row(
children: [
// 左侧头像
SizedBox(width: 40.rpx),
CircleAvatar(
radius: 27.rpx, // 可根据需求调整
backgroundImage: login == 1
? (userInfoController.model.user!.avatar == null ||
userInfoController
.model.user!.avatar!.isEmpty
? const AssetImage(
"assets/images/people_avatar.png",
)
: NetworkImage(
userInfoController.model.user!.avatar!,
))
: const AssetImage(
"assets/images/people_avatar.png",
)
: NetworkImage(
userInfoController.model.user!.avatar!,
))
: const AssetImage(
"assets/images/people_avatar.png",
),
),
SizedBox(width: 23.rpx), // 左侧头像和文本之间的间距
Text(
'Eason Chan',
style: TextStyle(fontSize: 30.rpx, color: Colors.white),
),
),
),
SizedBox(width: 23.rpx), // 左侧头像和文本之间的间距
Text(
'Eason Chan',
style: TextStyle(fontSize: 30.rpx, color: Colors.white),
),
const Spacer(), // 左右分隔
// Container(
// width: 61.rpx,
// height: 78.rpx,
// alignment: const Alignment(0, 0),
// child: Image.asset(
// "assets/images/xiaoe.png",
// fit: BoxFit.cover,
// ),
// ),
// SizedBox(width: 46.rpx), // icon 之间的间距
ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: Colors.transparent,
padding: EdgeInsets.only(right: 0),
onTap: () {},
child: Container(
height: 60.rpx,
width: 75.rpx,
child: SvgPicture.asset(
'assets/img/icon/xiaoe.svg',
// color: Colors.white,
))),
const Spacer(), // 左右分隔
// Container(
// width: 61.rpx,
// height: 78.rpx,
// alignment: const Alignment(0, 0),
// child: Image.asset(
// "assets/images/xiaoe.png",
// fit: BoxFit.cover,
// ),
// ),
// SizedBox(width: 46.rpx), // icon 之间的间距
// SizedBox(width: 40.rpx),
],
),
centerTitle: false,
),
FloatingSvgIcon(
assetPath: 'assets/img/icon/xiaoe.svg',
width: 60.rpx,
height: 60.rpx,
onTap: () {
print("点击了小鹅图标");
},
),
SizedBox(width: 40.rpx),
],
),
)),
body: SafeArea(
child: Container(
width: MediaQuery.sizeOf(context).width,
@@ -165,9 +165,7 @@ class _NewHomePageState extends State<NewHomePage> {
// fit: BoxFit.cover,
// ),
// ),
decoration: BoxDecoration(
),
decoration: BoxDecoration(),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,