Skip to content

Commit f0b6673

Browse files
authored
Merge pull request #10 from gucio321/inbetweening
inbetweening
2 parents 38b24ce + 5d30bfe commit f0b6673

18 files changed

+591
-330
lines changed

README.md

+101-59
Original file line numberDiff line numberDiff line change
@@ -6,85 +6,100 @@
66
This is a module for [giu](https://github.com/AllenDang/giu) providing an
77
animation system.
88

9-
![giu-animations demo](./docs/demo.gif)
10-
119
# Documentation
1210

1311
## How to use?
1412

15-
For complete code, please check out [`_examples`](./_examples)
13+
For complete code, please check out [examples](./_examples)
1614

1715
### important note
1816

19-
Please make shure that you're using the same version of giu
17+
Please make sure that you're using the same version of giu
2018
as this project (technically, you need to use giu version
2119
that uses the same imgui-go version as yours)
2220

2321
### Defining an animation
2422

25-
At the moment, there are two implementations of animations:
23+
At the moment, there are three implementations of animations:
24+
2625
- [Transition](#transition) - a smooth transition between two windows/sets of windows e.t.c.
2726
**NOTE** applying this animation to single widgets is not implemented yet and may
2827
not work as expected.
29-
- [on-hover color change](#hover-color) - you can apply this animation to any **single widget**
30-
that `imgui.IsItemHovered` applies for.
31-
You can specify any style color you want.
32-
- [Movement](#move) - moves DrawCursor emulating moving an object (`giu.Widget`).
28+
- [Color Flow](#color-flow) - you can apply this animation to any widget
29+
You can configure this animation to make your button hover smoother or change it into a rainbow!
30+
- [Movement](#move) - moves DrawCursor emulating moving an object (aka `giu.Widget`).
3331

3432
Lets shortly discuss particular types of animations:
3533

36-
#### transition
34+
#### Transition
3735

3836
Lets look at the API:
37+
3938
```go
40-
func Transition(renderer1, renderer2 func(starter func()) *TransitionAnimation {...}
39+
func Transition(renderers ...func(starter func(mode PlayMode))) *TransitionAnimation {...}
4140
```
4241

43-
`renderer1` and `renderer2` are just two stages of trasition.
44-
In stage 1, `renderer1` is called, and in stage 2 - `renderer2`.
45-
When animation is being plaid, both renderers are called and suitable
46-
alpha value is pushed to imgui style for both of them.
47-
48-
The argument to the poth renderers is a pointer to Animator.Start (see later)
42+
`renderers` are just [key frames](key-frame) of trasition.
43+
In each stage appropiate renderer is called.
44+
The argument to the renderers is a pointer to Animator.Start (see later)
4945
so that you can call it to play the animation.
5046

51-
#### Hover color
47+
#### Color flow
5248

5349
```go
54-
func HoverColor(
50+
func ColorFlow(
5551
widget giu.Widget,
56-
hoverColor, normalColor func() color.RGBA,
57-
hoverID, normalID giu.StyleColorID,
58-
) *HoverColorAnimation {...}
52+
applying []giu.StyleColorID,
53+
colors ...func() color.RGBA,
54+
) *ColorFlowAnimation {...}
5955
```
6056

61-
- The first argument is a widget that should apply to
62-
- hoverColor and normalColor are functions that returns hover and standard (non-hover) colors
63-
- hoverID, normalID - style IDs that hovering applies to.
57+
- The first argument is a **function** producing a **widget** that the animation should apply to
58+
- next is the list of style-color identifiers. Color changes applies to all of them.
59+
- and the last list of arguments are [key frames](#key-frame) of color flow.
6460

65-
There is also a variant of the above method called `HoverColorStyle`, which does not need
66-
`hoverColor` and `normalColor` arguments. These colors are obtained
61+
There is also a variant of the above method called `ColorFlowStyle`, which does not need
62+
colors list. These colors are obtained
6763
by function like this:
64+
6865
```go
6966
func() color.RGBA {
7067
return imgui.CurrentStyle().GetStyleColor(styleID)
7168
}
7269
```
7370

74-
#### Move
71+
#### Move
7572

7673
```go
77-
Move(
78-
w giu.Widget,
79-
delta imgui.Vec2,
80-
) *MoveAnimation {...}
74+
func Move(w func(starter StarterFunc) giu.Widget, steps ...*MoveStep) *MoveAnimation {...}
8175
```
8276

83-
This will mmove `w` from the position, it was
84-
at the moment of calling `Move(...)` (called `start`)
85-
to `start` + `delta`.
86-
87-
##### Easing
77+
This will move `w` around the steps.
78+
Lets take a closer look on steps now:
79+
80+
- You create a step with `Step` or `StepVec` methods.
81+
- You have two options of specifying position:
82+
- you can make it relative to the previous step. This way the system will
83+
take position and add it to the position of the previous step
84+
(and do it until it reaches first step or any step with absolute position)
85+
- After calling `Absolute()` method of the MoveStep, its position becomes
86+
absolute so that it does not rely on any previous step.
87+
- An additional feature of Steps is a Bezier Curve implementation.
88+
In order to enable it, simply call `Bezier` method and specify as meny points as you wish.
89+
90+
One more important thing to mention is the first step.
91+
By default, position of the first step you specify **will be treated
92+
absolute, even thouth it wasn't set to be.** To change this
93+
there are two additional methods of `MoveAnimation`.
94+
95+
- the first one is called `StartPos` and takes one argument of the following type:
96+
`func(startPos imgui.Vec2) *MoveStep`. It is expected to return non-nil MoveStep.
97+
`startPos` argument is the position of drawing cursor at the moment **of first call** of
98+
`Animator`.
99+
- another method is tu simply call `DefaultStartPos` method. It takes no arguments and acts
100+
like most users would like to use `StartPos` - it returns `Step(startPos)`.
101+
102+
### Easing
88103

89104
There are some extra ways of playing animation flow:
90105

@@ -108,24 +123,14 @@ const (
108123

109124
for further reference, see https://easings.net
110125

111-
##### Bezier curve
126+
### Note about StarterFunc
112127

113-
Move animation supports [Bezier Curve](https://pomax.github.io/bezierinfo/).
114-
It meas that move animation could be very smooth if you find it necessary.
115-
You have two ways to calculate these points:
116-
- go through [google](https://google.com) and use tone of paper to understand this strange math or
117-
- just type random values and check what happens :smile:
128+
This interface holds a reference to the part of `AnimatorWidget` responsible
129+
for starting animations. At the moment, there are three functions
118130

119-
The api looks as follows:
120-
121-
```go
122-
func (m *MoveAnimation) Bezier(controlPoints ...imgui.Vec2) *MoveAnimation {...}
123-
```
124-
125-
you can add as many control points as you which to.
126-
Each point will make the curve stranger.
127-
The only thing you need to remember is, that **these points
128-
are relative to `startPos`**. They will automatically become `startPos` + `controlPoint`.
131+
- Start(PlayMode) go to the next KeyFrame (forwards or backwards)
132+
- StartKF(base, destiny KeyFrame, mode PlayMode) go from `base` to `destiny` in `mode` direction (frame by frame)
133+
- StartWhole(mode) - play from 0 to last Key Frame
129134

130135
### Using animator
131136

@@ -138,24 +143,56 @@ animator's api is designed so that you don't need to do so every time.
138143
As an argument to `Animator(...)` constuctor, you pass perviously created animation.
139144

140145
Animator has some useful methods:
146+
141147
- `Duration` allows you to specify animation's duration (default is 0.25 s)
142148
- `FPS` sets Frames per second value for animation playback (default is 60)
143-
**NOTE** it is not real application's FPS! It just describes how often
144-
animation's status is updated.
149+
**NOTE** it is not real application's FPS! It just describes how often
150+
animation's status is updated.
145151
- `Start` - this method you can use to invoke animation play.
146152
- `IsRunning` returns true, if animation is being plaid right now.
147153

154+
#### ID
155+
156+
`AnimatorWidget` has a special ID method that allows you to specify
157+
your own giu-ID. This ID is used to store an internal aniamtor's state
158+
inside of giu context.
159+
Using this method is extremely important if you want to avoid confusing panics
160+
when using TransitionAnimation along with sub-animators inide that animation.
161+
It may happen, that one animator receives the same ID as the previous one.
162+
This may lead to unexpected behaviour or even panic! Its good practice to set
163+
unique ID everywhere!
164+
165+
### Auto triggering
166+
167+
Animator provides a simple way of automatical animations start.
168+
You can do this by using `Trigger` method. This method takes three arguments:
169+
170+
- `TriggerType` (TriggerNever, TriggerOnChange, TriggerOnTrue) tells Animator when
171+
to start the animation.
172+
- play mode - tells how to start it
173+
- and the last argument is a trigger function `func() bool` (**TIP** you can use any of `imgui` functions like `imgui.IsItemHovered`)
174+
175+
### Key Frame
176+
177+
Key frames are states of animation with a specified animation states.
178+
All other states between them are calculated on the go.
179+
Key frames system in this module is not much experienced, but it should
180+
suit needs of most users. For more information about implementation
181+
of this system in particular animation types, see above.
182+
148183
## Creating your own animation
149184

150185
You can use this API to create your own animation.
151186
To do soo, lets take a look on `Animation` interface.
187+
152188
```go
153189
type Animation interface {
154190
Init()
155191
Reset()
192+
KeyFramesCount() int
156193

157-
BuildNormal(starter func())
158-
BuildAnimation(animationPercentage float32, starter func())
194+
BuildNormal(currentKeyFrame KeyFrame, starter func())
195+
BuildAnimation(animationPercentage, animationPurePercentage float32, startKeyFrame, destinationKeyFrame KeyFrame, starter func())
159196
}
160197
```
161198

@@ -170,6 +207,11 @@ you can put some initialization here.
170207

171208
Reset is called along with `(*Animator).Start`
172209

210+
### KeyFramesCount
211+
212+
Returns a number of key frames the animation implements.
213+
This number determines behaviour of Animator while calling Start\*
214+
173215
### BuildNormal
174216

175217
is called when `!(*Animator).IsRunning()`
@@ -186,7 +228,7 @@ You can do some calculations there.
186228
# Contribution
187229

188230
If you implement something interessting, find any bugs, or
189-
improvements and would be so kind to open a PR,
231+
improvements and if you would be so kind to open a PR,
190232
your contribution is welcome!
191233

192234
# Motivation
@@ -196,4 +238,4 @@ But (as I'm an author of that system) I've decided to share it for public - feel
196238

197239
# License
198240

199-
This project is shared under (attached) MIT License.
241+
This project is shared under (attached) [MIT License](LICENSE).

_examples/main.go

+53-17
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,38 @@ func loop() {
2121
a := int32(easingAlg)
2222
animations.Animator(
2323
animations.Transition(
24-
func(starter func()) {
24+
func(starterFunc animations.StarterFunc) {
2525
giu.Window("window1").Layout(
2626
giu.Label("I'm a window 1"),
2727
animations.Animator(
2828
animations.ColorFlow(
2929
giu.Button("start transition").OnClick(func() {
30-
starter()
30+
starterFunc.Start(animations.PlayForward)
3131
}),
32+
[]giu.StyleColorID{
33+
giu.StyleColorButtonHovered,
34+
giu.StyleColorButton,
35+
},
36+
func() color.RGBA {
37+
return colornames.Blue
38+
},
3239
func() color.RGBA {
3340
return colornames.Red
3441
},
3542
func() color.RGBA {
36-
return colornames.Blue
43+
return colornames.Green
44+
},
45+
func() color.RGBA {
46+
return colornames.Yellow
3747
},
38-
giu.StyleColorButtonHovered,
39-
giu.StyleColorButton,
4048
),
4149
).
4250
Duration(time.Second).
4351
FPS(60).
44-
Trigger(animations.TriggerOnChange, imgui.IsItemHovered),
52+
Trigger(animations.TriggerOnTrue, animations.PlayForward, imgui.IsItemHovered),
53+
giu.Button("Play whole transition!").OnClick(func() {
54+
starterFunc.StartWhole(animations.PlayForward)
55+
}),
4556
giu.Checkbox("Play on hover", &playOnHover),
4657
animations.Animator(
4758
animations.Move(func(starter animations.StarterFunc) giu.Widget {
@@ -52,26 +63,51 @@ func loop() {
5263
easingAlg = animations.EasingAlgorithmType(a)
5364
}),
5465
),
55-
giu.Button("move me!").OnClick(func() {
56-
starter(animations.PlayForward)
57-
}),
66+
giu.Row(
67+
giu.Button("play backwards").OnClick(func() {
68+
starter.Start(animations.PlayBackwards)
69+
}),
70+
giu.Button("move me!").OnClick(func() {
71+
starter.Start(animations.PlayForward)
72+
}),
73+
),
5874
).Size(200, 80)
59-
}, imgui.Vec2{X: 20, Y: 100}).
60-
Bezier(imgui.Vec2{X: 20, Y: 20}, imgui.Vec2{X: 90}),
75+
},
76+
animations.Step(20, 100).
77+
Bezier(imgui.Vec2{X: 20, Y: 20}, imgui.Vec2{X: 90}),
78+
).DefaultStartPos(),
6179
).Duration(time.Second*3).
6280
FPS(120).
6381
EasingAlgorithm(easingAlg).
64-
Trigger(animations.TriggerOnTrue, func() bool {
82+
Trigger(animations.TriggerOnTrue, animations.PlayForward, func() bool {
6583
return playOnHover && giu.IsItemHovered()
6684
}),
6785
)
6886
},
69-
func(starter func()) {
87+
func(starterFunc animations.StarterFunc) {
7088
giu.Window("window2").Layout(
71-
giu.Label("I'm a window 1"),
72-
giu.Button("start transition").OnClick(func() {
73-
starter()
74-
}),
89+
giu.Label("I'm a window 2"),
90+
animations.Animator(
91+
animations.ColorFlowStyle(
92+
giu.Button("start transition").OnClick(func() {
93+
starterFunc.Start(animations.PlayForward)
94+
}),
95+
giu.StyleColorButton, giu.StyleColorButtonHovered,
96+
),
97+
).Trigger(animations.TriggerOnChange, animations.PlayForward, imgui.IsItemHovered),
98+
)
99+
},
100+
func(starterFunc animations.StarterFunc) {
101+
giu.Window("window 3").Layout(
102+
giu.Label("I'm third window!"),
103+
giu.Row(
104+
giu.Button("<< Previous Window").OnClick(func() {
105+
starterFunc.Start(animations.PlayBackwards)
106+
}),
107+
giu.Button("Next Window >>").OnClick(func() {
108+
starterFunc.Start(animations.PlayForward)
109+
}),
110+
),
75111
)
76112
},
77113
),

animation.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
package animations
22

3-
type StarterFunc func(playMode PlayMode)
4-
53
// Animation is an interface implemented by each animation.
64
type Animation interface {
75
// Init is called once, immediately on start.
86
Init()
97
// Reset is called whenever needs to restart animation.
108
Reset()
119

10+
// KeyFramesCount is used mainly by the AnimatorWidget.
11+
// It returns a number of key frames.
12+
KeyFramesCount() int
13+
1214
// BuildNormal is called every frame when animation is not running
1315
// starter is a link to Animator.Start
14-
BuildNormal(starterFunc StarterFunc)
16+
BuildNormal(currentKeyFrame KeyFrame, starterFunc StarterFunc)
1517
// BuildAnimation is called when running an animation.
1618
// It receives two values:
1719
// - first one is animationPercentage after applying specified by Animator
@@ -25,5 +27,10 @@ type Animation interface {
2527
// does not want this. Exceptions, I see for now may be:
2628
// - your animation does not accept negative (or larger than 1) progress values
2729
// starter is a link to (*Animator).Start() method.
28-
BuildAnimation(animationPercentage, arbitraryPercentage float32, starterFunc StarterFunc)
30+
BuildAnimation(
31+
animationPercentage, arbitraryPercentage float32,
32+
baseKeyFrame, destinationKeyFrame KeyFrame,
33+
mode PlayMode,
34+
starterFunc StarterFunc,
35+
)
2936
}

0 commit comments

Comments
 (0)