Color animation + changing RectButton colors

This commit is contained in:
mars 2022-07-10 13:42:42 -06:00
parent 58d9945e40
commit 3e4be0ea0b
3 changed files with 176 additions and 53 deletions

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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);
}
}