kumiki 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +256 -0
- data/lib/kumiki/animation/animated_state.rb +83 -0
- data/lib/kumiki/animation/easing.rb +62 -0
- data/lib/kumiki/animation/value_tween.rb +69 -0
- data/lib/kumiki/app.rb +381 -0
- data/lib/kumiki/box.rb +40 -0
- data/lib/kumiki/chart/area_chart.rb +308 -0
- data/lib/kumiki/chart/bar_chart.rb +291 -0
- data/lib/kumiki/chart/base_chart.rb +213 -0
- data/lib/kumiki/chart/chart_helpers.rb +74 -0
- data/lib/kumiki/chart/gauge_chart.rb +174 -0
- data/lib/kumiki/chart/heatmap_chart.rb +223 -0
- data/lib/kumiki/chart/line_chart.rb +292 -0
- data/lib/kumiki/chart/pie_chart.rb +222 -0
- data/lib/kumiki/chart/scales.rb +79 -0
- data/lib/kumiki/chart/scatter_chart.rb +306 -0
- data/lib/kumiki/chart/stacked_bar_chart.rb +279 -0
- data/lib/kumiki/column.rb +351 -0
- data/lib/kumiki/core.rb +2511 -0
- data/lib/kumiki/dsl.rb +408 -0
- data/lib/kumiki/frame_ranma.rb +570 -0
- data/lib/kumiki/markdown/ast.rb +127 -0
- data/lib/kumiki/markdown/mermaid/layout.rb +389 -0
- data/lib/kumiki/markdown/mermaid/models.rb +235 -0
- data/lib/kumiki/markdown/mermaid/parser.rb +522 -0
- data/lib/kumiki/markdown/mermaid/renderer.rb +339 -0
- data/lib/kumiki/markdown/parser.rb +808 -0
- data/lib/kumiki/markdown/renderer.rb +642 -0
- data/lib/kumiki/markdown/theme.rb +168 -0
- data/lib/kumiki/render_node.rb +262 -0
- data/lib/kumiki/row.rb +288 -0
- data/lib/kumiki/spacer.rb +20 -0
- data/lib/kumiki/style.rb +799 -0
- data/lib/kumiki/theme.rb +567 -0
- data/lib/kumiki/themes/material.rb +40 -0
- data/lib/kumiki/themes/tokyo_night.rb +11 -0
- data/lib/kumiki/version.rb +5 -0
- data/lib/kumiki/widgets/button.rb +105 -0
- data/lib/kumiki/widgets/calendar.rb +1028 -0
- data/lib/kumiki/widgets/checkbox.rb +119 -0
- data/lib/kumiki/widgets/container.rb +111 -0
- data/lib/kumiki/widgets/data_table.rb +670 -0
- data/lib/kumiki/widgets/divider.rb +31 -0
- data/lib/kumiki/widgets/image.rb +105 -0
- data/lib/kumiki/widgets/input.rb +485 -0
- data/lib/kumiki/widgets/markdown.rb +58 -0
- data/lib/kumiki/widgets/modal.rb +165 -0
- data/lib/kumiki/widgets/multiline_input.rb +970 -0
- data/lib/kumiki/widgets/multiline_text.rb +180 -0
- data/lib/kumiki/widgets/net_image.rb +100 -0
- data/lib/kumiki/widgets/progress_bar.rb +72 -0
- data/lib/kumiki/widgets/radio_buttons.rb +93 -0
- data/lib/kumiki/widgets/slider.rb +135 -0
- data/lib/kumiki/widgets/switch.rb +84 -0
- data/lib/kumiki/widgets/tabs.rb +175 -0
- data/lib/kumiki/widgets/text.rb +120 -0
- data/lib/kumiki/widgets/tree.rb +434 -0
- data/lib/kumiki/widgets/webview.rb +87 -0
- data/lib/kumiki.rb +130 -0
- metadata +113 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2a5c07f3fe7c9d026edda7c5bea10a3c782b4957d468d2345285ea6abb2cdccd
|
|
4
|
+
data.tar.gz: fb06aaa4ad15d1c2abac021233158aa7aca6d2a07510267edc57bf351bbd841c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c39a4c8d085fc796b6644710637f634e30bb3b2a62f06bff480a970784588beea74e42ce09b1aa83a48c848ea02233095a8fb320d3c7e7ebd7d3d4ce5b79ca42
|
|
7
|
+
data.tar.gz: 2c9111e037718a27643a88ec98e1d385d30233e120cae0670c5bcb90cf1979699373c7d93b06c9b9ea98e753de7b5b1cf3a80c9ebf1fbb114990275fe25466fb
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yasushi Itoh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# Kumiki
|
|
2
|
+
|
|
3
|
+
**A declarative, reactive GUI framework for Ruby**
|
|
4
|
+
|
|
5
|
+
[](https://www.ruby-lang.org/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
Kumiki (組木) is a component-based desktop GUI framework for Ruby, based on a Ruby port of [castella](https://github.com/i2y/castella). Define your UI declaratively with a clean block-based DSL; the reactive state system handles updates automatically. Rendering is powered by [ranma](https://github.com/i2y/ranma) (tao + Vello GPU).
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Reactive state** — `State` objects trigger automatic UI rebuilds on change
|
|
15
|
+
- **Block-based DSL** — `column`, `row`, `text`, `button`, ... with keyword-arg styling
|
|
16
|
+
- **GPU rendering** — hardware-accelerated via ranma (tao + Vello)
|
|
17
|
+
- **Rich widget set** — 20+ widgets covering inputs, layout, data display, and more
|
|
18
|
+
- **Animation** — `AnimatedState` with built-in easing functions
|
|
19
|
+
- **Theming** — Tokyo Night (default) and Material Design themes included
|
|
20
|
+
- **Charts** — 8 chart types: Bar, Line, Pie, Scatter, Area, StackedBar, Gauge, Heatmap
|
|
21
|
+
- **Markdown** — Markdown rendering with Mermaid diagram support
|
|
22
|
+
- **WebView** — embedded browser widget
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
Add to your `Gemfile`:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
gem "kumiki"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or install directly:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
gem install kumiki
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
require "kumiki"
|
|
46
|
+
include Kumiki
|
|
47
|
+
|
|
48
|
+
class Counter < Component
|
|
49
|
+
def initialize
|
|
50
|
+
super
|
|
51
|
+
@count = state(0)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def view
|
|
55
|
+
column(padding: 16.0, spacing: 8.0) {
|
|
56
|
+
text "Count: #{@count}", font_size: 32.0, align: :center
|
|
57
|
+
row(spacing: 8.0) {
|
|
58
|
+
button(" - ") { @count -= 1 }
|
|
59
|
+
button(" + ") { @count += 1 }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
Kumiki.run("Counter", 400, 300) { Counter.new }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Widget Reference
|
|
71
|
+
|
|
72
|
+
### Layouts
|
|
73
|
+
|
|
74
|
+
| Widget | Description |
|
|
75
|
+
|--------|-------------|
|
|
76
|
+
| `Column` / `column` | Vertical stack |
|
|
77
|
+
| `Row` / `row` | Horizontal stack |
|
|
78
|
+
| `Box` / `box` | Z-stack overlay |
|
|
79
|
+
|
|
80
|
+
### Container
|
|
81
|
+
|
|
82
|
+
| Widget | Description |
|
|
83
|
+
|--------|-------------|
|
|
84
|
+
| `Container` / `container` | Background, border, border-radius, scrollable |
|
|
85
|
+
|
|
86
|
+
### Leaf Widgets
|
|
87
|
+
|
|
88
|
+
| Widget | Description |
|
|
89
|
+
|--------|-------------|
|
|
90
|
+
| `Text` / `text` | Static or dynamic text |
|
|
91
|
+
| `Button` / `button` | Clickable button with `kind:` variants |
|
|
92
|
+
| `Input` / `input` | Single-line text input |
|
|
93
|
+
| `MultilineInput` / `multiline_input` | Multi-line text input |
|
|
94
|
+
| `MultilineText` / `multiline_text` | Read-only multi-line text |
|
|
95
|
+
| `Checkbox` / `checkbox` | Boolean toggle |
|
|
96
|
+
| `RadioButtons` / `radio_buttons` | Single selection from options |
|
|
97
|
+
| `Switch` / `switch` | Toggle switch |
|
|
98
|
+
| `Slider` / `slider` | Value slider with range |
|
|
99
|
+
| `ProgressBar` / `progress_bar` | Progress indicator |
|
|
100
|
+
| `Divider` / `divider` | Horizontal rule |
|
|
101
|
+
| `Spacer` / `spacer` | Flexible space |
|
|
102
|
+
| `ImageWidget` / `image` | Local image |
|
|
103
|
+
| `NetImageWidget` / `net_image` | Async remote image |
|
|
104
|
+
| `WebViewWidget` / `webview` | Embedded web browser |
|
|
105
|
+
|
|
106
|
+
### Complex Widgets
|
|
107
|
+
|
|
108
|
+
| Widget | Description |
|
|
109
|
+
|--------|-------------|
|
|
110
|
+
| `Tabs` / `tabs` | Tabbed panels |
|
|
111
|
+
| `Tree` / `tree` | Collapsible tree navigation |
|
|
112
|
+
| `Calendar` / `calendar` | Month calendar with selection |
|
|
113
|
+
| `Modal` / `modal` | Overlay dialog |
|
|
114
|
+
| `DataTable` / `data_table` | Sortable, scrollable table |
|
|
115
|
+
|
|
116
|
+
### Charts
|
|
117
|
+
|
|
118
|
+
| Widget | Description |
|
|
119
|
+
|--------|-------------|
|
|
120
|
+
| `BarChart` | Vertical bar chart |
|
|
121
|
+
| `LineChart` | Line / time-series chart |
|
|
122
|
+
| `PieChart` | Pie chart |
|
|
123
|
+
| `ScatterChart` | Scatter plot |
|
|
124
|
+
| `AreaChart` | Area chart |
|
|
125
|
+
| `StackedBarChart` | Stacked bar chart |
|
|
126
|
+
| `GaugeChart` | Circular gauge |
|
|
127
|
+
| `HeatmapChart` | 2D heatmap |
|
|
128
|
+
|
|
129
|
+
### Markdown
|
|
130
|
+
|
|
131
|
+
| Widget | Description |
|
|
132
|
+
|--------|-------------|
|
|
133
|
+
| `Markdown` / `markdown_text` | Renders Markdown with Mermaid diagram support |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## DSL Style vs Object Style
|
|
138
|
+
|
|
139
|
+
Both styles can be mixed freely. The DSL is preferred for new code.
|
|
140
|
+
|
|
141
|
+
**DSL (block-based):**
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
column(padding: 16.0, spacing: 8.0) {
|
|
145
|
+
text "Hello", font_size: 24.0
|
|
146
|
+
button("Click me") { puts "clicked" }
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Object-based:**
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
Column(
|
|
154
|
+
Text("Hello").font_size(24.0),
|
|
155
|
+
Button("Click me") { puts "clicked" }
|
|
156
|
+
).padding(16.0).spacing(8.0)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Animation
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
class MyApp < Component
|
|
165
|
+
def initialize
|
|
166
|
+
super
|
|
167
|
+
# AnimatedState.new(initial_value, duration_ms, easing)
|
|
168
|
+
@x = AnimatedState.new(0.0, 400.0, :ease_out)
|
|
169
|
+
@x.attach(self)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def view
|
|
173
|
+
column {
|
|
174
|
+
box {
|
|
175
|
+
container(x: @x.value, width: 60.0, height: 60.0, background: 0xFF7AA2F7)
|
|
176
|
+
}
|
|
177
|
+
button("Animate") { @x.set(200.0) }
|
|
178
|
+
}
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Available easing functions: `:linear`, `:ease_in`, `:ease_out`, `:ease_in_out`, `:ease_in_cubic`, `:ease_out_cubic`, `:ease_in_out_cubic`, `:bounce`
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Theming
|
|
188
|
+
|
|
189
|
+
```ruby
|
|
190
|
+
require "kumiki"
|
|
191
|
+
|
|
192
|
+
# Default: Tokyo Night
|
|
193
|
+
Kumiki.run("App", 800, 600) { MyApp.new }
|
|
194
|
+
|
|
195
|
+
# Material Design (light)
|
|
196
|
+
Kumiki.theme = Kumiki.material_theme
|
|
197
|
+
Kumiki.run("App", 800, 600) { MyApp.new }
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Widget `kind:` prop applies semantic colors from the active theme:
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
button("OK", kind: :success)
|
|
204
|
+
button("Cancel", kind: :danger)
|
|
205
|
+
button("Info", kind: :info)
|
|
206
|
+
button("Warn", kind: :warning)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Examples
|
|
212
|
+
|
|
213
|
+
The `examples/` directory contains runnable demos:
|
|
214
|
+
|
|
215
|
+
| File | Description |
|
|
216
|
+
|------|-------------|
|
|
217
|
+
| `dsl_counter_demo.rb` | Minimal counter — reactive state + DSL |
|
|
218
|
+
| `counter.rb` | Counter using object-based style |
|
|
219
|
+
| `all_widgets_demo.rb` | Comprehensive showcase of every widget |
|
|
220
|
+
| `animation_demo.rb` | Animation system with various easing functions |
|
|
221
|
+
| `chart_demo.rb` | All 8 chart types |
|
|
222
|
+
| `data_table_demo.rb` | Sortable DataTable |
|
|
223
|
+
| `calendar_demo.rb` | Calendar widget |
|
|
224
|
+
| `tree_demo.rb` | Tree navigation |
|
|
225
|
+
| `tabs_demo.rb` | Tabbed interface |
|
|
226
|
+
| `modal_demo.rb` | Modal dialogs |
|
|
227
|
+
| `markdown_demo.rb` | Markdown + Mermaid rendering |
|
|
228
|
+
| `input_demo.rb` | Input fields and text widgets |
|
|
229
|
+
| `scroll_demo.rb` | Scrollable containers |
|
|
230
|
+
| `focus_demo.rb` | Keyboard focus and Tab cycling |
|
|
231
|
+
| `theme_demo.rb` | Theme presets and `kind:` variants |
|
|
232
|
+
| `theme_mode_demo.rb` | Light / dark mode switching |
|
|
233
|
+
| `dsl_style_demo.rb` | DSL styling examples |
|
|
234
|
+
| `dsl_calc.rb` | Calculator app |
|
|
235
|
+
| `widgets_demo.rb` | Basic widgets overview |
|
|
236
|
+
|
|
237
|
+
Run any example:
|
|
238
|
+
|
|
239
|
+
```sh
|
|
240
|
+
bundle install
|
|
241
|
+
bundle exec ruby examples/dsl_counter_demo.rb
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Requirements
|
|
247
|
+
|
|
248
|
+
- Ruby >= 3.1.0
|
|
249
|
+
- macOS or Linux
|
|
250
|
+
- The `ranma` gem is installed automatically as a dependency
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## License
|
|
255
|
+
|
|
256
|
+
MIT — Copyright (c) 2026 Yasushi Itoh. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module Kumiki
|
|
2
|
+
# AnimatedState - observable value that transitions smoothly
|
|
3
|
+
# Inherits from ObservableBase so Components can subscribe
|
|
4
|
+
|
|
5
|
+
class AnimatedState < ObservableBase
|
|
6
|
+
def initialize(initial_value, duration, easing_fn)
|
|
7
|
+
super()
|
|
8
|
+
@value = initial_value
|
|
9
|
+
@target = initial_value
|
|
10
|
+
@duration = duration # milliseconds
|
|
11
|
+
@easing_fn = easing_fn # Symbol
|
|
12
|
+
@tween = nil
|
|
13
|
+
@animating = false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def value
|
|
17
|
+
@value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def target
|
|
21
|
+
@target
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def animating?
|
|
25
|
+
@animating
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Set a new target — begins animation from current value
|
|
29
|
+
def set(new_target)
|
|
30
|
+
if new_target == @target && !@animating
|
|
31
|
+
return
|
|
32
|
+
end
|
|
33
|
+
@target = new_target
|
|
34
|
+
@tween = ValueTween.new(@value, @target, @duration, @easing_fn)
|
|
35
|
+
@animating = true
|
|
36
|
+
# Register with App's animation loop
|
|
37
|
+
app = App.current
|
|
38
|
+
if app != nil
|
|
39
|
+
app.register_animation(self)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Immediately jump to a value (no animation)
|
|
44
|
+
def set_immediate(new_value)
|
|
45
|
+
@value = new_value
|
|
46
|
+
@target = new_value
|
|
47
|
+
@tween = nil
|
|
48
|
+
@animating = false
|
|
49
|
+
notify_observers
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Call each frame with delta time in ms
|
|
53
|
+
def tick(dt)
|
|
54
|
+
return false if !@animating || @tween == nil
|
|
55
|
+
@tween.tick(dt)
|
|
56
|
+
@value = @tween.current
|
|
57
|
+
if @tween.finished?
|
|
58
|
+
@value = @target
|
|
59
|
+
@animating = false
|
|
60
|
+
@tween = nil
|
|
61
|
+
end
|
|
62
|
+
notify_observers
|
|
63
|
+
@animating
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def duration
|
|
67
|
+
@duration
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def duration=(d)
|
|
71
|
+
@duration = d
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def easing_fn
|
|
75
|
+
@easing_fn
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def easing_fn=(e)
|
|
79
|
+
@easing_fn = e
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Kumiki
|
|
2
|
+
# Easing functions for animation
|
|
3
|
+
# All functions take t in [0.0, 1.0] and return a value in [0.0, 1.0]
|
|
4
|
+
|
|
5
|
+
EASING_PI = 3.14159265358979323846
|
|
6
|
+
|
|
7
|
+
module Easing
|
|
8
|
+
def self.linear(t)
|
|
9
|
+
t
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.ease_in(t)
|
|
13
|
+
t * t
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.ease_out(t)
|
|
17
|
+
1.0 - (1.0 - t) * (1.0 - t)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.ease_in_out(t)
|
|
21
|
+
if t < 0.5
|
|
22
|
+
2.0 * t * t
|
|
23
|
+
else
|
|
24
|
+
1.0 - (-2.0 * t + 2.0) * (-2.0 * t + 2.0) / 2.0
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.ease_in_cubic(t)
|
|
29
|
+
t * t * t
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.ease_out_cubic(t)
|
|
33
|
+
v = 1.0 - t
|
|
34
|
+
1.0 - v * v * v
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.ease_in_out_cubic(t)
|
|
38
|
+
if t < 0.5
|
|
39
|
+
4.0 * t * t * t
|
|
40
|
+
else
|
|
41
|
+
v = -2.0 * t + 2.0
|
|
42
|
+
1.0 - v * v * v / 2.0
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.bounce(t)
|
|
47
|
+
if t < 1.0 / 2.75
|
|
48
|
+
7.5625 * t * t
|
|
49
|
+
elsif t < 2.0 / 2.75
|
|
50
|
+
t2 = t - 1.5 / 2.75
|
|
51
|
+
7.5625 * t2 * t2 + 0.75
|
|
52
|
+
elsif t < 2.5 / 2.75
|
|
53
|
+
t2 = t - 2.25 / 2.75
|
|
54
|
+
7.5625 * t2 * t2 + 0.9375
|
|
55
|
+
else
|
|
56
|
+
t2 = t - 2.625 / 2.75
|
|
57
|
+
7.5625 * t2 * t2 + 0.984375
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module Kumiki
|
|
2
|
+
# ValueTween - interpolates between two numeric values over a duration
|
|
3
|
+
# Frame-based: call tick(dt) each frame to advance
|
|
4
|
+
|
|
5
|
+
class ValueTween
|
|
6
|
+
def initialize(from_val, to_val, duration, easing_fn)
|
|
7
|
+
@from_val = from_val
|
|
8
|
+
@to_val = to_val
|
|
9
|
+
@duration = duration # milliseconds
|
|
10
|
+
@easing_fn = easing_fn # Symbol: :linear, :ease_in, etc.
|
|
11
|
+
@elapsed = 0.0
|
|
12
|
+
@current = from_val
|
|
13
|
+
@finished = false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def tick(dt)
|
|
17
|
+
return if @finished
|
|
18
|
+
@elapsed = @elapsed + dt
|
|
19
|
+
if @elapsed >= @duration
|
|
20
|
+
@elapsed = @duration
|
|
21
|
+
@finished = true
|
|
22
|
+
end
|
|
23
|
+
t = @elapsed / @duration
|
|
24
|
+
eased = apply_easing(t)
|
|
25
|
+
@current = @from_val + (@to_val - @from_val) * eased
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def current
|
|
29
|
+
@current
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def finished?
|
|
33
|
+
@finished
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def reset(from_val, to_val)
|
|
37
|
+
@from_val = from_val
|
|
38
|
+
@to_val = to_val
|
|
39
|
+
@elapsed = 0.0
|
|
40
|
+
@current = from_val
|
|
41
|
+
@finished = false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def apply_easing(t)
|
|
47
|
+
if @easing_fn == :linear
|
|
48
|
+
Easing.linear(t)
|
|
49
|
+
elsif @easing_fn == :ease_in
|
|
50
|
+
Easing.ease_in(t)
|
|
51
|
+
elsif @easing_fn == :ease_out
|
|
52
|
+
Easing.ease_out(t)
|
|
53
|
+
elsif @easing_fn == :ease_in_out
|
|
54
|
+
Easing.ease_in_out(t)
|
|
55
|
+
elsif @easing_fn == :ease_in_cubic
|
|
56
|
+
Easing.ease_in_cubic(t)
|
|
57
|
+
elsif @easing_fn == :ease_out_cubic
|
|
58
|
+
Easing.ease_out_cubic(t)
|
|
59
|
+
elsif @easing_fn == :ease_in_out_cubic
|
|
60
|
+
Easing.ease_in_out_cubic(t)
|
|
61
|
+
elsif @easing_fn == :bounce
|
|
62
|
+
Easing.bounce(t)
|
|
63
|
+
else
|
|
64
|
+
t
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|