-
Notifications
You must be signed in to change notification settings - Fork 79
Open
Description
Would like to share a fast and (appears) reliable Lottie file loader and player. One that uses SkiaSharp.Skottie.Animation:
public class LottieData : MyData
{
public string Filename;
public SKPoint Pos = new SKPoint { };
public float Scale = 1.0f;
public int Width = 0;
public int Height = 0;
public SkiaSharp.Skottie.Animation? Animation;
public bool PlayForwards = true;
public int RepeatsCompleted = 0;
protected TimeSpan duration = new TimeSpan();
public TimeSpan Duration
{
get => duration;
set => duration = value;
}
protected TimeSpan progress = new TimeSpan();
public TimeSpan Progress
{
get => progress;
set => progress = value;
}
protected bool isComplete = false;
public bool IsComplete
{
get => isComplete;
set => isComplete = value;
}
int repeatCount = 0;
public int RepeatCount
{
get => repeatCount;
set => repeatCount = value;
}
protected SKLottieRepeatMode repeatMode = SKLottieRepeatMode.Restart;
public SKLottieRepeatMode RepeatMode
{
get => repeatMode;
set => repeatMode = value;
}
protected MyEventArgs myArgs;
public MyEventArgs MyArgs
{
get => myArgs;
set => myArgs = value;
}
public event EventHandler? AnimationFailed;
public event EventHandler? AnimationLoaded;
public event EventHandler<MyEventArgs>? AnimationCompleted;
public void OnAnimationCompleted(LottiePlayer lottiePlayer)
{
AnimationCompleted?.Invoke(lottiePlayer, myArgs);
}
}
public class LottiePlayer
{
public new LottieData MyData { get => (LottieData)base.MyData; set => base.MyData = value; }
public LottiePlayer() : base(ShapeType.Lottie)
{
MyData = new LottieData();
InitWidget();
}
public LottiePlayer(LottieData lottieData) : base(ShapeType.Lottie, lottieData)
{
InitWidget();
}
protected void InitWidget()
{
CreateLottie();
//CreateLottieView();
}
protected void CreateLottie()
{
LottieData lottieData = MyData;
if (lottieData != null)
{
if (!string.IsNullOrEmpty(lottieData.Filename))
{
string lottieStr = Utils.LoadStringResource(lottieData.Filename);
if (!string.IsNullOrEmpty(lottieStr))
{
//Stream stream = new MemoryStream(bytes);
var bytes = System.Text.Encoding.UTF8.GetBytes(lottieStr);
var data = SKData.CreateCopy(bytes);
if (SkiaSharp.Skottie.Animation.TryCreate(data.AsStream(), out lottieData.Animation))
{
}
}
}
ResetAnimation();
}
}
void ResetAnimation()
{
LottieData lottieData = MyData;
if (lottieData != null)
{
lottieData.PlayForwards = true;
lottieData.RepeatsCompleted = 0;
lottieData.Progress = TimeSpan.Zero;
lottieData.Duration = TimeSpan.Zero;
if (lottieData.Animation != null)
lottieData.Duration = lottieData.Animation.Duration;
}
}
public virtual void Update(TimeSpan deltaTime)
{
LottieData lottieData = MyData;
if (lottieData == null || !lottieData.IsEnabled) return;
// TODO: handle case where a repeat or revers cases the progress
// to either wrap or start the next round
if (!lottieData.PlayForwards)
deltaTime = -deltaTime;
var newProgress = lottieData.Progress + deltaTime;
if (newProgress > lottieData.Duration)
newProgress = lottieData.Duration;
if (newProgress < TimeSpan.Zero)
newProgress = TimeSpan.Zero;
lottieData.Progress = newProgress;
UpdateProgress(lottieData.Progress);
}
private void UpdateProgress(TimeSpan progress)
{
LottieData lottieData = MyData;
if (lottieData == null || lottieData.Animation == null)
{
if (lottieData != null)
lottieData.IsComplete = true;
return;
}
lottieData.Animation.SeekFrameTime(progress.TotalSeconds);
var repeatMode = lottieData.RepeatMode;
var duration = lottieData.Duration;
// have we reached the end of this run
var atStart = !lottieData.PlayForwards && progress <= TimeSpan.Zero;
var atEnd = lottieData.PlayForwards && progress >= duration;
var isFinishedRun = repeatMode == SKLottieRepeatMode.Restart ? atEnd : atStart;
// maybe the direction changed
var needsFlip = (atEnd && repeatMode == SKLottieRepeatMode.Reverse) || (atStart && repeatMode == SKLottieRepeatMode.Restart);
if (needsFlip)
{
// we need to reverse to finish the run
lottieData.PlayForwards = !lottieData.PlayForwards;
lottieData.IsComplete = false;
}
else
{
// make sure repeats are positive to make things easier
var totalRepeatCount = lottieData.RepeatCount;
if (totalRepeatCount < 0)
totalRepeatCount = int.MaxValue;
// infinite
var infinite = totalRepeatCount == int.MaxValue;
if (infinite)
lottieData.RepeatsCompleted = 0;
// if we are at the end and we are repeating, then repeat
if (isFinishedRun && lottieData.RepeatsCompleted < totalRepeatCount)
{
if (!infinite)
lottieData.RepeatsCompleted++;
isFinishedRun = false;
if (repeatMode == SKLottieRepeatMode.Restart)
lottieData.Progress = TimeSpan.Zero;
else if (repeatMode == SKLottieRepeatMode.Reverse)
lottieData.PlayForwards = !lottieData.PlayForwards;
}
lottieData.IsComplete = isFinishedRun && lottieData.RepeatsCompleted >= totalRepeatCount;
if (lottieData.IsComplete)
lottieData.OnAnimationCompleted(this);
}
}
public override void Draw(SKCanvas canvas, RectF dirtyRect)
{
canvas.Save();
base.Draw(canvas, dirtyRect);
LottieData lottieData = MyData;
if (lottieData != null && lottieData.IsVisible)
{
int width = lottieData.Width;
int height = lottieData.Height;
// set transforms
canvas.Translate(lottieData.Pos);
canvas.Scale(lottieData.Scale);
SKRect rect = new SKRect(0, 0, width, height);
if (lottieData.Animation != null)
{
lottieData.Animation.Render(canvas, rect);
}
}
canvas.Restore();
}
}
Metadata
Metadata
Assignees
Labels
No labels