2 * Copyright(c) 2019 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using global::System.Runtime.InteropServices;
20 using System.ComponentModel;
21 using System.Collections.Generic;
23 namespace Tizen.NUI.BaseComponents
26 using tlog = Tizen.Log;
30 /// LottieAnimationView renders an animated vector image (Lottie file).
32 /// <since_tizen> 7 </since_tizen>
33 public class LottieAnimationView : ImageView
35 #region Constructor, Distructor, Dispose
37 /// LottieAnimationView constructor
39 /// <param name="scale">The factor of scaling image, default : 1.0f</param>
40 /// <param name="shown">false : not displayed (hidden), true : displayed (shown), default : true</param>
42 /// If the shown parameter is false, the animation is not visible even if the LottieAnimationView instance is created.
46 /// LottieAnimationView myLottie = new LottieAnimationView();
47 /// LottieAnimationView myLottie2 = new LottieAnimationView(2.0f);
48 /// LottieAnimationView myLottie3 = new LottieAnimationView(1.0f, false);
51 /// <since_tizen> 7 </since_tizen>
52 public LottieAnimationView(float scale = 1.0f, bool shown = true) : base()
54 tlog.Fatal(tag, $"< constructor GetId={GetId()} >");
55 currentStates.url = "";
56 currentStates.frame = -1;
57 currentStates.loopCount = 1;
58 currentStates.loopMode = LoopingModeType.Restart;
59 currentStates.stopEndAction = StopBehaviorType.CurrentFrame;
60 currentStates.framePlayRangeMin = -1;
61 currentStates.framePlayRangeMax = -1;
62 currentStates.changed = false;
63 currentStates.totalFrame = -1;
64 currentStates.scale = scale;
69 /// Dispose(DisposeTypes type)
71 /// <param name="type"></param>
72 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
73 [EditorBrowsable(EditorBrowsableState.Never)]
74 protected override void Dispose(DisposeTypes type)
81 tlog.Fatal(tag, $"<[{GetId()}] type={type}");
83 //Release your own unmanaged resources here.
84 //You should not access any managed member here except static instance.
85 //because the execution order of Finalizes is non-deterministic.
87 //disconnect event signal
88 if (finishedEventHandler != null && visualEventSignalCallback != null)
90 VisualEventSignal().Disconnect(visualEventSignalCallback);
91 finishedEventHandler = null;
92 tlog.Fatal(tag, $"disconnect event signal");
96 tlog.Fatal(tag, $"[{GetId()}]>");
98 #endregion Constructor, Distructor, Dispose
103 /// Set or Get resource URL of Lottie file.
105 /// <since_tizen> 7 </since_tizen>
110 string ret = (value == null ? "" : value);
111 currentStates.url = ret;
112 currentStates.changed = true;
114 tlog.Fatal(tag, $"<[{GetId()}]SET url={currentStates.url}");
116 PropertyMap map = new PropertyMap();
117 map.Add(Visual.Property.Type, new PropertyValue((int)DevelVisual.Type.AnimatedVectorImage))
118 .Add(ImageVisualProperty.URL, new PropertyValue(currentStates.url))
119 .Add(ImageVisualProperty.LoopCount, new PropertyValue(currentStates.loopCount))
120 .Add(ImageVisualProperty.StopBehavior, new PropertyValue((int)currentStates.stopEndAction))
121 .Add(ImageVisualProperty.LoopingMode, new PropertyValue((int)currentStates.loopMode));
124 currentStates.contentInfo = null;
126 if (currentStates.scale != 1.0f)
128 Scale = new Vector3(currentStates.scale, currentStates.scale, 0.0f);
130 tlog.Fatal(tag, $"<[{GetId()}]>");
134 string ret = currentStates.url;
135 tlog.Fatal(tag, $"<[{GetId()}] GET");
137 PropertyMap map = Image;
140 PropertyValue val = map.Find(ImageVisualProperty.URL);
143 if (val.Get(out ret))
145 tlog.Fatal(tag, $"gotten url={ret} >");
150 Tizen.Log.Error(tag, $" [ERROR][{GetId()}](LottieAnimationView) Fail to get URL from dali >");
156 /// Gets the playing state
158 /// <since_tizen> 7 </since_tizen>
159 public PlayStateType PlayState
163 tlog.Fatal(tag, $"< Get!");
164 PropertyMap map = base.Image;
168 PropertyValue val = map.Find(ImageVisualProperty.PlayState);
171 if (val.Get(out ret))
173 currentStates.playState = (PlayStateType)ret;
174 tlog.Fatal(tag, $"gotten play state={ret} >");
180 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}]Fail to get PlayState from dali currentStates.playState={currentStates.playState}>");
182 return currentStates.playState;
187 /// Get the number of total frames
189 /// <since_tizen> 7 </since_tizen>
190 public int TotalFrame
195 PropertyMap map = Image;
198 PropertyValue val = map.Find(ImageVisualProperty.TotalFrameNumber);
201 if (val.Get(out ret))
203 //tlog.Fatal(tag, $"TotalFrameNumber get! ret={ret}");
204 currentStates.totalFrame = ret;
209 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get TotalFrameNumber from dali>");
215 /// Set or get the current frame. When setting a specific frame, it is displayed as a still image.
218 /// Gets the value set by a user. If the setting value is out-ranged, it is reset as a minimum frame or a maximum frame.
221 /// We assume that the animation in myLottie.json file has 100 frames originally. If so, its frame index will be 0 - 99.
223 /// LottieAnimationView myLottie = new LottieAnimationView();
224 /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
225 /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
226 /// myLottie.CurrentFrame = 200; //display 99 frame
227 /// myLottie.SetMinMaxFrame(10, 20);
228 /// myLottie.CurrentFrame = 15; //display 15 frame
229 /// myLottie.CurrentFrame = 50; //display 20 frame, because the MinMax is set (10,20) above
232 /// <since_tizen> 7 </since_tizen>
233 public int CurrentFrame
237 currentStates.frame = value;
238 tlog.Fatal(tag, $"<[{GetId()}]SET frame={currentStates.frame}>");
239 DoAction(ImageView.Property.IMAGE, (int)actionType.jumpTo, new PropertyValue(currentStates.frame));
244 PropertyMap map = Image;
247 PropertyValue val = map.Find(ImageVisualProperty.CurrentFrameNumber);
250 if (val.Get(out ret))
252 //tlog.Fatal(tag, $"CurrentFrameNumber get! val={ret}");
257 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get CurrentFrameNumber from dali!! ret={ret}>");
263 /// Sets or gets the looping mode of Lottie animation.
265 /// <since_tizen> 7 </since_tizen>
266 public LoopingModeType LoopingMode
270 currentStates.loopMode = (LoopingModeType)value;
271 currentStates.changed = true;
273 tlog.Fatal(tag, $"<[{GetId()}] SET loopMode={currentStates.loopMode}>");
274 PropertyMap map = new PropertyMap();
275 map.Add(ImageVisualProperty.LoopingMode, new PropertyValue((int)currentStates.loopMode));
276 DoAction(ImageView.Property.IMAGE, (int)actionType.updateProperty, new PropertyValue(map));
280 //tlog.Fatal(tag, $"LoopMode get!");
281 PropertyMap map = base.Image;
285 PropertyValue val = map.Find(ImageVisualProperty.LoopingMode);
288 if (val.Get(out ret))
290 //tlog.Fatal(tag, $"gotten LoopMode={ret}");
291 if (ret != (int)currentStates.loopMode && ret > 0)
293 tlog.Fatal(tag, $" [ERROR][{GetId()}](LottieAnimationView) different LoopMode! gotten={ret}, loopMode={currentStates.loopMode}");
295 currentStates.loopMode = (LoopingModeType)ret;
296 return (LoopingModeType)ret;
300 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get loopMode from dali>");
301 return currentStates.loopMode;
306 /// Sets or gets the loop count.
309 /// The minus value means the infinite loop count.
313 /// LottieAnimationView myLottie = new LottieAnimationView();
314 /// myLottie.URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "myLottie.json"; //myLottie.json's total frame is 100 (frame: 0~99)
315 /// NUIApplication.GetDefaultWindow().GetDefaultLayer().Add(myLottie);
316 /// myLottie.LoopCount = -1; //infinite loop
318 /// myLottie.Stop(); //it plays continuously unless Stop() is called
319 /// myLottie.LoopCount = 2;
320 /// myLottie.Play(); //it plays only 2 times and stops automatically
323 /// <since_tizen> 7 </since_tizen>
328 currentStates.changed = true;
329 currentStates.loopCount = value;
330 tlog.Fatal(tag, $"<[{GetId()}]SET currentStates.loopCount={currentStates.loopCount}>");
331 PropertyMap map = new PropertyMap();
332 map.Add(ImageVisualProperty.LoopCount, new PropertyValue(currentStates.loopCount));
333 DoAction(ImageView.Property.IMAGE, (int)actionType.updateProperty, new PropertyValue(map));
337 //tlog.Fatal(tag, $"LoopCount get!");
338 PropertyMap map = base.Image;
342 PropertyValue val = map.Find(ImageVisualProperty.LoopCount);
345 if (val.Get(out ret))
347 //tlog.Fatal(tag, $"gotten loop count={ret}");
348 if (ret != currentStates.loopCount && ret > 0)
350 tlog.Fatal(tag, $"<[ERROR][{GetId()}](LottieAnimationView) different loop count! gotten={ret}, loopCount={currentStates.loopCount}>");
352 currentStates.loopCount = ret;
353 return currentStates.loopCount;
357 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get LoopCount from dali currentStates.loopCount={currentStates.loopCount}>");
358 return currentStates.loopCount;
363 /// Sets or gets the stop behavior.
365 /// <since_tizen> 7 </since_tizen>
366 public StopBehaviorType StopBehavior
370 currentStates.stopEndAction = (StopBehaviorType)value;
371 currentStates.changed = true;
373 tlog.Fatal(tag, $"<[{GetId()}]SET val={currentStates.stopEndAction}>");
374 PropertyMap map = new PropertyMap();
375 map.Add(ImageVisualProperty.StopBehavior, new PropertyValue((int)currentStates.stopEndAction));
376 DoAction(ImageView.Property.IMAGE, (int)actionType.updateProperty, new PropertyValue(map));
380 //tlog.Fatal(tag, $"StopBehavior get!");
381 PropertyMap map = base.Image;
385 PropertyValue val = map.Find(ImageVisualProperty.StopBehavior);
388 if (val.Get(out ret))
390 //tlog.Fatal(tag, $"gotten StopBehavior={ret}");
391 if (ret != (int)currentStates.stopEndAction)
393 tlog.Fatal(tag, $"<[ERROR][{GetId()}](LottieAnimationView) different StopBehavior! gotten={ret}, StopBehavior={currentStates.stopEndAction}>");
395 currentStates.stopEndAction = (StopBehaviorType)ret;
396 return (StopBehaviorType)ret;
400 Tizen.Log.Error(tag, $"<[ERROR][{GetId()}](LottieAnimationView) Fail to get StopBehavior from dali>");
401 return currentStates.stopEndAction;
409 /// Set the minimum and the maximum frame.
411 /// <param name="minFrame">minimum frame</param>
412 /// <param name="maxFrame">maximum frame</param>
413 /// <since_tizen> 7 </since_tizen>
414 public void SetMinMaxFrame(int minFrame, int maxFrame)
416 tlog.Fatal(tag, $"< [{GetId()}] SetPlayRange({minFrame}, {maxFrame})");
418 currentStates.changed = true;
419 currentStates.framePlayRangeMin = minFrame;
420 currentStates.framePlayRangeMax = maxFrame;
422 PropertyArray array = new PropertyArray();
423 array.PushBack(new PropertyValue(currentStates.framePlayRangeMin));
424 array.PushBack(new PropertyValue(currentStates.framePlayRangeMax));
426 PropertyMap map = new PropertyMap();
427 map.Add(ImageVisualProperty.PlayRange, new PropertyValue(array));
428 DoAction(ImageView.Property.IMAGE, (int)actionType.updateProperty, new PropertyValue(map));
429 tlog.Fatal(tag, $" [{GetId()}] currentStates.min:({currentStates.framePlayRangeMin}, max:{currentStates.framePlayRangeMax})>");
435 /// <since_tizen> 7 </since_tizen>
436 public new void Play()
438 tlog.Fatal(tag, $"<[{GetId()}] Play()");
441 tlog.Fatal(tag, $"[{GetId()}]>");
447 /// <since_tizen> 7 </since_tizen>
448 public new void Pause()
450 tlog.Fatal(tag, $"<[{GetId()}] Pause()>");
453 tlog.Fatal(tag, $"[{GetId()}]>");
459 /// <since_tizen> 7 </since_tizen>
460 public new void Stop()
462 tlog.Fatal(tag, $"<[{GetId()}] Stop()");
465 tlog.Fatal(tag, $"[{GetId()}]>");
469 /// Get the list of layers' information such as the start frame and the end frame in the Lottie file.
471 /// <returns>List of Tuple (string of layer name, integer of start frame, integer of end frame)</returns>
472 /// <since_tizen> 7 </since_tizen>
473 public List<Tuple<string, int, int>> GetContentInfo()
475 tlog.Fatal(tag, $"<");
476 if (currentStates.contentInfo != null)
478 return currentStates.contentInfo;
481 PropertyMap imageMap = base.Image;
482 PropertyMap contentMap = new PropertyMap();
483 if (imageMap != null)
485 PropertyValue val = imageMap.Find(ImageVisualProperty.ContentInfo);
488 if (val.Get(contentMap))
490 currentStates.contentInfo = new List<Tuple<string, int, int>>();
491 for (uint i = 0; i < contentMap.Count(); i++)
493 string key = contentMap.GetKeyAt(i).StringKey;
494 PropertyArray arr = new PropertyArray();
495 contentMap.GetValue(i).Get(arr);
498 int startFrame, endFrame;
499 arr.GetElementAt(0).Get(out startFrame);
500 arr.GetElementAt(1).Get(out endFrame);
502 tlog.Fatal(tag, $"[{i}] layer name={key}, startFrame={startFrame}, endFrame={endFrame}");
504 Tuple<string, int, int> item = new Tuple<string, int, int>(key, startFrame, endFrame);
506 currentStates.contentInfo?.Add(item);
512 tlog.Fatal(tag, $">");
513 return currentStates.contentInfo;
517 /// A marker has its start frame and end frame.
518 /// Animation will play between the start frame and the end frame of the marker if one marker is specified.
519 /// Or animation will play between the start frame of the first marker and the end frame of the second marker if two markers are specified. *
521 /// <param name="marker1">First marker</param>
522 /// <param name="marker2">Second marker</param>
523 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
524 [EditorBrowsable(EditorBrowsableState.Never)]
525 public void SetMinMaxFrameByMarker(string marker1, string marker2 = null)
527 tlog.Fatal(tag, $"< [{GetId()}] SetMinMaxFrameByMarker({marker1}, {marker2})");
529 currentStates.changed = true;
530 currentStates.mark1 = marker1;
531 currentStates.mark2 = marker2;
533 PropertyArray array = new PropertyArray();
534 array.PushBack(new PropertyValue(currentStates.mark1));
537 array.PushBack(new PropertyValue(currentStates.mark2));
540 PropertyMap map = new PropertyMap();
541 map.Add(ImageVisualProperty.PlayRange, new PropertyValue(array));
542 DoAction(ImageView.Property.IMAGE, (int)actionType.updateProperty, new PropertyValue(map));
543 tlog.Fatal(tag, $" [{GetId()}] currentStates.mark1:{currentStates.mark1}, mark2:{currentStates.mark2} >");
549 /// <returns>Tuple of Min and Max frames</returns>
550 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
551 [EditorBrowsable(EditorBrowsableState.Never)]
552 public Tuple<int, int> GetMinMaxFrame()
554 tlog.Fatal(tag, $"< [{GetId()}] GetMinMaxFrame()! total frame={currentStates.totalFrame}");
556 PropertyMap map = Image;
559 PropertyValue val = map.Find(ImageVisualProperty.PlayRange);
562 PropertyArray array = new PropertyArray();
565 uint cnt = array.Count();
566 int item1 = -1, item2 = -1;
567 for (uint i = 0; i < cnt; i++)
569 PropertyValue v = array.GetElementAt(i);
571 if (v.Get(out intRet))
573 tlog.Fatal(tag, $"Got play range of string [{i}]: {intRet}");
585 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#1");
588 tlog.Fatal(tag, $" [{GetId()}] GetMinMaxFrame(min:{item1}, max:{item2})! >");
589 return new Tuple<int, int>(item1, item2);
593 Tizen.Log.Error("NUI", $"[ERR] fail to get play range from dali! case#2");
594 return new Tuple<int, int>(-1, -1);
599 #region Event, Enum, Struct, ETC
601 /// Animation finished event.
603 /// <since_tizen> 7 </since_tizen>
604 public event EventHandler Finished
608 if (finishedEventHandler == null)
610 tlog.Fatal(tag, $"<[{GetId()}] Finished eventhandler added>");
611 visualEventSignalCallback = onVisualEventSignal;
612 VisualEventSignal().Connect(visualEventSignalCallback);
614 finishedEventHandler += value;
618 tlog.Fatal(tag, $"<[{GetId()}] Finished eventhandler removed>");
619 finishedEventHandler -= value;
620 if (finishedEventHandler == null && visualEventSignalCallback != null)
622 VisualEventSignal().Disconnect(visualEventSignalCallback);
628 /// Enumeration for what state the vector animation is in
630 /// <since_tizen> 7 </since_tizen>
631 public enum PlayStateType
636 /// <since_tizen> 7 </since_tizen>
639 /// Vector Animation has stopped
641 /// <since_tizen> 7 </since_tizen>
644 /// The vector animation is playing
646 /// <since_tizen> 7 </since_tizen>
649 /// The vector animation is paused
651 /// <since_tizen> 7 </since_tizen>
656 /// Enumeration for what to do when the animation is stopped.
658 /// <since_tizen> 7 </since_tizen>
659 public enum StopBehaviorType
662 /// When the animation is stopped, the current frame is shown.
664 /// <since_tizen> 7 </since_tizen>
667 /// When the animation is stopped, the min frame (first frame) is shown.
669 /// <since_tizen> 7 </since_tizen>
672 /// When the animation is stopped, the max frame (last frame) is shown.
674 /// <since_tizen> 7 </since_tizen>
679 /// Enumeration for what looping mode is in.
681 /// <since_tizen> 7 </since_tizen>
682 public enum LoopingModeType
685 /// When the animation arrives at the end in looping mode, the animation restarts from the beginning.
687 /// <since_tizen> 7 </since_tizen>
690 /// When the animation arrives at the end in looping mode, the animation reverses direction and runs backwards again.
692 /// <since_tizen> 7 </since_tizen>
695 #endregion Event, Enum, Struct, ETC
699 internal class VisualEventSignalArgs : EventArgs
701 public int VisualIndex
713 internal event EventHandler<VisualEventSignalArgs> VisualEvent
717 if (visualEventSignalHandler == null)
719 visualEventSignalCallback = onVisualEventSignal;
720 VisualEventSignal().Connect(visualEventSignalCallback);
722 visualEventSignalHandler += value;
726 visualEventSignalHandler -= value;
727 if (visualEventSignalHandler == null && VisualEventSignal().Empty() == false)
729 VisualEventSignal().Disconnect(visualEventSignalCallback);
734 internal void EmitVisualEventSignal(int visualIndex, int signalId)
736 VisualEventSignal().Emit(this, visualIndex, signalId);
739 internal VisualEventSignal VisualEventSignal()
741 VisualEventSignal ret = new VisualEventSignal(Interop.VisualEventSignal.NewWithView(View.getCPtr(this)), false);
742 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
749 private struct states
753 internal int loopCount;
754 internal LoopingModeType loopMode;
755 internal StopBehaviorType stopEndAction;
756 internal int framePlayRangeMin;
757 internal int framePlayRangeMax;
758 internal bool changed;
759 internal int totalFrame;
760 internal float scale;
761 internal PlayStateType playState;
762 internal List<Tuple<string, int, int>> contentInfo;
763 internal string mark1, mark2;
765 private states currentStates;
767 private enum actionType
776 private struct DevelVisual
780 AnimatedGradient = Visual.Type.AnimatedImage + 1,
781 AnimatedVectorImage = Visual.Type.AnimatedImage + 2,
785 private const string tag = "NUITEST";
786 private event EventHandler finishedEventHandler;
788 private void OnFinished()
790 tlog.Fatal(tag, $"<[{GetId()}] OnFinished()>");
791 finishedEventHandler?.Invoke(this, null);
794 private void onVisualEventSignal(IntPtr targetView, int visualIndex, int signalId)
798 if (targetView != IntPtr.Zero)
800 View v = Registry.GetManagedBaseHandleFromNativePtr(targetView) as View;
803 tlog.Fatal(tag, $"targetView is not null! name={v.Name}");
807 tlog.Fatal(tag, $"target is something created from dali");
810 VisualEventSignalArgs e = new VisualEventSignalArgs();
811 e.VisualIndex = visualIndex;
812 e.SignalId = signalId;
813 visualEventSignalHandler?.Invoke(this, e);
815 tlog.Fatal(tag, $"<[{GetId()}] onVisualEventSignal()! visualIndex={visualIndex}, signalId={signalId}>");
818 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
819 private delegate void VisualEventSignalCallbackType(IntPtr targetView, int visualIndex, int signalId);
821 private VisualEventSignalCallbackType visualEventSignalCallback;
822 private EventHandler<VisualEventSignalArgs> visualEventSignalHandler;
824 private void debugPrint()
826 tlog.Fatal(tag, $"===================================");
827 tlog.Fatal(tag, $"<[{GetId()}] get currentStates : url={currentStates.url}, loopCount={currentStates.loopCount}, framePlayRangeMin/Max({currentStates.framePlayRangeMin},{currentStates.framePlayRangeMax}) ");
828 tlog.Fatal(tag, $" get from Property : StopBehavior={StopBehavior}, LoopMode={LoopingMode}, LoopCount={LoopCount}, PlayState={PlayState} >");
829 tlog.Fatal(tag, $"===================================");
835 /// A class containing frame informations for a LottieAnimationView.
837 [EditorBrowsable(EditorBrowsableState.Never)]
838 public class LottieFrameInfo : ICloneable
841 /// Creates a new instance with a playing range.
843 [EditorBrowsable(EditorBrowsableState.Never)]
844 public LottieFrameInfo(int startFrame, int endFrame)
846 StartFrame = startFrame;
851 /// Creates a new instance with a still image frame.
853 [EditorBrowsable(EditorBrowsableState.Never)]
854 public LottieFrameInfo(int stillImageFrame) : this(stillImageFrame, stillImageFrame)
859 /// Create a new instance from a pair notation.
861 [EditorBrowsable(EditorBrowsableState.Never)]
862 public static implicit operator LottieFrameInfo((int, int) pair)
864 return new LottieFrameInfo(pair.Item1, pair.Item2);
868 /// Create a new instance from an int value.
870 [EditorBrowsable(EditorBrowsableState.Never)]
871 public static implicit operator LottieFrameInfo(int stillImageFrame)
873 return new LottieFrameInfo(stillImageFrame);
877 /// Create a new instance from string.
878 /// Possible input : "0, 10", "10"
880 [EditorBrowsable(EditorBrowsableState.Never)]
881 public static implicit operator LottieFrameInfo(string pair)
888 string[] parts = pair.Split(',');
889 if (parts.Length == 1)
891 return new LottieFrameInfo(Int32.Parse(parts[0].Trim()));
893 else if (parts.Length == 2)
895 return new LottieFrameInfo(Int32.Parse(parts[0].Trim()), Int32.Parse(parts[1].Trim()));
898 throw new InvalidCastException($"Can not convert string {pair} to LottieFrameInfo");
902 /// The start frame of the lottie animation.
904 [EditorBrowsable(EditorBrowsableState.Never)]
905 public int StartFrame { get; }
908 /// The end frame of the lottie animation.
910 [EditorBrowsable(EditorBrowsableState.Never)]
911 public int EndFrame { get; }
914 /// Create LottieFrameInfo struct with animation range information
916 [EditorBrowsable(EditorBrowsableState.Never)]
917 public static LottieFrameInfo CreateAnimationRange(int startFrame, int endFrame)
919 return new LottieFrameInfo(startFrame, endFrame);
923 /// Create LottieFrameInfo struct with still image information
925 [EditorBrowsable(EditorBrowsableState.Never)]
926 public static LottieFrameInfo CreateStillImage(int stillImageFrame)
928 return new LottieFrameInfo(stillImageFrame, stillImageFrame);
933 /// Whether this LottieFrameInfo represents one frame or more.
935 [EditorBrowsable(EditorBrowsableState.Never)]
936 public bool IsStillImage()
938 return StartFrame == EndFrame;
943 /// Play specified LottieAnimationView with this frame information.
945 /// <param name="lottieView">The target LottieAnimationView to play.</param>
946 /// <param name="noPlay">Whether go direct to the EndFrame. It is false by default.</param>
947 [EditorBrowsable(EditorBrowsableState.Never)]
948 public void Show(LottieAnimationView lottieView, bool noPlay = false)
950 if (!BeReadyToShow(lottieView))
955 lottieView.SetMinMaxFrame(StartFrame, Math.Min(EndFrame, lottieView.TotalFrame - 1));
957 if (IsStillImage() || noPlay)
959 lottieView.CurrentFrame = EndFrame;
963 lottieView.CurrentFrame = StartFrame;
969 [EditorBrowsable(EditorBrowsableState.Never)]
970 public object Clone() => new LottieFrameInfo(StartFrame, EndFrame);
972 private bool BeReadyToShow(LottieAnimationView lottieView)
974 // Validate input lottieView
975 if (null== lottieView || lottieView.PlayState == LottieAnimationView.PlayStateType.Invalid)
980 // Stop if it was playing
981 if (lottieView.PlayState == LottieAnimationView.PlayStateType.Playing)