Documentation
¶
Overview ¶
Package springbutton ships the spring-physics variant of prism/button: the same button, with a press that scales down and springs back.
It is a sibling of github.com/vibrantgio/prism/button.Button, not a decorator over it. Pulse never animates prism globally — a caller picks the animated component by name, so the call site says what it does and the dependency runs pulse → prism and never back. SpringButton owns its own widget.Clickable and renders through github.com/vibrantgio/prism/button.Render, the pure renderer, then wraps the output in an op.Affine scale driven by a github.com/vibrantgio/pulse/spring.Spring. The underlying button's visual contract — hover, focus, press, disabled colours; 44 dp minimum hit target; semantic ops — is preserved.
// Static prism.Button:
w, _ := button.Button(theme, button.Props{Label: "Save", OnClick: save}).First()
// Spring-physics variant:
w, _ := springbutton.SpringButton(theme,
button.Props{Label: "Save", OnClick: save},
springbutton.Options{}, // zero-valued: package defaults
).First()
Physics ¶
On press, the spring retargets to Options.PressScale; on release, back to 1.0. The free particle's position is read every frame as a scale factor applied around the button's centre. The default parameters (Stiffness 300, Damping 22, Mass 1) are underdamped, so the release overshoots slightly and comes back — the "pop". Measured against this package's own settle tolerance, a press-and-release settles 25 frames after the release.
While the spring is in flight the widget schedules its own redraws via op.InvalidateCmd; once it settles, no further frames are requested, so a SpringButton at rest costs the same per frame as a static prism button.
Frames, not seconds ¶
The spring is ticked at a hard-coded inverse step of 60 rather than at the window's real frame rate, so those 25 frames are 25 frames whatever the display does. On a 120 Hz screen the press animation takes half the wall-clock time it takes on a 60 Hz one, and under a stuttering host it stretches out to match. Nothing here reads the frame rate, and there is no option to supply it; a frame-rate-aware step is a later refinement, shared with github.com/vibrantgio/pulse/conductor.
Fonts ¶
The label is shaped with the theme's cached shaper (Typography.Shaper(), ADR-003: the theme owns the typeface) in the LabelLarge role. That shaper is built once for the process and shared by every component reading the same typography: spectrum's cache lives behind the Typography value, so it survives the copy the map function below makes of it (spectrum F5.1). It is not safe to use from two goroutines — Gio lays the widget forest out on the one goroutine that runs the event loop, which is what makes sharing it correct. Props.Shaper is an explicit per-instance override only; leave it nil in normal use. Since prism v0.2.0 the role's whole text style — typeface, weight, size and line height — reaches github.com/vibrantgio/prism/button.Render, as does the theme's Density, so a spring button is glyph- and pixel-identical to the static one at scale 1.
State scope ¶
The clickable and the spring are allocated inside the rx.Defer closure, so they persist across theme and disabled emissions and across frames for the lifetime of one subscription. A new subscription resets the physics — a button that is subscribed twice has two independent springs, and one that is resubscribed mid-press restarts at scale 1.
Index ¶
Constants ¶
const ( DefaultStiffness = 300.0 DefaultDamping = 22.0 DefaultMass = 1.0 DefaultPressScale = 0.92 )
Defaults tuned for a visible-but-snappy button press. The amplitude of the scale change is small (8 %), so the spring must be stiff enough to traverse most of it within a ~150 ms press window at a 60 Hz frame rate. Stiffness 300 with Damping 22 (zeta ≈ 0.635) gives a small overshoot for "pop" feel. Measured against settleTolerance, each leg settles 25 frames after the pointer event — ~415 ms at 60 Hz, not the ~250 ms this was tuned for.
Variables ¶
This section is empty.
Functions ¶
func SpringButton ¶
func SpringButton( th rx.Observable[theme.Theme], props button.Props, opts Options, ) rx.Observable[layout.Widget]
SpringButton returns an rx.Observable[layout.Widget] that renders the prism.Button visual with a spring-driven scale on press/release. All button.Props fields are honoured (label, description, disabled observable, OnClick, Message, custom Shaper) — the FRP and MVU integration paths from prism/button continue to work unchanged.
Types ¶
type Options ¶
type Options struct {
// Stiffness, Damping, Mass are the same spring parameters
// documented in [pulse/spring.Options]. Zero values are replaced
// with [DefaultStiffness], [DefaultDamping], [DefaultMass].
Stiffness float64
Damping float64
Mass float64
// PressScale is the scale the button shrinks to while pressed.
// 1.0 means no shrink (the spring becomes a no-op visual);
// values below 1.0 produce a press-down effect. Zero is replaced
// with [DefaultPressScale].
PressScale float64
}
Options configures the spring physics layered on top of prism/button. Zero-valued fields use package defaults so an empty Options{} produces the canonical SpringButton feel.