219 lines
6.6 KiB
Dart
219 lines
6.6 KiB
Dart
// Author: fruit.com.cn
|
|
// 绘制个人数据区统一的红色叠层质感图标。
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../../../core/theme/app_colors.dart';
|
|
import '../../../core/theme/app_dimens.dart';
|
|
|
|
const double _designSize = 48;
|
|
const double _depth = 2;
|
|
const double _edgeWidth = 0.8;
|
|
const double _ringWidth = 1.1;
|
|
const double _groundBlur = 3;
|
|
const double _sparkleWaist = 0.28;
|
|
const double _screenTilt = -0.12;
|
|
const double _bookmarkTilt = 0.1;
|
|
const Offset _artCenter = Offset(24, 24);
|
|
const Rect _groundBounds = Rect.fromLTWH(8, 41, 32, 4);
|
|
const LinearGradient _faceGradient = LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [
|
|
AppColors.mineStatHighlight,
|
|
AppColors.brandRed,
|
|
AppColors.mineStatShade,
|
|
],
|
|
);
|
|
const LinearGradient _backGradient = LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [AppColors.brandRed, AppColors.mineStatDepth],
|
|
);
|
|
|
|
/// 个人数据的三种图标语义。
|
|
enum MineStatIconType { coins, history, favorites }
|
|
|
|
/// 无位图依赖的品牌红图标,统一亮面、厚度与高光语言。
|
|
class MineStatIcon extends StatelessWidget {
|
|
/// 根据数据类型绘制图标。
|
|
const MineStatIcon({super.key, required this.type});
|
|
|
|
final MineStatIconType type;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SizedBox.square(
|
|
dimension: AppDimens.mineStatsIcon,
|
|
child: DecoratedBox(
|
|
decoration: const BoxDecoration(
|
|
gradient: RadialGradient(
|
|
colors: [AppColors.mineStatGlow, AppColors.transparent],
|
|
),
|
|
),
|
|
child: CustomPaint(painter: _MineStatPainter(type)),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _MineStatPainter extends CustomPainter {
|
|
const _MineStatPainter(this.type);
|
|
|
|
final MineStatIconType type;
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
canvas.save();
|
|
canvas.scale(size.width / _designSize, size.height / _designSize);
|
|
canvas.drawOval(
|
|
_groundBounds,
|
|
Paint()
|
|
..color = AppColors.mineStatGlow
|
|
..maskFilter = const MaskFilter.blur(BlurStyle.normal, _groundBlur),
|
|
);
|
|
switch (type) {
|
|
case MineStatIconType.coins:
|
|
_coins(canvas);
|
|
case MineStatIconType.history:
|
|
_tilt(canvas, _screenTilt);
|
|
_screen(canvas);
|
|
case MineStatIconType.favorites:
|
|
_tilt(canvas, _bookmarkTilt);
|
|
_bookmark(canvas);
|
|
}
|
|
canvas.restore();
|
|
}
|
|
|
|
void _coins(Canvas canvas) {
|
|
const backBounds = Rect.fromLTWH(23, 6, 19, 19);
|
|
const frontBounds = Rect.fromLTWH(5, 15, 29, 29);
|
|
const innerCenter = Offset(19.5, 29.5);
|
|
const innerRadius = 11.3;
|
|
const stampRadius = 6.5;
|
|
const glintCenter = Offset(39, 33);
|
|
const double glintRadius = 3;
|
|
_surface(canvas, Path()..addOval(backBounds), backBounds, _backGradient);
|
|
_surface(canvas, Path()..addOval(frontBounds), frontBounds, _faceGradient);
|
|
canvas.drawCircle(
|
|
innerCenter,
|
|
innerRadius,
|
|
Paint()
|
|
..color = AppColors.mineStatEdge
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = _ringWidth,
|
|
);
|
|
_sparkle(canvas, innerCenter, stampRadius);
|
|
_sparkle(canvas, glintCenter, glintRadius);
|
|
}
|
|
|
|
void _screen(Canvas canvas) {
|
|
const backBounds = Rect.fromLTWH(12, 6, 29, 26);
|
|
const frontBounds = Rect.fromLTWH(6, 12, 32, 27);
|
|
const innerBounds = Rect.fromLTWH(10, 16, 24, 19);
|
|
const outerRadius = Radius.circular(6);
|
|
const innerRadius = Radius.circular(3);
|
|
const playPoints = [Offset(19, 20), Offset(19, 31), Offset(28, 25.5)];
|
|
const reflectionStart = Offset(13, 14);
|
|
const reflectionEnd = Offset(26, 14);
|
|
final back = Path()
|
|
..addRRect(RRect.fromRectAndRadius(backBounds, outerRadius));
|
|
final front = Path()
|
|
..addRRect(RRect.fromRectAndRadius(frontBounds, outerRadius));
|
|
_surface(canvas, back, backBounds, _backGradient);
|
|
_surface(canvas, front, frontBounds, _faceGradient);
|
|
canvas.drawRRect(
|
|
RRect.fromRectAndRadius(innerBounds, innerRadius),
|
|
Paint()..shader = _backGradient.createShader(innerBounds),
|
|
);
|
|
canvas.drawPath(
|
|
_polygon(playPoints),
|
|
Paint()..color = AppColors.mineStatGlint,
|
|
);
|
|
_reflection(canvas, reflectionStart, reflectionEnd);
|
|
}
|
|
|
|
void _bookmark(Canvas canvas) {
|
|
const bounds = Rect.fromLTWH(12, 7, 23, 35);
|
|
const radius = Radius.circular(4);
|
|
const cutout = [Offset(11, 44), Offset(23.5, 34), Offset(36, 44)];
|
|
const rearOffset = Offset(5, -3);
|
|
const stampCenter = Offset(23.5, 21);
|
|
const double stampRadius = 6;
|
|
const reflectionStart = Offset(17, 10);
|
|
const reflectionEnd = Offset(28, 10);
|
|
final cover = Path()..addRRect(RRect.fromRectAndRadius(bounds, radius));
|
|
final front = Path.combine(
|
|
PathOperation.difference,
|
|
cover,
|
|
_polygon(cutout),
|
|
);
|
|
_surface(
|
|
canvas,
|
|
front.shift(rearOffset),
|
|
bounds.shift(rearOffset),
|
|
_backGradient,
|
|
);
|
|
_surface(canvas, front, bounds, _faceGradient);
|
|
_sparkle(canvas, stampCenter, stampRadius);
|
|
_reflection(canvas, reflectionStart, reflectionEnd);
|
|
}
|
|
|
|
void _surface(Canvas canvas, Path path, Rect bounds, Gradient gradient) {
|
|
canvas.save();
|
|
canvas.translate(0, _depth);
|
|
canvas.drawPath(path, Paint()..color = AppColors.mineStatDepth);
|
|
canvas.restore();
|
|
canvas.drawPath(path, Paint()..shader = gradient.createShader(bounds));
|
|
canvas.drawPath(
|
|
path,
|
|
Paint()
|
|
..color = AppColors.mineStatEdge
|
|
..style = PaintingStyle.stroke
|
|
..strokeJoin = StrokeJoin.round
|
|
..strokeWidth = _edgeWidth,
|
|
);
|
|
}
|
|
|
|
void _sparkle(Canvas canvas, Offset center, double radius) {
|
|
final waist = radius * _sparkleWaist;
|
|
final points = [
|
|
center + Offset(0, -radius),
|
|
center + Offset(waist, -waist),
|
|
center + Offset(radius, 0),
|
|
center + Offset(waist, waist),
|
|
center + Offset(0, radius),
|
|
center + Offset(-waist, waist),
|
|
center + Offset(-radius, 0),
|
|
center + Offset(-waist, -waist),
|
|
];
|
|
canvas.drawPath(_polygon(points), Paint()..color = AppColors.mineStatGlint);
|
|
}
|
|
|
|
Path _polygon(List<Offset> points) {
|
|
return Path()..addPolygon(points, true);
|
|
}
|
|
|
|
void _reflection(Canvas canvas, Offset start, Offset end) {
|
|
canvas.drawLine(
|
|
start,
|
|
end,
|
|
Paint()
|
|
..color = AppColors.mineStatEdge
|
|
..strokeCap = StrokeCap.round
|
|
..strokeWidth = _ringWidth,
|
|
);
|
|
}
|
|
|
|
void _tilt(Canvas canvas, double angle) {
|
|
canvas.translate(_artCenter.dx, _artCenter.dy);
|
|
canvas.rotate(angle);
|
|
canvas.translate(-_artCenter.dx, -_artCenter.dy);
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(covariant _MineStatPainter oldDelegate) {
|
|
return oldDelegate.type != type;
|
|
}
|
|
}
|