diff --git a/flare_dart/lib/actor.dart b/flare_dart/lib/actor.dart index 161e003..80b051c 100644 --- a/flare_dart/lib/actor.dart +++ b/flare_dart/lib/actor.dart @@ -3,6 +3,7 @@ import "dart:convert"; import "dart:typed_data"; import 'package:flare_dart/actor_layer_effect_renderer.dart'; +import 'package:flare_dart/custom_property.dart'; import "actor_artboard.dart"; import "actor_color.dart"; diff --git a/flare_dart/lib/actor_artboard.dart b/flare_dart/lib/actor_artboard.dart index 99f64e9..48f325f 100644 --- a/flare_dart/lib/actor_artboard.dart +++ b/flare_dart/lib/actor_artboard.dart @@ -2,6 +2,7 @@ import "dart:math"; import "dart:typed_data"; import 'package:flare_dart/actor_layer_effect_renderer.dart'; +import 'package:flare_dart/custom_property.dart'; import "actor.dart"; import 'actor_blur.dart'; @@ -419,19 +420,21 @@ class ActorArtboard { break; case BlockTypes.customIntProperty: - //component = CustomIntProperty.Read(this, nodeBlock); + component = CustomProperty.readCustomProperty(this, nodeBlock); break; case BlockTypes.customFloatProperty: - //component = CustomFloatProperty.Read(this, nodeBlock); + component = + CustomProperty.readCustomProperty(this, nodeBlock); break; case BlockTypes.customStringProperty: - //component = CustomStringProperty.Read(this, nodeBlock); + component = + CustomProperty.readCustomProperty(this, nodeBlock); break; case BlockTypes.customBooleanProperty: - //component = CustomBooleanProperty.Read(this, nodeBlock); + component = CustomProperty.readCustomProperty(this, nodeBlock); break; case BlockTypes.actorColliderRectangle: diff --git a/flare_dart/lib/actor_component.dart b/flare_dart/lib/actor_component.dart index 12ea89d..08228ff 100644 --- a/flare_dart/lib/actor_component.dart +++ b/flare_dart/lib/actor_component.dart @@ -1,3 +1,5 @@ +import 'package:flare_dart/custom_property.dart'; + import "actor_artboard.dart"; import "actor_node.dart"; import "stream_reader.dart"; @@ -23,6 +25,7 @@ abstract class ActorComponent { int graphOrder = 0; int dirtMask = 0; List dependents; + List _customProperties; ActorComponent(); ActorComponent.withArtboard(this.artboard); @@ -31,6 +34,8 @@ abstract class ActorComponent { return _name; } + int get parentIdx => _parentIdx; + void resolveComponentIndices(List components) { ActorNode node = components[_parentIdx] as ActorNode; if (node != null) { @@ -59,4 +64,15 @@ abstract class ActorComponent { _parentIdx = component._parentIdx; idx = component.idx; } + + void addCustomProperty(CustomProperty property) { + _customProperties ??= []; + _customProperties.add(property); + } + + /// Finds the [CustomProperty] with the given name. + CustomProperty getCustomProperty(String name) { + return _customProperties?.firstWhere((element) => element.name == name, + orElse: () => null); + } } diff --git a/flare_dart/lib/animation/actor_animation.dart b/flare_dart/lib/animation/actor_animation.dart index 2162f1e..c591b6c 100644 --- a/flare_dart/lib/animation/actor_animation.dart +++ b/flare_dart/lib/animation/actor_animation.dart @@ -315,6 +315,10 @@ class ActorAnimation { List get animatedComponents => _components; + /// Call this function once per frame, as you advance the animation. + /// Call from the time [fromTime] of the last frame, to the time [toTime] + /// of the current frame, and it'll add the triggered events in that + /// time frame in the [triggerEvents] list. void triggerEvents(List components, double fromTime, double toTime, List triggerEvents) { for (int i = 0; i < _triggerComponents.length; i++) { @@ -332,25 +336,25 @@ class ActorAnimation { int idx = 0; // Binary find the keyframe index. { - int mid = 0; - double element = 0.0; - int start = 0; - int end = kfl - 1; - - while (start <= end) { - mid = (start + end) >> 1; - element = keyFrames[mid].time; - if (element < toTime) { - start = mid + 1; - } else if (element > toTime) { - end = mid - 1; + var elementTime = 0.0; + var startIdx = 0; + var midIdx = 0; + var endIdx = kfl - 1; + + while (startIdx <= endIdx) { + midIdx = (startIdx + endIdx) >> 1; + elementTime = keyFrames[midIdx].time; + if (elementTime < toTime) { + startIdx = midIdx + 1; + } else if (elementTime > toTime) { + endIdx = midIdx - 1; } else { - start = mid; + startIdx = midIdx; break; } } - idx = start; + idx = startIdx; } if (idx == 0) { diff --git a/flare_dart/lib/animation/keyframe.dart b/flare_dart/lib/animation/keyframe.dart index e59bd04..66b9d89 100644 --- a/flare_dart/lib/animation/keyframe.dart +++ b/flare_dart/lib/animation/keyframe.dart @@ -1,6 +1,8 @@ import "dart:collection"; import "dart:typed_data"; +import 'package:flare_dart/custom_property.dart'; + import "../actor_artboard.dart"; import "../actor_blur.dart"; import "../actor_bone_base.dart"; @@ -94,8 +96,8 @@ class KeyFrameBooleanProperty extends KeyFrame { bool _value; @override void apply(ActorComponent component, double mix) { - // CustomBooleanProperty prop = component as CustomBooleanProperty; - // prop.value = _value; + var prop = component as CustomProperty; + prop.value = _value; } @override @@ -299,9 +301,8 @@ class KeyFrameFillColor extends KeyFrameWithInterpolation { class KeyFrameFloatProperty extends KeyFrameNumeric { @override void setValue(ActorComponent component, double value, double mix) { - // TODO - // CustomFloatProperty node = component as CustomFloatProperty; - // node.value = node.value * (1.0 - mix) + value * mix; + var node = component as CustomProperty; + node.value = node.value * (1.0 - mix) + value * mix; } static KeyFrame read(StreamReader reader, ActorComponent component) { @@ -526,7 +527,7 @@ abstract class KeyFrameInt extends KeyFrameWithInterpolation { @override void applyInterpolation( ActorComponent component, double time, KeyFrame toFrame, double mix) { - KeyFrameNumeric to = toFrame as KeyFrameNumeric; + var to = toFrame as KeyFrameInt; double f = (time - _time) / (to._time - _time); if (_interpolator != null) { f = _interpolator.getEasedMix(f); @@ -548,9 +549,8 @@ abstract class KeyFrameInt extends KeyFrameWithInterpolation { class KeyFrameIntProperty extends KeyFrameInt { @override void setValue(ActorComponent component, double value, double mix) { - // TODO - //CustomIntProperty node = component as CustomIntProperty; - //node.value = (node.value * (1.0 - mix) + value * mix).round(); + var node = component as CustomProperty; + node.value = (node.value * (1.0 - mix) + value * mix).round(); } static KeyFrame read(StreamReader reader, ActorComponent component) { @@ -1057,8 +1057,8 @@ class KeyFrameStringProperty extends KeyFrame { String _value; @override void apply(ActorComponent component, double mix) { - // CustomStringProperty prop = component as CustomStringProperty; - // prop.value = _value; + var prop = component as CustomProperty; + prop.value = _value; } @override diff --git a/flare_dart/lib/custom_property.dart b/flare_dart/lib/custom_property.dart new file mode 100644 index 0000000..03b4880 --- /dev/null +++ b/flare_dart/lib/custom_property.dart @@ -0,0 +1,71 @@ +import 'package:flare_dart/actor_artboard.dart'; +import 'package:flare_dart/actor_component.dart'; +import 'package:flare_dart/stream_reader.dart'; + +/// Generic class for CustomProperties in the Rive editor. +/// They are represented by a single value of generic type T, +/// where T can be a [int], [double], [String] or [bool]. +/// +/// Trying to [read()] a CustomProperty with any other type will result +/// in an [UnsupportedError]. +class CustomProperty extends ActorComponent { + T _value; + + CustomProperty._(); + + T get value => _value; + set value(T newValue) { + if (newValue != _value) { + _value = newValue; + } + } + + @override + ActorComponent makeInstance(ActorArtboard resetArtboard) { + var instance = CustomProperty._(); + instance.copyComponent(this, resetArtboard); + instance._value = _value; + return instance; + } + + @override + void completeResolve() {} + + @override + void onDirty(int dirt) {} + + @override + void update(int dirt) {} + + static CustomProperty readCustomProperty( + ActorArtboard artboard, StreamReader reader) { + var component = CustomProperty._(); + ActorComponent.read(artboard, reader, component); + switch (T) { + case int: + component._value = reader.readInt32("int") as T; + break; + case double: + component._value = reader.readFloat32("float") as T; + break; + case String: + component._value = reader.readString("string") as T; + break; + case bool: + component._value = reader.readBool("bool") as T; + break; + default: + throw UnsupportedError( + "Custom Property for type $T is not currently supported"); + } + return component; + } + + @override + void resolveComponentIndices(List components) { + super.resolveComponentIndices(components); + if (parentIdx >= 0) { + parent.addCustomProperty(this); + } + } +}