Line data Source code
1 : import 'dart:ui'; 2 : 3 : import 'package:flutter/animation.dart'; 4 : 5 : import '../../components.dart'; 6 : import '../extensions/vector2.dart'; 7 : import 'effects.dart'; 8 : 9 : class Vector2Percentage { 10 : final Vector2 v; 11 : final Vector2 previous; 12 : final double startAt; 13 : final double endAt; 14 : 15 3 : Vector2Percentage( 16 : this.v, 17 : this.previous, 18 : this.startAt, 19 : this.endAt, 20 : ); 21 : } 22 : 23 : class MoveEffect extends SimplePositionComponentEffect { 24 : List<Vector2> path; 25 : Vector2Percentage? _currentSubPath; 26 : List<Vector2Percentage>? _percentagePath; 27 : late Vector2 _startPosition; 28 : 29 : /// Duration or speed needs to be defined 30 3 : MoveEffect({ 31 : required this.path, 32 : double? duration, 33 : double? speed, 34 : Curve? curve, 35 : bool isInfinite = false, 36 : bool isAlternating = false, 37 : bool isRelative = false, 38 : VoidCallback? onComplete, 39 : }) : assert( 40 3 : (duration != null) ^ (speed != null), 41 : 'Either speed or duration necessary', 42 : ), 43 3 : super( 44 : isInfinite, 45 : isAlternating, 46 : duration: duration, 47 : speed: speed, 48 : curve: curve, 49 : isRelative: isRelative, 50 : modifiesPosition: true, 51 : onComplete: onComplete, 52 : ); 53 : 54 3 : @override 55 : void initialize(PositionComponent component) { 56 3 : super.initialize(component); 57 : List<Vector2> _movePath; 58 9 : _startPosition = component.position.clone(); 59 : // With relative here we mean that any vector in the list is relative 60 : // to the previous vector in the list, except the first one which is 61 : // relative to the start position of the component. 62 3 : if (isRelative) { 63 0 : var lastPosition = _startPosition; 64 0 : _movePath = []; 65 0 : for (final v in path) { 66 0 : final nextPosition = v + lastPosition; 67 0 : _movePath.add(nextPosition); 68 : lastPosition = nextPosition; 69 : } 70 : } else { 71 3 : _movePath = path; 72 : } 73 12 : endPosition = isAlternating ? _startPosition : _movePath.last; 74 : 75 : var pathLength = 0.0; 76 3 : var lastPosition = _startPosition; 77 6 : for (final v in _movePath) { 78 6 : pathLength += v.distanceTo(lastPosition); 79 : lastPosition = v; 80 : } 81 : 82 6 : _percentagePath = <Vector2Percentage>[]; 83 3 : lastPosition = _startPosition; 84 6 : for (final v in _movePath) { 85 3 : final lengthToPrevious = lastPosition.distanceTo(v); 86 : final lastEndAt = 87 15 : _percentagePath!.isNotEmpty ? _percentagePath!.last.endAt : 0.0; 88 6 : final endPercentage = lastEndAt + lengthToPrevious / pathLength; 89 6 : _percentagePath!.add( 90 3 : Vector2Percentage( 91 : v, 92 : lastPosition, 93 : lastEndAt, 94 6 : _movePath.last == v ? 1.0 : endPercentage, 95 : ), 96 : ); 97 : lastPosition = v; 98 : } 99 6 : final totalPathLength = isAlternating ? pathLength * 2 : pathLength; 100 9 : speed ??= totalPathLength / duration!; 101 : 102 : // `duration` is not null when speed is null 103 3 : duration ??= totalPathLength / speed!; 104 : 105 : // `speed` is always not null here already 106 15 : peakTime = isAlternating ? duration! / 2 : duration!; 107 : } 108 : 109 3 : @override 110 : void reset() { 111 3 : super.reset(); 112 6 : if (_percentagePath?.isNotEmpty ?? false) { 113 9 : _currentSubPath = _percentagePath!.first; 114 : } 115 : } 116 : 117 3 : @override 118 : void update(double dt) { 119 3 : super.update(dt); 120 9 : _currentSubPath ??= _percentagePath!.first; 121 18 : if (!curveDirection.isNegative && _currentSubPath!.endAt < curveProgress || 122 18 : curveDirection.isNegative && _currentSubPath!.startAt > curveProgress) { 123 3 : _currentSubPath = 124 18 : _percentagePath!.firstWhere((v) => v.endAt >= curveProgress); 125 : } 126 6 : final lastEndAt = _currentSubPath!.startAt; 127 : final localPercentage = 128 18 : (curveProgress - lastEndAt) / (_currentSubPath!.endAt - lastEndAt); 129 18 : component?.position.setFrom(_currentSubPath!.previous + 130 18 : ((_currentSubPath!.v - _currentSubPath!.previous) * localPercentage)); 131 : } 132 : }