Compare commits

...

7 Commits

120 changed files with 2253 additions and 756 deletions

View File

@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
flutter: ['stable', 'dev']
runs-on: ubuntu-latest
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: subosito/flutter-action@v1
@ -19,12 +19,16 @@ jobs:
channel: ${{ matrix.flutter }}
- run: flutter doctor
- run: flutter --version
- run: flutter pub get
working-directory: example
- run: flutter analyze
- run: flutter test test # https://github.com/flutter/flutter/issues/20907
- run: flutter test test
working-directory: example
- run: flutter pub run tool/prepare_submit.dart
- name: "check for uncommitted changes"
run: |
git diff --exit-code --stat -- . \
git diff --exit-code --stat -- . ':(exclude)*pubspec.lock' \
|| (echo "##[error] found changed files after build. please run 'dart tool/prepare_submit.dart'" \
"and check in all changes" \
&& exit 1)

2
.gitignore vendored
View File

@ -4,6 +4,8 @@ _*
!.gitignore
!.github
**/failures/*.png
*.iml
**/doc/api/
build/

View File

@ -1,3 +1,9 @@
## [0.3.0] - 2020-03-02
- Add `LottieDelegates` a group of options to customize the lottie animation at runtime.
ie: Dynamically modify color, position, size, text... of every elements of the animation.
- Correctly display Linear and Radial Gradients
- Integrate latest changes from Lottie-android
## [0.2.2] - 2020-02-21
- Add a [repeat] parameter to specify if the automatic animation should loop.
- Add the [animate], [reverse], [repeat] properties on `LottieBuilder`

22
LICENSE
View File

@ -1 +1,21 @@
TODO: Add your license here.
MIT License
Copyright (c) [year] [fullname]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -4,7 +4,7 @@
[![pub package](https://img.shields.io/pub/v/lottie.svg)](https://pub.dev/packages/lottie)
Lottie is a mobile library for Android and iOS that parses [Adobe After Effects](http://www.adobe.com/products/aftereffects.html)
animations exported as json with [Bodymovin](https://github.com/bodymovin/bodymovin) and renders them natively on mobile!
animations exported as json with [Bodymovin](https://github.com/airbnb/lottie-web) and renders them natively on mobile!
This repository is a unofficial conversion of the [Lottie-android](https://github.com/airbnb/lottie-android) library in pure Dart.
@ -37,7 +37,7 @@ class MyApp extends StatelessWidget {
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/A.json'),
// Load an animation and its images from a zip file
Lottie.asset('assets/lottiesfiles/angel.zip'),
Lottie.asset('assets/lottiefiles/angel.zip'),
],
),
),
@ -46,15 +46,8 @@ class MyApp extends StatelessWidget {
}
```
To load an animation from the assets folder, we need to add an `assets` section in the `pubspec.yaml`:
```yaml
flutter:
assets:
- assets/
```
### Specify a custom `AnimationController`
This example shows how to have full control over the animation by providing your own `AnimationController`.
This example shows how to take full control over the animation by providing your own `AnimationController`.
```dart
import 'package:flutter/material.dart';
@ -201,7 +194,9 @@ class _Painter extends CustomPainter {
var columns = 10;
for (var i = 0; i < frameCount; i++) {
var destRect = Offset(i % columns * 50.0, i ~/ 10 * 80.0) & (size / 5);
drawable.draw(canvas, destRect, progress: i / frameCount);
drawable
..setProgress(i / frameCount)
..draw(canvas, destRect);
}
}
@ -212,14 +207,45 @@ class _Painter extends CustomPainter {
}
````
### Modify properties at runtime
This example shows how to modify some properties of the animation at runtime. Here we change the text,
the color, the opacity and the position of some layers.
For each `ValueDelegate` we can either provide a static `value` or a `callback` to compute a value for a each frame.
````dart
class _Animation extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Lottie.asset(
'assets/Tests/Shapes.json',
delegates: LottieDelegates(
text: (initialText) => translate(initialText),
values: [
ValueDelegate.color(
const ['Shape Layer 1', 'Rectangle', 'Fill 1'],
value: Colors.red,
),
ValueDelegate.opacity(
const ['Shape Layer 1', 'Rectangle'],
callback: (frameInfo) =>
(frameInfo.overallProgress * 100).round(),
),
ValueDelegate.position(
const ['Shape Layer 1', 'Rectangle'],
relative: Offset(100, 200),
),
]),
);
}
}
````
## 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)
- Expose `Value callback` to modify dynamically some properties of the animation
- Text in animations has very basic support (unoptimized and buggy)
## Flutter Web
Run the app with `flutter run -d Chrome --dart-define=FLUTTER_WEB_USE_SKIA=true --release`

View File

@ -4,7 +4,7 @@
[![pub package](https://img.shields.io/pub/v/lottie.svg)](https://pub.dev/packages/lottie)
Lottie is a mobile library for Android and iOS that parses [Adobe After Effects](http://www.adobe.com/products/aftereffects.html)
animations exported as json with [Bodymovin](https://github.com/bodymovin/bodymovin) and renders them natively on mobile!
animations exported as json with [Bodymovin](https://github.com/airbnb/lottie-web) and renders them natively on mobile!
This repository is a unofficial conversion of the [Lottie-android](https://github.com/airbnb/lottie-android) library in pure Dart.
@ -20,15 +20,8 @@ The `Lottie` widget will load the json file and run the animation indefinitely.
import 'example/lib/examples/main.dart';
```
To load an animation from the assets folder, we need to add an `assets` section in the `pubspec.yaml`:
```yaml
flutter:
assets:
- assets/
```
### Specify a custom `AnimationController`
This example shows how to have full control over the animation by providing your own `AnimationController`.
This example shows how to take full control over the animation by providing your own `AnimationController`.
```dart
import 'example/lib/examples/animation_controller.dart';
@ -69,14 +62,21 @@ a specific position and size.
import 'example/lib/examples/custom_draw.dart#example';
````
### Modify properties at runtime
This example shows how to modify some properties of the animation at runtime. Here we change the text,
the color, the opacity and the position of some layers.
For each `ValueDelegate` we can either provide a static `value` or a `callback` to compute a value for a each frame.
````dart
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)
- Expose `Value callback` to modify dynamically some properties of the animation
- Text in animations has very basic support (unoptimized and buggy)
## Flutter Web
Run the app with `flutter run -d Chrome --dart-define=FLUTTER_WEB_USE_SKIA=true --release`

View File

@ -0,0 +1,185 @@
{
"v": "5.5.7",
"meta": {
"g": "LottieFiles AE 0.1.14",
"a": "",
"k": "",
"d": "",
"tc": ""
},
"fr": 60,
"ip": 0,
"op": 180,
"w": 300,
"h": 300,
"nm": " Square",
"ddd": 0,
"assets": [],
"layers": [
{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "Shape Layer 1",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [
150,
150,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100,
100
],
"ix": 6
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"ty": "rc",
"d": 1,
"s": {
"a": 0,
"k": [
182.992,
182.992
],
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 4
},
"nm": "Rectangle Path 1",
"mn": "ADBE Vector Shape - Rect",
"hd": false
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [
1,
0,
0,
255
],
"ix": 4
},
"o": {
"a": 0,
"k": 100,
"ix": 5
},
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 0,
"k": [
-8.504,
1.496
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 6
},
"o": {
"a": 0,
"k": 100,
"ix": 7
},
"sk": {
"a": 0,
"k": 0,
"ix": 4
},
"sa": {
"a": 0,
"k": 0,
"ix": 5
},
"nm": "Transform"
}
],
"nm": "Rectangle 1",
"np": 2,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
}
],
"ip": 0,
"op": 180,
"st": 0,
"bm": 0
}
],
"markers": []
}

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() async {
runApp(App());
}
class App extends StatefulWidget {
const App({Key key}) : super(key: key);
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return MaterialApp(
color: Colors.lightBlue,
home: Scaffold(
backgroundColor: Colors.lightBlue,
appBar: AppBar(
title: Text(''),
),
body: Center(
child: SizedBox(
width: 300,
height: 300,
child: Lottie.asset(
'assets/LottieLogo1.json',
animate: true,
repeat: false,
),
),
),
),
);
}
}

View File

@ -81,7 +81,9 @@ class _Painter extends CustomPainter {
var columns = 10;
for (var i = 0; i < frameCount; i++) {
var destRect = Offset(i % columns * 50.0, i ~/ 10 * 80.0) & (size / 5);
drawable.draw(canvas, destRect, progress: i / frameCount);
drawable
..setProgress(i / frameCount)
..draw(canvas, destRect);
}
}

View File

@ -0,0 +1,86 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:lottie/lottie.dart';
void main() async {
runApp(App());
}
class App extends StatefulWidget {
const App({Key key}) : super(key: key);
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> with TickerProviderStateMixin {
Color _color = Colors.green;
double _opacity = 0.5;
bool _useDelegates = true;
@override
Widget build(BuildContext context) {
var valueDelegates = [
ValueDelegate.color(['Shape Layer 1', 'Rectangle', 'Fill 1'],
value: _color),
ValueDelegate.opacity(['Shape Layer 1', 'Rectangle', 'Fill 1'],
callback: (_) => (_opacity * 100).round()),
];
return MaterialApp(
color: Colors.blue,
home: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('Dynamic properties'),
),
body: ListView(
children: <Widget>[
SizedBox(
width: 300,
height: 300,
child: Lottie.asset(
'assets/Tests/Shapes.json',
delegates: LottieDelegates(
values: _useDelegates ? valueDelegates : null),
),
),
Checkbox(
value: _useDelegates,
onChanged: (newValue) {
setState(() {
_useDelegates = newValue;
});
},
),
Slider(
value: _opacity,
onChanged: (newOpacity) {
setState(() {
_opacity = newOpacity;
});
},
),
Center(
child: Container(
width: 500,
child: ColorPicker(
pickerColor: _color,
onColorChanged: (newColor) {
setState(() {
_color = newColor;
});
},
showLabel: false,
enableAlpha: false,
pickerAreaHeightPercent: 0.8,
),
),
),
],
),
),
);
}
}

View File

@ -0,0 +1,72 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() async {
runApp(App());
}
class App extends StatefulWidget {
const App({Key key}) : super(key: key);
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> with TickerProviderStateMixin {
TextEditingController _textController;
@override
void initState() {
super.initState();
_textController = TextEditingController(text: /*'🔥Fire🔥'*/ 'Fire');
}
@override
void dispose() {
_textController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
color: Colors.blue,
home: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('Dynamic text'),
),
body: Center(
child: Column(
children: <Widget>[
SizedBox(
width: 300,
height: 300,
child: Lottie.asset(
'assets/Tests/DynamicText.json',
delegates: LottieDelegates(
text: (animationText) => _textController.text,
textStyle: (font) => TextStyle(
fontFamily: font.fontFamily,
fontStyle: FontStyle.italic),
),
),
),
Container(
width: 300,
child: CupertinoTextField(
controller: _textController,
onChanged: (newText) {
setState(() {});
},
),
)
],
),
),
),
);
}
}

View File

@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() async {
runApp(App());
}
class App extends StatefulWidget {
const App({Key key}) : super(key: key);
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> with TickerProviderStateMixin {
AnimationController _animationController;
bool _showAnimation = true;
@override
void initState() {
super.initState();
_animationController = AnimationController(vsync: this)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
setState(() {
_showAnimation = false;
});
}
});
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
color: Colors.lightBlue,
home: Scaffold(
backgroundColor: Colors.lightBlue,
appBar: AppBar(
title: Text('Show lottie animation: $_showAnimation'),
),
body: SingleChildScrollView(
child: Center(
child: Column(
children: [
if (_showAnimation)
Lottie.asset(
'assets/LottieLogo1.json',
controller: _animationController,
width: 200,
onLoaded: (composition) {
_animationController
..duration = composition.duration
..reset()
..forward();
},
),
],
),
),
),
),
);
}
}

View File

@ -18,7 +18,7 @@ class MyApp extends StatelessWidget {
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/A.json'),
// Load an animation and its images from a zip file
Lottie.asset('assets/lottiesfiles/angel.zip'),
Lottie.asset('assets/lottiefiles/angel.zip'),
],
),
),

View File

@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:lottie/lottie.dart';
import '../src/all_files.g.dart';
void main() async {
Logger.root
..level = Level.ALL
..onRecord.listen(print);
runApp(App());
}
class App extends StatefulWidget {
const App({Key key}) : super(key: key);
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> with TickerProviderStateMixin {
int _index = 0;
AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(vsync: this)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
setState(() {
++_index;
});
}
});
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
color: Colors.lightBlue,
home: Scaffold(
backgroundColor: Colors.lightBlue,
appBar: AppBar(
title: Text('$_index'),
),
body: SingleChildScrollView(
child: Center(
child: Column(
children: [
Lottie.asset(files[_index % files.length],
controller: _animationController, onLoaded: (composition) {
_animationController
..duration = composition.duration
..reset()
..forward();
}),
],
),
),
),
),
);
}
}

