Files
ReelShort/lib/pages/player/widgets/interaction_rail_burst.dart

89 lines
2.6 KiB
Dart

// Author: fruit.com.cn
// 职责:绘制收藏点赞周围的紧凑双向短线,无圆环或阴影,不参与手势。
import 'dart:math' as math;
import 'package:flutter/material.dart';
const int _rayCount = 8;
const double _startProgress = 0.1;
const double _radiusStart = 0.34;
const double _radiusTravel = 0.14;
const double _rayLength = 0.07;
const double _strokeFraction = 0.025;
const double _rayAlpha = 0.65;
const double _fullCircle = math.pi * 2;
const double _rayRotation = math.pi / _rayCount;
/// 收藏/点赞的装饰反馈:选中外扩,取消收束。
class RailTapBurst extends StatelessWidget {
/// [progress] 来自按钮的点击动画,不创建独立计时器。
const RailTapBurst({
super.key,
required this.progress,
required this.color,
required this.contracting,
});
final double progress;
final Color color;
final bool contracting;
@override
Widget build(BuildContext context) => IgnorePointer(
child: CustomPaint(
painter: _RailBurstPainter(
progress: progress,
color: color,
contracting: contracting,
),
),
);
}
class _RailBurstPainter extends CustomPainter {
const _RailBurstPainter({
required this.progress,
required this.color,
required this.contracting,
});
final double progress;
final Color color;
final bool contracting;
@override
void paint(Canvas canvas, Size size) {
final phase = ((progress - _startProgress) / (1 - _startProgress)).clamp(
0.0,
1.0,
);
if (phase == 0 || phase == 1) return;
final eased = Curves.easeOutCubic.transform(phase);
final travel = contracting ? 1 - eased : eased;
final base = size.shortestSide;
final radius = base * (_radiusStart + _radiusTravel * travel);
final alpha = math.sin(math.pi * phase);
final paint = Paint()
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeWidth = base * _strokeFraction
..color = color.withValues(alpha: alpha * _rayAlpha);
final center = size.center(Offset.zero);
final length = base * _rayLength * (1 - phase);
for (var index = 0; index < _rayCount; index++) {
final angle = _fullCircle * index / _rayCount + _rayRotation;
canvas.drawLine(
center + Offset.fromDirection(angle, radius),
center + Offset.fromDirection(angle, radius + length),
paint,
);
}
}
@override
bool shouldRepaint(covariant _RailBurstPainter oldDelegate) =>
oldDelegate.progress != progress ||
oldDelegate.color != color ||
oldDelegate.contracting != contracting;
}