Drop `web/particle_background` (#1453)

pull/1492/head
Brett Morgan 2 years ago committed by GitHub
parent cb46fade86
commit ee72fc8ad7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2019 Felix Blaschke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -1,7 +0,0 @@
Flutter app demonstrating
[simple_animations](https://pub.dev/packages/simple_animations) in action.
Contributed by Felix Blaschke.
One of a series of samples
[published here](https://github.com/felixblaschke/simple_animations/tree/2.4.2/example).

@ -1,11 +0,0 @@
include: ../../analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: true
implicit-dynamic: true
linter:
rules:
constant_identifier_names: false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

@ -1,181 +0,0 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:particle_background/simple_animations_package.dart';
void main() => runApp(const ParticleApp());
class ParticleApp extends StatelessWidget {
const ParticleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: ParticleBackgroundPage(),
),
);
}
}
class ParticleBackgroundPage extends StatelessWidget {
const ParticleBackgroundPage({super.key});
@override
Widget build(BuildContext context) {
return Stack(
children: const [
Positioned.fill(child: AnimatedBackground()),
Positioned.fill(child: Particles(30)),
Positioned.fill(child: CenteredText()),
],
);
}
}
class Particles extends StatefulWidget {
final int numberOfParticles;
const Particles(this.numberOfParticles, {super.key});
@override
State<Particles> createState() => _ParticlesState();
}
class _ParticlesState extends State<Particles> {
final Random random = Random();
final List<ParticleModel> particles = [];
@override
void initState() {
List.generate(widget.numberOfParticles, (index) {
particles.add(ParticleModel(random));
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Rendering(
startTime: const Duration(seconds: 30),
onTick: _simulateParticles,
builder: (context, time) {
return CustomPaint(
painter: ParticlePainter(particles, time),
);
},
);
}
_simulateParticles(Duration time) {
for (var particle in particles) {
particle.maintainRestart(time);
}
}
}
class ParticleModel {
late Animatable tween;
late double size;
late AnimationProgress animationProgress;
Random random;
ParticleModel(this.random) {
restart();
}
restart({Duration time = Duration.zero}) {
final startPosition = Offset(-0.2 + 1.4 * random.nextDouble(), 1.2);
final endPosition = Offset(-0.2 + 1.4 * random.nextDouble(), -0.2);
final duration = Duration(milliseconds: 3000 + random.nextInt(6000));
tween = MultiTrackTween([
Track("x").add(
duration, Tween(begin: startPosition.dx, end: endPosition.dx),
curve: Curves.easeInOutSine),
Track("y").add(
duration, Tween(begin: startPosition.dy, end: endPosition.dy),
curve: Curves.easeIn),
]);
animationProgress = AnimationProgress(duration: duration, startTime: time);
size = 0.2 + random.nextDouble() * 0.4;
}
maintainRestart(Duration time) {
if (animationProgress.progress(time) == 1.0) {
restart(time: time);
}
}
}
class ParticlePainter extends CustomPainter {
List<ParticleModel> particles;
Duration time;
ParticlePainter(this.particles, this.time);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.white.withAlpha(50);
for (var particle in particles) {
var progress = particle.animationProgress.progress(time);
final animation = particle.tween.transform(progress);
final position =
Offset(animation["x"] * size.width, animation["y"] * size.height);
canvas.drawCircle(position, size.width * 0.2 * particle.size, paint);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
class AnimatedBackground extends StatelessWidget {
const AnimatedBackground({super.key});
@override
Widget build(BuildContext context) {
final tween = MultiTrackTween([
Track("color1").add(
const Duration(seconds: 3),
ColorTween(
begin: const Color(0xff8a113a), end: Colors.lightBlue.shade900)),
Track("color2").add(const Duration(seconds: 3),
ColorTween(begin: const Color(0xff440216), end: Colors.blue.shade600))
]);
return ControlledAnimation(
playback: Playback.MIRROR,
tween: tween,
duration: tween.duration,
builder: (context, dynamic animation) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [animation["color1"], animation["color2"]])),
);
},
);
}
}
class CenteredText extends StatelessWidget {
const CenteredText({
super.key,
});
@override
Widget build(BuildContext context) {
return const Center(
child: Text(
"Welcome to Flutter for web",
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w200),
textScaleFactor: 4,
),
);
}
}

