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

@ -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. 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` ### `SequenceEffect`
This effect can be used to run multiple other effects one after another. The constituent effects 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_shadow3d.dart';
import 'package:doc_flame_examples/decorator_tint.dart'; import 'package:doc_flame_examples/decorator_tint.dart';
import 'package:doc_flame_examples/drag_events.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_along_path_effect.dart';
import 'package:doc_flame_examples/move_by_effect.dart'; import 'package:doc_flame_examples/move_by_effect.dart';
import 'package:doc_flame_examples/move_to_effect.dart'; import 'package:doc_flame_examples/move_to_effect.dart';
@ -67,6 +68,7 @@ void main() {
'rive_example': RiveExampleGame.new, 'rive_example': RiveExampleGame.new,
'ray_cast': RayCastExample.new, 'ray_cast': RayCastExample.new,
'ray_trace': RayTraceExample.new, 'ray_trace': RayTraceExample.new,
'glow_effect': GlowEffectExample.new,
'remove_effect': RemoveEffectGame.new, 'remove_effect': RemoveEffectGame.new,
'color_effect': ColorEffectExample.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/controllers/zigzag_effect_controller.dart';
export 'src/effects/effect.dart'; export 'src/effects/effect.dart';
export 'src/effects/effect_target.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_along_path_effect.dart';
export 'src/effects/move_by_effect.dart'; export 'src/effects/move_by_effect.dart';
export 'src/effects/move_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/components.dart';
import 'package:flame/effects.dart'; import 'package:flame/effects.dart';
import 'package:flame/src/effects/provider_interfaces.dart';
import 'package:flame/src/palette.dart'; import 'package:flame/src/palette.dart';
import 'package:meta/meta.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. /// [T], that can be omitted if the component only has one paint.
/// [paintLayers] paints should be drawn in list order during the render. The /// [paintLayers] paints should be drawn in list order during the render. The
/// main Paint is the first element. /// 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 = {}; late final Map<T, Paint> _paints = {};
@override
Paint paint = BasicPalette.white.paint(); Paint paint = BasicPalette.white.paint();
@internal @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'; import 'package:flame/components.dart';
/// Interface for a component that can be affected by move effects. /// Interface for a component that can be affected by move effects.
@ -64,3 +66,11 @@ abstract class OpacityProvider {
double get opacity; double get opacity;
set opacity(double value); 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 {}