feat: Added glow effect using maskFilter (#2129)

This commit is contained in:
Dipak Prajapati
2022-10-29 22:14:15 +05:30
committed by GitHub
parent 2fbf11d441
commit bcecd3c1bd
9 changed files with 181 additions and 6 deletions

View File

@ -82,17 +82,17 @@ The base `Effect` class is not usable on its own (it is abstract), but it provid
functionality inherited by all other effects. This includes:
- The ability to pause/resume the effect using `effect.pause()` and `effect.resume()`. You can
check whether the effect is currently paused using `effect.isPaused`.
check whether the effect is currently paused using `effect.isPaused`.
- The ability to reverse the effect's time direction using `effect.reverse()`. Use
`effect.isReversed` to check if the effect is currently running back in time.
`effect.isReversed` to check if the effect is currently running back in time.
- Property `removeOnFinish` (which is true by default) will cause the effect component to be
removed from the game tree and garbage-collected once the effect completes. Set this to false
if you plan to reuse the effect after it is finished.
removed from the game tree and garbage-collected once the effect completes. Set this to false
if you plan to reuse the effect after it is finished.
- Optional user-provided `onComplete`, which will be invoked when the effect has just
completed its execution but before it is removed from the game.
completed its execution but before it is removed from the game.
- The `reset()` method reverts the effect to its original state, allowing it to run once again.
@ -431,6 +431,30 @@ Currently this effect can only be applied to components that have a `HasPaint` m
uses multiple paints, the effect can target any individual color using the `paintId` parameter.
### GlowEffect
This effect will apply the glowing shade around target relative to the specified
`glow-strength`. The color of shade will be targets paint color. For example, the following effect
will apply the glowing shade around target by strength of `10`:
```{flutter-app}
:sources: ../flame/examples
:page: glow_effect
:show: widget code infobox
:width: 180
:height: 160
```
```dart
final effect = GlowEffect(
10.0,
EffectController(duration: 3),
);
```
Currently this effect can only be applied to components that have a `HasPaint` mixin.
### `SequenceEffect`
This effect can be used to run multiple other effects one after another. The constituent effects

View File

@ -0,0 +1,29 @@
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
class GlowEffectExample extends FlameGame {
@override
Future<void> onLoad() async {
final paint = Paint()..color = const Color(0xff39FF14);
add(
CircleComponent(
radius: size.y / 4,
position: size / 2,
anchor: Anchor.center,
paint: paint,
)..add(
GlowEffect(
10.0,
EffectController(
duration: 2,
infinite: true,
),
),
),
);
}
}

View File

@ -10,6 +10,7 @@ import 'package:doc_flame_examples/decorator_rotate3d.dart';
import 'package:doc_flame_examples/decorator_shadow3d.dart';
import 'package:doc_flame_examples/decorator_tint.dart';
import 'package:doc_flame_examples/drag_events.dart';
import 'package:doc_flame_examples/glow_effect.dart';
import 'package:doc_flame_examples/move_along_path_effect.dart';
import 'package:doc_flame_examples/move_by_effect.dart';
import 'package:doc_flame_examples/move_to_effect.dart';
@ -67,6 +68,7 @@ void main() {
'rive_example': RiveExampleGame.new,
'ray_cast': RayCastExample.new,
'ray_trace': RayTraceExample.new,
'glow_effect': GlowEffectExample.new,
'remove_effect': RemoveEffectGame.new,
'color_effect': ColorEffectExample.new,
};

View File

@ -0,0 +1,39 @@
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';
void main() {
runApp(GameWidget(game: GlowEffectExample()));
}
class GlowEffectExample extends FlameGame with TapDetector {
static const String description = '''
In this example we show how the `GlowEffect` can be used.
''';
@override
Future<void> onLoad() async {
final paint = Paint()
..color = const Color(0xff39FF14)
..style = PaintingStyle.stroke;
add(
CircleComponent(
radius: 50,
position: Vector2(300, 400),
paint: paint,
)..add(
GlowEffect(
10.0,
EffectController(
duration: 3,
reverseDuration: 1.5,
infinite: true,
),
),
),
);
}
}

View File

@ -21,6 +21,7 @@ export 'src/effects/controllers/speed_effect_controller.dart';
export 'src/effects/controllers/zigzag_effect_controller.dart';
export 'src/effects/effect.dart';
export 'src/effects/effect_target.dart';
export 'src/effects/glow_effect.dart';
export 'src/effects/move_along_path_effect.dart';
export 'src/effects/move_by_effect.dart';
export 'src/effects/move_effect.dart';

View File

@ -3,6 +3,7 @@ import 'dart:ui';
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/src/effects/provider_interfaces.dart';
import 'package:flame/src/palette.dart';
import 'package:meta/meta.dart';
@ -14,8 +15,11 @@ import 'package:meta/meta.dart';
/// [T], that can be omitted if the component only has one paint.
/// [paintLayers] paints should be drawn in list order during the render. The
/// main Paint is the first element.
mixin HasPaint<T extends Object> on Component implements OpacityProvider {
mixin HasPaint<T extends Object> on Component
implements OpacityProvider, PaintProvider {
late final Map<T, Paint> _paints = {};
@override
Paint paint = BasicPalette.white.paint();
@internal

View File

@ -0,0 +1,29 @@
import 'dart:ui';
import 'package:flame/effects.dart';
import 'package:flame/src/effects/provider_interfaces.dart';
/// Change the MaskFilter on Paint of a component over time.
///
/// This effect applies incremental changes to the MaskFilter on Paint of a
/// component and requires that any other effect or update logic applied to the
/// same component also used incremental updates.
class GlowEffect extends Effect with EffectTarget<PaintProvider> {
GlowEffect(this.strength, super.controller, {this.style = BlurStyle.outer});
final BlurStyle style;
final double strength;
@override
void apply(double progress) {
final _value = strength * progress;
target.paint.maskFilter = MaskFilter.blur(style, _value);
}
@override
void reset() {
super.reset();
target.paint.maskFilter = null;
}
}

View File

@ -1,3 +1,5 @@
import 'dart:ui';
import 'package:flame/components.dart';
/// Interface for a component that can be affected by move effects.
@ -64,3 +66,11 @@ abstract class OpacityProvider {
double get opacity;
set opacity(double value);
}
/// Interface for a component that can be affected by Paint effects.
///
/// See [HasPaint] for an example implementation.
abstract class PaintProvider {
Paint get paint;
set paint(Paint value);
}

View File

@ -0,0 +1,37 @@
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('GlowEffect', () {
testWithFlameGame('can apply to component having HasPaint', (game) async {
final component = _PaintComponent();
await game.ensureAdd(component);
await component.add(
GlowEffect(1, EffectController(duration: 1)),
);
game.update(0);
expect(component.children.length, 1);
expect(component.paint.maskFilter, isNotNull);
expect(
component.paint.maskFilter.toString(),
'MaskFilter.blur(BlurStyle.outer, 0.0)',
);
game.update(1);
expect(
component.paint.maskFilter.toString(),
'MaskFilter.blur(BlurStyle.outer, 1.0)',
);
});
});
}
class _PaintComponent extends Component with HasPaint {}