CommunityToolkit / CommunityToolkit/Lottie-Windows

Fast, reliable Lottie file loader and player

Open
#556 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
717
Forks
76
PR merge metrics
No merged PRs in 30d

Description

Would like to share a fast and (appears) reliable Lottie file loader and player. One that uses SkiaSharp.Skottie.Animation:

```text
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? 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();
}
}
```

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.