Last active
October 1, 2024 07:51
-
-
Save pskink/f80b86cb98839ce74cc78e307a6412fb to your computer and use it in GitHub Desktop.
This file contains hidden or 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:math'; | |
import 'dart:ui' as ui; | |
import 'package:flutter/material.dart'; | |
import 'package:collection/collection.dart'; | |
void main() => runApp(MaterialApp(home: Scaffold(body: Foo()))); | |
class Foo extends StatefulWidget { | |
@override | |
State<Foo> createState() => _FooState(); | |
} | |
class _FooState extends State<Foo> with TickerProviderStateMixin { | |
late final controller = AnimationController(vsync: this, duration: Durations.extralong4 * 3); | |
@override | |
Widget build(BuildContext context) { | |
return Column( | |
children: [ | |
ElevatedButton( | |
onPressed: () => controller.forward(from: 0), | |
child: Text('animate for ${controller.duration!.inMilliseconds}ms'), | |
), | |
Expanded( | |
child: ClipRect( | |
child: CustomPaint( | |
painter: FooPainter(controller), | |
child: const SizedBox.expand(), | |
), | |
), | |
), | |
], | |
); | |
} | |
} | |
class FooPainter extends CustomPainter { | |
FooPainter(this.controller) : super(repaint: controller) { | |
final recorder = ui.PictureRecorder(); | |
final canvas = Canvas(recorder); | |
// different shapes of 'point': | |
(switch (1) { | |
// filled circle | |
0 => const ShapeDecoration(color: Colors.black, shape: CircleBorder()), | |
// outlined circle | |
1 => const ShapeDecoration(shape: CircleBorder(side: BorderSide(color: Colors.black, width: R * 0.6))), | |
// 8-pointed star | |
_ => const ShapeDecoration(color: Colors.black, shape: StarBorder(points: 8, innerRadiusRatio: 0.6)), | |
}) | |
.createBoxPainter(() {}) | |
.paint(canvas, Offset.zero, const ImageConfiguration(size: Size.fromRadius(R))); | |
image = recorder.endRecording().toImageSync((2 * R).ceil(), (2 * R).ceil()); | |
} | |
static const R = 6.0; // radius | |
static const D = 3.0; // delta +/- | |
static const N = 1000; | |
final AnimationController controller; | |
late ui.Image image; | |
final rnd = Random(); | |
List<ui.Offset>? offsets; | |
final rects = List.filled(N, Offset.zero & const Size.fromRadius(R)); | |
final colors = List.generate(N, (i) => HSVColor.fromAHSV(1, 120 * i / N, 1, 0.8).toColor()); | |
@override | |
void paint(Canvas canvas, Size size) { | |
offsets ??= [ // initialize offsets | |
for (int i = 0; i < N; i++) | |
Offset(rnd.nextDouble() * size.width, rnd.nextDouble() * size.height) | |
]; | |
if (controller.isAnimating) { | |
offsets = [ // update offsets by random delta | |
...offsets!.mapIndexed((i, o) { | |
final factor = D * ui.lerpDouble(1, 2, colors[i].green / 255)!; | |
final x = (o.dx + factor * ui.lerpDouble(-1, 1, rnd.nextDouble())!).clamp(0.0, size.width); | |
final y = (o.dy + factor * ui.lerpDouble(-1, 1, rnd.nextDouble())!).clamp(0.0, size.height); | |
return Offset(x, y); | |
}) | |
]; | |
} | |
List<ui.RSTransform> transforms = [ | |
...offsets!.map((o) => ui.RSTransform(1, 0, o.dx - R, o.dy - R)) | |
]; | |
canvas.drawAtlas(image, transforms, rects, colors, BlendMode.dstATop, null, Paint()); | |
} | |
@override | |
bool shouldRepaint(covariant CustomPainter oldDelegate) => false; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment