首页小e动画
This commit is contained in:
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 |
273
lib/pages/mh_page/FloatingSvgIcon.dart
Normal file
273
lib/pages/mh_page/FloatingSvgIcon.dart
Normal 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 > 1,dy 为正时,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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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/device/body_device_controller.dart';
|
||||||
import 'package:vbvs_app/controller/main_bottom/global_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/controller/mh_controller/muser_info_controller.dart';
|
||||||
|
import 'package:vbvs_app/pages/mh_page/FloatingSvgIcon.dart';
|
||||||
|
|
||||||
class NewHomePage extends StatefulWidget {
|
class NewHomePage extends StatefulWidget {
|
||||||
const NewHomePage({super.key});
|
const NewHomePage({super.key});
|
||||||
@@ -96,64 +96,64 @@ class _NewHomePageState extends State<NewHomePage> {
|
|||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
iconTheme: IconThemeData(color: themeController.currentColor.sc3),
|
iconTheme:
|
||||||
backgroundColor: Colors.transparent,
|
IconThemeData(color: themeController.currentColor.sc3),
|
||||||
automaticallyImplyLeading: false,
|
backgroundColor: Colors.transparent,
|
||||||
titleSpacing: 0,
|
automaticallyImplyLeading: false,
|
||||||
title: Row(
|
titleSpacing: 0,
|
||||||
children: [
|
title: Container(
|
||||||
// 左侧头像
|
height: 180.rpx,
|
||||||
SizedBox(width: 40.rpx),
|
child: Row(
|
||||||
CircleAvatar(
|
children: [
|
||||||
radius: 27.rpx, // 可根据需求调整
|
// 左侧头像
|
||||||
backgroundImage: login == 1
|
SizedBox(width: 40.rpx),
|
||||||
? (userInfoController.model.user!.avatar == null ||
|
CircleAvatar(
|
||||||
userInfoController.model.user!.avatar!.isEmpty
|
radius: 27.rpx, // 可根据需求调整
|
||||||
? const AssetImage(
|
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",
|
"assets/images/people_avatar.png",
|
||||||
)
|
),
|
||||||
: NetworkImage(
|
),
|
||||||
userInfoController.model.user!.avatar!,
|
SizedBox(width: 23.rpx), // 左侧头像和文本之间的间距
|
||||||
))
|
Text(
|
||||||
: const AssetImage(
|
'Eason Chan',
|
||||||
"assets/images/people_avatar.png",
|
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(), // 左右分隔
|
const Spacer(), // 左右分隔
|
||||||
// Container(
|
// Container(
|
||||||
// width: 61.rpx,
|
// width: 61.rpx,
|
||||||
// height: 78.rpx,
|
// height: 78.rpx,
|
||||||
// alignment: const Alignment(0, 0),
|
// alignment: const Alignment(0, 0),
|
||||||
// child: Image.asset(
|
// child: Image.asset(
|
||||||
// "assets/images/xiaoe.png",
|
// "assets/images/xiaoe.png",
|
||||||
// fit: BoxFit.cover,
|
// fit: BoxFit.cover,
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// SizedBox(width: 46.rpx), // icon 之间的间距
|
// 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,
|
|
||||||
))),
|
|
||||||
|
|
||||||
// SizedBox(width: 40.rpx),
|
FloatingSvgIcon(
|
||||||
],
|
assetPath: 'assets/img/icon/xiaoe.svg',
|
||||||
),
|
width: 60.rpx,
|
||||||
centerTitle: false,
|
height: 60.rpx,
|
||||||
),
|
onTap: () {
|
||||||
|
print("点击了小鹅图标");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(width: 40.rpx),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Container(
|
child: Container(
|
||||||
width: MediaQuery.sizeOf(context).width,
|
width: MediaQuery.sizeOf(context).width,
|
||||||
@@ -165,9 +165,7 @@ class _NewHomePageState extends State<NewHomePage> {
|
|||||||
// fit: BoxFit.cover,
|
// fit: BoxFit.cover,
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(),
|
||||||
|
|
||||||
),
|
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
|||||||
Reference in New Issue
Block a user