Color animation + changing RectButton colors
This commit is contained in:
parent
58d9945e40
commit
3e4be0ea0b
101
src/anim.rs
101
src/anim.rs
|
@ -1,19 +1,24 @@
|
|||
use keyframe::EasingFunction;
|
||||
use crate::Color;
|
||||
|
||||
pub trait AnimationLerp<T> {
|
||||
fn lerp(&self, x: f32) -> T;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Animation<F> {
|
||||
pub struct Animation<F, T = f32> {
|
||||
time: f32,
|
||||
duration: f32,
|
||||
in_delay: f32,
|
||||
out_delay: f32,
|
||||
from: f32,
|
||||
to: f32,
|
||||
from: T,
|
||||
to: T,
|
||||
function: F,
|
||||
direction: bool,
|
||||
}
|
||||
|
||||
impl<F: EasingFunction> Animation<F> {
|
||||
pub fn new(function: F, duration: f32, from: f32, to: f32) -> Self {
|
||||
impl<F: EasingFunction, T: Copy> Animation<F, T> {
|
||||
pub fn new(function: F, duration: f32, from: T, to: T) -> Self {
|
||||
Self {
|
||||
time: duration,
|
||||
duration,
|
||||
|
@ -34,34 +39,6 @@ impl<F: EasingFunction> Animation<F> {
|
|||
self.time < self.duration
|
||||
}
|
||||
|
||||
pub fn get(&self) -> f32 {
|
||||
if self.is_active() {
|
||||
if self.time <= 0.0 {
|
||||
if self.direction {
|
||||
self.from
|
||||
} else {
|
||||
self.to
|
||||
}
|
||||
} else {
|
||||
let x = self.time / self.duration;
|
||||
let x = if self.direction { x } else { 1.0 - x };
|
||||
let lerp = self.function.y(x as f64) as f32;
|
||||
(1.0 - lerp) * self.from + lerp * self.to
|
||||
}
|
||||
} else if self.direction {
|
||||
self.to
|
||||
} else {
|
||||
self.from
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ease_to(&mut self, to: f32) {
|
||||
self.from = self.get();
|
||||
self.to = to;
|
||||
self.time = 0.0;
|
||||
self.direction = true;
|
||||
}
|
||||
|
||||
pub fn ease_in(&mut self) {
|
||||
if !self.direction {
|
||||
self.ease_toggle();
|
||||
|
@ -93,4 +70,62 @@ impl<F: EasingFunction> Animation<F> {
|
|||
pub fn set_out_delay(&mut self, delay: f32) {
|
||||
self.out_delay = delay;
|
||||
}
|
||||
|
||||
pub fn clamped(&self) -> Option<T> {
|
||||
if self.is_active() {
|
||||
if self.time <= 0.0 {
|
||||
if self.direction {
|
||||
Some(self.from)
|
||||
} else {
|
||||
Some(self.to)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if self.direction {
|
||||
Some(self.to)
|
||||
} else {
|
||||
Some(self.from)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: EasingFunction> AnimationLerp<f32> for Animation<F, f32> {
|
||||
fn lerp(&self, x: f32) -> f32 {
|
||||
(1.0 - x) * self.from + x * self.to
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: EasingFunction> AnimationLerp<Color> for Animation<F, Color> {
|
||||
fn lerp(&self, x: f32) -> Color {
|
||||
let from: glam::Vec4 = self.from.into();
|
||||
let to: glam::Vec4 = self.to.into();
|
||||
let lerp = (1.0 - x) * from + x * to;
|
||||
lerp.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T> Animation<F, T>
|
||||
where
|
||||
F: EasingFunction,
|
||||
T: Copy,
|
||||
Animation<F, T>: AnimationLerp<T>,
|
||||
{
|
||||
pub fn get(&self) -> T {
|
||||
if let Some(clamped) = self.clamped() {
|
||||
clamped
|
||||
} else {
|
||||
let x = self.time / self.duration;
|
||||
let x = if self.direction { x } else { 1.0 - x };
|
||||
let lerp = self.function.y(x as f64) as f32;
|
||||
self.lerp(lerp)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ease_to(&mut self, to: T) {
|
||||
self.from = self.get();
|
||||
self.to = to;
|
||||
self.time = 0.0;
|
||||
self.direction = true;
|
||||
}
|
||||
}
|
||||
|
|
24
src/lib.rs
24
src/lib.rs
|
@ -48,6 +48,30 @@ pub struct Color {
|
|||
pub a: f32,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub const WHITE: Self = Self::new(1., 1., 1., 1.);
|
||||
pub const BLACK: Self = Self::new(0., 0., 0., 1.);
|
||||
pub const TRANSPARENT: Self = Self::new(0., 0., 0., 0.);
|
||||
|
||||
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
Self { r, g, b, a }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<glam::Vec4> for Color {
|
||||
fn from(other: glam::Vec4) -> Self {
|
||||
let glam::Vec4 { x, y, z, w } = other;
|
||||
Self::new(x, y, z, w)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for glam::Vec4 {
|
||||
fn from(other: Color) -> Self {
|
||||
let Color { r, g, b, a } = other;
|
||||
Self::new(r, g, b, a)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum CursorEventKind {
|
||||
Hover,
|
||||
|
|
104
src/widgets.rs
104
src/widgets.rs
|
@ -94,7 +94,9 @@ pub struct RectButton {
|
|||
pub pos: Vec2,
|
||||
pub size: Vec2,
|
||||
pub was_clicked: bool,
|
||||
pub color: Color,
|
||||
pub is_selected: bool,
|
||||
pub is_hovering: bool,
|
||||
pub color_anim: Animation<EaseInQuad, Color>,
|
||||
}
|
||||
|
||||
impl Button for RectButton {
|
||||
|
@ -104,17 +106,23 @@ impl Button for RectButton {
|
|||
}
|
||||
|
||||
impl RectButton {
|
||||
pub const INACTIVE_COLOR: Color = Color::new(1., 1., 1., 0.2);
|
||||
pub const HOVER_COLOR: Color = Color::new(1., 1., 1., 0.8);
|
||||
pub const SELECTED_COLOR: Color = Color::new(1., 1., 0., 1.);
|
||||
|
||||
pub fn new(pos: Vec2, size: Vec2) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
size,
|
||||
was_clicked: true,
|
||||
color: Color {
|
||||
r: 1.0,
|
||||
g: 1.0,
|
||||
b: 1.0,
|
||||
a: 0.2,
|
||||
},
|
||||
was_clicked: false,
|
||||
is_selected: false,
|
||||
is_hovering: false,
|
||||
color_anim: Animation::new(
|
||||
EaseInQuad,
|
||||
0.05,
|
||||
Self::INACTIVE_COLOR,
|
||||
Self::INACTIVE_COLOR,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,21 +130,46 @@ impl RectButton {
|
|||
impl Widget for RectButton {
|
||||
fn update(&mut self, dt: f32) {
|
||||
self.was_clicked = false;
|
||||
self.color_anim.update(dt);
|
||||
}
|
||||
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
ctx.draw_rect(self.pos, self.size, self.color);
|
||||
ctx.draw_rect(self.pos, self.size, self.color_anim.get());
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
let local = at - self.pos;
|
||||
if kind == CursorEventKind::Deselect
|
||||
&& local.x > 0.0
|
||||
&& local.y > 0.0
|
||||
&& local.x < self.size.x
|
||||
&& local.y < self.size.y
|
||||
{
|
||||
self.was_clicked = true;
|
||||
let is_on = local.x > 0. && local.y > 0. && local.x < self.size.x && local.y < self.size.y;
|
||||
|
||||
match kind {
|
||||
CursorEventKind::Hover | CursorEventKind::Drag => {
|
||||
if is_on {
|
||||
if !self.is_hovering && !self.is_selected {
|
||||
self.color_anim.ease_to(Self::HOVER_COLOR);
|
||||
}
|
||||
|
||||
self.is_hovering = true;
|
||||
} else {
|
||||
if self.is_hovering && !self.is_selected {
|
||||
self.color_anim.ease_to(Self::INACTIVE_COLOR);
|
||||
}
|
||||
|
||||
self.is_hovering = false;
|
||||
}
|
||||
}
|
||||
CursorEventKind::Select => {
|
||||
if is_on {
|
||||
self.is_selected = true;
|
||||
self.color_anim.ease_to(Self::SELECTED_COLOR);
|
||||
}
|
||||
}
|
||||
CursorEventKind::Deselect => {
|
||||
if self.is_selected {
|
||||
self.was_clicked = true;
|
||||
self.is_selected = false;
|
||||
self.color_anim.ease_to(Self::HOVER_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -346,6 +379,33 @@ impl<T: Widget + Button> Widget for ScrollMenu<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Offset<T> {
|
||||
inner: T,
|
||||
offset: Vec2,
|
||||
}
|
||||
|
||||
impl<T: Widget> Offset<T> {
|
||||
pub fn new(inner: T, offset: Vec2) -> Self {
|
||||
Self { inner, offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Widget> Widget for Offset<T> {
|
||||
fn update(&mut self, dt: f32) {
|
||||
self.inner.update(dt);
|
||||
}
|
||||
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
let ctx = ctx.with_offset(self.offset);
|
||||
self.inner.draw(&ctx);
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
let at = at - self.offset;
|
||||
self.inner.on_cursor_event(kind, at);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Reveal<T> {
|
||||
inner: T,
|
||||
slide_anim: Animation<EaseIn>,
|
||||
|
@ -412,7 +472,11 @@ impl<T: Widget> Widget for Reveal<T> {
|
|||
|
||||
pub struct MainMenu {
|
||||
pub menu: ScrollMenu<RoundButton>,
|
||||
pub inventory: Reveal<Inventory>,
|
||||
pub inventory: Reveal<Offset<Inventory>>,
|
||||
}
|
||||
|
||||
impl MainMenu {
|
||||
pub const SUBMENU_SPACING: f32 = 0.05;
|
||||
}
|
||||
|
||||
impl Default for MainMenu {
|
||||
|
@ -424,6 +488,7 @@ impl Default for MainMenu {
|
|||
}
|
||||
|
||||
let inventory = Inventory::new();
|
||||
let inventory = Offset::new(inventory, Vec2::new(Self::SUBMENU_SPACING, 0.0));
|
||||
let inventory = Reveal::new(inventory, -0.02, 0.1);
|
||||
|
||||
Self {
|
||||
|
@ -453,13 +518,12 @@ impl Widget for MainMenu {
|
|||
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
self.menu.draw(&ctx);
|
||||
|
||||
let inventory_ctx = ctx.with_offset(Vec2::new(0.05, 0.0));
|
||||
self.inventory.draw(&inventory_ctx);
|
||||
self.inventory.draw(&ctx);
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
self.menu.on_cursor_event(kind, at);
|
||||
self.inventory.on_cursor_event(kind, at);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue