-
-
Notifications
You must be signed in to change notification settings - Fork 899
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds a new component called ClipComponent that clips the canvas area based on its size and shape.
- Loading branch information
1 parent
96be840
commit f34d86d
Showing
9 changed files
with
323 additions
and
2 deletions.
There are no files selected for viewing
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
87 changes: 87 additions & 0 deletions
87
examples/lib/stories/components/clip_component_example.dart
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import 'dart:math'; | ||
import 'dart:ui'; | ||
|
||
import 'package:flame/components.dart'; | ||
import 'package:flame/effects.dart'; | ||
import 'package:flame/game.dart'; | ||
import 'package:flame/input.dart'; | ||
import 'package:flutter/material.dart' hide Gradient; | ||
|
||
class _Rectangle extends RectangleComponent { | ||
_Rectangle() | ||
: super( | ||
size: Vector2(200, 200), | ||
anchor: Anchor.center, | ||
paint: Paint() | ||
..shader = Gradient.linear( | ||
Offset.zero, | ||
const Offset(0, 100), | ||
[Colors.orange, Colors.blue], | ||
), | ||
children: [ | ||
SequenceEffect( | ||
[ | ||
RotateEffect.by( | ||
pi * 2, | ||
LinearEffectController(.4), | ||
), | ||
RotateEffect.by( | ||
0, | ||
LinearEffectController(.4), | ||
), | ||
], | ||
infinite: true, | ||
), | ||
], | ||
); | ||
} | ||
|
||
class ClipComponentExample extends FlameGame with TapDetector { | ||
static String description = 'Tap on the objects to increase their size.'; | ||
|
||
@override | ||
Future<void> onLoad() async { | ||
addAll( | ||
[ | ||
ClipComponent.circle( | ||
position: Vector2(100, 100), | ||
size: Vector2.all(50), | ||
children: [_Rectangle()], | ||
), | ||
ClipComponent.rectangle( | ||
position: Vector2(200, 100), | ||
size: Vector2.all(50), | ||
children: [_Rectangle()], | ||
), | ||
ClipComponent.polygon( | ||
points: [ | ||
Vector2(1, 0), | ||
Vector2(1, 1), | ||
Vector2(0, 1), | ||
Vector2(1, 0), | ||
], | ||
position: Vector2(200, 200), | ||
size: Vector2.all(50), | ||
children: [_Rectangle()], | ||
), | ||
], | ||
); | ||
} | ||
|
||
@override | ||
void onTapUp(TapUpInfo info) { | ||
final position = info.eventPosition.game; | ||
final hit = children | ||
.whereType<PositionComponent>() | ||
.where( | ||
(component) => component.containsLocalPoint( | ||
position - component.position, | ||
), | ||
) | ||
.toList(); | ||
|
||
hit.forEach((component) { | ||
component.size += Vector2.all(10); | ||
}); | ||
} | ||
} |
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import 'dart:ui'; | ||
|
||
import 'package:flame/components.dart'; | ||
import 'package:flame/experimental.dart'; | ||
|
||
/// A function that creates a shape based on a size represented by a [Vector2] | ||
typedef ShapeBuilder = Shape Function(Vector2 size); | ||
|
||
/// {@template clip_component} | ||
/// A component that will clip its content. | ||
/// {@endtemplate} | ||
class ClipComponent extends PositionComponent { | ||
/// {@macro clip_component} | ||
/// | ||
/// Clips the canvas based its shape and size. | ||
ClipComponent({ | ||
required ShapeBuilder builder, | ||
super.position, | ||
super.size, | ||
super.scale, | ||
super.angle, | ||
super.anchor, | ||
super.children, | ||
super.priority, | ||
}) : _builder = builder; | ||
|
||
/// {@macro circle_clip_component} | ||
/// | ||
/// Clips the canvas in the form of a circle based on its size. | ||
factory ClipComponent.circle({ | ||
Vector2? position, | ||
Vector2? size, | ||
Vector2? scale, | ||
double? angle, | ||
Anchor? anchor, | ||
Iterable<Component>? children, | ||
int? priority, | ||
}) { | ||
return ClipComponent( | ||
builder: (size) => Circle(size / 2, size.x / 2), | ||
position: position, | ||
size: size, | ||
scale: scale, | ||
angle: angle, | ||
anchor: anchor, | ||
children: children, | ||
priority: priority, | ||
); | ||
} | ||
|
||
/// {@macro rectangle_clip_component} | ||
/// | ||
/// Clips the canvas in the form of a rectangle based on its size. | ||
factory ClipComponent.rectangle({ | ||
Vector2? position, | ||
Vector2? size, | ||
Vector2? scale, | ||
double? angle, | ||
Anchor? anchor, | ||
Iterable<Component>? children, | ||
int? priority, | ||
}) { | ||
return ClipComponent( | ||
builder: (size) => Rectangle.fromRect(size.toRect()), | ||
position: position, | ||
size: size, | ||
scale: scale, | ||
angle: angle, | ||
anchor: anchor, | ||
children: children, | ||
priority: priority, | ||
); | ||
} | ||
|
||
/// {@macro polygon_clip_component} | ||
/// | ||
/// Clips the canvas in the form of a polygon based on its size. | ||
factory ClipComponent.polygon({ | ||
required List<Vector2> points, | ||
Vector2? position, | ||
Vector2? size, | ||
Vector2? scale, | ||
double? angle, | ||
Anchor? anchor, | ||
Iterable<Component>? children, | ||
int? priority, | ||
}) { | ||
assert( | ||
points.length > 2, | ||
'PolygonClipComponent requires at least 3 points.', | ||
); | ||
|
||
return ClipComponent( | ||
builder: (size) { | ||
final translatedPoints = points | ||
.map( | ||
(p) => p.clone()..multiply(size), | ||
) | ||
.toList(); | ||
return Polygon(translatedPoints); | ||
}, | ||
position: position, | ||
size: size, | ||
scale: scale, | ||
angle: angle, | ||
anchor: anchor, | ||
children: children, | ||
priority: priority, | ||
); | ||
} | ||
|
||
late Path _path; | ||
late Shape _shape; | ||
final ShapeBuilder _builder; | ||
|
||
@override | ||
Future<void> onLoad() async { | ||
_prepare(); | ||
size.addListener(_prepare); | ||
} | ||
|
||
void _prepare() { | ||
_shape = _builder(size); | ||
_path = _shape.asPath(); | ||
} | ||
|
||
@override | ||
void render(Canvas canvas) => canvas.clipPath(_path); | ||
|
||
@override | ||
bool containsPoint(Vector2 point) { | ||
return _shape.containsPoint(point - position); | ||
} | ||
|
||
@override | ||
bool containsLocalPoint(Vector2 point) { | ||
return _shape.containsPoint(point); | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import 'package:flame/components.dart'; | ||
import 'package:flame_test/flame_test.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
|
||
class _Rectangle extends RectangleComponent { | ||
_Rectangle() | ||
: super( | ||
size: Vector2(200, 200), | ||
anchor: Anchor.center, | ||
paint: Paint()..color = Colors.blue, | ||
); | ||
} | ||
|
||
void main() { | ||
group('ClipComponent', () { | ||
group('RectangleClipComponent', () { | ||
testGolden( | ||
'renders correctly', | ||
(game) async { | ||
await game.add( | ||
ClipComponent.rectangle( | ||
size: Vector2(100, 100), | ||
children: [_Rectangle()], | ||
), | ||
); | ||
}, | ||
goldenFile: '../_goldens/clip_component_rect.png', | ||
); | ||
}); | ||
|
||
group('CircleClipComponent', () { | ||
testGolden( | ||
'renders correctly', | ||
(game) async { | ||
await game.add( | ||
ClipComponent.circle( | ||
size: Vector2(100, 100), | ||
children: [_Rectangle()], | ||
), | ||
); | ||
}, | ||
goldenFile: '../_goldens/clip_component_circle.png', | ||
); | ||
}); | ||
|
||
group('PolygonClipComponent', () { | ||
testGolden( | ||
'renders correctly', | ||
(game) async { | ||
await game.add( | ||
ClipComponent.polygon( | ||
points: [ | ||
Vector2(1, 0), | ||
Vector2(1, 1), | ||
Vector2(0, 1), | ||
Vector2(1, 0), | ||
], | ||
size: Vector2(100, 100), | ||
children: [_Rectangle()], | ||
), | ||
); | ||
}, | ||
goldenFile: '../_goldens/clip_component_polygon.png', | ||
); | ||
}); | ||
}); | ||
} |