mirror of
https://github.com/rive-app/rive-flutter.git
synced 2025-07-14 14:11:01 +08:00
Merge pull request #3 from rive-app/better_controllers
Improving controllers so they can mix animations more easily.
This commit is contained in:
@ -94,7 +94,7 @@ packages:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1+3"
|
||||
version: "0.0.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -2,9 +2,11 @@ library rive;
|
||||
|
||||
export 'package:rive/src/rive_file.dart';
|
||||
export 'package:rive/src/rive.dart';
|
||||
export 'package:rive/src/runtime_artboard.dart';
|
||||
export 'package:rive/src/controllers/simple_controller.dart';
|
||||
export 'package:rive/src/rive_core/rive_animation_controller.dart';
|
||||
export 'package:rive/src/controllers/simple_controller.dart';
|
||||
export 'package:rive/src/controllers/simple_controller.dart';
|
||||
export 'package:rive/src/rive_core/animation/linear_animation.dart';
|
||||
export 'package:rive/src/rive_core/animation/linear_animation_instance.dart';
|
||||
export 'package:rive/src/rive_core/artboard.dart';
|
||||
export 'package:rive/src/rive_core/shapes/shape.dart';
|
||||
export 'package:rive/src/rive_core/shapes/paint/fill.dart';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:rive/src/rive_core/animation/linear_animation.dart';
|
||||
import 'package:rive/src/rive_core/animation/loop.dart';
|
||||
import 'package:rive/src/rive_core/animation/linear_animation_instance.dart';
|
||||
import 'package:rive/src/rive_core/rive_animation_controller.dart';
|
||||
import 'package:rive/src/runtime_artboard.dart';
|
||||
|
||||
@ -7,70 +7,29 @@ import 'package:rive/src/runtime_artboard.dart';
|
||||
/// by an artist. All playback parameters (looping, speed, keyframes) are artist
|
||||
/// defined in the Rive editor.
|
||||
class SimpleAnimation extends RiveAnimationController<RuntimeArtboard> {
|
||||
LinearAnimation _animation;
|
||||
double _time = 0;
|
||||
int _direction = 1;
|
||||
LinearAnimationInstance _instance;
|
||||
final String animationName;
|
||||
SimpleAnimation(this.animationName);
|
||||
|
||||
@override
|
||||
bool init(RuntimeArtboard artboard) {
|
||||
_animation = artboard.animations.firstWhere(
|
||||
(animation) => animation.name == animationName,
|
||||
var animation = artboard.animations.firstWhere(
|
||||
(animation) =>
|
||||
animation is LinearAnimation && animation.name == animationName,
|
||||
orElse: () => null,
|
||||
) as LinearAnimation;
|
||||
);
|
||||
if (animation != null) {
|
||||
_instance = LinearAnimationInstance(animation as LinearAnimation);
|
||||
}
|
||||
isActive = true;
|
||||
return _animation != null;
|
||||
return _instance != null;
|
||||
}
|
||||
|
||||
@override
|
||||
void apply(RuntimeArtboard artboard, double elapsedSeconds) {
|
||||
_animation.apply(_time, coreContext: artboard);
|
||||
_time += elapsedSeconds * _animation.speed * _direction;
|
||||
|
||||
double frames = _time * _animation.fps;
|
||||
|
||||
var start = _animation.enableWorkArea ? _animation.workStart : 0;
|
||||
var end =
|
||||
_animation.enableWorkArea ? _animation.workEnd : _animation.duration;
|
||||
var range = end - start;
|
||||
|
||||
switch (_animation.loop) {
|
||||
case Loop.oneShot:
|
||||
if (frames > end) {
|
||||
isActive = false;
|
||||
frames = end.toDouble();
|
||||
_time = frames / _animation.fps;
|
||||
}
|
||||
break;
|
||||
case Loop.loop:
|
||||
if (frames >= end) {
|
||||
frames = _time * _animation.fps;
|
||||
frames = start + (frames - start) % range;
|
||||
_time = frames / _animation.fps;
|
||||
}
|
||||
break;
|
||||
case Loop.pingPong:
|
||||
// ignore: literal_only_boolean_expressions
|
||||
while (true) {
|
||||
if (_direction == 1 && frames >= end) {
|
||||
_direction = -1;
|
||||
frames = end + (end - frames);
|
||||
_time = frames / _animation.fps;
|
||||
} else if (_direction == -1 && frames < start) {
|
||||
_direction = 1;
|
||||
frames = start + (start - frames);
|
||||
_time = frames / _animation.fps;
|
||||
} else {
|
||||
// we're within the range, we can stop fixing. We do this in a
|
||||
// loop to fix conditions when time has advanced so far that we've
|
||||
// ping-ponged back and forth a few times in a single frame. We
|
||||
// want to accomodate for this in cases where animations are not
|
||||
// advanced on regular intervals.
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
_instance.animation.apply(_instance.time, coreContext: artboard);
|
||||
if (!_instance.advance(elapsedSeconds)) {
|
||||
isActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
58
lib/src/rive_core/animation/linear_animation_instance.dart
Normal file
58
lib/src/rive_core/animation/linear_animation_instance.dart
Normal file
@ -0,0 +1,58 @@
|
||||
import 'package:rive/src/rive_core/animation/linear_animation.dart';
|
||||
import 'package:rive/src/rive_core/animation/loop.dart';
|
||||
|
||||
class LinearAnimationInstance {
|
||||
final LinearAnimation animation;
|
||||
double _time = 0;
|
||||
int direction = 1;
|
||||
LinearAnimationInstance(this.animation);
|
||||
double get time => _time;
|
||||
set time(double value) {
|
||||
if (_time == value) {
|
||||
return;
|
||||
}
|
||||
_time = value;
|
||||
direction = 1;
|
||||
}
|
||||
|
||||
bool advance(double elapsedSeconds) {
|
||||
_time += elapsedSeconds * animation.speed * direction;
|
||||
double frames = _time * animation.fps;
|
||||
var start = animation.enableWorkArea ? animation.workStart : 0;
|
||||
var end = animation.enableWorkArea ? animation.workEnd : animation.duration;
|
||||
var range = end - start;
|
||||
bool keepGoing = true;
|
||||
switch (animation.loop) {
|
||||
case Loop.oneShot:
|
||||
if (frames > end) {
|
||||
keepGoing = false;
|
||||
frames = end.toDouble();
|
||||
_time = frames / animation.fps;
|
||||
}
|
||||
break;
|
||||
case Loop.loop:
|
||||
if (frames >= end) {
|
||||
frames = _time * animation.fps;
|
||||
frames = start + (frames - start) % range;
|
||||
_time = frames / animation.fps;
|
||||
}
|
||||
break;
|
||||
case Loop.pingPong:
|
||||
while (true) {
|
||||
if (direction == 1 && frames >= end) {
|
||||
direction = -1;
|
||||
frames = end + (end - frames);
|
||||
_time = frames / animation.fps;
|
||||
} else if (direction == -1 && frames < start) {
|
||||
direction = 1;
|
||||
frames = start + (start - frames);
|
||||
_time = frames / animation.fps;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return keepGoing;
|
||||
}
|
||||
}
|
@ -189,6 +189,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
||||
}
|
||||
canvas.save();
|
||||
canvas.clipRect(Rect.fromLTWH(0, 0, width, height));
|
||||
canvas.translate(width * (originX ?? 0), height * (originY ?? 0));
|
||||
for (final drawable in _drawables) {
|
||||
drawable.draw(canvas);
|
||||
}
|
||||
@ -202,9 +203,15 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
||||
@override
|
||||
Mat2D get worldTransform => Mat2D();
|
||||
@override
|
||||
void originXChanged(double from, double to) {}
|
||||
void originXChanged(double from, double to) {
|
||||
addDirt(ComponentDirt.worldTransform);
|
||||
}
|
||||
|
||||
@override
|
||||
void originYChanged(double from, double to) {}
|
||||
void originYChanged(double from, double to) {
|
||||
addDirt(ComponentDirt.worldTransform);
|
||||
}
|
||||
|
||||
bool internalAddAnimation(Animation animation) {
|
||||
if (_animations.contains(animation)) {
|
||||
return false;
|
||||
|
@ -12,7 +12,7 @@ class Segment2D {
|
||||
Vec2D diff;
|
||||
double lengthSquared;
|
||||
Segment2D(this.start, this.end);
|
||||
ProjectionResult projectPoint(Vec2D point) {
|
||||
ProjectionResult projectPoint(Vec2D point, {bool clamp = true}) {
|
||||
if (diff == null) {
|
||||
diff = Vec2D.subtract(Vec2D(), start, end);
|
||||
lengthSquared = Vec2D.squaredLength(diff);
|
||||
@ -23,11 +23,13 @@ class Segment2D {
|
||||
double t = ((point[0] - start[0]) * (end[0] - start[0]) +
|
||||
(point[1] - start[1]) * (end[1] - start[1])) /
|
||||
lengthSquared;
|
||||
if (t < 0.0) {
|
||||
return ProjectionResult(0, start);
|
||||
}
|
||||
if (t > 1.0) {
|
||||
return ProjectionResult(1, end);
|
||||
if (clamp) {
|
||||
if (t < 0.0) {
|
||||
return ProjectionResult(0, start);
|
||||
}
|
||||
if (t > 1.0) {
|
||||
return ProjectionResult(1, end);
|
||||
}
|
||||
}
|
||||
return ProjectionResult(
|
||||
t,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||
import 'package:rive/src/rive_core/container_component.dart';
|
||||
import 'package:rive/src/rive_core/math/aabb.dart';
|
||||
import 'package:rive/src/rive_core/math/mat2d.dart';
|
||||
import 'package:rive/src/rive_core/math/vec2d.dart';
|
||||
import 'package:rive/src/generated/node_base.dart';
|
||||
@ -127,4 +128,6 @@ class Node extends NodeBase {
|
||||
super.parentChanged(from, to);
|
||||
markWorldTransformDirty();
|
||||
}
|
||||
|
||||
AABB get localBounds => AABB.fromValues(x, y, x, y);
|
||||
}
|
||||
|
@ -204,7 +204,10 @@ abstract class Path extends PathBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
AABB preciseComputeBounds(List<PathVertex> renderPoints, Mat2D transform) {
|
||||
@override
|
||||
AABB get localBounds => preciseComputeBounds(renderVertices, Mat2D());
|
||||
AABB preciseComputeBounds(List<PathVertex> renderPoints, Mat2D transform,
|
||||
{bool debug = false}) {
|
||||
if (renderPoints.isEmpty) {
|
||||
return AABB();
|
||||
}
|
||||
@ -230,7 +233,6 @@ abstract class Path extends PathBase {
|
||||
cin = Vec2D.transformMat2D(Vec2D(), cin, transform);
|
||||
cout = Vec2D.transformMat2D(Vec2D(), cout, transform);
|
||||
}
|
||||
const double epsilon = 0.000000001;
|
||||
final double startX = lastPoint[0];
|
||||
final double startY = lastPoint[1];
|
||||
final double cpX1 = cout[0];
|
||||
@ -240,121 +242,55 @@ abstract class Path extends PathBase {
|
||||
final double endX = next[0];
|
||||
final double endY = next[1];
|
||||
lastPoint = next;
|
||||
double extremaX;
|
||||
double extremaY;
|
||||
double a, b, c;
|
||||
if (!(((startX < cpX1) && (cpX1 < cpX2) && (cpX2 < endX)) ||
|
||||
((startX > cpX1) && (cpX1 > cpX2) && (cpX2 > endX)))) {
|
||||
a = -startX + (3 * (cpX1 - cpX2)) + endX;
|
||||
b = 2 * (startX - (2 * cpX1) + cpX2);
|
||||
c = -startX + cpX1;
|
||||
double s = (b * b) - (4 * a * c);
|
||||
if ((s >= 0.0) && (a.abs() > epsilon)) {
|
||||
if (s == 0.0) {
|
||||
final double t = -b / (2 * a);
|
||||
final double tprime = 1.0 - t;
|
||||
if ((t >= 0.0) && (t <= 1.0)) {
|
||||
extremaX = ((tprime * tprime * tprime) * startX) +
|
||||
((3 * tprime * tprime * t) * cpX1) +
|
||||
((3 * tprime * t * t) * cpX2) +
|
||||
(t * t * t * endX);
|
||||
if (extremaX < bounds[0]) {
|
||||
bounds[0] = extremaX;
|
||||
}
|
||||
if (extremaX > bounds[2]) {
|
||||
bounds[2] = extremaX;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s = sqrt(s);
|
||||
double t = (-b - s) / (2 * a);
|
||||
double tprime = 1.0 - t;
|
||||
if ((t >= 0.0) && (t <= 1.0)) {
|
||||
extremaX = ((tprime * tprime * tprime) * startX) +
|
||||
((3 * tprime * tprime * t) * cpX1) +
|
||||
((3 * tprime * t * t) * cpX2) +
|
||||
(t * t * t * endX);
|
||||
if (extremaX < bounds[0]) {
|
||||
bounds[0] = extremaX;
|
||||
}
|
||||
if (extremaX > bounds[2]) {
|
||||
bounds[2] = extremaX;
|
||||
}
|
||||
}
|
||||
t = (-b + s) / (2 * a);
|
||||
tprime = 1.0 - t;
|
||||
if ((t >= 0.0) && (t <= 1.0)) {
|
||||
extremaX = ((tprime * tprime * tprime) * startX) +
|
||||
((3 * tprime * tprime * t) * cpX1) +
|
||||
((3 * tprime * t * t) * cpX2) +
|
||||
(t * t * t * endX);
|
||||
if (extremaX < bounds[0]) {
|
||||
bounds[0] = extremaX;
|
||||
}
|
||||
if (extremaX > bounds[2]) {
|
||||
bounds[2] = extremaX;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(((startY < cpY1) && (cpY1 < cpY2) && (cpY2 < endY)) ||
|
||||
((startY > cpY1) && (cpY1 > cpY2) && (cpY2 > endY)))) {
|
||||
a = -startY + (3 * (cpY1 - cpY2)) + endY;
|
||||
b = 2 * (startY - (2 * cpY1) + cpY2);
|
||||
c = -startY + cpY1;
|
||||
double s = (b * b) - (4 * a * c);
|
||||
if ((s >= 0.0) && (a.abs() > epsilon)) {
|
||||
if (s == 0.0) {
|
||||
final double t = -b / (2 * a);
|
||||
final double tprime = 1.0 - t;
|
||||
if ((t >= 0.0) && (t <= 1.0)) {
|
||||
extremaY = ((tprime * tprime * tprime) * startY) +
|
||||
((3 * tprime * tprime * t) * cpY1) +
|
||||
((3 * tprime * t * t) * cpY2) +
|
||||
(t * t * t * endY);
|
||||
if (extremaY < bounds[1]) {
|
||||
bounds[1] = extremaY;
|
||||
}
|
||||
if (extremaY > bounds[3]) {
|
||||
bounds[3] = extremaY;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s = sqrt(s);
|
||||
final double t = (-b - s) / (2 * a);
|
||||
final double tprime = 1.0 - t;
|
||||
if ((t >= 0.0) && (t <= 1.0)) {
|
||||
extremaY = ((tprime * tprime * tprime) * startY) +
|
||||
((3 * tprime * tprime * t) * cpY1) +
|
||||
((3 * tprime * t * t) * cpY2) +
|
||||
(t * t * t * endY);
|
||||
if (extremaY < bounds[1]) {
|
||||
bounds[1] = extremaY;
|
||||
}
|
||||
if (extremaY > bounds[3]) {
|
||||
bounds[3] = extremaY;
|
||||
}
|
||||
}
|
||||
final double t2 = (-b + s) / (2 * a);
|
||||
final double tprime2 = 1.0 - t2;
|
||||
if ((t2 >= 0.0) && (t2 <= 1.0)) {
|
||||
extremaY = ((tprime2 * tprime2 * tprime2) * startY) +
|
||||
((3 * tprime2 * tprime2 * t2) * cpY1) +
|
||||
((3 * tprime2 * t2 * t2) * cpY2) +
|
||||
(t2 * t2 * t2 * endY);
|
||||
if (extremaY < bounds[1]) {
|
||||
bounds[1] = extremaY;
|
||||
}
|
||||
if (extremaY > bounds[3]) {
|
||||
bounds[3] = extremaY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_expandBoundsForAxis(bounds, 0, startX, cpX1, cpX2, endX);
|
||||
_expandBoundsForAxis(bounds, 1, startY, cpY1, cpY2, endY);
|
||||
}
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
}
|
||||
|
||||
void _expandBoundsToCubicPoint(AABB bounds, int component, double t, double a,
|
||||
double b, double c, double d) {
|
||||
if (t >= 0 && t <= 1) {
|
||||
var ti = 1 - t;
|
||||
double extremaY = ((ti * ti * ti) * a) +
|
||||
((3 * ti * ti * t) * b) +
|
||||
((3 * ti * t * t) * c) +
|
||||
(t * t * t * d);
|
||||
if (extremaY < bounds[component]) {
|
||||
bounds[component] = extremaY;
|
||||
}
|
||||
if (extremaY > bounds[component + 2]) {
|
||||
bounds[component + 2] = extremaY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _expandBoundsForAxis(AABB bounds, int component, double start, double cp1,
|
||||
double cp2, double end) {
|
||||
if (!(((start < cp1) && (cp1 < cp2) && (cp2 < end)) ||
|
||||
((start > cp1) && (cp1 > cp2) && (cp2 > end)))) {
|
||||
var a = 3 * (cp1 - start);
|
||||
var b = 3 * (cp2 - cp1);
|
||||
var c = 3 * (end - cp2);
|
||||
var d = a - 2 * b + c;
|
||||
if (d != 0) {
|
||||
var m1 = -sqrt(b * b - a * c);
|
||||
var m2 = -a + b;
|
||||
_expandBoundsToCubicPoint(
|
||||
bounds, component, -(m1 + m2) / d, start, cp1, cp2, end);
|
||||
_expandBoundsToCubicPoint(
|
||||
bounds, component, -(-m1 + m2) / d, start, cp1, cp2, end);
|
||||
} else if (b != c && d == 0) {
|
||||
_expandBoundsToCubicPoint(
|
||||
bounds, component, (2 * b - c) / (2 * (b - c)), start, cp1, cp2, end);
|
||||
}
|
||||
var d2a = 2 * (b - a);
|
||||
var d2b = 2 * (c - b);
|
||||
if (d2a != b) {
|
||||
_expandBoundsToCubicPoint(
|
||||
bounds, component, d2a / (d2a - d2b), start, cp1, cp2, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
name: rive
|
||||
description: Rive 2 Flutter Runtime
|
||||
version: 0.0.1+3
|
||||
version: 0.0.2
|
||||
repository: https://github.com/rive-app/rive-flutter
|
||||
homepage: https://rive.app
|
||||
|
||||
|
Reference in New Issue
Block a user