-
-
Save slightfoot/a868a1d0fcd2d28b13d08284a0c8aed1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:ui'; | |
import 'package:flutter/cupertino.dart'; | |
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
debugShowCheckedModeBanner: false, | |
home: Scaffold( | |
backgroundColor: Colors.black, | |
body: Center( | |
child: Spinner( | |
radius: 16.0, | |
), | |
), | |
), | |
); | |
} | |
} | |
class Spinner extends StatefulWidget { | |
const Spinner({ | |
Key key, | |
this.radius = 12.0, | |
}) : assert(radius != null), | |
super(key: key); | |
final double radius; | |
@override | |
_SpinnerState createState() => _SpinnerState(); | |
} | |
class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin { | |
AnimationController _controller; | |
static final _colors = <Color>[ | |
Color(0xFFE6194B), | |
Color(0xFFF58231), | |
Color(0xFFBFEF45), | |
Color(0xFFF032E6), | |
Color(0xFF42D4F4), | |
]; | |
@override | |
void initState() { | |
super.initState(); | |
_controller = AnimationController( | |
duration: Duration(milliseconds: 3000), | |
vsync: this, | |
)..repeat(); | |
} | |
@override | |
void dispose() { | |
_controller.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
final widthCircles = _colors.length * widget.radius * 2.0; | |
final widthSpacing = (_colors.length - 1) * widget.radius; | |
return RepaintBoundary( | |
child: CustomPaint( | |
painter: _SpinnerPainter( | |
animation: _controller, | |
colors: _colors, | |
radius: widget.radius, | |
), | |
size: Size(widthCircles + widthSpacing, widget.radius * 6), | |
willChange: true, | |
), | |
); | |
} | |
} | |
class _SpinnerPainter extends CustomPainter { | |
_SpinnerPainter({ | |
this.animation, | |
this.colors, | |
this.radius = 12.0, | |
}) : super(repaint: animation); | |
final Animation<double> animation; | |
final List<Color> colors; | |
final double radius; | |
static Curve _curveOfCurve = Cubic(.51, .23, .8, .38); | |
@override | |
void paint(Canvas canvas, Size size) { | |
final range = animation.value * colors.length; | |
final numerator = range.truncate(); | |
final fractional = range - numerator; | |
final centerX = size.width / 2.0; | |
final baselineY = size.height - radius; | |
for (int i = 1; i < colors.length; i++) { | |
final offset = Offset((radius + i * radius * 3) - (radius * 3 * fractional), baselineY); | |
canvas.drawCircle(offset, radius, Paint()..color = colors[(i + numerator) % colors.length]); | |
} | |
final time = _curveOfCurve.transform(fractional); | |
final offset = bezierPoint(radius, baselineY, centerX, -radius * 2.5, size.width - radius, baselineY, time); | |
canvas.drawCircle(offset, radius, Paint()..color = colors[numerator % colors.length]); | |
} | |
@override | |
bool shouldRepaint(_SpinnerPainter old) => | |
this.animation != old.animation || this.colors != old.colors || this.radius != old.radius; | |
static Offset bezierPoint( | |
double startX, double startY, double controlX, double controlY, double endX, double endY, double time) { | |
// Bézier curve calculation https://en.wikipedia.org/wiki/B%C3%A9zier_curve | |
return Offset( | |
(1 - time) * (1 - time) * startX + 2 * (1 - time) * time * controlX + time * time * endX, | |
(1 - time) * (1 - time) * startY + 2 * (1 - time) * time * controlY + time * time * endY, | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment