cogentcore / core

A free and open source framework for building powerful, fast, elegant 2D and 3D apps that run on macOS, Windows, Linux, iOS, Android, and the web with a single Go codebase, allowing you to Code Once, Run Everywhere.
http://cogentcore.org/core
BSD 3-Clause "New" or "Revised" License
1.74k stars 82 forks source link

Add transitions and animations #512

Open kkoreilly opened 1 year ago

kkoreilly commented 1 year ago

For example, to transition the background color of a button on hover.

rcoreilly commented 1 year ago

some key logic issues:

kkoreilly commented 10 months ago

I implemented a basic version of transitions previously, but it still needs to be cleaned up. Also, we need to decide when, if ever, we want to use basic property transitions, and we still need to support more general animations.

kkoreilly commented 9 months ago

Basic property transitions only make things look slow and do not improve the end-user experience. The only transitions that it makes sense to implement are real animations, which are more difficult and not a critical thing that we need to implement for v1. Therefore, we will work on implementing this eventually, but it will not be part of the v1 milestone.

kkoreilly commented 9 months ago

For reference, this is what the previous transition API was:

// todo: store a map of [value]ticker -- close the ticker if existing.

// Transition transitions the given pointer value (typically a pointer to a style property
// that is a number, unit value, or color) to the given destination value (typically not a pointer)
// over the given duration, using the given timing function. The timing function takes the proportion
// of the time of the transition that has taken place (0-1) and returns the total proportion of the
// difference between the starting and ending value that should be applied at that point in time (0-1).
// For example, a standard linear timing function would just return the value it gets. The transition runs
// at the FPS of the window, and it tells the widget to render on every FPS tick. If the starting and ending
// value are the same, it does nothing. If not, it runs the transition in a separate goroutine.
func (wb *WidgetBase) Transition(value any, to any, duration time.Duration, timingFunc func(prop float32) float32) {
    vn := grr.Log1(laser.ToFloat32(value))
    tn := grr.Log1(laser.ToFloat32(to))
    diff := tn - vn
    if diff == 0 {
        return
    }
    rate := time.Second / 60
    propPer := float32(rate) / float32(duration)
    tick := time.NewTicker(rate)
    go func() {
        for i := 0; i < int(duration/rate); i++ {
            <-tick.C
            // fmt.Println("transitioning", wb)
            prop := float32(i) * propPer
            inc := timingFunc(prop)
            nv := vn + inc*diff
            grr.Log(laser.SetRobust(value, nv))
            wb.SetNeedsRender(true)
        }
        tick.Stop()
    }()
}

// LinearTransition is a simple, linear, 1 to 1 timing function that can be passed to [WidgetBase.Transition]
func LinearTransition(prop float32) float32 {
    return prop
}