@ -1,455 +0,0 @@
// Package simple_animations:
// https://pub.dev/packages/simple_animations
import 'dart:math';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
/// Widget to easily create a continuous animation.
///
/// You need to specify a [builder] function that gets the build context passed
/// along with the [timeElapsed] (as a [Duration]) since the rendering started.
/// You can use this time to specify custom animations on it.
///
/// The [builder] rebuilds all sub-widgets every frame.
///
/// You define an [onTick] function that is called before builder to update
/// you rendered scene. It's also utilized during fast-forwarding the animation.
///
/// Specify a [startTime] to fast-forward your animation in the beginning.
/// The widget will interpolate the animation by calling the [onTick] function
/// multiple times. (Default value is `20`. You can tune it by setting the
/// [startTimeSimulationTicks] property.)
class Rendering extends StatefulWidget {
final Widget Function(BuildContext context, Duration timeElapsed) builder;
final Function(Duration timeElapsed)? onTick;
final Duration startTime;
final int startTimeSimulationTicks;
const Rendering(
{required this.builder,
this.onTick,
this.startTime = Duration.zero,
this.startTimeSimulationTicks = 20,
super.key});
@override
State<Rendering> createState() => _RenderingState();
}
class _RenderingState extends State<Rendering>
with SingleTickerProviderStateMixin {
late Ticker _ticker;
Duration _timeElapsed = const Duration(milliseconds: 0);
@override
void initState() {
if (widget.startTime > Duration.zero) {
_simulateStartTimeTicks();
}
_ticker = createTicker((elapsed) {
_onRender(elapsed + widget.startTime);
});
_ticker.start();
super.initState();
}
void _onRender(Duration effectiveElapsed) {
if (widget.onTick != null) {
widget.onTick!(effectiveElapsed);
}
setState(() {
_timeElapsed = effectiveElapsed;
});
}
void _simulateStartTimeTicks() {
if (widget.onTick != null) {
for (var i in Iterable.generate(widget.startTimeSimulationTicks + 1)) {
final simulatedTime = Duration(
milliseconds: (widget.startTime.inMilliseconds *
i /
widget.startTimeSimulationTicks)
.round());
widget.onTick!(simulatedTime);
}
}
}
@override
void dispose() {
_ticker.stop(canceled: true);
super.dispose();
}
@override
Widget build(BuildContext context) {
return widget.builder(context, _timeElapsed);
}
}
/// Animatable that tweens multiple parallel properties (called [Track]s).
/// ---
/// The constructor of [MultiTrackTween] expects a list of [Track] objects.
/// You can fetch the specified total duration via [duration] getter.
/// ---
/// Example:
///
/// ```dart
/// final tween = MultiTrackTween([
/// Track("color")
/// .add(Duration(seconds: 1), ColorTween(begin: Colors.red, end: Colors.blue))
/// .add(Duration(seconds: 1), ColorTween(begin: Colors.blue, end: Colors.yellow)),
/// Track("width")
/// .add(Duration(milliseconds: 500), ConstantTween(200.0))
/// .add(Duration(milliseconds: 1500), Tween(begin: 200.0, end: 400.0),
/// curve: Curves.easeIn)
/// ]);
///
/// return ControlledAnimation(
/// duration: tween.duration,
/// tween: tween,
/// builder: (context, values) {
/// ...
/// }
/// );
/// ```
class MultiTrackTween extends Animatable<Map<String, dynamic>> {
final _tracksToTween = <String, _TweenData>{};
var _maxDuration = 0;
MultiTrackTween(List<Track> tracks)
: assert(tracks.isNotEmpty, "Add a List<Track> to configure the tween."),
assert(tracks.where((track) => track.items.isEmpty).isEmpty,
"Each Track needs at least one added Tween by using the add()-method.") {
_computeMaxDuration(tracks);
_computeTrackTweens(tracks);
}
void _computeMaxDuration(List<Track> tracks) {
for (var track in tracks) {
final trackDuration = track.items
.map((item) => item.duration.inMilliseconds)
.reduce((sum, item) => sum + item);
_maxDuration = max(_maxDuration, trackDuration);
}
}
void _computeTrackTweens(List<Track> tracks) {
for (var track in tracks) {
final trackDuration = track.items
.map((item) => item.duration.inMilliseconds)
.reduce((sum, item) => sum + item);
final sequenceItems = track.items
.map((item) => TweenSequenceItem(
tween: item.tween,
weight: item.duration.inMilliseconds / _maxDuration))
.toList();
if (trackDuration < _maxDuration) {
sequenceItems.add(TweenSequenceItem(
tween: ConstantTween(null),
weight: (_maxDuration - trackDuration) / _maxDuration));
}
final sequence = TweenSequence(sequenceItems);
_tracksToTween[track.name] =
_TweenData(tween: sequence, maxTime: trackDuration / _maxDuration);
}
}
/// Returns the highest duration specified by [Track]s.
/// ---
/// Use it to pass it into an [ControlledAnimation].
///
/// You can also scale it by multiplying a double value.
///
/// Example:
/// ```dart
/// final tween = MultiTrackTween(listOfTracks);
///
/// return ControlledAnimation(
/// duration: tween.duration * 1.25, // stretch animation by 25%
/// tween: tween,
/// builder: (context, values) {
/// ...
/// }
/// );
/// ```
Duration get duration {
return Duration(milliseconds: _maxDuration);
}
@override
Map<String, dynamic> transform(double t) {
final Map<String, dynamic> result = {};
_tracksToTween.forEach((name, tweenData) {
final double tTween = max(0, min(t, tweenData.maxTime! - 0.0001));
result[name] = tweenData.tween!.transform(tTween);
});
return result;
}
}
/// Single property to tween. Used by [MultiTrackTween].
class Track<T> {
final String name;
final List<TrackItem> items = [];
Track(this.name);
/// Adds a "piece of animation" to a [Track].
///
/// You need to pass a [duration] and a [tween]. It will return the track, so
/// you can specify a track in a builder's style.
///
/// Optionally you can set a named parameter [curve] that applies an easing
/// curve to the tween.
Track<T> add(Duration duration, Animatable<T> tween, {Curve? curve}) {
items.add(TrackItem(duration, tween, curve: curve));
return this;
}
}
class TrackItem<T> {
final Duration duration;
late Animatable<T> tween;
TrackItem(this.duration, Animatable<T> tween, {Curve? curve}) {
if (curve != null) {
this.tween = tween.chain(CurveTween(curve: curve));
} else {
this.tween = tween;
}
}
}
class _TweenData<T> {
final Animatable<T>? tween;
final double? maxTime;
_TweenData({this.tween, this.maxTime});
}
/// Playback tell the controller of the animation what to do.
enum Playback {
/// Animation stands still.
PAUSE,
/// Animation plays forwards and stops at the end.
PLAY_FORWARD,
/// Animation plays backwards and stops at the beginning.
PLAY_REVERSE,
/// Animation will reset to the beginning and start playing forward.
START_OVER_FORWARD,
/// Animation will reset to the end and start play backward.
START_OVER_REVERSE,
/// Animation will play forwards and start over at the beginning when it
/// reaches the end.
LOOP,
/// Animation will play forward until the end and will reverse playing until
/// it reaches the beginning. Then it starts over playing forward. And so on.
MIRROR
}
/// Widget to create custom, managed, tween-based animations in a very simple way.
///
/// ---
///
/// An internal [AnimationController] will do everything you tell him by
/// dynamically assigning the one [Playback] to [playback] property.
/// By default the animation will start playing forward and stops at the end.
///
/// A minimum set of properties are [duration] (time span of the animation),
/// [tween] (values to interpolate among the animation) and a [builder] function
/// (defines the animated scene).
///
/// Instead of using [builder] as building function you can use for performance
/// critical scenarios [builderWithChild] along with a prebuild [child].
///
/// ---
///
/// The following properties are optional:
///
/// - You can apply a [delay] that forces the animation to pause a
/// specified time before the animation will perform the defined [playback]
/// instruction.
///
/// - You can specify a [curve] that modifies the [tween] by applying a
/// non-linear animation function. You can find curves in [Curves], for
/// example [Curves.easeOut] or [Curves.easeIn].
///
/// - You can track the animation by setting an [AnimationStatusListener] to
/// the property [animationControllerStatusListener]. The internal [AnimationController] then
/// will route out any events that occur. [ControlledAnimation] doesn't filter
/// or modifies these events. These events are currently only reliable for the
/// [playback]-types [Playback.PLAY_FORWARD] and [Playback.PLAY_REVERSE].
///
/// - You can set the start position of animation by specifying [startPosition]
/// with a value between *0.0* and *1.0*. The [startPosition] is only
/// evaluated once during the initialization of the widget.
///
class ControlledAnimation<T> extends StatefulWidget {
final Playback playback;
final Animatable<T> tween;
final Curve curve;
final Duration duration;
final Duration? delay;
final Widget Function(BuildContext buildContext, T animatedValue)? builder;
final Widget Function(BuildContext, Widget? child, T animatedValue)?
builderWithChild;
final Widget? child;
final AnimationStatusListener? animationControllerStatusListener;
final double startPosition;
const ControlledAnimation(
{this.playback = Playback.PLAY_FORWARD,
required this.tween,
this.curve = Curves.linear,
required this.duration,
this.delay,
this.builder,
this.builderWithChild,
this.child,
this.animationControllerStatusListener,
this.startPosition = 0.0,
super.key})
: assert(
(builderWithChild != null && child != null && builder == null) ||
(builder != null && builderWithChild == null && child == null),
"Either use just builder and keep buildWithChild and child null. "
"Or keep builder null and set a builderWithChild and a child."),
assert(
startPosition >= 0 && startPosition <= 1,
"The property startPosition "
"must have a value between 0.0 and 1.0.");
@override
State<ControlledAnimation> createState() => _ControlledAnimationState<T>();
}
class _ControlledAnimationState<T> extends State<ControlledAnimation>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<T?> _animation;
bool _isDisposed = false;
bool _waitForDelay = true;
bool _isCurrentlyMirroring = false;
@override
void initState() {
_controller = AnimationController(vsync: this, duration: widget.duration)
..addListener(() {
setState(() {});
})
..value = widget.startPosition;
_animation = widget.tween
.chain(CurveTween(curve: widget.curve))
.animate(_controller) as Animation<T?>;
if (widget.animationControllerStatusListener != null) {
_controller.addStatusListener(widget.animationControllerStatusListener!);
}
initialize();
super.initState();
}
Future<void> initialize() async {
if (widget.delay != null) {
await Future.delayed(widget.delay!);
}
_waitForDelay = false;
await executeInstruction();
}
@override
void didUpdateWidget(ControlledAnimation oldWidget) {
_controller.duration = widget.duration;
executeInstruction();
super.didUpdateWidget(oldWidget);
}
Future<void> executeInstruction() async {
if (_isDisposed || _waitForDelay) {
return;
}
if (widget.playback == Playback.PAUSE) {
_controller.stop();
}
if (widget.playback == Playback.PLAY_FORWARD) {
await _controller.forward();
}
if (widget.playback == Playback.PLAY_REVERSE) {
await _controller.reverse();
}
if (widget.playback == Playback.START_OVER_FORWARD) {
await _controller.forward(from: 0.0);
}
if (widget.playback == Playback.START_OVER_REVERSE) {
await _controller.reverse(from: 1.0);
}
if (widget.playback == Playback.LOOP) {
await _controller.repeat();
}
if (widget.playback == Playback.MIRROR && !_isCurrentlyMirroring) {
_isCurrentlyMirroring = true;
await _controller.repeat(reverse: true);
}
if (widget.playback != Playback.MIRROR) {
_isCurrentlyMirroring = false;
}
}
@override
Widget build(BuildContext context) {
if (widget.builder != null) {
return widget.builder!(context, _animation.value);
} else if (widget.builderWithChild != null && widget.child != null) {
return widget.builderWithChild!(context, widget.child, _animation.value);
}
_controller.stop(canceled: true);
throw FlutterError(
"I don't know how to build the animation. Make sure to either specify "
"a builder or a builderWithChild (along with a child).");
}
@override
void dispose() {
_isDisposed = true;
_controller.dispose();
super.dispose();
}
}
/// Utility class to compute an animation progress between two points in time.
///
/// On creation you specify a [startTime] and a [duration].
///
/// You can query the progress value - a value between `0.0` and `11.0` - by
/// calling [progress] and passing the current time.
class AnimationProgress {
final Duration duration;
final Duration startTime;
/// Creates an [AnimationProgress].
AnimationProgress({required this.duration, required this.startTime});
/// Queries the current progress value based on the specified [startTime] and
/// [duration] as a value between `0.0` and `1.0`. It will automatically
/// clamp values this interval to fit in.
double progress(Duration time) => max(0.0,
min((time - startTime).inMilliseconds / duration.inMilliseconds, 1.0));
}

@ -1,14 +0,0 @@
name: particle_background
description: Example for the simple_animations package
homepage: https://github.com/felixblaschke/simple_animations
environment:
sdk: ">=2.17.0-0 <3.0.0"
dependencies:
flutter:
sdk: flutter
flutter_lints: ^2.0.1
flutter:
assets:
- preview.png

@ -1,11 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<script defer src="main.dart.js" type="application/javascript"></script>
</head>
<body>
</body>
</html>

@ -634,23 +634,6 @@ samples:
web: web/github_dataviz
type: demo
- name: Particle Background
author: Felix Blaschke
screenshots:
- url: images/particle_background1.png
alt: Particle Background screenshot
source: https://github.com/flutter/samples/tree/main/web/particle_background
description: >
Flutter app demonstrating package:simple_animations in action.
difficulty: intermediate
widgets: []
packages: []
platforms: ['web']
links: []
tags: ['demo', 'animation']
web: web/particle_background
type: demo
- name: Dice
author: Jaime Blasco
screenshots:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 KiB

Loading…
Cancel
Save