import 'package:flutter/animation.dart'; import 'package:meta/meta.dart'; import '../extensions/vector2.dart'; import 'effects.dart'; class Vector2Percentage { final Vector2 v; final Vector2 previous; final double startAt; final double endAt; Vector2Percentage( this.v, this.previous, this.startAt, this.endAt, ); } class MoveEffect extends SimplePositionComponentEffect { List path; Vector2Percentage _currentSubPath; List _percentagePath; Vector2 _startPosition; MoveEffect({ @required this.path, double duration, double speed, Curve curve, bool isInfinite = false, bool isAlternating = false, bool isRelative = false, void Function() onComplete, }) : assert( duration != null || speed != null, "Either speed or duration necessary", ), super( isInfinite, isAlternating, duration: duration, speed: speed, curve: curve, isRelative: isRelative, onComplete: onComplete, ); @override void initialize(_comp) { super.initialize(_comp); List _movePath; _startPosition = component.position.clone(); // With relative here we mean that any vector in the list is relative // to the previous vector in the list, except the first one which is // relative to the start position of the component. if (isRelative) { Vector2 lastPosition = _startPosition; _movePath = []; for (Vector2 v in path) { final nextPosition = v + lastPosition; _movePath.add(nextPosition); lastPosition = nextPosition; } } else { _movePath = path; } endPosition = isAlternating ? _startPosition : _movePath.last; double pathLength = 0; Vector2 lastPosition = _startPosition; for (Vector2 v in _movePath) { pathLength += v.distanceTo(lastPosition); lastPosition = v; } _percentagePath = []; lastPosition = _startPosition; for (Vector2 v in _movePath) { final lengthToPrevious = lastPosition.distanceTo(v); final lastEndAt = _percentagePath.isNotEmpty ? _percentagePath.last.endAt : 0.0; final endPercentage = lastEndAt + lengthToPrevious / pathLength; _percentagePath.add( Vector2Percentage( v, lastPosition, lastEndAt, _movePath.last == v ? 1.0 : endPercentage, ), ); lastPosition = v; } final double totalPathLength = isAlternating ? pathLength * 2 : pathLength; speed ??= totalPathLength / duration; duration ??= totalPathLength / speed; peakTime = isAlternating ? duration / 2 : duration; } @override void reset() { super.reset(); if (_percentagePath?.isNotEmpty ?? false) { _currentSubPath = _percentagePath.first; } } @override void update(double dt) { super.update(dt); if (!hasFinished()) { _currentSubPath ??= _percentagePath.first; if (!curveDirection.isNegative && _currentSubPath.endAt < curveProgress || curveDirection.isNegative && _currentSubPath.startAt > curveProgress) { _currentSubPath = _percentagePath.firstWhere((v) => v.endAt >= curveProgress); } final double lastEndAt = _currentSubPath.startAt; final double localPercentage = (curveProgress - lastEndAt) / (_currentSubPath.endAt - lastEndAt); component.position = _currentSubPath.previous + ((_currentSubPath.v - _currentSubPath.previous) * localPercentage); } } }