chore: SpriteAnimationWidget from pre release flame (#228)

pull/226/head
arturplaczek 3 years ago committed by GitHub
parent 1f72dbfd4a
commit 20529c1406
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3,3 +3,4 @@ library pinball_flame;
export 'src/blueprint.dart';
export 'src/component_controller.dart';
export 'src/keyboard_input_controller.dart';
export 'src/sprite_animation.dart';

@ -0,0 +1,102 @@
// ignore_for_file: public_member_api_docs
import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame/image_composition.dart';
import 'package:flutter/material.dart' hide Animation;
/// {@template flame.widgets.sprite_animation_widget}
/// A [StatelessWidget] that renders a [SpriteAnimation].
/// {@endtemplate}
// TODO(arturplaczek): Remove when this PR will be merged.
// https://github.com/flame-engine/flame/pull/1552
class SpriteAnimationWidget extends StatelessWidget {
/// {@macro flame.widgets.sprite_animation_widget}
const SpriteAnimationWidget({
required this.controller,
this.anchor = Anchor.topLeft,
Key? key,
}) : super(key: key);
/// The positioning [Anchor].
final Anchor anchor;
final SpriteAnimationController controller;
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (_, __) {
return CustomPaint(
painter: SpritePainter(
controller.animation.getSprite(),
anchor,
),
);
},
);
}
}
class SpriteAnimationController extends AnimationController {
SpriteAnimationController({
required TickerProvider vsync,
required this.animation,
}) : super(vsync: vsync) {
duration = Duration(seconds: animation.totalDuration().ceil());
}
final SpriteAnimation animation;
double? _lastUpdated;
@override
void notifyListeners() {
super.notifyListeners();
final now = DateTime.now().millisecond.toDouble();
final dt = max<double>(0, (now - (_lastUpdated ?? 0)) / 1000);
animation.update(dt);
_lastUpdated = now;
}
}
class SpritePainter extends CustomPainter {
SpritePainter(
this._sprite,
this._anchor, {
double angle = 0,
}) : _angle = angle;
final Sprite _sprite;
final Anchor _anchor;
final double _angle;
@override
bool shouldRepaint(SpritePainter oldDelegate) {
return oldDelegate._sprite != _sprite ||
oldDelegate._anchor != _anchor ||
oldDelegate._angle != _angle;
}
@override
void paint(Canvas canvas, Size size) {
final boxSize = size.toVector2();
final rate = boxSize.clone()..divide(_sprite.srcSize);
final minRate = min(rate.x, rate.y);
final paintSize = _sprite.srcSize * minRate;
final anchorPosition = _anchor.toVector2();
final boxAnchorPosition = boxSize.clone()..multiply(anchorPosition);
final spriteAnchorPosition = anchorPosition..multiply(paintSize);
canvas
..translateVector(boxAnchorPosition..sub(spriteAnchorPosition))
..renderRotated(
_angle,
spriteAnchorPosition,
(canvas) => _sprite.render(canvas, size: paintSize),
);
}
}

@ -0,0 +1,74 @@
import 'package:flame/components.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_flame/pinball_flame.dart';
class MockSpriteAnimationController extends Mock
implements SpriteAnimationController {}
class MockSpriteAnimation extends Mock implements SpriteAnimation {}
class MockSprite extends Mock implements Sprite {}
// TODO(arturplaczek): Remove when this PR will be merged.
// https://github.com/flame-engine/flame/pull/1552
void main() {
group('PinballSpriteAnimationWidget', () {
late SpriteAnimationController controller;
late SpriteAnimation animation;
late Sprite sprite;
setUp(() {
controller = MockSpriteAnimationController();
animation = MockSpriteAnimation();
sprite = MockSprite();
when(() => controller.animation).thenAnswer((_) => animation);
when(() => animation.totalDuration()).thenAnswer((_) => 1);
when(() => animation.getSprite()).thenAnswer((_) => sprite);
when(() => sprite.srcSize).thenAnswer((_) => Vector2(1, 1));
when(() => sprite.srcSize).thenAnswer((_) => Vector2(1, 1));
});
testWidgets('renders correctly', (tester) async {
await tester.pumpWidget(
SpriteAnimationWidget(
controller: controller,
),
);
await tester.pumpAndSettle();
await tester.pumpAndSettle();
expect(find.byType(SpriteAnimationWidget), findsOneWidget);
});
test('SpriteAnimationController is updating animations', () {
SpriteAnimationController(
vsync: const TestVSync(),
animation: animation,
).notifyListeners();
verify(() => animation.update(any())).called(1);
});
testWidgets('SpritePainter shouldRepaint returns true when Sprite changed',
(tester) async {
final spritePainter = SpritePainter(
sprite,
Anchor.center,
angle: 45,
);
final anotherPainter = SpritePainter(
sprite,
Anchor.center,
angle: 30,
);
expect(spritePainter.shouldRepaint(anotherPainter), isTrue);
});
});
}
Loading…
Cancel
Save