diff --git a/README.md b/README.md index 5d7d623..e8cfe7f 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,8 @@ or in Alloy: Name | Parameter | Info | Platforms --- | --- | --- | -- | start() | | Starts an animation from the beginning | iOS, Android | -start(int from, int to) | Startframe, Endframe | Plays an animation from frame `from` to `to` | iOS, Android | +start(int from, int to) | Startframe, Endframe | Plays an animation from frame `from` to `to` | Android | +start({string animationName, bool loop}) | | Plays the rive animation | Android | pause() | | Pause an animation | iOS, Android | resume() | | Resumes an animation from the current point | iOS, Android | stop() | | Stops an animation an resets it | iOS, Android | @@ -56,14 +57,16 @@ setText(String layer, String text) | Layer, Text | Sets the text in the layer `l ## Properties -Name | Parameter | Info | Platforms ---- | --- | --- | --- | -progress | float | Get/set the current progress (in percentage) | iOS, Android | -loop | boolean | Get/set if the animation should loop | iOS, Android | -speed | float | Get/set the speed of the animation | iOS, Android | -duration | float | Get/set the duration of the animation | iOS, Android | -isPlaying | boolean | Get the animation status | iOS, Android | -newRenderingEngineEnabled | boolean | Use the core animation background rendering engine instead of the main thread | iOS | +Name | Parameter | Info | Framework | Platforms +--- | --- | --- | --- | --- | +progress | float | Get/set the current progress (in percentage) | Lottie | Android | +loop | boolean | Get/set if the animation should loop | Lottie | Android | +speed | float | Get/set the speed of the animation | Lottie | Android | +duration | float | Get/set the duration of the animation | Lottie | Android | +isPlaying | boolean | Get the animation status | Lottie | Android | +cache() | boolean | - | Lottie | iOS | +animationName | String or Array | Sets the Rive animation name | Rive | Android | +newRenderingEngineEnabled | boolean | Use the core animation background rendering engine instead of the main thread | Lottie | iOS | creation (tss) only: @@ -74,6 +77,7 @@ file | String | JSON file. Files go into app/assets/ (Alloy)
Android: Suppor jsonString | String | Pass a raw JSON string to the module | iOS | loop | boolean | loop the animation | iOS, Android | autoStart | boolean | automatically start the animation | iOS, Android | +animationType | int | One of the constants `ANIMATION_LOTTIE` or `ANIMATION_RIVE` | iOS, Android | ## Events @@ -83,6 +87,24 @@ Name | Info | Properties | Platforms complete | When the animation is done | Status:int, Loop:boolean | iOS, Android | update | Fires during the animation | Frame:int, status:int (ANIMATION_START, ANIMATION_END, ANIMATION_CANCEL, ANIMATION_REPEAT, ANIMATION_RUNNING) | Android | +## Constants + +Name | Platforms +--- | --- | +ANIMATION_RIVE | Android | +ANIMATION_LOTTIE | Android | + +used in setValueDelegateForKeyPath.type (iOS): + +Name | Platforms +--- | --- | +CALLBACK_COLOR_VALUE | iOS | +CALLBACK_NUMBER_VALUE | iOS | +CALLBACK_POINT_VALUE | iOS | +CALLBACK_SIZE_VALUE | iOS | +CALLBACK_PATH_VALUE | iOS | + + ## Example ```xml diff --git a/android/build.gradle b/android/build.gradle index f923d76..7a51fc9 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -3,5 +3,7 @@ repositories { } dependencies { + implementation "androidx.startup:startup-runtime:1.1.0" implementation 'com.airbnb.android:lottie:5.2.0' + implementation 'app.rive:rive-android:4.2.2' } diff --git a/android/manifest b/android/manifest index 21657cd..cb9fdaa 100644 --- a/android/manifest +++ b/android/manifest @@ -2,7 +2,7 @@ # this is your module manifest and used by Titanium # during compilation, packaging, distribution, etc. # -version: 4.3.0 +version: 4.4.0 apiversion: 4 architectures: arm64-v8a armeabi-v7a x86 x86_64 description: ti.animation diff --git a/android/platform/android/res/layout/layout_rive.xml b/android/platform/android/res/layout/layout_rive.xml new file mode 100644 index 0000000..e424fba --- /dev/null +++ b/android/platform/android/res/layout/layout_rive.xml @@ -0,0 +1,12 @@ + + + diff --git a/android/src/ti/animation/AnimationView.java b/android/src/ti/animation/AnimationView.java index 53a4f74..26dfcba 100644 --- a/android/src/ti/animation/AnimationView.java +++ b/android/src/ti/animation/AnimationView.java @@ -7,10 +7,18 @@ */ package ti.animation; +import static ti.animation.TiAnimationModule.ANIMATION_LOTTIE; +import static ti.animation.TiAnimationModule.ANIMATION_RIVE; + +import androidx.annotation.NonNull; +import androidx.startup.AppInitializer; + import android.animation.Animator; import android.animation.ValueAnimator; +import android.annotation.SuppressLint; import android.content.res.Resources; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.widget.ImageView.ScaleType; @@ -34,20 +42,39 @@ import org.appcelerator.titanium.view.TiUIView; import org.json.JSONObject; +import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +import app.rive.runtime.kotlin.RiveAnimationView; +import app.rive.runtime.kotlin.RiveArtboardRenderer; +import app.rive.runtime.kotlin.RiveInitializer; +import app.rive.runtime.kotlin.core.Alignment; +import app.rive.runtime.kotlin.core.Direction; +import app.rive.runtime.kotlin.core.Fit; +import app.rive.runtime.kotlin.core.LinearAnimationInstance; +import app.rive.runtime.kotlin.core.Loop; +import app.rive.runtime.kotlin.core.PlayableInstance; +import app.rive.runtime.kotlin.core.StateMachineInstance; public class AnimationView extends TiUIView implements LottieOnCompositionLoadedListener { private static final String LCAT = "AnimationViewProxy"; - - private final LottieAnimationView lottieView; private final TiViewProxy proxy; - private final TextDelegate delegate; + public int animationType = ANIMATION_LOTTIE; + ByteArrayOutputStream byteBuffer; + private LottieAnimationView lottieView; + private RiveAnimationView riveView; + private TextDelegate delegate; private KrollFunction callbackReady = null; private float initialDuration = 0; private ValueAnimator va = null; + @SuppressLint("ClickableViewAccessibility") AnimationView(TiViewProxy proxy) { super(proxy); @@ -57,29 +84,46 @@ public class AnimationView extends TiUIView implements LottieOnCompositionLoaded View viewWrapper; int resId_viewHolder; - int resId_lottie; + int resId_aniView; - resId_viewHolder = resources.getIdentifier("layout_lottie", "layout", packageName); - resId_lottie = resources.getIdentifier("animation_view", "id", packageName); + if (TiConvert.toInt(proxy.getProperty("animationType")) == ANIMATION_RIVE) { + AppInitializer.getInstance(TiApplication.getAppCurrentActivity()) + .initializeComponent(RiveInitializer.class); + resId_viewHolder = resources.getIdentifier("layout_rive", "layout", packageName); + } else { + resId_viewHolder = resources.getIdentifier("layout_lottie", "layout", packageName); + } + resId_aniView = resources.getIdentifier("animation_view", "id", packageName); LayoutInflater inflater = LayoutInflater.from(proxy.getActivity()); viewWrapper = inflater.inflate(resId_viewHolder, null); - lottieView = viewWrapper.findViewById(resId_lottie); - delegate = new TextDelegate(lottieView); - setNativeView(viewWrapper); - - setScaleMode(TiConvert.toString(proxy.getProperty("scaleMode"))); - lottieView.addAnimatorUpdateListener(new AnimatorUpdateListener()); - lottieView.addAnimatorListener(new AnimatorListener()); - lottieView.addLottieOnCompositionLoadedListener(this); - - if (TiConvert.toBoolean(proxy.getProperty("disableHardwareAcceleration"))) { - lottieView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); + if (TiConvert.toInt(proxy.getProperty("animationType")) == ANIMATION_RIVE) { + riveView = viewWrapper.findViewById(resId_aniView); + animationType = ANIMATION_RIVE; + MotionEvent mEvent = null; + riveView.setOnTouchListener((view, motionEvent) -> { + if (motionEvent.getAction() == MotionEvent.ACTION_UP) { + fireEvent("click", new KrollDict()); + } + return false; + }); } else { - lottieView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + lottieView = viewWrapper.findViewById(resId_aniView); + delegate = new TextDelegate(lottieView); + setScaleMode(TiConvert.toString(proxy.getProperty("scaleMode"))); + lottieView.addAnimatorUpdateListener(new AnimatorUpdateListener()); + lottieView.addAnimatorListener(new AnimatorListener()); + lottieView.addLottieOnCompositionLoadedListener(this); + + if (TiConvert.toBoolean(proxy.getProperty("disableHardwareAcceleration"))) { + lottieView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); + } else { + lottieView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + lottieView.enableMergePathsForKitKatAndAbove(TiConvert.toBoolean(proxy.getProperty("mergePath"))); } - lottieView.enableMergePathsForKitKatAndAbove(TiConvert.toBoolean(proxy.getProperty("mergePath"))); + setNativeView(viewWrapper); } @Override @@ -91,19 +135,23 @@ public void processProperties(KrollDict d) { } if (d.containsKey("disableHardwareAcceleration")) { if (d.getBoolean("disableHardwareAcceleration")) { - lottieView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); + if (lottieView != null) lottieView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); } else { - lottieView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + if (lottieView != null) lottieView.setLayerType(View.LAYER_TYPE_HARDWARE, null); } } if (d.containsKey("loop")) { - lottieView.setRepeatCount(d.getBoolean("loop") ? LottieDrawable.INFINITE : 0); + if (animationType == ANIMATION_LOTTIE) { + if (lottieView != null) + lottieView.setRepeatCount(d.getBoolean("loop") ? LottieDrawable.INFINITE : 0); + } } if (d.containsKey("ready")) { callbackReady = (KrollFunction) d.get("ready"); } if (d.containsKey("mergePath")) { - lottieView.enableMergePathsForKitKatAndAbove(d.getBoolean("mergePath")); + if (lottieView != null) + lottieView.enableMergePathsForKitKatAndAbove(d.getBoolean("mergePath")); } if (d.containsKey("progress")) { setProgress(Float.parseFloat(d.getString("progress"))); @@ -146,16 +194,20 @@ public void propertyChanged(String key, Object oldValue, Object newValue, KrollP private void setScaleMode(String smode) { // Set scale mode on view // + if (animationType == ANIMATION_RIVE) { + return; + } + switch (smode) { case "center": - lottieView.setScaleType(ScaleType.CENTER); + if (lottieView != null) lottieView.setScaleType(ScaleType.CENTER); break; case "centerCrop": - lottieView.setScaleType(ScaleType.CENTER_CROP); + if (lottieView != null) lottieView.setScaleType(ScaleType.CENTER_CROP); break; case "centerInside": default: - lottieView.setScaleType(ScaleType.CENTER_INSIDE); + if (lottieView != null) lottieView.setScaleType(ScaleType.CENTER_INSIDE); break; } } @@ -211,21 +263,76 @@ void loadFile(String f) { if (file.exists()) { try { InputStream stream = file.getInputStream(); - int size = stream.available(); - byte[] buffer = new byte[size]; - stream.read(buffer); - String json = new String(buffer, StandardCharsets.UTF_8); - parseJson(json); - lottieView.setAnimation(url.replaceAll("file:///android_asset/", "")); + + if (animationType == ANIMATION_RIVE) { + byteBuffer = new ByteArrayOutputStream(); + int bufferSize = 1024; + byte[] buffer = new byte[bufferSize]; + int len = 0; + while ((len = stream.read(buffer)) != -1) { + byteBuffer.write(buffer, 0, len); + } + + String artboard = null; + String ani = null; + String state = null; + Loop loop = Loop.ONESHOT; + + if (proxy.hasPropertyAndNotNull("artboardName")) { + artboard = TiConvert.toString(proxy.getProperty("artboardName")); + } + if (proxy.hasPropertyAndNotNull("animationName")) { + ani = TiConvert.toString(proxy.getProperty("animationName")); + } + if (proxy.hasPropertyAndNotNull("stateName")) { + state = TiConvert.toString(proxy.getProperty("stateName")); + } + if (proxy.hasPropertyAndNotNull("loop")) { + if (TiConvert.toInt(proxy.getProperty("loop")) == 0) { + loop = Loop.ONESHOT; + } else if (TiConvert.toInt(proxy.getProperty("loop")) == 1) { + loop = Loop.LOOP; + } + } + riveView.setRiveBytes(byteBuffer.toByteArray(), + artboard, + ani, + state, + true, + Fit.CONTAIN, + Alignment.CENTER, + loop); + } else { + int size = stream.available(); + byte[] buffer = new byte[size]; + stream.read(buffer); + String json = new String(buffer, StandardCharsets.UTF_8); + parseJson(json); + lottieView.setAnimation(url.replaceAll("file:///android_asset/", "")); + } } catch (Exception e) { - Log.e(LCAT, "Error opening file " + file.name()); + Log.e(LCAT, "Error opening file " + file.name() + "\n" + e.getMessage()); } } else { Log.e(LCAT, "File " + file.name() + " not found!"); } } + void startAnimation(Object data) { + if (animationType == ANIMATION_RIVE) { + HashMap kd = (HashMap) data; + String aniName = TiConvert.toString(kd.get("animationName")); + Boolean loop = TiConvert.toBoolean(kd.get("loop")); + Loop l = Loop.ONESHOT; + if (loop) { + l = Loop.LOOP; + } + riveView.play(aniName, l, Direction.AUTO, false, false); + } + } + void startAnimation(int startFrame, int endFrame) { + if (animationType == ANIMATION_RIVE) return; lottieView.cancelAnimation(); lottieView.setProgress(0f); proxy.setProperty("paused", false); @@ -294,28 +401,41 @@ public void onAnimationEnd(Animator animation) { void pauseAnimation() { proxy.setProperty("paused", true); - if (va != null) { - va.pause(); + if (animationType == ANIMATION_RIVE) { + riveView.pause(); } else { - lottieView.pauseAnimation(); + if (va != null) { + va.pause(); + } else { + lottieView.pauseAnimation(); + } } } void resumeAnimation() { proxy.setProperty("paused", false); - if (va != null) { - va.resume(); + if (animationType == ANIMATION_RIVE) { + riveView.play(Loop.AUTO, Direction.AUTO, true); } else { - lottieView.resumeAnimation(); + if (va != null) { + va.resume(); + } else { + lottieView.resumeAnimation(); + } } } void stopAnimation() { proxy.setProperty("paused", false); - if (va != null) { - va.cancel(); + + if (animationType == ANIMATION_RIVE) { + riveView.stop(); } else { - lottieView.cancelAnimation(); + if (va != null) { + va.cancel(); + } else { + lottieView.cancelAnimation(); + } } } @@ -324,21 +444,53 @@ void setText(String layer, String text) { } float getProgress() { + if (animationType == ANIMATION_RIVE) return 0; return lottieView.getProgress(); } void setProgress(float val) { + if (animationType == ANIMATION_RIVE) return; lottieView.setProgress(val); } int getFrame() { + if (animationType == ANIMATION_RIVE) return 0; return lottieView.getFrame(); } void setFrame(int val) { + if (animationType == ANIMATION_RIVE) return; lottieView.setFrame(val); } + public void setAnimationName(Object animationName) { + if (animationType == ANIMATION_RIVE) { + if (animationName instanceof String) { + riveView.play((String) animationName, Loop.ONESHOT, Direction.AUTO, false, true); + } else if (animationName instanceof Object[]) { + Object[] objVal = (Object[]) animationName; + List lst = new ArrayList(); + for (Object s : objVal) { + lst.add((String) s); + } + riveView.play(lst, Loop.ONESHOT, Direction.AUTO, false, true); + } + } + } + + public void resetAnimation() { + if (animationType == ANIMATION_RIVE) { + try { + riveView.stop(); + riveView.clearAnimation(); + riveView.reset(); + riveView.stop(); + } catch (Exception e) { + Log.e(LCAT, e.getMessage()); + } + } + } + protected class AnimatorUpdateListener implements ValueAnimator.AnimatorUpdateListener { public void onAnimationUpdate(ValueAnimator animation) { KrollDict event = new KrollDict(); @@ -375,4 +527,5 @@ public void onAnimationRepeat(Animator animation) { ((AnimationViewProxy) proxy).completeEvent(event); } } + } diff --git a/android/src/ti/animation/AnimationViewProxy.java b/android/src/ti/animation/AnimationViewProxy.java index b4affd2..bca3d4c 100644 --- a/android/src/ti/animation/AnimationViewProxy.java +++ b/android/src/ti/animation/AnimationViewProxy.java @@ -7,6 +7,8 @@ */ package ti.animation; +import static ti.animation.TiAnimationModule.ANIMATION_LOTTIE; + import android.app.Activity; import android.os.Message; @@ -23,7 +25,7 @@ @Kroll.proxy(creatableInModule = TiAnimationModule.class, propertyAccessors = {"file", "scaleMode", "disableHardwareAcceleration", "mergePath", "update", "autoStart", "loop", "assetFolder", "width", "height", "duration", "paused", "speed", - "startFrame", "endFrame", "json"}) + "startFrame", "endFrame", "json", "animationType", "artboardName", "stateName"}) public class AnimationViewProxy extends TiViewProxy { static final int MSG_START_ANIMATION = KrollProxy.MSG_LAST_ID + 101; @@ -31,6 +33,7 @@ public class AnimationViewProxy extends TiViewProxy { static final int MSG_PAUSE_ANIMATION = KrollProxy.MSG_LAST_ID + 103; static final int MSG_RESUME_ANIMATION = KrollProxy.MSG_LAST_ID + 104; static final int MSG_STOP_ANIMATION = KrollProxy.MSG_LAST_ID + 105; + static final int MSG_RESET_ANIMATION = KrollProxy.MSG_LAST_ID + 106; @Kroll.constant static final int ANIMATION_START = 1; @Kroll.constant @@ -57,6 +60,7 @@ public AnimationViewProxy() { defaultValues.put("duration", 0); defaultValues.put("file", ""); defaultValues.put("json", ""); + defaultValues.put("animationType", ANIMATION_LOTTIE); defaultValues.put("paused", false); } @@ -83,8 +87,12 @@ public boolean handleMessage(Message message) { } case MSG_START_ANIMATION: { result = (AsyncResult) message.obj; - int[] frames = (int[]) result.getArg(); - getView().startAnimation(frames[0], frames[1]); + if (result.getArg() instanceof Object) { + getView().startAnimation(result.getArg()); + } else { + int[] frames = (int[]) result.getArg(); + getView().startAnimation(frames[0], frames[1]); + } result.setResult(null); return true; } @@ -103,6 +111,11 @@ public boolean handleMessage(Message message) { result.setResult(null); return true; } + case MSG_RESET_ANIMATION: { + getView().resetAnimation(); + result.setResult(null); + return true; + } } return super.handleMessage(message); @@ -118,6 +131,19 @@ public void start(@Kroll.argument(optional = true) int fromFrame, @Kroll.argumen } } + @Kroll.method + public void start(@Kroll.argument(optional = true) Object data) { + if (data != null) { + if (TiApplication.isUIThread()) { + getView().startAnimation(data); + } else { + TiMessenger.sendBlockingMainMessage(getMainHandler().obtainMessage(MSG_START_ANIMATION), data); + } + } else { + start(-1, -1); + } + } + @Kroll.method public void resume() { if (TiApplication.isUIThread()) { @@ -127,6 +153,16 @@ public void resume() { } } + @Kroll.getProperty + public String getAnimationName() { + return ""; + } + + @Kroll.setProperty + public void setAnimationName(Object animationName) { + getView().setAnimationName(animationName); + } + @Kroll.method public void stop() { if (TiApplication.isUIThread()) { @@ -145,6 +181,15 @@ public void pause() { } } + @Kroll.method + public void reset() { + if (TiApplication.isUIThread()) { + getView().resetAnimation(); + } else { + TiMessenger.sendBlockingMainMessage(getMainHandler().obtainMessage(MSG_RESET_ANIMATION)); + } + } + // clang-format off @Kroll.setProperty @Kroll.method @@ -156,7 +201,6 @@ public void setText(String layer, String text) // clang-format off @Kroll.getProperty - @Kroll.method public float getProgress() // clang-format on { @@ -165,7 +209,6 @@ public float getProgress() // clang-format off @Kroll.setProperty - @Kroll.method public void setProgress(float val) // clang-format on { @@ -174,7 +217,6 @@ public void setProgress(float val) // clang-format off @Kroll.getProperty - @Kroll.method public int getFrame() // clang-format on { @@ -183,7 +225,6 @@ public int getFrame() // clang-format off @Kroll.setProperty - @Kroll.method public void setFrame(int val) // clang-format on { diff --git a/android/src/ti/animation/TiAnimationModule.java b/android/src/ti/animation/TiAnimationModule.java index 6c1187b..8d73ce0 100644 --- a/android/src/ti/animation/TiAnimationModule.java +++ b/android/src/ti/animation/TiAnimationModule.java @@ -8,9 +8,13 @@ package ti.animation; import org.appcelerator.kroll.KrollModule; +import org.appcelerator.kroll.annotations.Kroll; import org.appcelerator.kroll.annotations.Kroll.module; @module(name = "TiAnimation", id = "ti.animation") public class TiAnimationModule extends KrollModule { - + @Kroll.constant + static final int ANIMATION_LOTTIE = 0; + @Kroll.constant + static final int ANIMATION_RIVE = 1; } diff --git a/example/app.js b/example/app.js index e320cf8..54ac2a4 100644 --- a/example/app.js +++ b/example/app.js @@ -1,89 +1,156 @@ const TiAnimation = require('ti.animation'); +const isAndroid = (Ti.Platform.osname == 'android'); +var offset = 0; +var isDouble = false; +var isLoop = false; +var isDay = true; + +if (OS_IOS){ + TiAnimation.newRenderingEngineEnabled = true; +} -const isAndroid = (Ti.Platform.osname === 'android'); -let offset = 20; -let isDouble = false; +const win = Ti.UI.createWindow({ + backgroundColor: '#fff', + title: 'Titanium demo', + fullscreen: true +}); -TiAnimation.newRenderingEngineEnabled = true; // iOS-only: Use the CoreAnimation background rendering engine instead of the main thread +var buttonRow = Ti.UI.createView({ + height: Ti.UI.SIZE, + width: Ti.UI.SIZE, + layout: "horizontal", + top: 10 +}); +var buttonRow2 = Ti.UI.createView({ + height: Ti.UI.SIZE, + width: Ti.UI.SIZE, + layout: "horizontal", + top: 65 +}); +var buttonRow3 = Ti.UI.createView({ + height: Ti.UI.SIZE, + width: Ti.UI.SIZE, + layout: "horizontal", + bottom: 100 +}); -const win = Ti.UI.createWindow({ - backgroundColor: '#fff', - title: 'Ti.Animation Demo' - }), - lbl = Ti.UI.createLabel({ - bottom: 50, - color: '#000', - textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER, - font: { - fontSize: 12 - } - }), - view = TiAnimation.createAnimationView({ - file: 'sample_lottie_new_rendering_engine.json', - loop: false, - bottom: 200, - left: 30, - height: 120, - width: 120, - borderRadius: 60, - autoStart: false - }), - view2 = TiAnimation.createAnimationView({ - file: 'sample_lottie.json', - loop: true, - bottom: 200, - right: 30, - height: 120, - width: 120, - borderRadius: 60, - autoStart: true - }), - slider = Ti.UI.createSlider({ - value: 0, - min: 0, - max: 1, - bottom: 10, - width: 300 - }); + +function onClickLoop(e) { + isLoop = !isLoop; + if (isLoop) { + e.source.backgroundColor = "#afa"; + } else { + e.source.backgroundColor = "#fff"; + } +} + + +buttonRow.add([ + createButtonWithAction('Start', startAnimation), + createButtonWithAction('Pause', pauseAnimation), + createButtonWithAction('Resume', resumeAnimation), + createButtonWithAction('Stop/Reset', resetAnimation), +]); +buttonRow2.add([ + createButtonWithAction('Loop', onClickLoop), +]); +buttonRow3.add([ + createButtonWithAction('Double speed', doubleSpeed), + createButtonWithAction('Get frame', getFrame), +]); + +const lbl_title = Ti.UI.createLabel({ + bottom: 300, + color: "#000", + text: "Lottie:" +}); +const lbl = Ti.UI.createLabel({ + bottom: 40, + color: "#000", + textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER, + font: { + fontSize: 12 + } +}); +const view = TiAnimation.createAnimationView({ + file: '/sample_lottie.json', + loop: false, + bottom: 200, + left: 30, + height: 120, + width: 120, + borderRadius: 60, + autoStart: false +}); +const view2 = TiAnimation.createAnimationView({ + file: '/sample_lottie.json', + loop: false, + bottom: 200, + right: 30, + height: 120, + width: 120, + borderRadius: 60, + autoStart: true, + speed: 2, + loop: true +}); +const riveView = TiAnimation.createAnimationView({ + file: '/knight.riv', + top: 140, + height: 200, + width: 200, + animationType: TiAnimation.ANIMATION_RIVE, + //artboardName: "Main", + animationName: "idle", + //stateName: "idle" +}); +const slider = Ti.UI.createSlider({ + value: 0, + min: 0, + max: 1, + bottom: 10, + width: 300 +}); if (isAndroid) { - view.addEventListener('update', function (e) { - console.log('current frame: ' + e.frame); + view.addEventListener("update", function(e) { + console.log("current frame: " + e.frame) }); } -view.addEventListener('ready', function () { +view.addEventListener("ready", function() { var dur = (isAndroid) ? view.duration : (Math.floor(view.duration * 1000)); - lbl.text = 'Lottie: Duration: ' + dur + 'ms\n'; + lbl.text = "Lottie: Duration: " + dur + "ms\n"; }); -view.addEventListener('complete', function () { - console.log('view: complete'); +view.addEventListener("complete", function() { + console.log("view: complete"); }); -view2.addEventListener('complete', function () { - console.log('view2: complete'); +view2.addEventListener("complete", function() { + console.log("view2: complete"); }); slider.addEventListener('change', seekToProgress); win.add([ - view, view2, lbl, createButtonWithAction('Start animation', startAnimation), createButtonWithAction('Pause animation', pauseAnimation), - createButtonWithAction('Resume animation', resumeAnimation), createButtonWithAction('Double speed', doubleSpeed), createButtonWithAction('Get frame', getFrame), slider + view, view2, riveView, lbl_title, lbl, buttonRow, slider, buttonRow2, buttonRow3 ]); -if (isAndroid) { - win.open(); -} else { - var nav = Ti.UI.createNavigationWindow({ - window: win - }); - nav.open(); -} +win.open(); + +riveView.addEventListener("click", function() { + if (isDay) { + riveView.animationName = "day_night"; + } else { + riveView.animationName = "night_day"; + } + isDay = !isDay; +}) function doubleSpeed(e) { if (isDouble) { - e.source.title = 'Double speed'; + e.source.title = " Double speed "; view.speed = 1; } else { - e.source.title = 'Normal speed'; + e.source.title = "Normal speed"; view.speed = 2; } isDouble = !isDouble; @@ -94,36 +161,47 @@ function seekToProgress(e) { } function getFrame() { - console.log('Current frame view-animation: ' + view.frame); + console.log("Current frame view-animation: " + view.frame); } function createButtonWithAction(title, action) { var btn = Ti.UI.createButton({ title: title, - top: offset, - height: 30, - width: 200, + height: 50, borderRadius: 4, borderWidth: 1, - borderColor: '#000', - color: '#000', - backgroundColor: '#fff' + right: 2, + borderColor: "#000", + color: "#000", + backgroundColor: "#eee", + touchFeedback: true, + touchFeedbackColor: "#aaa" }); - btn.addEventListener('singletap', action); + btn.addEventListener('click', action); - offset += 32; return btn; } function startAnimation() { + view.loop = isLoop; view.start(); + riveView.start({ + animationName: "idle", + loop: isLoop + }); +} + +function resetAnimation() { + riveView.reset(); } function pauseAnimation() { view.pause(); + riveView.pause(); } function resumeAnimation() { view.resume(); + riveView.resume(); } diff --git a/example/knight.riv b/example/knight.riv new file mode 100644 index 0000000..97bb9a3 Binary files /dev/null and b/example/knight.riv differ diff --git a/example/vehicles.riv b/example/vehicles.riv new file mode 100644 index 0000000..5574a91 Binary files /dev/null and b/example/vehicles.riv differ