136 lines
2.7 KiB
Go
136 lines
2.7 KiB
Go
package animation
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"fyne.io/fyne/v2"
|
|
)
|
|
|
|
// Runner is the main driver for animations package
|
|
type Runner struct {
|
|
animationMutex sync.RWMutex
|
|
animations []*anim
|
|
pendingAnimations []*anim
|
|
|
|
runnerStarted bool
|
|
}
|
|
|
|
// Start will register the passed application and initiate its ticking.
|
|
func (r *Runner) Start(a *fyne.Animation) {
|
|
r.animationMutex.Lock()
|
|
defer r.animationMutex.Unlock()
|
|
|
|
if !r.runnerStarted {
|
|
r.runnerStarted = true
|
|
r.animations = append(r.animations, newAnim(a))
|
|
r.runAnimations()
|
|
} else {
|
|
r.pendingAnimations = append(r.pendingAnimations, newAnim(a))
|
|
}
|
|
}
|
|
|
|
// Stop causes an animation to stop ticking (if it was still running) and removes it from the runner.
|
|
func (r *Runner) Stop(a *fyne.Animation) {
|
|
r.animationMutex.Lock()
|
|
defer r.animationMutex.Unlock()
|
|
|
|
newList := make([]*anim, 0, len(r.animations))
|
|
stopped := false
|
|
for _, item := range r.animations {
|
|
if item.a != a {
|
|
newList = append(newList, item)
|
|
} else {
|
|
item.setStopped()
|
|
stopped = true
|
|
}
|
|
}
|
|
r.animations = newList
|
|
if stopped {
|
|
return
|
|
}
|
|
|
|
newList = make([]*anim, 0, len(r.pendingAnimations))
|
|
for _, item := range r.pendingAnimations {
|
|
if item.a != a {
|
|
newList = append(newList, item)
|
|
} else {
|
|
item.setStopped()
|
|
}
|
|
}
|
|
r.pendingAnimations = newList
|
|
}
|
|
|
|
func (r *Runner) runAnimations() {
|
|
draw := time.NewTicker(time.Second / 60)
|
|
|
|
go func() {
|
|
for done := false; !done; {
|
|
<-draw.C
|
|
r.animationMutex.Lock()
|
|
oldList := r.animations
|
|
r.animationMutex.Unlock()
|
|
newList := make([]*anim, 0, len(oldList))
|
|
for _, a := range oldList {
|
|
if !a.isStopped() && r.tickAnimation(a) {
|
|
newList = append(newList, a)
|
|
}
|
|
}
|
|
r.animationMutex.Lock()
|
|
r.animations = append(newList, r.pendingAnimations...)
|
|
r.pendingAnimations = nil
|
|
done = len(r.animations) == 0
|
|
r.animationMutex.Unlock()
|
|
}
|
|
r.animationMutex.Lock()
|
|
r.runnerStarted = false
|
|
r.animationMutex.Unlock()
|
|
draw.Stop()
|
|
}()
|
|
}
|
|
|
|
// tickAnimation will process a frame of animation and return true if this should continue animating
|
|
func (r *Runner) tickAnimation(a *anim) bool {
|
|
if time.Now().After(a.end) {
|
|
if a.reverse {
|
|
a.a.Tick(0.0)
|
|
if a.repeatsLeft == 0 {
|
|
return false
|
|
}
|
|
a.reverse = false
|
|
} else {
|
|
a.a.Tick(1.0)
|
|
if a.a.AutoReverse {
|
|
a.reverse = true
|
|
}
|
|
}
|
|
if !a.reverse {
|
|
if a.repeatsLeft == 0 {
|
|
return false
|
|
}
|
|
if a.repeatsLeft > 0 {
|
|
a.repeatsLeft--
|
|
}
|
|
}
|
|
|
|
a.start = time.Now()
|
|
a.end = a.start.Add(a.a.Duration)
|
|
return true
|
|
}
|
|
|
|
delta := time.Since(a.start).Milliseconds()
|
|
|
|
val := float32(delta) / float32(a.total)
|
|
curve := a.a.Curve
|
|
if curve == nil {
|
|
curve = fyne.AnimationEaseInOut
|
|
}
|
|
if a.reverse {
|
|
a.a.Tick(curve(1 - val))
|
|
} else {
|
|
a.a.Tick(curve(val))
|
|
}
|
|
|
|
return true
|
|
}
|