Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
78aeed452f | |||
af9b69326a |
@ -1,3 +1,9 @@
|
||||
## [0.3.2] - 2020-03-16
|
||||
- Fix a bug with "repeater" content
|
||||
|
||||
## [0.3.1] - 2020-03-05
|
||||
- Support dashed path
|
||||
|
||||
## [0.3.0+1] - 2020-03-05
|
||||
- Specify a version range for the dependency on `characters`.
|
||||
|
||||
|
@ -83,7 +83,7 @@ class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
|
||||
body: ListView(
|
||||
children: [
|
||||
Lottie.network(
|
||||
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/sample_app/assets/Mobilo/C.json',
|
||||
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/C.json',
|
||||
controller: _controller,
|
||||
onLoaded: (composition) {
|
||||
// Configure the AnimationController with the duration of the
|
||||
@ -243,9 +243,8 @@ class _Animation extends StatelessWidget {
|
||||
## Limitations
|
||||
This is a new library so usability, documentation and performance are still work in progress.
|
||||
|
||||
The following features are not yet implemented:
|
||||
- Dash path effects
|
||||
- Transforms on gradients (stroke and fills)
|
||||
Only the [supported features of Lottie Android](https://airbnb.io/lottie/#/supported-features)
|
||||
are supported in this port.
|
||||
|
||||
## Flutter Web
|
||||
Run the app with `flutter run -d Chrome --dart-define=FLUTTER_WEB_USE_SKIA=true --release`
|
||||
|
@ -74,9 +74,8 @@ import 'example/lib/examples/simple_dynamic_properties.dart#example';
|
||||
## Limitations
|
||||
This is a new library so usability, documentation and performance are still work in progress.
|
||||
|
||||
The following features are not yet implemented:
|
||||
- Dash path effects
|
||||
- Transforms on gradients (stroke and fills)
|
||||
Only the [supported features of Lottie Android](https://airbnb.io/lottie/#/supported-features)
|
||||
are supported in this port.
|
||||
|
||||
## Flutter Web
|
||||
Run the app with `flutter run -d Chrome --dart-define=FLUTTER_WEB_USE_SKIA=true --release`
|
||||
|
@ -3,6 +3,7 @@ analyzer:
|
||||
errors:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
||||
implicit-dynamic: false
|
||||
linter:
|
||||
rules:
|
||||
avoid_renaming_method_parameters: true
|
||||
|
1458
example/assets/17297-fireworks.json
Normal file
@ -31,7 +31,7 @@ class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
|
||||
body: ListView(
|
||||
children: [
|
||||
Lottie.network(
|
||||
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/sample_app/assets/Mobilo/C.json',
|
||||
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/C.json',
|
||||
controller: _controller,
|
||||
onLoaded: (composition) {
|
||||
// Configure the AnimationController with the duration of the
|
||||
|
@ -2,14 +2,13 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:lottie/src/l.dart';
|
||||
import 'src/all_files.g.dart';
|
||||
|
||||
void main() {
|
||||
Logger.root
|
||||
..level = Level.ALL
|
||||
..onRecord.listen(print);
|
||||
L.setTraceEnabled(true);
|
||||
Lottie.traceEnabled = true;
|
||||
runApp(App());
|
||||
}
|
||||
|
||||
@ -43,8 +42,8 @@ class App extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) => Detail(assetName)));
|
||||
Navigator.of(context).push(MaterialPageRoute<void>(
|
||||
builder: (context) => Detail(assetName)));
|
||||
},
|
||||
);
|
||||
},
|
||||
|
@ -216,9 +216,9 @@ final files = [
|
||||
'assets/lottiefiles/light.json',
|
||||
'assets/lottiefiles/lightsaber.json',
|
||||
'assets/lottiefiles/little_girl_jumping_-_loader.json',
|
||||
'assets/lottiefiles/loading copy.json',
|
||||
'assets/lottiefiles/loading disc.json',
|
||||
'assets/lottiefiles/loading.json',
|
||||
'assets/lottiefiles/loading_copy.json',
|
||||
'assets/lottiefiles/loading_disc.json',
|
||||
'assets/lottiefiles/loading_semicircle.json',
|
||||
'assets/lottiefiles/location.json',
|
||||
'assets/lottiefiles/location_marker.json',
|
||||
|
@ -108,7 +108,7 @@ packages:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.3.0+1"
|
||||
version: "0.3.2"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
23
example/test/fireworks_test.dart
Normal file
@ -0,0 +1,23 @@
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'utils.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Animations with stroke', (tester) async {
|
||||
var size = Size(500, 400);
|
||||
tester.binding.window.physicalSizeTestValue = size;
|
||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
||||
|
||||
var composition = await LottieComposition.fromBytes(
|
||||
File('assets/17297-fireworks.json').readAsBytesSync());
|
||||
|
||||
await tester.pumpWidget(FilmStrip(composition, size: size));
|
||||
|
||||
await expectLater(find.byType(FilmStrip),
|
||||
matchesGoldenFile(p.join('goldens/fireworks.png')));
|
||||
});
|
||||
}
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/fireworks.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
example/test/goldens/strokes/g.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
example/test/goldens/strokes/intelia_logo_animation.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
example/test/goldens/strokes/landing_page.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
example/test/goldens/strokes/little_girl_jumping_-_loader.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
example/test/goldens/strokes/loading_disc.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
example/test/goldens/strokes/permission.png
Normal file
After Width: | Height: | Size: 97 KiB |
BIN
example/test/goldens/strokes/playing.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
example/test/goldens/strokes/stroke.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
example/test/goldens/strokes/truecosmos.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
example/test/goldens/strokes/win_result_2.png
Normal file
After Width: | Height: | Size: 62 KiB |
37
example/test/stroke_test.dart
Normal file
@ -0,0 +1,37 @@
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'utils.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Animations with stroke', (tester) async {
|
||||
var size = Size(500, 400);
|
||||
tester.binding.window.physicalSizeTestValue = size;
|
||||
tester.binding.window.devicePixelRatioTestValue = 1.0;
|
||||
|
||||
for (var asset in [
|
||||
'assets/Tests/Stroke.json',
|
||||
'assets/lottiefiles/loading_disc.json',
|
||||
'assets/Mobilo/G.json',
|
||||
'assets/lottiefiles/truecosmos.json',
|
||||
'assets/lottiefiles/intelia_logo_animation.json',
|
||||
'assets/lottiefiles/landing_page.json',
|
||||
'assets/lottiefiles/permission.json',
|
||||
'assets/lottiefiles/little_girl_jumping_-_loader.json',
|
||||
'assets/lottiefiles/playing.json',
|
||||
'assets/lottiefiles/win_result_2.json',
|
||||
]) {
|
||||
var composition =
|
||||
await LottieComposition.fromBytes(File(asset).readAsBytesSync());
|
||||
|
||||
await tester.pumpWidget(FilmStrip(composition, size: size));
|
||||
|
||||
var fileName = '${p.basenameWithoutExtension(asset)}.png'.toLowerCase();
|
||||
await expectLater(find.byType(FilmStrip),
|
||||
matchesGoldenFile(p.join('goldens/strokes', fileName)));
|
||||
}
|
||||
});
|
||||
}
|
51
example/test/utils.dart
Normal file
@ -0,0 +1,51 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
|
||||
class FilmStrip extends StatelessWidget {
|
||||
final LottieComposition composition;
|
||||
final Size size;
|
||||
|
||||
const FilmStrip(this.composition, {Key key, @required this.size})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomPaint(
|
||||
size: size,
|
||||
painter: _CustomerPainter(composition),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CustomerPainter extends CustomPainter {
|
||||
static const _columns = 5;
|
||||
final LottieComposition composition;
|
||||
|
||||
_CustomerPainter(this.composition);
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
var thumbSize = Size(size.width / _columns, size.width / _columns);
|
||||
var drawable = LottieDrawable(composition);
|
||||
|
||||
var index = 0;
|
||||
for (var progress = 0.0; progress <= 1; progress += 0.05) {
|
||||
var x = index % _columns;
|
||||
var y = index ~/ _columns;
|
||||
|
||||
var rect = Offset(x * thumbSize.width, y.toDouble() * thumbSize.height) &
|
||||
thumbSize;
|
||||
|
||||
drawable
|
||||
..setProgress(progress)
|
||||
..draw(canvas, rect);
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CustomPainter oldDelegate) {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import '../../model/content/shape_trim_path.dart';
|
||||
import '../../model/key_path.dart';
|
||||
import '../../model/layer/base_layer.dart';
|
||||
import '../../utils.dart';
|
||||
import '../../utils/dash_path.dart';
|
||||
import '../../utils/misc.dart';
|
||||
import '../../utils/utils.dart';
|
||||
import '../../value/lottie_value_callback.dart';
|
||||
@ -132,7 +133,6 @@ abstract class BaseStrokeContent
|
||||
L.endSection('StrokeContent#draw');
|
||||
return;
|
||||
}
|
||||
_applyDashPatternIfNeeded(parentMatrix);
|
||||
|
||||
if (_colorFilterAnimation != null) {
|
||||
paint.colorFilter = _colorFilterAnimation.value;
|
||||
@ -152,7 +152,7 @@ abstract class BaseStrokeContent
|
||||
}
|
||||
L.endSection('StrokeContent#buildPath');
|
||||
L.beginSection('StrokeContent#drawPath');
|
||||
canvas.drawPath(_path, paint);
|
||||
canvas.drawPath(_withDashPattern(_path, parentMatrix), paint);
|
||||
L.endSection('StrokeContent#drawPath');
|
||||
}
|
||||
}
|
||||
@ -199,13 +199,13 @@ abstract class BaseStrokeContent
|
||||
}
|
||||
var endValue = min((endLength - totalLength) / length, 1).toDouble();
|
||||
Utils.applyTrimPathIfNeeded(_trimPathPath, startValue, endValue, 0.0);
|
||||
canvas.drawPath(_trimPathPath, paint);
|
||||
canvas.drawPath(_withDashPattern(_trimPathPath, parentMatrix), paint);
|
||||
} else if (currentLength + length < startLength ||
|
||||
currentLength > endLength) {
|
||||
// Do nothing
|
||||
} else if (currentLength + length <= endLength &&
|
||||
startLength < currentLength) {
|
||||
canvas.drawPath(_trimPathPath, paint);
|
||||
canvas.drawPath(_withDashPattern(_trimPathPath, parentMatrix), paint);
|
||||
} else {
|
||||
double startValue;
|
||||
if (startLength < currentLength) {
|
||||
@ -220,7 +220,7 @@ abstract class BaseStrokeContent
|
||||
endValue = (endLength - currentLength) / length;
|
||||
}
|
||||
Utils.applyTrimPathIfNeeded(_trimPathPath, startValue, endValue, 0);
|
||||
canvas.drawPath(_trimPathPath, paint);
|
||||
canvas.drawPath(_withDashPattern(_trimPathPath, parentMatrix), paint);
|
||||
}
|
||||
currentLength += length;
|
||||
}
|
||||
@ -250,11 +250,11 @@ abstract class BaseStrokeContent
|
||||
return bounds;
|
||||
}
|
||||
|
||||
void _applyDashPatternIfNeeded(Matrix4 parentMatrix) {
|
||||
Path _withDashPattern(Path path, Matrix4 parentMatrix) {
|
||||
L.beginSection('StrokeContent#applyDashPattern');
|
||||
if (_dashPatternAnimations.isEmpty) {
|
||||
L.endSection('StrokeContent#applyDashPattern');
|
||||
return;
|
||||
return path;
|
||||
}
|
||||
|
||||
var scale = parentMatrix.getScale();
|
||||
@ -275,12 +275,12 @@ abstract class BaseStrokeContent
|
||||
}
|
||||
_dashPatternValues[i] *= scale;
|
||||
}
|
||||
//TODO(xha): implement DashPathEffect with https://github.com/dnfield/flutter_path_drawing/blob/master/lib/src/dash_path.dart
|
||||
// var offset = _dashPatternOffsetAnimation == null
|
||||
// ? 0.0
|
||||
// : _dashPatternOffsetAnimation.value * scale;
|
||||
//paint.setPathEffect(DashPathEffect(_dashPatternValues, offset));
|
||||
|
||||
var newPath =
|
||||
dashPath(path, dashArray: CircularIntervalList(_dashPatternValues));
|
||||
L.endSection('StrokeContent#applyDashPattern');
|
||||
|
||||
return newPath;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -71,10 +71,8 @@ class RepeaterContent
|
||||
var newContents = <Content>[];
|
||||
while (index >= 0) {
|
||||
var content = contents[index];
|
||||
if (content is PathContent) {
|
||||
newContents.add(content);
|
||||
contents.removeAt(index);
|
||||
}
|
||||
newContents.add(content);
|
||||
contents.removeAt(index);
|
||||
--index;
|
||||
}
|
||||
newContents = newContents.reversed.toList();
|
||||
|
@ -35,7 +35,7 @@ class TransformKeyframeAnimation {
|
||||
final Matrix4 _skewMatrix3;
|
||||
|
||||
BaseKeyframeAnimation<Offset, Offset> /*?*/ _anchorPoint;
|
||||
BaseKeyframeAnimation<dynamic, Offset> /*?*/ _position;
|
||||
BaseKeyframeAnimation<Offset, Offset> /*?*/ _position;
|
||||
BaseKeyframeAnimation<Offset, Offset> /*?*/ _scale;
|
||||
BaseKeyframeAnimation<double, double> /*?*/ _rotation;
|
||||
DoubleKeyframeAnimation /*?*/ _skew;
|
||||
@ -44,12 +44,11 @@ class TransformKeyframeAnimation {
|
||||
BaseKeyframeAnimation<int, int> /*?*/ _opacity;
|
||||
BaseKeyframeAnimation<int, int> /*?*/ get opacity => _opacity;
|
||||
|
||||
BaseKeyframeAnimation<dynamic, double> /*?*/ _startOpacity;
|
||||
BaseKeyframeAnimation<dynamic, double> /*?*/ get startOpacity =>
|
||||
_startOpacity;
|
||||
BaseKeyframeAnimation<double, double> /*?*/ _startOpacity;
|
||||
BaseKeyframeAnimation<double, double> /*?*/ get startOpacity => _startOpacity;
|
||||
|
||||
BaseKeyframeAnimation<dynamic, double> /*?*/ _endOpacity;
|
||||
BaseKeyframeAnimation<dynamic, double> /*?*/ get endOpacity => _endOpacity;
|
||||
BaseKeyframeAnimation<double, double> /*?*/ _endOpacity;
|
||||
BaseKeyframeAnimation<double, double> /*?*/ get endOpacity => _endOpacity;
|
||||
|
||||
void addAnimationsToLayer(BaseLayer layer) {
|
||||
layer.addAnimation(_opacity);
|
||||
@ -159,23 +158,25 @@ class TransformKeyframeAnimation {
|
||||
Matrix4 getMatrixForRepeater(double amount) {
|
||||
final position = _position?.value;
|
||||
final scale = _scale?.value;
|
||||
final rotation = _rotation?.value;
|
||||
|
||||
_matrix.setIdentity();
|
||||
|
||||
if (position != null) {
|
||||
_matrix.translate(position.dx, position.dy);
|
||||
_matrix.translate(position.dx * amount, position.dy * amount);
|
||||
}
|
||||
|
||||
if (scale != null) {
|
||||
_matrix.scale(scale.dx, scale.dy);
|
||||
_matrix.scale(
|
||||
pow(scale.dx, amount).toDouble(), pow(scale.dy, amount).toDouble());
|
||||
}
|
||||
|
||||
if (rotation != null) {
|
||||
final anchorPoint = _anchorPoint?.value ?? Offset.zero;
|
||||
_matrix.translate(anchorPoint.dx, anchorPoint.dy);
|
||||
_matrix.rotateZ(rotation * pi / 180.0);
|
||||
_matrix.translate(-anchorPoint.dx, -anchorPoint.dy);
|
||||
if (_rotation != null) {
|
||||
var rotation = _rotation.value;
|
||||
var anchorPoint = _anchorPoint?.value;
|
||||
_matrix.rotate(
|
||||
Vector3(anchorPoint == null ? 0.0 : anchorPoint.dx,
|
||||
anchorPoint == null ? 0.0 : anchorPoint.dy, 1.0),
|
||||
radians(rotation * amount));
|
||||
}
|
||||
|
||||
return _matrix;
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:developer';
|
||||
|
||||
class L {
|
||||
static final int _maxDepth = 20;
|
||||
static bool _traceEnabled = false;
|
||||
@ -6,7 +8,8 @@ class L {
|
||||
static int _traceDepth = 0;
|
||||
static int _depthPastMaxDepth = 0;
|
||||
|
||||
static void setTraceEnabled(bool enabled) {
|
||||
static bool get traceEnabled => _traceEnabled;
|
||||
static set traceEnabled(bool enabled) {
|
||||
if (_traceEnabled == enabled) {
|
||||
return;
|
||||
}
|
||||
@ -27,9 +30,7 @@ class L {
|
||||
}
|
||||
_sections[_traceDepth] = section;
|
||||
_startTimeNs[_traceDepth] = DateTime.now().microsecondsSinceEpoch;
|
||||
//TODO(xha): use flutter trace infrastructure
|
||||
//TraceCompat.beginSection(section);
|
||||
//print(section);
|
||||
Timeline.startSync('Lottie::$section');
|
||||
_traceDepth++;
|
||||
}
|
||||
|
||||
@ -49,8 +50,7 @@ class L {
|
||||
throw StateError('Unbalanced trace call $section'
|
||||
'. Expected ${_sections[_traceDepth]}.');
|
||||
}
|
||||
//TODO(xha)
|
||||
//TraceCompat.endSection();
|
||||
Timeline.finishSync();
|
||||
return (DateTime.now().microsecondsSinceEpoch - _startTimeNs[_traceDepth]) /
|
||||
1000;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import 'dart:typed_data';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import '../lottie.dart';
|
||||
import 'l.dart';
|
||||
import 'lottie_builder.dart';
|
||||
import 'providers/load_image.dart';
|
||||
|
||||
@ -235,6 +236,11 @@ class Lottie extends StatefulWidget {
|
||||
/// and the font family in your assets.
|
||||
final LottieDelegates delegates;
|
||||
|
||||
static bool get traceEnabled => L.traceEnabled;
|
||||
static set traceEnabled(bool enabled) {
|
||||
L.traceEnabled = enabled;
|
||||
}
|
||||
|
||||
@override
|
||||
_LottieState createState() => _LottieState();
|
||||
}
|
||||
|
@ -708,7 +708,7 @@ class JsonUtf8Reader extends JsonReader {
|
||||
builder ??= StringBuffer();
|
||||
builder.write(buffer.readUtf8(index));
|
||||
buffer.readByte(); // '\'
|
||||
builder.write(readEscapeCharacter());
|
||||
builder.writeCharCode(readEscapeCharacter());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ Future<ui.Image> loadImage(LottieComposition composition,
|
||||
imageStream.removeListener(listener);
|
||||
|
||||
completer.complete(image.image);
|
||||
}, onError: (e, __) {
|
||||
}, onError: (dynamic e, __) {
|
||||
imageStream.removeListener(listener);
|
||||
|
||||
composition.addWarning('Failed to load image ${lottieImage.id}: $e');
|
||||
|
@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart';
|
||||
import '../../lottie.dart';
|
||||
import 'load_image.dart';
|
||||
|
||||
abstract class LottieProvider<T> {
|
||||
abstract class LottieProvider {
|
||||
LottieProvider({this.imageProviderFactory});
|
||||
|
||||
final LottieImageProviderFactory imageProviderFactory;
|
||||
|
101
lib/src/utils/dash_path.dart
Normal file
@ -0,0 +1,101 @@
|
||||
// Copied from https://github.com/dnfield/flutter_path_drawing
|
||||
// We don't depend directly on this package to save 2 dependencies
|
||||
|
||||
import 'dart:ui';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
/// Creates a new path that is drawn from the segments of `source`.
|
||||
///
|
||||
/// Dash intervals are controled by the `dashArray` - see [CircularIntervalList]
|
||||
/// for examples.
|
||||
///
|
||||
/// `dashOffset` specifies an initial starting point for the dashing.
|
||||
///
|
||||
/// Passing in a null `source` will result in a null result. Passing a `source`
|
||||
/// that is an empty path will return an empty path.
|
||||
Path dashPath(
|
||||
Path source, {
|
||||
@required CircularIntervalList<double> dashArray,
|
||||
DashOffset dashOffset,
|
||||
}) {
|
||||
assert(dashArray != null);
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
dashOffset = dashOffset ?? const DashOffset.absolute(0.0);
|
||||
// TODO: Is there some way to determine how much of a path would be visible today?
|
||||
|
||||
var dest = Path();
|
||||
for (final metric in source.computeMetrics()) {
|
||||
var distance = dashOffset._calculate(metric.length);
|
||||
var draw = true;
|
||||
while (distance < metric.length) {
|
||||
final len = dashArray.next;
|
||||
if (draw) {
|
||||
dest.addPath(metric.extractPath(distance, distance + len), Offset.zero);
|
||||
}
|
||||
distance += len;
|
||||
draw = !draw;
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
enum _DashOffsetType { absolute, percentage }
|
||||
|
||||
/// Specifies the starting position of a dash array on a path, either as a
|
||||
/// percentage or absolute value.
|
||||
///
|
||||
/// The internal value will be guaranteed to not be null.
|
||||
class DashOffset {
|
||||
/// Create a DashOffset that will be measured as a percentage of the length
|
||||
/// of the segment being dashed.
|
||||
///
|
||||
/// `percentage` will be clamped between 0.0 and 1.0; null will be converted
|
||||
/// to 0.0.
|
||||
DashOffset.percentage(double percentage)
|
||||
: _rawVal = percentage.clamp(0.0, 1.0) as double ?? 0.0,
|
||||
_dashOffsetType = _DashOffsetType.percentage;
|
||||
|
||||
/// Create a DashOffset that will be measured in terms of absolute pixels
|
||||
/// along the length of a [Path] segment.
|
||||
///
|
||||
/// `start` will be coerced to 0.0 if null.
|
||||
const DashOffset.absolute(double start)
|
||||
: _rawVal = start ?? 0.0,
|
||||
_dashOffsetType = _DashOffsetType.absolute;
|
||||
|
||||
final double _rawVal;
|
||||
final _DashOffsetType _dashOffsetType;
|
||||
|
||||
double _calculate(double length) {
|
||||
return _dashOffsetType == _DashOffsetType.absolute
|
||||
? _rawVal
|
||||
: length * _rawVal;
|
||||
}
|
||||
}
|
||||
|
||||
/// A circular array of dash offsets and lengths.
|
||||
///
|
||||
/// For example, the array `[5, 10]` would result in dashes 5 pixels long
|
||||
/// followed by blank spaces 10 pixels long. The array `[5, 10, 5]` would
|
||||
/// result in a 5 pixel dash, a 10 pixel gap, a 5 pixel dash, a 5 pixel gap,
|
||||
/// a 10 pixel dash, etc.
|
||||
///
|
||||
/// Note that this does not quite conform to an [Iterable<T>], because it does
|
||||
/// not have a moveNext.
|
||||
class CircularIntervalList<T> {
|
||||
CircularIntervalList(this._vals);
|
||||
|
||||
final List<T> _vals;
|
||||
int _idx = 0;
|
||||
|
||||
T get next {
|
||||
if (_idx >= _vals.length) {
|
||||
_idx = 0;
|
||||
}
|
||||
return _vals[_idx++];
|
||||
}
|
||||
}
|
@ -272,7 +272,7 @@ class ValueDelegate<T> {
|
||||
bool isSameProperty(ValueDelegate other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is ValueDelegate<T> &&
|
||||
const ListEquality().equals(other.keyPath, keyPath) &&
|
||||
const ListEquality<String>().equals(other.keyPath, keyPath) &&
|
||||
other.property == property;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
name: lottie
|
||||
description: Render After Effects animations natively on Flutter. This package is a pure Dart implementation of a Lottie player.
|
||||
version: 0.3.0+1
|
||||
version: 0.3.2
|
||||
homepage: https://github.com/xvrh/lottie-flutter
|
||||
|
||||
environment:
|
||||
|
@ -69,7 +69,7 @@ class DartProject {
|
||||
static String _getPackageName(String projectRoot) {
|
||||
var pubspecContent =
|
||||
File(p.join(projectRoot, 'pubspec.yaml')).readAsStringSync();
|
||||
var loadedPubspec = loadYaml(pubspecContent);
|
||||
var loadedPubspec = loadYaml(pubspecContent) as YamlMap;
|
||||
|
||||
return loadedPubspec['name'] as String;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ void main(List<String> args) {
|
||||
var pubspec = File('pubspec.yaml');
|
||||
var content = loadYaml(pubspec.readAsStringSync()) as YamlMap;
|
||||
|
||||
var pubspecVersion = content['version'];
|
||||
var pubspecVersion = content['version'] as String;
|
||||
var tagVersion = '';
|
||||
if (args.isNotEmpty) {
|
||||
tagVersion = args[0].split('/').last;
|
||||
|