View File

@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView(
children: [_Animation()],
),
),
);
}
}
String translate(String input) => '**$input**';
//--- example
class _Animation extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Lottie.asset(
'assets/Tests/Shapes.json',
delegates: LottieDelegates(
text: (initialText) => translate(initialText),
values: [
ValueDelegate.color(
const ['Shape Layer 1', 'Rectangle', 'Fill 1'],
value: Colors.red,
),
ValueDelegate.opacity(
const ['Shape Layer 1', 'Rectangle'],
callback: (frameInfo) =>
(frameInfo.overallProgress * 100).round(),
),
ValueDelegate.position(
const ['Shape Layer 1', 'Rectangle'],
relative: Offset(100, 200),
),
]),
);
}
}
//---

View File

@ -66,7 +66,6 @@ class _Item extends StatelessWidget {
child: Container(
decoration: BoxDecoration(
color: Colors.white,
//border: Border.all(color: Colors.black12),
borderRadius: BorderRadius.all(Radius.circular(10)),
boxShadow: [
BoxShadow(

View File

@ -1,341 +1,343 @@
// Generated from tool/generate_file_list.dart
final files = [
'assets/14595-thumbs-up.json',
'assets/Mobilo/T.json',
'assets/AndroidWave.json',
'assets/DynamicGradient.json',
'assets/HamburgerArrow.json',
'assets/Logo/LogoSmall.json',
'assets/LottieLogo1.json',
'assets/LottieLogo2.json',
'assets/Mobilo/A.json',
'assets/Mobilo/X.json',
'assets/Mobilo/BlinkingCursor.json',
'assets/Mobilo/M.json',
'assets/Mobilo/L.json',
'assets/Mobilo/Y.json',
'assets/Mobilo/U.json',
'assets/Mobilo/K.json',
'assets/Mobilo/R.json',
'assets/Mobilo/G.json',
'assets/Mobilo/F.json',
'assets/Mobilo/S.json',
'assets/Mobilo/J.json',
'assets/Mobilo/I.json',
'assets/Mobilo/E.json',
'assets/Mobilo/Apostrophe.json',
'assets/Mobilo/B.json',
'assets/Mobilo/BlinkingCursor.json',
'assets/Mobilo/C.json',
'assets/Mobilo/Colon.json',
'assets/Mobilo/Comma.json',
'assets/Mobilo/D.json',
'assets/Mobilo/E.json',
'assets/Mobilo/F.json',
'assets/Mobilo/G.json',
'assets/Mobilo/H.json',
'assets/Mobilo/I.json',
'assets/Mobilo/J.json',
'assets/Mobilo/K.json',
'assets/Mobilo/L.json',
'assets/Mobilo/M.json',
'assets/Mobilo/N.json',
'assets/Mobilo/O.json',
'assets/Mobilo/P.json',
'assets/Mobilo/Q.json',
'assets/Mobilo/D.json',
'assets/Mobilo/Colon.json',
'assets/Mobilo/H.json',
'assets/Mobilo/C.json',
'assets/Mobilo/R.json',
'assets/Mobilo/S.json',
'assets/Mobilo/T.json',
'assets/Mobilo/U.json',
'assets/Mobilo/V.json',
'assets/Mobilo/Comma.json',
'assets/Mobilo/O.json',
'assets/Mobilo/Z.json',
'assets/Mobilo/N.json',
'assets/Mobilo/W.json',
'assets/Mobilo/B.json',
'assets/HamburgerArrow.json',
'assets/Tests/Remap.json',
'assets/Tests/Airbnb400.zip',
'assets/Tests/catrim_converted.json',
'assets/Tests/StartEndFrame.json',
'assets/Tests/TrackMattes.json',
'assets/Tests/DynamicText.json',
'assets/Tests/Precomps.json',
'assets/Tests/2ParentsMatte.json',
'assets/Mobilo/X.json',
'assets/Mobilo/Y.json',
'assets/Mobilo/Z.json',
'assets/Tests/2FrameAnimation.json',
'assets/Tests/LoopPlayOnce.json',
'assets/Tests/bm_converted.json',
'assets/Tests/RGBMarker.json',
'assets/Tests/WeAccept.json',
'assets/Tests/MiterLimit.json',
'assets/Tests/dalek.json',
'assets/Tests/MaskInv.json',
'assets/Tests/MatteTimeStretchScan.json',
'assets/Tests/Marker.json',
'assets/Tests/TrimPathsInsideAndOutsideGroup.json',
'assets/Tests/MaskNone.json',
'assets/Tests/Text_Justification_Translate_Scale_Rotation_Text.json',
'assets/Tests/InterpolatorLoopBack.json',
'assets/Tests/SplitDimensions.json',
'assets/Tests/EndFrame.json',
'assets/Tests/RGB.json',
'assets/Tests/DynamicGradient.json',
'assets/Tests/Font_Text_Justification_Translate_Scale_Rotation_Test.json',
'assets/Tests/adrock_converted.json',
'assets/Tests/ShapeTypes.json',
'assets/Tests/Squares.json',
'assets/Tests/Parenting.json',
'assets/Tests/catrim.json',
'assets/Tests/Stroke.json',
'assets/Tests/TimeStretch.json',
'assets/Tests/CheckSwitch.json',
'assets/Tests/hd.json',
'assets/Tests/OverlapShapeWithOpacity.json',
'assets/Tests/Shapes.json',
'assets/Tests/Masks.json',
'assets/Tests/SolidLayerTransform.json',
'assets/Tests/WeAcceptInlineImage.json',
'assets/Tests/TrimPaths.json',
'assets/Tests/2ParentsMatte.json',
'assets/Tests/Airbnb.zip',
'assets/Tests/DifferentPointsCount.json',
'assets/Tests/Skew.json',
'assets/Tests/GradientFill.json',
'assets/Tests/TransformWithoutEndValues.json',
'assets/Tests/EllipseDirection.json',
'assets/Tests/TimeStretchMask.json',
'assets/Tests/Frame.json',
'assets/Tests/map.zip',
'assets/Tests/august_view_pulse.zip',
'assets/Tests/KeyframeTypes.json',
'assets/Tests/dalek_converted.json',
'assets/Tests/Laugh4.json',
'assets/Tests/Scale0.json',
'assets/Tests/StarSkew.json',
'assets/Tests/TextBaseline.json',
'assets/Tests/Text.json',
'assets/Tests/bm.json',
'assets/Tests/Fill.json',
'assets/Tests/MaskA.json',
'assets/Tests/Repeater.json',
'assets/Tests/TimeStretchPrecomp.json',
'assets/Tests/adrock.json',
'assets/Tests/MatteTimeStretchLine.json',
'assets/Tests/Airbnb400.zip',
'assets/Tests/Airbnb800.zip',
'assets/DynamicGradient.json',
'assets/LottieLogo2.json',
'assets/AndroidWave.json',
'assets/LottieLogo1.json',
'assets/Logo/LogoSmall.json',
'assets/lottiefiles/the_final_frontier.json',
'assets/Tests/CheckSwitch.json',
'assets/Tests/DifferentPointsCount.json',
'assets/Tests/DynamicGradient.json',
'assets/Tests/DynamicText.json',
'assets/Tests/EllipseDirection.json',
'assets/Tests/EndFrame.json',
'assets/Tests/Fill.json',
'assets/Tests/Font_Text_Justification_Translate_Scale_Rotation_Test.json',
'assets/Tests/Frame.json',
'assets/Tests/GradientFill.json',
'assets/Tests/InterpolatorLoopBack.json',
'assets/Tests/KeyframeTypes.json',
'assets/Tests/Laugh4.json',
'assets/Tests/LoopPlayOnce.json',
'assets/Tests/Marker.json',
'assets/Tests/MaskA.json',
'assets/Tests/MaskInv.json',
'assets/Tests/MaskNone.json',
'assets/Tests/Masks.json',
'assets/Tests/MatteTimeStretchLine.json',
'assets/Tests/MatteTimeStretchScan.json',
'assets/Tests/MiterLimit.json',
'assets/Tests/OverlapShapeWithOpacity.json',
'assets/Tests/Parenting.json',
'assets/Tests/Precomps.json',
'assets/Tests/RGB.json',
'assets/Tests/RGBMarker.json',
'assets/Tests/Remap.json',
'assets/Tests/Repeater.json',
'assets/Tests/Scale0.json',
'assets/Tests/ShapeTypes.json',
'assets/Tests/Shapes.json',
'assets/Tests/Skew.json',
'assets/Tests/SolidLayerTransform.json',
'assets/Tests/SplitDimensions.json',
'assets/Tests/Squares.json',
'assets/Tests/StarSkew.json',
'assets/Tests/StartEndFrame.json',
'assets/Tests/Stroke.json',
'assets/Tests/TelegramAlphaCompat.json',
'assets/Tests/Text.json',
'assets/Tests/TextBaseline.json',
'assets/Tests/Text_Justification_Translate_Scale_Rotation_Text.json',
'assets/Tests/TimeRemapAndStartOffset.json',
'assets/Tests/TimeStretch.json',
'assets/Tests/TimeStretchMask.json',
'assets/Tests/TimeStretchPrecomp.json',
'assets/Tests/TrackMattes.json',
'assets/Tests/TransformWithoutEndValues.json',
'assets/Tests/TrimPaths.json',
'assets/Tests/TrimPathsInsideAndOutsideGroup.json',
'assets/Tests/WeAccept.json',
'assets/Tests/WeAcceptInlineImage.json',
'assets/Tests/adrock.json',
'assets/Tests/adrock_converted.json',
'assets/Tests/august_view_pulse.zip',
'assets/Tests/bm.json',
'assets/Tests/bm_converted.json',
'assets/Tests/catrim.json',
'assets/Tests/catrim_converted.json',
'assets/Tests/dalek.json',
'assets/Tests/dalek_converted.json',
'assets/Tests/hd.json',
'assets/Tests/map.zip',
'assets/lf20_w2Afea.json',
'assets/lottiefiles/100_percent.json',
'assets/lottiefiles/Plane.json',
'assets/lottiefiles/StreetByMorning.json',
'assets/lottiefiles/___.json',
'assets/lottiefiles/a_mountain.json',
'assets/lottiefiles/accept_arrows.json',
'assets/lottiefiles/airbnb.json',
'assets/lottiefiles/android_fingerprint.json',
'assets/lottiefiles/angel.zip',
'assets/lottiefiles/anima.json',
'assets/lottiefiles/animated_graph.json',
'assets/lottiefiles/animated_laptop_.json',
'assets/lottiefiles/animated_logo.json',
'assets/lottiefiles/atm_link.json',
'assets/lottiefiles/autoconnect_loading.json',
'assets/lottiefiles/ball_&_map.zip',
'assets/lottiefiles/banner_animation.json',
'assets/lottiefiles/bb8.json',
'assets/lottiefiles/bell.json',
'assets/lottiefiles/birds.json',
'assets/lottiefiles/bitcoin_to_the_moon.json',
'assets/lottiefiles/blood_transfusion_kawaii.json',
'assets/lottiefiles/bomb.json',
'assets/lottiefiles/books.json',
'assets/lottiefiles/bootymovin.json',
'assets/lottiefiles/bounching_ball.json',
'assets/lottiefiles/brain__.json',
'assets/lottiefiles/browser.json',
'assets/lottiefiles/building_evolution_animation.json',
'assets/lottiefiles/camera.json',
'assets/lottiefiles/camptravel.zip',
'assets/lottiefiles/cancel_button.json',
'assets/lottiefiles/cash.json',
'assets/lottiefiles/change_to_search_bar.json',
'assets/lottiefiles/check_pop.json',
'assets/lottiefiles/chinese.json',
'assets/lottiefiles/clock.json',
'assets/lottiefiles/cloud_disconnection.json',
'assets/lottiefiles/code_invite_success.json',
'assets/lottiefiles/coding_ape.json',
'assets/lottiefiles/coinfall.json',
'assets/lottiefiles/colorline.json',
'assets/lottiefiles/confusion.json',
'assets/lottiefiles/construction_site.json',
'assets/lottiefiles/cooking_app.json',
'assets/lottiefiles/countdown.json',
'assets/lottiefiles/credit_card.json',
'assets/lottiefiles/credit_level.json',
'assets/lottiefiles/cube_loader.json',
'assets/lottiefiles/cubo_livre.json',
'assets/lottiefiles/curly_hair_character_loop.json',
'assets/lottiefiles/cycle_animation.json',
'assets/lottiefiles/day_night_cycle.json',
'assets/lottiefiles/day_of_the_dead.json',
'assets/lottiefiles/deadpool.json',
'assets/lottiefiles/delivery_van.json',
'assets/lottiefiles/developer-animation.zip',
'assets/lottiefiles/dna_loader.json',
'assets/lottiefiles/dog.json',
'assets/lottiefiles/done.json',
'assets/lottiefiles/download copy.json',
'assets/lottiefiles/download.json',
'assets/lottiefiles/downloader.json',
'assets/lottiefiles/drop.json',
'assets/lottiefiles/drop_to_refresh.json',
'assets/lottiefiles/edited-landscape.json',
'assets/lottiefiles/elephant_trunk_swing.json',
'assets/lottiefiles/emoji_shock.json',
'assets/lottiefiles/emoji_tongue.json',
'assets/lottiefiles/emoji_wink.json',
'assets/lottiefiles/empty_status.json',
'assets/lottiefiles/estimate.json',
'assets/lottiefiles/fab_animate.json',
'assets/lottiefiles/fabulous_onboarding_animation.json',
'assets/lottiefiles/favourite_app_icon.json',
'assets/lottiefiles/file_error.json',
'assets/lottiefiles/finance_animation.json',
'assets/lottiefiles/fingerprint_scanner.json',
'assets/lottiefiles/finish,done.json',
'assets/lottiefiles/fish.json',
'assets/lottiefiles/flag_of_mexico.json',
'assets/lottiefiles/flow.json',
'assets/lottiefiles/frog.json',
'assets/lottiefiles/funky_chicken.json',
'assets/lottiefiles/gaming_pad.json',
'assets/lottiefiles/gears.json',
'assets/lottiefiles/geometry.zip',
'assets/lottiefiles/glow_loading.json',
'assets/lottiefiles/gradient_animated_background.json',
'assets/lottiefiles/happy birthday.json',
'assets/lottiefiles/hardware.json',
'assets/lottiefiles/hint_01.json',
'assets/lottiefiles/im_thirsty.json',
'assets/lottiefiles/immiguide_.json',
'assets/lottiefiles/in-app_purchasing.json',
'assets/lottiefiles/india.json',
'assets/lottiefiles/infinite_rainbow.json',
'assets/lottiefiles/intelia_logo_animation.json',
'assets/lottiefiles/iphone_x_loading.json',
'assets/lottiefiles/jojo_the_bird.json',
'assets/lottiefiles/jolly_walker.json',
'assets/lottiefiles/judgement.json',
'assets/lottiefiles/kod.io_logo_reveal.json',
'assets/lottiefiles/la_calavera.json',
'assets/lottiefiles/landing_page.json',
'assets/lottiefiles/lego_loader.json',
'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_semicircle.json',
'assets/lottiefiles/location.json',
'assets/lottiefiles/location_marker.json',
'assets/lottiefiles/location_pin.json',
'assets/lottiefiles/lottie_logo_1.json',
'assets/lottiefiles/lottiepreview_qr.json',
'assets/lottiefiles/mailsent.json',
'assets/lottiefiles/man_and_pay_with_credit_card.json',
'assets/lottiefiles/map_animation.json',
'assets/lottiefiles/material loading.json',
'assets/lottiefiles/material_loader.json',
'assets/lottiefiles/material_loading_2.json',
'assets/lottiefiles/material_wave_loading.json',
'assets/lottiefiles/finish,done.json',
'assets/lottiefiles/rocket.json',
'assets/lottiefiles/chinese.json',
'assets/lottiefiles/cubo_livre.json',
'assets/lottiefiles/slack_app_loader.json',
'assets/lottiefiles/no_internet_connection.json',
'assets/lottiefiles/moving_eye.json',
'assets/lottiefiles/downloader.json',
'assets/lottiefiles/poo_loader.json',
'assets/lottiefiles/airbnb.json',
'assets/lottiefiles/phonological.json',
'assets/lottiefiles/camptravel.zip',
'assets/lottiefiles/bounching_ball.json',
'assets/lottiefiles/walking.json',
'assets/lottiefiles/coding_ape.json',
'assets/lottiefiles/clock.json',
'assets/lottiefiles/timer_(3_second_loop).json',
'assets/lottiefiles/file_error.json',
'assets/lottiefiles/android_fingerprint.json',
'assets/lottiefiles/patient_successfully_added.json',
'assets/lottiefiles/toggle_switch.json',
'assets/lottiefiles/notification_request.json',
'assets/lottiefiles/loading disc.json',
'assets/lottiefiles/tractor_animation.json',
'assets/lottiefiles/Plane.json',
'assets/lottiefiles/retweet.json',
'assets/lottiefiles/blood_transfusion_kawaii.json',
'assets/lottiefiles/truecosmos.json',
'assets/lottiefiles/banner_animation.json',
'assets/lottiefiles/gears.json',
'assets/lottiefiles/progress_bar 2.json',
'assets/lottiefiles/flag_of_mexico.json',
'assets/lottiefiles/location_pin.json',
'assets/lottiefiles/play,_pause.json',
'assets/lottiefiles/panel2d.json',
'assets/lottiefiles/emoji_tongue.json',
'assets/lottiefiles/jolly_walker.json',
'assets/lottiefiles/switch_loop.json',
'assets/lottiefiles/intelia_logo_animation.json',
'assets/lottiefiles/credit_card.json',
'assets/lottiefiles/empty_status.json',
'assets/lottiefiles/autoconnect_loading.json',
'assets/lottiefiles/camera.json',
'assets/lottiefiles/volume_indicator.json',
'assets/lottiefiles/bell.json',
'assets/lottiefiles/colorline.json',
'assets/lottiefiles/mailsent.json',
'assets/lottiefiles/square_drop_loader.json',
'assets/lottiefiles/gradient_animated_background.json',
'assets/lottiefiles/vr_animation.json',
'assets/lottiefiles/rocksauce_title_card.json',
'assets/lottiefiles/streetby_test_loading.json',
'assets/lottiefiles/hardware.json',
'assets/lottiefiles/man_and_pay_with_credit_card.json',
'assets/lottiefiles/tractor.json',
'assets/lottiefiles/brain__.json',
'assets/lottiefiles/summer.json',
'assets/lottiefiles/passport.json',
'assets/lottiefiles/drop_to_refresh.json',
'assets/lottiefiles/penguin.json',
'assets/lottiefiles/location_marker.json',
'assets/lottiefiles/dna_loader.json',
'assets/lottiefiles/geometry.zip',
'assets/lottiefiles/pink_drink_machine.json',
'assets/lottiefiles/sensor_scan.zip',
'assets/lottiefiles/fingerprint_scanner.json',
'assets/lottiefiles/play_button.json',
'assets/lottiefiles/jojo_the_bird.json',
'assets/lottiefiles/cloud_disconnection.json',
'assets/lottiefiles/dog.json',
'assets/lottiefiles/loading.json',
'assets/lottiefiles/curly_hair_character_loop.json',
'assets/lottiefiles/india.json',
'assets/lottiefiles/anima.json',
'assets/lottiefiles/volume_shake_indicator.json',
'assets/lottiefiles/rating.json',
'assets/lottiefiles/birds.json',
'assets/lottiefiles/in-app_purchasing.json',
'assets/lottiefiles/rejection.json',
'assets/lottiefiles/judgement.json',
'assets/lottiefiles/splashy_loader.json',
'assets/lottiefiles/construction_site.json',
'assets/lottiefiles/download.json',
'assets/lottiefiles/uk.json',
'assets/lottiefiles/location.json',
'assets/lottiefiles/change_to_search_bar.json',
'assets/lottiefiles/x_pop.json',
'assets/lottiefiles/socar_logo.json',
'assets/lottiefiles/fish.json',
'assets/lottiefiles/landing_page.json',
'assets/lottiefiles/turbine.json',
'assets/lottiefiles/ofrenda.json',
'assets/lottiefiles/emoji_shock.json',
'assets/lottiefiles/sky.zip',
'assets/lottiefiles/bb8.json',
'assets/lottiefiles/powerupp_app_onboard (1).json',
'assets/lottiefiles/im_thirsty.json',
'assets/lottiefiles/permission.json',
'assets/lottiefiles/animated_logo.json',
'assets/lottiefiles/youtube_icon_reveal.json',
'assets/lottiefiles/fab_animate.json',
'assets/lottiefiles/money.json',
'assets/lottiefiles/pagado.json',
'assets/lottiefiles/servishero_loading.json',
'assets/lottiefiles/me_at_office.json',
'assets/lottiefiles/swiftkey-logo.json',
'assets/lottiefiles/favourite_app_icon.json',
'assets/lottiefiles/atm_link.json',
'assets/lottiefiles/emoji_wink.json',
'assets/lottiefiles/preloader.json',
'assets/lottiefiles/us.json',
'assets/lottiefiles/soda_loader.json',
'assets/lottiefiles/medal.json',
'assets/lottiefiles/menuButton2.json',
'assets/lottiefiles/menu_button_alt.json',
'assets/lottiefiles/mindful.json',
'assets/lottiefiles/mnemonics.json',
'assets/lottiefiles/money.json',
'assets/lottiefiles/motorcycle.json',
'assets/lottiefiles/moving bus.json',
'assets/lottiefiles/moving_eye.json',
'assets/lottiefiles/no_internet_connection.json',
'assets/lottiefiles/notification_request.json',
'assets/lottiefiles/octopus.json',
'assets/lottiefiles/ofrenda.json',
'assets/lottiefiles/on_off_settings_switch.json',
'assets/lottiefiles/pagado.json',
'assets/lottiefiles/pagination.json',
'assets/lottiefiles/panel2d.json',
'assets/lottiefiles/passport.json',
'assets/lottiefiles/patient_successfully_added.json',
'assets/lottiefiles/payme.json',
'assets/lottiefiles/peli-canon.json',
'assets/lottiefiles/pen_tool_loop.json',
'assets/lottiefiles/pencil_write.json',
'assets/lottiefiles/penguin.json',
'assets/lottiefiles/permission.json',
'assets/lottiefiles/personal_character.json',
'assets/lottiefiles/ph_onboarding_.json',
'assets/lottiefiles/phonological.json',
'assets/lottiefiles/pink_drink_machine.json',
'assets/lottiefiles/plane_to_dollar.json',
'assets/lottiefiles/play,_pause.json',
'assets/lottiefiles/play_and_like_it.json',
'assets/lottiefiles/play_button.json',
'assets/lottiefiles/playing.json',
'assets/lottiefiles/point.json',
'assets/lottiefiles/poo_loader.json',
'assets/lottiefiles/powerupp_app_onboard (1).json',
'assets/lottiefiles/powerupp_app_onboard.json',
'assets/lottiefiles/preloader.json',
'assets/lottiefiles/print.json',
'assets/lottiefiles/progress_bar 2.json',
'assets/lottiefiles/progress_bar.json',
'assets/lottiefiles/rating.json',
'assets/lottiefiles/red_pocket_pop_up.json',
'assets/lottiefiles/rejection.json',
'assets/lottiefiles/retweet.json',
'assets/lottiefiles/rocket.json',
'assets/lottiefiles/rocksauce_title_card.json',
'assets/lottiefiles/scan.json',
'assets/lottiefiles/scan_qr_code_success.json',
'assets/lottiefiles/search_button.json',
'assets/lottiefiles/security_token_roundtable.zip',
'assets/lottiefiles/sensor_scan.zip',
'assets/lottiefiles/servishero_loading.json',
'assets/lottiefiles/simple_loader.json',
'assets/lottiefiles/sky.zip',
'assets/lottiefiles/slack_app_loader.json',
'assets/lottiefiles/snowcation.json',
'assets/lottiefiles/socar_logo.json',
'assets/lottiefiles/soda_loader.json',
'assets/lottiefiles/spacehub.json',
'assets/lottiefiles/spinner loading.json',
'assets/lottiefiles/splashy_loader.json',
'assets/lottiefiles/square_drop_loader.json',
'assets/lottiefiles/star 2.json',
'assets/lottiefiles/streetby_test_loading.json',
'assets/lottiefiles/submit button.json',
'assets/lottiefiles/summer.json',
'assets/lottiefiles/sushi_and_wine.zip',
'assets/lottiefiles/swiftkey-logo.json',
'assets/lottiefiles/swipe_menu.json',
'assets/lottiefiles/swipe_right_indicator.json',
'assets/lottiefiles/switch_loop.json',
'assets/lottiefiles/the_final_frontier.json',
'assets/lottiefiles/tigerobo_demo.json',
'assets/lottiefiles/timer_(3_second_loop).json',
'assets/lottiefiles/toggle.json',
'assets/lottiefiles/toggle_switch.json',
'assets/lottiefiles/touch_id.json',
'assets/lottiefiles/tractor.json',
'assets/lottiefiles/tractor_animation.json',
'assets/lottiefiles/trail_loading.json',
'assets/lottiefiles/trophy.json',
'assets/lottiefiles/trophy_animation.zip',
'assets/lottiefiles/countdown.json',
'assets/lottiefiles/glow_loading.json',
'assets/lottiefiles/developer-animation.zip',
'assets/lottiefiles/bitcoin_to_the_moon.json',
'assets/lottiefiles/credit_level.json',
'assets/lottiefiles/medal.json',
'assets/lottiefiles/xamarin_logo_2.json',
'assets/lottiefiles/loading copy.json',
'assets/lottiefiles/done.json',
'assets/lottiefiles/plane_to_dollar.json',
'assets/lottiefiles/octopus.json',
'assets/lottiefiles/finance_animation.json',
'assets/lottiefiles/download copy.json',
'assets/lottiefiles/frog.json',
'assets/lottiefiles/yoga_carpet.json',
'assets/lottiefiles/sushi_and_wine.zip',
'assets/lottiefiles/payme.json',
'assets/lottiefiles/lego_loader.json',
'assets/lottiefiles/deadpool.json',
'assets/lottiefiles/updating_map.json',
'assets/lottiefiles/elephant_trunk_swing.json',
'assets/lottiefiles/lottie_logo_1.json',
'assets/lottiefiles/tigerobo_demo.json',
'assets/lottiefiles/100_percent.json',
'assets/lottiefiles/flow.json',
'assets/lottiefiles/hint_01.json',
'assets/lottiefiles/moving bus.json',
'assets/lottiefiles/light.json',
'assets/lottiefiles/little_girl_jumping_-_loader.json',
'assets/lottiefiles/red_pocket_pop_up.json',
'assets/lottiefiles/star 2.json',
'assets/lottiefiles/playing.json',
'assets/lottiefiles/motorcycle.json',
'assets/lottiefiles/submit button.json',
'assets/lottiefiles/infinite_rainbow.json',
'assets/lottiefiles/print.json',
'assets/lottiefiles/powerupp_app_onboard.json',
'assets/lottiefiles/cycle_animation.json',
'assets/lottiefiles/vigilance_camera.json',
'assets/lottiefiles/code_invite_success.json',
'assets/lottiefiles/walking_arrow.json',
'assets/lottiefiles/pen_tool_loop.json',
'assets/lottiefiles/mnemonics.json',
'assets/lottiefiles/bootymovin.json',
'assets/lottiefiles/happy birthday.json',
'assets/lottiefiles/books.json',
'assets/lottiefiles/angel.zip',
'assets/lottiefiles/building_evolution_animation.json',
'assets/lottiefiles/edited-landscape.json',
'assets/lottiefiles/spinner loading.json',
'assets/lottiefiles/accept_arrows.json',
'assets/lottiefiles/mindful.json',
'assets/lottiefiles/gaming_pad.json',
'assets/lottiefiles/menuButton2.json',
'assets/lottiefiles/wolf_peek.json',
'assets/lottiefiles/security_token_roundtable.zip',
'assets/lottiefiles/day_night_cycle.json',
'assets/lottiefiles/cancel_button.json',
'assets/lottiefiles/menu_button_alt.json',
'assets/lottiefiles/animated_graph.json',
'assets/lottiefiles/point.json',
'assets/lottiefiles/search_button.json',
'assets/lottiefiles/a_mountain.json',
'assets/lottiefiles/on_off_settings_switch.json',
'assets/lottiefiles/truecosmos.json',
'assets/lottiefiles/turbine.json',
'assets/lottiefiles/typing dot.json',
'assets/lottiefiles/kod.io_logo_reveal.json',
'assets/lottiefiles/___.json',
'assets/lottiefiles/animated_laptop_.json',
'assets/lottiefiles/simple_loader.json',
'assets/lottiefiles/personal_character.json',
'assets/lottiefiles/check_pop.json',
'assets/lottiefiles/xuanwheel_logo.json',
'assets/lottiefiles/lightsaber.json',
'assets/lottiefiles/peli-canon.json',
'assets/lottiefiles/map_animation.json',
'assets/lottiefiles/funky_chicken.json',
'assets/lottiefiles/ball_&_map.zip',
'assets/lottiefiles/snowcation.json',
'assets/lottiefiles/estimate.json',
'assets/lottiefiles/delivery_van.json',
'assets/lottiefiles/spacehub.json',
'assets/lottiefiles/material loading.json',
'assets/lottiefiles/swipe_right_indicator.json',
'assets/lottiefiles/pagination.json',
'assets/lottiefiles/confusion.json',
'assets/lottiefiles/coinfall.json',
'assets/lottiefiles/fabulous_onboarding_animation.json',
'assets/lottiefiles/win_result_2.json',
'assets/lottiefiles/scan.json',
'assets/lottiefiles/la_calavera.json',
'assets/lottiefiles/drop.json',
'assets/lottiefiles/scan_qr_code_success.json',
'assets/lottiefiles/loading_semicircle.json',
'assets/lottiefiles/immiguide_.json',
'assets/lottiefiles/vr_sickness.json',
'assets/lottiefiles/cash.json',
'assets/lottiefiles/ph_onboarding_.json',
'assets/lottiefiles/uk.json',
'assets/lottiefiles/updating_map.json',
'assets/lottiefiles/us.json',
'assets/lottiefiles/video_cam.json',
'assets/lottiefiles/material_loader.json',
'assets/lottiefiles/toggle.json',
'assets/lottiefiles/browser.json',
'assets/lottiefiles/day_of_the_dead.json',
'assets/lottiefiles/cube_loader.json',
'assets/lottiefiles/washing_machine.json',
'assets/lottiefiles/vigilance_camera.json',
'assets/lottiefiles/volume_indicator.json',
'assets/lottiefiles/volume_shake_indicator.json',
'assets/lottiefiles/vr_animation.json',
'assets/lottiefiles/vr_sickness.json',
'assets/lottiefiles/walking.json',
'assets/lottiefiles/walking_arrow.json',
'assets/lottiefiles/wallet recharge.json',
'assets/lottiefiles/pencil_write.json',
'assets/lottiefiles/progress_bar.json',
'assets/lottiefiles/iphone_x_loading.json',
'assets/lottiefiles/bomb.json',
'assets/lottiefiles/swipe_menu.json',
'assets/lottiefiles/trail_loading.json',
'assets/lottiefiles/StreetByMorning.json',
'assets/lottiefiles/cooking_app.json',
'assets/lottiefiles/lottiepreview_qr.json',
'assets/lottiefiles/touch_id.json',
'assets/lf20_w2Afea.json',
'assets/lottiefiles/washing_machine.json',
'assets/lottiefiles/win_result_2.json',
'assets/lottiefiles/wolf_peek.json',
'assets/lottiefiles/x_pop.json',
'assets/lottiefiles/xamarin_logo_2.json',
'assets/lottiefiles/xuanwheel_logo.json',
'assets/lottiefiles/yoga_carpet.json',
'assets/lottiefiles/youtube_icon_reveal.json',
];

View File

@ -29,6 +29,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
charcode:
dependency: transitive
description:
@ -62,11 +69,25 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_colorpicker:
dependency: "direct main"
description:
name: flutter_colorpicker
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.2"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
golden_toolkit:
dependency: "direct dev"
description:
name: golden_toolkit
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.1"
image:
dependency: transitive
description:
@ -87,7 +108,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.2.1"
version: "0.3.0"
matcher:
dependency: transitive
description:
@ -176,7 +197,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.11"
version: "0.2.15"
typed_data:
dependency: transitive
description:
@ -200,3 +221,4 @@ packages:
version: "3.5.0"
sdks:
dart: ">=2.7.0 <3.0.0"
flutter: ">=1.5.4 <2.0.0"

View File

@ -10,10 +10,12 @@ dependencies:
sdk: flutter
lottie:
path: ../
flutter_colorpicker:
dev_dependencies:
flutter_test:
sdk: flutter
golden_toolkit:
dependency_overrides:
pedantic: ^1.9.0
@ -34,33 +36,25 @@ flutter:
- assets/Images/
- assets/Images/WeAccept/
# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages
fonts:
- family: Comic Neue
fonts:
- asset: assets/fonts/Comic-Neue.ttf
- family: Helvetica
fonts:
- asset: assets/fonts/Helvetica.ttf
- family: Helvetica Neue
fonts:
- asset: assets/fonts/Helvetica-Neue.ttf
- family: Open Sans
fonts:
- asset: assets/fonts/Open-Sans.ttf
- family: PT Serif
fonts:
- asset: assets/fonts/PT-Serif.ttf
- family: Roboto
fonts:
- asset: assets/fonts/Roboto.ttf
- family: Noto Emoji
fonts:
- asset: assets/fonts/NotoEmoji-Regular.ttf

View File

@ -0,0 +1,293 @@
import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:lottie/lottie.dart';
void main() {
LottieComposition composition;
setUpAll(() async {
composition = await LottieComposition.fromBytes(
File('assets/Tests/Shapes.json').readAsBytesSync());
});
void testGolden(String description, ValueDelegate delegate,
{double progress}) async {
var screenshotName = description
.toLowerCase()
.replaceAll(RegExp('[^a-z0-9 ]'), '')
.replaceAll(' ', '_');
testWidgets(description, (tester) async {
var animation =
AnimationController(vsync: tester, duration: composition.duration);
if (progress != null) {
animation.value = progress;
}
await tester.pumpWidget(
Lottie(
composition: composition,
controller: animation,
delegates: LottieDelegates(values: [delegate]),
),
);
await tester.pump();
await expectLater(find.byType(Lottie),
matchesGoldenFile('goldens/dynamic/$screenshotName.png'));
if (progress == null || progress == 0) {
await tester.pumpWidget(
Lottie(
composition: composition,
controller: animation,
delegates: LottieDelegates(values: []),
),
);
await tester.pump();
await expectLater(find.byType(Lottie),
matchesGoldenFile('goldens/dynamic_without_delegate.png'));
}
});
}
testGolden(
'Fill color (Green)',
ValueDelegate.color(['Shape Layer 1', 'Rectangle', 'Fill 1'],
value: Colors.green),
);
testGolden(
'Fill color (Yellow)',
ValueDelegate.color(['Shape Layer 1', 'Rectangle', 'Fill 1'],
value: Colors.yellow),
);
testGolden(
'Fill opacity',
ValueDelegate.opacity(['Shape Layer 1', 'Rectangle', 'Fill 1'], value: 50),
);
testGolden(
'Stroke color',
ValueDelegate.strokeColor(['Shape Layer 1', 'Rectangle', 'Stroke 1'],
value: Colors.green),
);
testGolden(
'Stroke width',
ValueDelegate.strokeWidth(['Shape Layer 1', 'Rectangle', 'Stroke 1'],
value: 50),
);
testGolden(
'Stroke opacity',
ValueDelegate.opacity(['Shape Layer 1', 'Rectangle', 'Stroke 1'],
value: 50),
);
testGolden(
'Transform anchor point',
ValueDelegate.transformAnchorPoint(['Shape Layer 1', 'Rectangle'],
value: Offset(20, 20)),
);
testGolden(
'Transform position',
ValueDelegate.transformPosition(['Shape Layer 1', 'Rectangle'],
value: Offset(20, 20)),
);
testGolden(
'Transform position (relative)',
ValueDelegate.transformPosition(['Shape Layer 1', 'Rectangle'],
relative: Offset(20, 20)),
);
testGolden(
'Transform opacity',
ValueDelegate.transformOpacity(['Shape Layer 1', 'Rectangle'], value: 50),
);
testGolden(
'Transform rotation',
ValueDelegate.transformRotation(['Shape Layer 1', 'Rectangle'], value: 45),
);
testGolden(
'Transform scale',
ValueDelegate.transformScale(['Shape Layer 1', 'Rectangle'],
value: Offset(0.5, 0.5)),
);
testGolden(
'Rectangle corner roundedness',
ValueDelegate.cornerRadius(
['Shape Layer 1', 'Rectangle', 'Rectangle Path 1'],
value: 7),
);
testGolden(
'Rectangle position',
ValueDelegate.position(['Shape Layer 1', 'Rectangle', 'Rectangle Path 1'],
relative: Offset(20, 20)),
);
testGolden(
'Rectangle size',
ValueDelegate.rectangleSize(
['Shape Layer 1', 'Rectangle', 'Rectangle Path 1'],
relative: Offset(30, 40)),
);
testGolden(
'Ellipse position',
ValueDelegate.position(['Shape Layer 1', 'Ellipse', 'Ellipse Path 1'],
relative: Offset(20, 20)),
);
testGolden(
'Ellipse size',
ValueDelegate.ellipseSize(['Shape Layer 1', 'Ellipse', 'Ellipse Path 1'],
relative: Offset(40, 60)),
);
testGolden(
'Star points',
ValueDelegate.polystarPoints(['Shape Layer 1', 'Star', 'Polystar Path 1'],
value: 8),
);
testGolden(
'Star rotation',
ValueDelegate.polystarRotation(['Shape Layer 1', 'Star', 'Polystar Path 1'],
value: 10),
);
testGolden(
'Star position',
ValueDelegate.position(['Shape Layer 1', 'Star', 'Polystar Path 1'],
relative: Offset(20, 20)),
);
testGolden(
'Star inner radius',
ValueDelegate.polystarInnerRadius(
['Shape Layer 1', 'Star', 'Polystar Path 1'],
value: 10),
);
testGolden(
'Star inner roundedness',
ValueDelegate.polystarInnerRoundedness(
['Shape Layer 1', 'Star', 'Polystar Path 1'],
value: 100),
);
testGolden(
'Star outer radius',
ValueDelegate.polystarOuterRadius(
['Shape Layer 1', 'Star', 'Polystar Path 1'],
value: 60),
);
testGolden(
'Star outer roundedness',
ValueDelegate.polystarOuterRoundedness(
['Shape Layer 1', 'Star', 'Polystar Path 1'],
value: 100),
);
testGolden(
'Polygon points',
ValueDelegate.polystarPoints(['Shape Layer 1', 'Star', 'Polystar Path 1'],
value: 8),
);
testGolden(
'Polygon rotation',
ValueDelegate.polystarRotation(['Shape Layer 1', 'Star', 'Polystar Path 1'],
value: 10),
);
testGolden(
'Polygon position',
ValueDelegate.position(['Shape Layer 1', 'Star', 'Polystar Path 1'],
relative: Offset(20, 20)),
);
testGolden(
'Polygon radius',
ValueDelegate.polystarOuterRadius(
['Shape Layer 1', 'Star', 'Polystar Path 1'],
relative: 60),
);
testGolden(
'Polygon roundedness',
ValueDelegate.polystarOuterRoundedness(
['Shape Layer 1', 'Star', 'Polystar Path 1'],
value: 100),
);
testGolden(
'Repeater transform position',
ValueDelegate.transformPosition(
['Shape Layer 1', 'Repeater Shape', 'Repeater 1'],
relative: Offset(100, 100)),
);
testGolden(
'Repeater transform start opacity',
ValueDelegate.transformStartOpacity(
['Shape Layer 1', 'Repeater Shape', 'Repeater 1'],
value: 25),
);
testGolden(
'Repeater transform end opacity',
ValueDelegate.transformEndOpacity(
['Shape Layer 1', 'Repeater Shape', 'Repeater 1'],
value: 25),
);
testGolden(
'Repeater transform rotation',
ValueDelegate.transformRotation(
['Shape Layer 1', 'Repeater Shape', 'Repeater 1'],
value: 45),
);
testGolden(
'Repeater transform scale',
ValueDelegate.transformScale(
['Shape Layer 1', 'Repeater Shape', 'Repeater 1'],
value: Offset(2, 2)),
);
testGolden('Time remapping', ValueDelegate.timeRemap(['Circle 1'], value: 1),
progress: 0.1);
testGolden(
'Color Filter',
ValueDelegate.colorFilter(['**'],
value: ColorFilter.mode(Colors.green, BlendMode.srcATop)),
);
testGolden(
'Null Color Filter',
ValueDelegate.colorFilter(['**'], value: null),
);
for (var progress in [0.0, 0.5, 1.0]) {
testGolden(
'Opacity interpolation ($progress)',
ValueDelegate.transformOpacity(['Shape Layer 1', 'Rectangle'],
callback: (frameInfo) => lerpDouble(
10, 100, Curves.linear.transform(frameInfo.overallProgress))
.round()),
progress: progress);
}
}

View File

@ -0,0 +1,29 @@
import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:lottie/lottie.dart';
void main() {
testWidgets('Dynamic test', (tester) async {
var composition = await LottieComposition.fromBytes(
File('assets/Tests/DynamicText.json').readAsBytesSync());
await tester.pumpWidget(
MaterialApp(
home: Lottie(
composition: composition,
animate: false,
delegates: LottieDelegates(
text: (input) => '🔥c🔥👮🏿🔥',
textStyle: (font) => TextStyle(
fontFamily: 'Roboto', fontFamilyFallback: ['Noto Emoji']),
values: []),
),
),
);
await expectLater(
find.byType(Lottie), matchesGoldenFile('goldens/dynamic_text.png'));
});
}

View File

@ -0,0 +1,7 @@
import 'dart:async';
import 'package:golden_toolkit/golden_toolkit.dart';
Future<void> main(FutureOr<void> Function() testMain) async {
await loadAppFonts();
return testMain();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View File

@ -0,0 +1,58 @@
import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:lottie/lottie.dart';
void main() {
void testGradient(String name, ValueDelegate valueDelegate) {
testWidgets(name, (tester) async {
var composition = await LottieComposition.fromBytes(
File('assets/Tests/DynamicGradient.json').readAsBytesSync());
var animation =
AnimationController(vsync: tester, duration: composition.duration);
await tester.pumpWidget(
Lottie(
composition: composition,
controller: animation,
delegates: LottieDelegates(values: [valueDelegate]),
),
);
var screenshotName = name
.toLowerCase()
.replaceAll(RegExp('[^a-z0-9 ]'), '')
.replaceAll(' ', '_');
await expectLater(find.byType(Lottie),
matchesGoldenFile('goldens/gradients/$screenshotName.png'));
});
}
testGradient(
'Linear Gradient Fill',
ValueDelegate.gradientColor(['Linear', 'Rectangle', 'Gradient Fill'],
value: [Color(0xFFFFFF00), Color(0xFF00FF00)]));
testGradient(
'Radial Gradient Fill',
ValueDelegate.gradientColor(['Radial', 'Rectangle', 'Gradient Fill'],
value: [Color(0xFFFFFF00), Color(0xFF00FF00)]));
testGradient(
'Linear Gradient Stroke',
ValueDelegate.gradientColor(['Linear', 'Rectangle', 'Gradient Stroke'],
value: [Color(0xFFFFFF00), Color(0xFF00FF00)]));
testGradient(
'Radial Gradient Stroke',
ValueDelegate.gradientColor(['Radial', 'Rectangle', 'Gradient Stroke'],
value: [Color(0xFFFFFF00), Color(0xFF00FF00)]));
testGradient(
'Opacity Linear Gradient Fill',
ValueDelegate.opacity(['Linear', 'Rectangle', 'Gradient Fill'],
value: 50));
}

View File

@ -1,12 +1,9 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter_test/flutter_test.dart';
import 'package:lottie_example/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {});
testWidgets('Main sample', (tester) async {
await tester.pumpWidget(App());
await tester.pump();
});
}

View File

@ -4,10 +4,14 @@ void main() {
var buffer = StringBuffer();
buffer.writeln('// Generated from tool/generate_file_list.dart');
buffer.writeln('final files = [');
for (var file in Directory('assets')
var allFiles = Directory('assets')
.listSync(recursive: true)
.whereType<File>()
.where((f) => f.path.endsWith('.json') || f.path.endsWith('.zip'))) {
.where((f) => f.path.endsWith('.json') || f.path.endsWith('.zip'))
.toList();
allFiles.sort((a, b) => a.path.compareTo(b.path));
for (var file in allFiles) {
buffer.writeln(" '${file.path}',");
}
buffer.writeln('];');

View File

@ -1,6 +1,8 @@
export 'src/composition.dart' show LottieComposition;
export 'src/lottie.dart' show Lottie;
export 'src/lottie_builder.dart' show LottieBuilder;
export 'src/lottie_drawable.dart' show LottieDrawable;
export 'src/lottie_delegates.dart' show LottieDelegates;
export 'src/lottie_drawable.dart' show LottieDrawable, LottieFontStyle;
export 'src/lottie_image_asset.dart' show LottieImageAsset;
export 'src/raw_lottie.dart' show RawLottie;
export 'src/value_delegate.dart' show ValueDelegate;

View File

@ -1,5 +1,4 @@
import 'dart:ui';
import 'package:flutter/painting.dart';
import 'package:vector_math/vector_math_64.dart';
import '../../l.dart';
import '../../lottie_drawable.dart';
@ -24,8 +23,8 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
static final int _cacheStepsMs = 32;
final BaseLayer layer;
final GradientFill _fill;
final _linearGradientCache = <int, LinearGradient>{};
final _radialGradientCache = <int, RadialGradient>{};
final _linearGradientCache = <int, Gradient>{};
final _radialGradientCache = <int, Gradient>{};
final _path = Path();
final _paint = Paint();
final _paths = <PathContent>[];
@ -90,20 +89,14 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
matrix4: parentMatrix.storage);
}
var boundsRect = _path.getBounds();
Gradient gradient;
if (_fill.gradientType == GradientType.linear) {
gradient = _getLinearGradient();
gradient = _getLinearGradient(parentMatrix);
} else {
gradient = _getRadialGradient();
gradient = _getRadialGradient(parentMatrix);
}
//TODO(xha)
//gradient.setLocalMatrix(parentMatrix);
//TODO(xha): cache the shader
_paint.shader = gradient.createShader(boundsRect);
_paint.shader = gradient;
if (_colorFilterAnimation != null) {
_paint.colorFilter = _colorFilterAnimation.value;
@ -131,8 +124,8 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
outBounds.right + 1, outBounds.bottom + 1);
}
LinearGradient _getLinearGradient() {
var gradientHash = getGradientHash();
Gradient _getLinearGradient(Matrix4 parentMatrix) {
var gradientHash = _getGradientHash(parentMatrix);
var gradient = _linearGradientCache[gradientHash];
if (gradient != null) {
return gradient;
@ -142,17 +135,14 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
var gradientColor = _colorAnimation.value;
var colors = _applyDynamicColorsIfNeeded(gradientColor.colors);
var positions = gradientColor.positions;
gradient = LinearGradient(
begin: Alignment(startPoint.dx, startPoint.dy),
end: Alignment(endPoint.dx, endPoint.dy),
colors: colors,
stops: positions);
gradient = Gradient.linear(startPoint, endPoint, colors, positions,
TileMode.clamp, parentMatrix.storage);
_linearGradientCache[gradientHash] = gradient;
return gradient;
}
RadialGradient _getRadialGradient() {
var gradientHash = getGradientHash();
Gradient _getRadialGradient(Matrix4 parentMatrix) {
var gradientHash = _getGradientHash(parentMatrix);
var gradient = _radialGradientCache[gradientHash];
if (gradient != null) {
return gradient;
@ -166,17 +156,17 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
var y0 = startPoint.dy;
var x1 = endPoint.dx;
var y1 = endPoint.dy;
var r = hypot(x1 - x0, y1 - y0).toDouble();
if (r <= 0) {
r = 0.001;
var radius = hypot(x1 - x0, y1 - y0).toDouble();
if (radius <= 0) {
radius = 0.001;
}
gradient = RadialGradient(
center: Alignment(x0, y0), radius: r, colors: colors, stops: positions);
gradient = Gradient.radial(startPoint, radius, colors, positions,
TileMode.clamp, parentMatrix.storage);
_radialGradientCache[gradientHash] = gradient;
return gradient;
}
int getGradientHash() {
int _getGradientHash(Matrix4 parentMatrix) {
var startPointProgress =
(_startPointAnimation.progress * _cacheSteps).round();
var endPointProgress = (_endPointAnimation.progress * _cacheSteps).round();
@ -191,6 +181,7 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
if (colorProgress != 0) {
hash = hash * 31 * colorProgress;
}
hash *= 31 * parentMatrix.hashCode;
return hash;
}

View File

@ -1,5 +1,4 @@
import 'dart:ui';
import 'package:flutter/painting.dart';
import 'package:vector_math/vector_math_64.dart';
import '../../lottie_drawable.dart';
import '../../lottie_property.dart';
@ -21,8 +20,8 @@ class GradientStrokeContent extends BaseStrokeContent {
@override
final String name;
final bool _hidden;
final _linearGradientCache = <int, LinearGradient>{};
final _radialGradientCache = <int, RadialGradient>{};
final _linearGradientCache = <int, Gradient>{};
final _radialGradientCache = <int, Gradient>{};
final GradientType _type;
final int _cacheSteps;
@ -66,25 +65,21 @@ class GradientStrokeContent extends BaseStrokeContent {
if (_hidden) {
return;
}
var boundsRect = getBounds(parentMatrix, applyParents: false);
Gradient gradient;
if (_type == GradientType.linear) {
gradient = _getLinearGradient();
gradient = _getLinearGradient(parentMatrix);
} else {
gradient = _getRadialGradient();
gradient = _getRadialGradient(parentMatrix);
}
//TODO(xha): transform the gradient with the matrix.
//shader.setLocalMatrix(parentMatrix);
//TODO(xha): cache the shader
paint.shader = gradient.createShader(boundsRect);
paint.shader = gradient;
super.draw(canvas, size, parentMatrix, parentAlpha: parentAlpha);
}
LinearGradient _getLinearGradient() {
var gradientHash = _getGradientHash();
Gradient _getLinearGradient(Matrix4 parentMatrix) {
var gradientHash = _getGradientHash(parentMatrix);
var gradient = _linearGradientCache[gradientHash];
if (gradient != null) {
return gradient;
@ -94,21 +89,15 @@ class GradientStrokeContent extends BaseStrokeContent {
var gradientColor = _colorAnimation.value;
var colors = _applyDynamicColorsIfNeeded(gradientColor.colors);
var positions = gradientColor.positions;
var x0 = startPoint.dx;
var y0 = startPoint.dy;
var x1 = endPoint.dx;
var y1 = endPoint.dy;
gradient = LinearGradient(
begin: Alignment(x0, y0),
end: Alignment(x1, y1),
colors: colors,
stops: positions);
gradient = Gradient.linear(startPoint, endPoint, colors, positions,
TileMode.clamp, parentMatrix.storage);
_linearGradientCache[gradientHash] = gradient;
return gradient;
}
RadialGradient _getRadialGradient() {
var gradientHash = _getGradientHash();
Gradient _getRadialGradient(Matrix4 parentMatrix) {
var gradientHash = _getGradientHash(parentMatrix);
var gradient = _radialGradientCache[gradientHash];
if (gradient != null) {
return gradient;
@ -122,14 +111,17 @@ class GradientStrokeContent extends BaseStrokeContent {
var y0 = startPoint.dy;
var x1 = endPoint.dx;
var y1 = endPoint.dy;
var r = hypot(x1 - x0, y1 - y0).toDouble();
gradient = RadialGradient(
center: Alignment(x0, y0), radius: r, colors: colors, stops: positions);
var radius = hypot(x1 - x0, y1 - y0).toDouble();
gradient = Gradient.radial(startPoint, radius, colors, positions,
TileMode.clamp, parentMatrix.storage);
_radialGradientCache[gradientHash] = gradient;
return gradient;
}
int _getGradientHash() {
//TODO(xha): cache the shader based on the input parameters and not the animation
// progress.
// At first, log when there is too many cache miss.
int _getGradientHash(Matrix4 parentMatrix) {
var startPointProgress =
(_startPointAnimation.progress * _cacheSteps).round();
var endPointProgress = (_endPointAnimation.progress * _cacheSteps).round();
@ -144,6 +136,7 @@ class GradientStrokeContent extends BaseStrokeContent {
if (colorProgress != 0) {
hash = hash * 31 * colorProgress;
}
hash *= 31 * parentMatrix.hashCode;
return hash;
}

View File

@ -1,32 +0,0 @@
import '../../value/keyframe.dart';
import '../../value/scale_xy.dart';
import 'keyframe_animation.dart';
class ScaleKeyframeAnimation extends KeyframeAnimation<ScaleXY> {
ScaleKeyframeAnimation(List<Keyframe<ScaleXY>> keyframes) : super(keyframes);
@override
ScaleXY getValue(Keyframe<ScaleXY> keyframe, double keyframeProgress) {
if (keyframe.startValue == null || keyframe.endValue == null) {
throw StateError('Missing values for keyframe.');
}
var startTransform = keyframe.startValue;
var endTransform = keyframe.endValue;
if (valueCallback != null) {
var value = valueCallback.getValueInternal(
keyframe.startFrame,
keyframe.endFrame,
startTransform,
endTransform,
keyframeProgress,
getLinearCurrentKeyframeProgress(),
progress);
if (value != null) {
return value;
}
}
return ScaleXY.lerp(startTransform, endTransform, keyframeProgress);
}
}

View File

@ -7,7 +7,6 @@ import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../value/keyframe.dart';
import '../../value/lottie_value_callback.dart';
import '../../value/scale_xy.dart';
import 'base_keyframe_animation.dart';
import 'double_keyframe_animation.dart';
import 'value_callback_keyframe_animation.dart';
@ -37,7 +36,7 @@ class TransformKeyframeAnimation {
BaseKeyframeAnimation<Offset, Offset> /*?*/ _anchorPoint;
BaseKeyframeAnimation<dynamic, Offset> /*?*/ _position;
BaseKeyframeAnimation<ScaleXY, ScaleXY> /*?*/ _scale;
BaseKeyframeAnimation<Offset, Offset> /*?*/ _scale;
BaseKeyframeAnimation<double, double> /*?*/ _rotation;
DoubleKeyframeAnimation /*?*/ _skew;
DoubleKeyframeAnimation /*?*/ _skewAngle;
@ -141,8 +140,8 @@ class TransformKeyframeAnimation {
if (_scale != null) {
final scale = _scale.value;
if (scale.x != 1 || scale.y != 1) {
_matrix.scale(scale.x, scale.y);
if (scale.dx != 1 || scale.dy != 1) {
_matrix.scale(scale.dx, scale.dy);
}
}
@ -169,7 +168,7 @@ class TransformKeyframeAnimation {
}
if (scale != null) {
_matrix.scale(scale.x, scale.y);
_matrix.scale(scale.dx, scale.dy);
}
if (rotation != null) {
@ -200,9 +199,9 @@ class TransformKeyframeAnimation {
} else if (property == LottieProperty.transformScale) {
if (_scale == null) {
_scale = ValueCallbackKeyframeAnimation(
callback as LottieValueCallback<ScaleXY>, ScaleXY.one());
callback as LottieValueCallback<Offset>, Offset(1, 1));
} else {
_scale.setValueCallback(callback as LottieValueCallback<ScaleXY>);
_scale.setValueCallback(callback as LottieValueCallback<Offset>);
}
} else if (property == LottieProperty.transformRotation) {
if (_rotation == null) {

View File

@ -34,7 +34,8 @@ class ValueCallbackKeyframeAnimation<K, A> extends BaseKeyframeAnimation<K, A> {
@override
A get value {
return valueCallback.getValueInternal(0.0, 0.0, valueCallbackValue,
valueCallbackValue, progress, progress, progress);
valueCallbackValue, progress, progress, progress) ??
valueCallbackValue;
}
@override

View File

@ -14,8 +14,6 @@ import 'parser/moshi/json_reader.dart';
import 'performance_tracker.dart';
import 'providers/load_image.dart';
LottieComposition internalCreateComposition() => LottieComposition._();
void internalInit(
LottieComposition composition,
Rectangle<int> bounds,
@ -46,11 +44,12 @@ void internalInit(
}
class LottieComposition {
static Future<LottieComposition> fromByteData(ByteData data) {
return fromBytes(data.buffer.asUint8List());
static Future<LottieComposition> fromByteData(ByteData data, {String name}) {
return fromBytes(data.buffer.asUint8List(), name: name);
}
static Future<LottieComposition> fromBytes(Uint8List bytes) async {
static Future<LottieComposition> fromBytes(Uint8List bytes,
{String name}) async {
Archive archive;
if (bytes[0] == 0x50 && bytes[1] == 0x4B) {
archive = ZipDecoder().decodeBytes(bytes);
@ -59,8 +58,8 @@ class LottieComposition {
}
//TODO(xha): try to decode using the "compute" function to release the UI thread?
var composition =
LottieCompositionParser.parse(JsonReader.fromBytes(bytes));
var composition = LottieCompositionParser.parse(
LottieComposition._(name), JsonReader.fromBytes(bytes));
if (archive != null) {
for (var image in composition.images.values) {
@ -78,8 +77,9 @@ class LottieComposition {
return composition;
}
LottieComposition._();
LottieComposition._(this.name);
final String name;
final _performanceTracker = PerformanceTracker();
// This is stored as a set to avoid duplicates.
final _warnings = <String>{};
@ -106,7 +106,8 @@ class LottieComposition {
int _maskAndMatteCount = 0;
void addWarning(String warning) {
logger.warning(warning);
var prefix = name != null && name.isNotEmpty ? '$name: ' : '';
logger.warning('$prefix$warning');
_warnings.add(warning);
}

View File

@ -23,16 +23,19 @@ class Lottie extends StatefulWidget {
bool animate,
bool repeat,
bool reverse,
this.delegates,
}) : animate = animate ?? true,
reverse = reverse ?? false,
repeat = repeat ?? true,
super(key: key);
/// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle].
static LottieBuilder asset(String name,
{Animation<double> controller,
bool animate,
bool repeat,
bool reverse,
LottieDelegates delegates,
void Function(LottieComposition) onLoaded,
LottieImageProviderFactory imageProviderFactory,
Key key,
@ -49,6 +52,7 @@ class Lottie extends StatefulWidget {
animate: animate,
repeat: repeat,
reverse: reverse,
delegates: delegates,
imageProviderFactory: imageProviderFactory,
onLoaded: onLoaded,
key: key,
@ -61,12 +65,14 @@ class Lottie extends StatefulWidget {
package: package,
);
/// Creates a widget that displays an [LottieComposition] obtained from a [File].
static LottieBuilder file(
File file, {
Animation<double> controller,
bool animate,
bool repeat,
bool reverse,
LottieDelegates delegates,
LottieImageProviderFactory imageProviderFactory,
void Function(LottieComposition) onLoaded,
Key key,
@ -82,6 +88,7 @@ class Lottie extends StatefulWidget {
animate: animate,
repeat: repeat,
reverse: reverse,
delegates: delegates,
imageProviderFactory: imageProviderFactory,
onLoaded: onLoaded,
key: key,
@ -92,12 +99,14 @@ class Lottie extends StatefulWidget {
alignment: alignment,
);
/// Creates a widget that displays an [LottieComposition] obtained from a [Uint8List].
static LottieBuilder memory(
Uint8List bytes, {
Animation<double> controller,
bool animate,
bool repeat,
bool reverse,
LottieDelegates delegates,
LottieImageProviderFactory imageProviderFactory,
void Function(LottieComposition) onLoaded,
Key key,
@ -113,6 +122,7 @@ class Lottie extends StatefulWidget {
animate: animate,
repeat: repeat,
reverse: reverse,
delegates: delegates,
imageProviderFactory: imageProviderFactory,
onLoaded: onLoaded,
key: key,
@ -123,12 +133,14 @@ class Lottie extends StatefulWidget {
alignment: alignment,
);
/// Creates a widget that displays an [LottieComposition] obtained from the network.
static LottieBuilder network(
String url, {
Animation<double> controller,
bool animate,
bool repeat,
bool reverse,
LottieDelegates delegates,
LottieImageProviderFactory imageProviderFactory,
void Function(LottieComposition) onLoaded,
Key key,
@ -144,6 +156,7 @@ class Lottie extends StatefulWidget {
animate: animate,
repeat: repeat,
reverse: reverse,
delegates: delegates,
imageProviderFactory: imageProviderFactory,
onLoaded: onLoaded,
key: key,
@ -180,13 +193,13 @@ class Lottie extends StatefulWidget {
/// The property has no effect if [animate] is false, [repeat] is false or [controller] is not null.
final bool reverse;
/// If non-null, require the Lottie composition to have this width.
/// If non-null, requires the composition to have this width.
///
/// If null, the composition will pick a size that best preserves its intrinsic
/// aspect ratio.
final double width;
/// If non-null, require the Lottie composition to have this height.
/// If non-null, require the composition to have this height.
///
/// If null, the composition will pick a size that best preserves its intrinsic
/// aspect ratio.
@ -215,6 +228,13 @@ class Lottie extends StatefulWidget {
/// relative to text direction.
final AlignmentGeometry alignment;
/// A group of options to further customize the lottie animation.
/// - A [text] delegate to dynamically change some text displayed in the animation
/// - A value callback to change the properties of the animation at runtime.
/// - A text style factory to map between a font family specified in the animation
/// and the font family in your assets.
final LottieDelegates delegates;
@override
_LottieState createState() => _LottieState();
}
@ -268,6 +288,7 @@ class _LottieState extends State<Lottie> with TickerProviderStateMixin {
animation: _progressAnimation,
builder: (context, _) => RawLottie(
composition: widget.composition,
delegates: widget.delegates,
progress: _progressAnimation.value,
width: widget.width,
height: widget.height,

View File

@ -6,7 +6,6 @@ import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import '../lottie.dart';
import 'lottie.dart';
import 'lottie_drawable.dart';
import 'providers/asset_provider.dart';
import 'providers/file_provider.dart';
import 'providers/load_image.dart';
@ -40,6 +39,7 @@ class LottieBuilder extends StatefulWidget {
this.animate,
this.reverse,
this.repeat,
this.delegates,
this.onLoaded,
this.frameBuilder,
this.width,
@ -49,7 +49,7 @@ class LottieBuilder extends StatefulWidget {
}) : assert(lottie != null),
super(key: key);
/// Creates a widget that displays an [LottieStream] obtained from the network.
/// Creates a widget that displays an [LottieComposition] obtained from the network.
LottieBuilder.network(
String src, {
Map<String, String> headers,
@ -57,6 +57,7 @@ class LottieBuilder extends StatefulWidget {
this.animate,
this.reverse,
this.repeat,
this.delegates,
LottieImageProviderFactory imageProviderFactory,
this.onLoaded,
Key key,
@ -69,13 +70,11 @@ class LottieBuilder extends StatefulWidget {
headers: headers, imageProviderFactory: imageProviderFactory),
super(key: key);
/// Creates a widget that displays an [ImageStream] obtained from a [File].
///
/// The [file], [scale], and [repeat] arguments must not be null.
/// Creates a widget that displays an [LottieComposition] obtained from a [File].
///
/// Either the [width] and [height] arguments should be specified, or the
/// widget should be placed in a context that sets tight layout constraints.
/// Otherwise, the image dimensions will change as the image is loaded, which
/// Otherwise, the image dimensions will change as the animation is loaded, which
/// will result in ugly layout changes.
///
/// On Android, this may require the
@ -87,6 +86,7 @@ class LottieBuilder extends StatefulWidget {
this.animate,
this.reverse,
this.repeat,
this.delegates,
LottieImageProviderFactory imageProviderFactory,
this.onLoaded,
Key key,
@ -98,12 +98,14 @@ class LottieBuilder extends StatefulWidget {
}) : lottie = FileLottie(file, imageProviderFactory: imageProviderFactory),
super(key: key);
/// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle].
LottieBuilder.asset(
String name, {
this.controller,
this.animate,
this.reverse,
this.repeat,
this.delegates,
LottieImageProviderFactory imageProviderFactory,
this.onLoaded,
Key key,
@ -120,13 +122,14 @@ class LottieBuilder extends StatefulWidget {
imageProviderFactory: imageProviderFactory),
super(key: key);
/// Creates a widget that displays an [LottieDrawable] obtained from a [Uint8List].
/// Creates a widget that displays an [LottieComposition] obtained from a [Uint8List].
LottieBuilder.memory(
Uint8List bytes, {
this.controller,
this.animate,
this.reverse,
this.repeat,
this.delegates,
LottieImageProviderFactory imageProviderFactory,
this.onLoaded,
Key key,
@ -139,7 +142,7 @@ class LottieBuilder extends StatefulWidget {
MemoryLottie(bytes, imageProviderFactory: imageProviderFactory),
super(key: key);
/// The lottie animation to display.
/// The lottie animation to load.
/// Example of providers: [AssetLottie], [NetworkLottie], [FileLottie], [MemoryLottie]
final LottieProvider lottie;
@ -170,6 +173,13 @@ class LottieBuilder extends StatefulWidget {
/// The property has no effect if [animate] is false, [repeat] is false or [controller] is not null.
final bool reverse;
/// A group of options to further customize the lottie animation.
/// - A [text] delegate to dynamically change some text displayed in the animation
/// - A value callback to change the properties of the animation at runtime.
/// - A text style factory to map between a font family specified in the animation
/// and the font family in your assets.
final LottieDelegates delegates;
/// A builder function responsible for creating the widget that represents
/// this lottie animation.
///
@ -368,6 +378,7 @@ class _LottieBuilderState extends State<LottieBuilder> {
animate: widget.animate,
reverse: widget.reverse,
repeat: widget.repeat,
delegates: widget.delegates,
width: widget.width,
height: widget.height,
fit: widget.fit,

View File

@ -0,0 +1,64 @@
import 'package:flutter/widgets.dart';
import 'lottie_drawable.dart';
import 'value_delegate.dart';
// TODO(xha): recognize style Bold, Medium, Regular, SemiBold, etc...
TextStyle _defaultTextStyleDelegate(LottieFontStyle font) =>
TextStyle(fontFamily: font.fontFamily);
@immutable
class LottieDelegates {
/// Specify a callback to dynamically changes the text displayed in the lottie
/// animation.
/// For instance, this is useful when you want to translate the text in the animation.
final String Function(String) /*?*/ text;
/// A callback to map between a font family specified in the json animation
/// with the font family in your assets.
/// This is useful either if:
/// - the name of the font in your asset doesn't match the one in the json file.
/// - you want to use an other font than the one declared in the json
///
/// If the callback is null, the font family from the json is used as it.
///
/// Given an object containing the font family and style specified in the json
/// return a configured `TextStyle` that will be used as the base style when
/// painting the text.
final TextStyle Function(LottieFontStyle) textStyle;
/// A list of value delegates to dynamically modify the animation
/// properties at runtime.
///
/// Example:
/// ```dart
/// Lottie.asset(
/// 'lottiefile.json',
/// delegates: LottieDelegates(
/// value: [
/// ValueDelegate.color(['lake', 'fill'], value: Colors.blue),
/// ValueDelegate.opacity(['**', 'fill'], callback: (frameInfo) => 0.5 * frameInfo.overallProgress),
/// ],
/// ),
/// );
/// ```
final List<ValueDelegate> values;
//TODO(xha): imageDelegate to change the image to display?
LottieDelegates({
this.text,
TextStyle Function(LottieFontStyle) textStyle,
this.values,
}) : textStyle = textStyle ?? _defaultTextStyleDelegate;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is LottieDelegates &&
text == other.text &&
textStyle == other.textStyle &&
values == other.values;
@override
int get hashCode => hashValues(text, textStyle, values);
}

View File

@ -1,34 +1,65 @@
import 'dart:ui' as ui;
import 'package:flutter/rendering.dart';
import 'package:meta/meta.dart';
import 'package:vector_math/vector_math_64.dart';
import 'composition.dart';
import 'lottie_delegates.dart';
import 'model/key_path.dart';
import 'model/layer/composition_layer.dart';
import 'parser/layer_parser.dart';
import 'text_delegate.dart';
import 'value_delegate.dart';
class LottieDrawable {
final LottieComposition composition;
final _matrix = Matrix4.identity();
CompositionLayer _compositionLayer;
final Size size;
TextDelegate /*?*/ textDelegate;
LottieDelegates _delegates;
bool _isDirty = true;
LottieDrawable(this.composition, {this.textDelegate})
LottieDrawable(this.composition, {LottieDelegates delegates})
: size = Size(composition.bounds.width.toDouble(),
composition.bounds.height.toDouble()) {
this.delegates = delegates;
_compositionLayer = CompositionLayer(
this, LayerParser.parse(composition), composition.layers, composition);
}
CompositionLayer get compositionLayer => _compositionLayer;
/// Sets whether to apply opacity to the each layer instead of shape.
///
/// Opacity is normally applied directly to a shape. In cases where translucent
/// shapes overlap, applying opacity to a layer will be more accurate at the
/// expense of performance.
///
/// The default value is false.
///
/// Note: This process is very expensive. The performance impact will be reduced
/// when hardware acceleration is enabled.
bool isApplyingOpacityToLayersEnabled = false;
bool isApplyingOpacityToLayersEnabled = true;
void invalidateSelf() {
_isDirty = true;
}
void invalidateSelf() {}
double get progress => _progress;
double _progress = 0.0;
bool setProgress(double value) {
_isDirty = false;
_progress = value;
_compositionLayer.setProgress(value);
return _isDirty;
}
LottieDelegates get delegates => _delegates;
set delegates(LottieDelegates delegates) {
delegates ??= LottieDelegates();
if (_delegates != delegates) {
_delegates = delegates;
_updateValueDelegates(delegates.values);
}
}
bool get useTextGlyphs {
return textDelegate == null && composition.characters.isNotEmpty;
return delegates.text == null && composition.characters.isNotEmpty;
}
ui.Image getImageAsset(String ref) {
@ -41,13 +72,54 @@ class LottieDrawable {
}
TextStyle getTextStyle(String font, String style) {
//TODO(xha): allow the user to map Font in the animation with FontFamily loaded for flutter
// Support to inherit TextStyle from DefaultTextStyle applied for the Lottie wiget
return TextStyle(fontFamily: font);
return _delegates
.textStyle(LottieFontStyle(fontFamily: font, style: style));
}
void draw(ui.Canvas canvas, ui.Rect rect,
{@required double progress, BoxFit fit, Alignment alignment}) {
List<ValueDelegate> _valueDelegates = <ValueDelegate>[];
void _updateValueDelegates(List<ValueDelegate> newDelegates) {
if (identical(_valueDelegates, newDelegates)) return;
newDelegates ??= const [];
var delegates = <ValueDelegate>[];
for (var newDelegate in newDelegates) {
var existingDelegate = _valueDelegates
.firstWhere((f) => f.isSameProperty(newDelegate), orElse: () => null);
if (existingDelegate != null) {
var resolved = internalResolved(existingDelegate);
resolved.updateDelegate(newDelegate);
delegates.add(existingDelegate);
} else {
var keyPaths = _resolveKeyPath(KeyPath(newDelegate.keyPath));
var resolvedValueDelegate = internalResolve(newDelegate, keyPaths);
resolvedValueDelegate.addValueCallback(this);
delegates.add(newDelegate);
}
}
for (var oldDelegate in _valueDelegates) {
if (delegates.every((c) => !c.isSameProperty(oldDelegate))) {
var resolved = internalResolved(oldDelegate);
resolved.clear();
}
}
_valueDelegates = delegates;
}
/// Takes a {@link KeyPath}, potentially with wildcards or globstars and resolve it to a list of
/// zero or more actual {@link KeyPath Keypaths} that exist in the current animation.
/// <p>
/// If you want to set value callbacks for any of these values, it is recommend to use the
/// returned {@link KeyPath} objects because they will be internally resolved to their content
/// and won't trigger a tree walk of the animation contents when applied.
List<KeyPath> _resolveKeyPath(KeyPath keyPath) {
var keyPaths = <KeyPath>[];
_compositionLayer.resolveKeyPath(keyPath, 0, keyPaths, KeyPath([]));
return keyPaths;
}
void draw(ui.Canvas canvas, ui.Rect rect, {BoxFit fit, Alignment alignment}) {
if (rect.isEmpty) {
return;
}
@ -71,9 +143,12 @@ class LottieDrawable {
_matrix.translate(destinationRect.left, destinationRect.top);
_matrix.scale(destinationRect.size.width / sourceRect.width,
destinationRect.size.height / sourceRect.height);
progress ??= 0;
_compositionLayer
..setProgress(progress)
..draw(canvas, rect.size, _matrix, parentAlpha: 255);
_compositionLayer.draw(canvas, rect.size, _matrix, parentAlpha: 255);
}
}
class LottieFontStyle {
final String fontFamily, style;
LottieFontStyle({this.fontFamily, this.style});
}

View File

@ -1,5 +1,4 @@
import 'dart:ui';
import 'value/scale_xy.dart';
/// Property values are the same type as the generic type of their corresponding
/// {@link LottieValueCallback}. With this, we can use generics to maintain type safety
@ -62,69 +61,69 @@ abstract class LottieProperty {
static final int opacity = 4;
/// In Px */
static final Offset transformAnchorPoint = Offset.zero;
static final Offset transformAnchorPoint = Offset(5, 5);
/// In Px */
static final Offset transformPosition = Offset.zero;
static final Offset transformPosition = Offset(6, 6);
/// In Px */
static final Offset ellipseSize = Offset.zero;
static final Offset ellipseSize = Offset(7, 7);
/// In Px */
static final Offset rectangleSize = Offset.zero;
static final Offset rectangleSize = Offset(8, 8);
/// In degrees */
static final double cornerRadius = 0.0;
static final double cornerRadius = 9.0;
/// In Px */
static final Offset position = Offset.zero;
static final ScaleXY transformScale = ScaleXY.one();
static final Offset position = Offset(10, 10);
static final Offset transformScale = Offset(11, 11);
/// In degrees */
static final double transformRotation = 1.0;
static final double transformRotation = 12.0;
/// 0-85 */
static final double transformSkew = 0.0;
static final double transformSkew = 13.0;
/// In degrees */
static final double transformSkewAngle = 0.0;
static final double transformSkewAngle = 14.0;
/// In Px */
static final double strokeWidth = 2.0;
static final double textTracking = 3.0;
static final double repeaterCopies = 4.0;
static final double repeaterOffset = 5.0;
static final double polystarPoints = 6.0;
static final double strokeWidth = 15.0;
static final double textTracking = 16.0;
static final double repeaterCopies = 17.0;
static final double repeaterOffset = 18.0;
static final double polystarPoints = 19.0;
/// In degrees */
static final double polystarRotation = 7.0;
static final double polystarRotation = 20.0;
/// In Px */
static final double polystarInnerRadius = 8.0;
static final double polystarInnerRadius = 21.0;
/// In Px */
static final double polystarOuterRadius = 9.0;
static final double polystarOuterRadius = 22.0;
/// [0,100] */
static final double polystarInnerRoundedness = 10.0;
static final double polystarInnerRoundedness = 23.0;
/// [0,100] */
static final double polystarOuterRoundedness = 11.0;
static final double polystarOuterRoundedness = 24.0;
/// [0,100] */
static final double transformStartOpacity = 12.0;
static final double transformStartOpacity = 25.0;
/// [0,100] */
static final double transformEndOpacity = 12.1;
static final double transformEndOpacity = 26.0;
/// The time value in seconds */
static final double timeRemap = 13.0;
static final double timeRemap = 27.0;
/// In Dp */
static final double textSize = 14.0;
/// In Dp
static final double textSize = 28.0;
static final ColorFilter colorFilter =
ColorFilter.mode(Color(0xFF000000), BlendMode.dst);
static final List<Color> gradientColor = const [];
static final List<Color> gradientColor = [];
}

View File

@ -1,19 +1,19 @@
import 'dart:ui';
import '../../animation/keyframe/base_keyframe_animation.dart';
import '../../animation/keyframe/scale_keyframe_animation.dart';
import '../../animation/keyframe/point_keyframe_animation.dart';
import '../../value/keyframe.dart';
import '../../value/scale_xy.dart';
import 'base_animatable_value.dart';
class AnimatableScaleValue extends BaseAnimatableValue<ScaleXY, ScaleXY> {
AnimatableScaleValue.one() : this(ScaleXY.one());
class AnimatableScaleValue extends BaseAnimatableValue<Offset, Offset> {
AnimatableScaleValue.one() : this(Offset(1, 1));
AnimatableScaleValue(ScaleXY value) : super.fromValue(value);
AnimatableScaleValue(Offset value) : super.fromValue(value);
AnimatableScaleValue.fromKeyframes(List<Keyframe<ScaleXY>> keyframes)
AnimatableScaleValue.fromKeyframes(List<Keyframe<Offset>> keyframes)
: super.fromKeyframes(keyframes);
@override
BaseKeyframeAnimation<ScaleXY, ScaleXY> createAnimation() {
return ScaleKeyframeAnimation(keyframes);
BaseKeyframeAnimation<Offset, Offset> createAnimation() {
return PointKeyframeAnimation(keyframes);
}
}

View File

@ -66,6 +66,7 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
final Layer layerModel;
MaskKeyframeAnimation /*?*/ _mask;
DoubleKeyframeAnimation _inOutAnimation;
BaseLayer /*?*/ _matteLayer;
/// This should only be used by {@link #buildParentLayerListIfNeeded()}
@ -117,13 +118,13 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
void _setupInOutAnimations() {
if (layerModel.inOutKeyframes.isNotEmpty) {
final inOutAnimation = DoubleKeyframeAnimation(layerModel.inOutKeyframes);
inOutAnimation.setIsDiscrete();
inOutAnimation.addUpdateListener(() {
_setVisible(inOutAnimation.value == 1);
_inOutAnimation = DoubleKeyframeAnimation(layerModel.inOutKeyframes);
_inOutAnimation.setIsDiscrete();
_inOutAnimation.addUpdateListener(() {
_setVisible(_inOutAnimation.value == 1);
});
_setVisible(inOutAnimation.value == 1);
addAnimation(inOutAnimation);
_setVisible(_inOutAnimation.value == 1);
addAnimation(_inOutAnimation);
} else {
_setVisible(true);
}
@ -521,6 +522,10 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
if (layerModel.timeStretch != 0) {
progress /= layerModel.timeStretch;
}
if (_inOutAnimation != null) {
// Time stretch needs to be divided again for the inOutAnimation.
_inOutAnimation.setProgress(progress / layerModel.timeStretch);
}
if (_matteLayer != null) {
// The matte layer's time stretch is pre-calculated.
var matteTimeStretch = _matteLayer.layerModel.timeStretch;

View File

@ -131,13 +131,13 @@ class CompositionLayer extends BaseLayer {
compositionDelayFrames;
progress = remappedFrames / durationFrames;
}
if (layerModel.timeStretch != 0) {
progress /= layerModel.timeStretch;
}
if (_timeRemapping == null) {
progress -= layerModel.startProgress;
}
if (layerModel.timeStretch != 0) {
progress /= layerModel.timeStretch;
}
for (var i = _layers.length - 1; i >= 0; i--) {
_layers[i].setProgress(progress);
}

View File

@ -1,4 +1,5 @@
import 'dart:ui';
import 'package:characters/characters.dart';
import 'package:flutter/widgets.dart';
import 'package:vector_math/vector_math_64.dart';
import '../../animation/content/content_group.dart';
@ -22,7 +23,6 @@ class TextLayer extends BaseLayer {
final _matrix = Matrix4.identity();
final _fillPaint = Paint()..style = PaintingStyle.fill;
final _strokePaint = Paint()..style = PaintingStyle.stroke;
TextStyle _textStyle;
final _contentsForCharacter = <FontCharacter, List<ContentGroup>>{};
final TextKeyframeAnimation _textAnimation;
final LottieComposition _composition;
@ -227,14 +227,14 @@ class TextLayer extends BaseLayer {
void _drawTextWithFont(DocumentData documentData, Font font,
Matrix4 parentMatrix, Canvas canvas) {
var parentScale = parentMatrix.getScale();
_textStyle = lottieDrawable.getTextStyle(font.family, font.style);
if (_textStyle == null) {
var textStyle = lottieDrawable.getTextStyle(font.family, font.style);
if (textStyle == null) {
return;
}
var text = documentData.text;
var textDelegate = lottieDrawable.textDelegate;
var textDelegate = lottieDrawable.delegates.text;
if (textDelegate != null) {
text = textDelegate.getTextInternal(text);
text = textDelegate(text);
}
double textSize;
if (_textSizeCallbackAnimation != null) {
@ -244,8 +244,8 @@ class TextLayer extends BaseLayer {
} else {
textSize = documentData.size;
}
_textStyle =
_textStyle.copyWith(fontSize: textSize * window.devicePixelRatio);
textStyle =
textStyle.copyWith(fontSize: textSize * window.devicePixelRatio);
// Line height
var lineHeight = documentData.lineHeight * window.devicePixelRatio;
@ -256,7 +256,7 @@ class TextLayer extends BaseLayer {
for (var l = 0; l < textLineCount; l++) {
var textLine = textLines[l];
var textPainter = TextPainter(
text: TextSpan(text: textLine, style: _textStyle),
text: TextSpan(text: textLine, style: textStyle),
textDirection: _textDirection);
textPainter.layout();
var textLineWidth = textPainter.width;
@ -270,7 +270,7 @@ class TextLayer extends BaseLayer {
canvas.translate(0, translateY);
// Draw each line
_drawFontTextLine(textLine, documentData, canvas, parentScale);
_drawFontTextLine(textLine, textStyle, documentData, canvas, parentScale);
// Reset canvas
canvas.transform(parentMatrix.storage);
@ -284,14 +284,13 @@ class TextLayer extends BaseLayer {
return textLinesArray;
}
void _drawFontTextLine(String text, DocumentData documentData, Canvas canvas,
double parentScale) {
for (var i = 0; i < text.length;) {
var charString = _codePointToString(text, i);
i += charString.length;
_drawCharacterFromFont(charString, documentData, canvas);
void _drawFontTextLine(String text, TextStyle textStyle,
DocumentData documentData, Canvas canvas, double parentScale) {
for (var char in text.characters) {
var charString = char;
_drawCharacterFromFont(charString, textStyle, documentData, canvas);
var textPainter = TextPainter(
text: TextSpan(text: charString, style: _textStyle),
text: TextSpan(text: charString, style: textStyle),
textDirection: _textDirection);
textPainter.layout();
var charWidth = textPainter.width;
@ -369,18 +368,19 @@ class TextLayer extends BaseLayer {
canvas.drawPath(path, paint);
}
void _drawCharacterFromFont(
String character, DocumentData documentData, Canvas canvas) {
void _drawCharacterFromFont(String character, TextStyle textStyle,
DocumentData documentData, Canvas canvas) {
if (documentData.strokeOverFill) {
_drawCharacter(character, _fillPaint, canvas);
_drawCharacter(character, _strokePaint, canvas);
_drawCharacter(character, textStyle, _fillPaint, canvas);
_drawCharacter(character, textStyle, _strokePaint, canvas);
} else {
_drawCharacter(character, _strokePaint, canvas);
_drawCharacter(character, _fillPaint, canvas);
_drawCharacter(character, textStyle, _strokePaint, canvas);
_drawCharacter(character, textStyle, _fillPaint, canvas);
}
}
void _drawCharacter(String character, Paint paint, Canvas canvas) {
void _drawCharacter(
String character, TextStyle textStyle, Paint paint, Canvas canvas) {
if (paint.color.alpha == 0) {
return;
}
@ -388,18 +388,17 @@ class TextLayer extends BaseLayer {
return;
}
TextStyle textStyle;
if (paint.style == PaintingStyle.fill) {
textStyle = _textStyle.copyWith(foreground: paint);
textStyle = textStyle.copyWith(foreground: paint);
} else if (paint.style == PaintingStyle.stroke) {
textStyle = _textStyle.copyWith(background: paint);
textStyle = textStyle.copyWith(background: paint);
}
var painter = TextPainter(
text: TextSpan(text: character, style: textStyle),
textDirection: _textDirection,
);
painter.layout();
painter.paint(canvas, Offset(0, -_textStyle.fontSize));
painter.paint(canvas, Offset(0, -textStyle.fontSize));
}
List<ContentGroup> _getContentsForCharacter(FontCharacter character) {
@ -417,11 +416,6 @@ class TextLayer extends BaseLayer {
return contents;
}
//TODO(xha): use package:character to correctly iterate over visual glyphs
String _codePointToString(String text, int startIndex) {
return text[startIndex];
}
@override
void addValueCallback<T>(T property, LottieValueCallback<T> /*?*/ callback) {
super.addValueCallback(property, callback);

View File

@ -170,7 +170,8 @@ class AnimatableTransformParser {
static bool isScaleIdentity(AnimatableScaleValue scale) {
return scale == null ||
(scale.isStatic && scale.keyframes.first.startValue.equals(1.0, 1.0));
(scale.isStatic &&
scale.keyframes.first.startValue == Offset(1.0, 1.0));
}
static bool isSkewIdentity(AnimatableDoubleValue skew) {

View File

@ -14,11 +14,14 @@ Color colorParser(JsonReader reader, {double scale}) {
reader.endArray();
}
if (r <= 1 && g <= 1 && b <= 1 && a <= 1) {
if (r <= 1 && g <= 1 && b <= 1) {
r *= 255;
g *= 255;
b *= 255;
a *= 255;
// It appears as if sometimes, Telegram Lottie stickers are exported with rgb [0,1] and a [0,255].
// This shouldn't happen but we can gracefully handle it when it does.
// https://github.com/airbnb/lottie-android/issues/1478
if (a <= 1) a *= 255;
}
return Color.fromARGB(a.round(), r.round(), g.round(), b.round());

View File

@ -1,5 +1,4 @@
import '../composition.dart';
import '../logger.dart';
import '../model/content/content_model.dart';
import 'animatable_transform_parser.dart';
import 'circle_shape_parser.dart';
@ -91,7 +90,7 @@ class ContentModelParser {
model = RepeaterParser.parse(reader, composition);
break;
default:
logger.warning('Unknown shape type $type');
composition.addWarning('Unknown shape type $type');
}
while (reader.hasNext()) {

View File

@ -135,7 +135,8 @@ class LayerParser {
solidHeight = (reader.nextInt() * window.devicePixelRatio).round();
break;
case 7:
solidColor = MiscUtils.parseColor(reader.nextString());
solidColor = MiscUtils.parseColor(reader.nextString(),
warningCallback: composition.addWarning);
break;
case 8:
transform = AnimatableTransformParser.parse(reader, composition);

View File

@ -1,7 +1,6 @@
import 'dart:math';
import 'dart:ui';
import '../composition.dart';
import '../logger.dart';
import '../lottie_image_asset.dart';
import '../model/font.dart';
import '../model/font_character.dart';
@ -28,7 +27,8 @@ class LottieCompositionParser {
'markers' // 10
]);
static LottieComposition parse(JsonReader reader) {
static LottieComposition parse(
LottieComposition composition, JsonReader reader) {
var scale = window.devicePixelRatio;
var startFrame = 0.0;
var endFrame = 0.0;
@ -43,7 +43,6 @@ class LottieCompositionParser {
var markers = <Marker>[];
var characters = <int, FontCharacter>{};
var composition = internalCreateComposition();
reader.beginObject();
while (reader.hasNext()) {
switch (reader.selectName(_names)) {
@ -116,7 +115,7 @@ class LottieCompositionParser {
layerMap[layer.id] = layer;
if (imageCount > 4) {
logger.warning(
composition.addWarning(
'You have $imageCount images. Lottie should primarily be '
'used with shapes. If you are using Adobe Illustrator, convert the Illustrator layers'
' to shape layers.');

View File

@ -1,5 +1,4 @@
import '../composition.dart';
import '../logger.dart';
import '../model/animatable/animatable_integer_value.dart';
import '../model/animatable/animatable_shape_value.dart';
import '../model/content/mask.dart';
@ -37,7 +36,8 @@ class MaskParser {
maskMode = MaskMode.maskModeIntersect;
break;
default:
logger.warning('Unknown mask mode $modeName. Defaulting to Add.');
composition.addWarning(
'Unknown mask mode $modeName. Defaulting to Add.');
maskMode = MaskMode.maskModeAdd;
}
break;

Some files were not shown because too many files have changed in this diff Show More