Better animation system

This commit is contained in:
2026-02-22 00:12:13 +01:00
parent e62da29a49
commit f952fa7095
11 changed files with 33 additions and 115 deletions
@@ -41,6 +41,12 @@ public class Element {
public void mouseReleased(double mouseX, double mouseY, int button) { public void mouseReleased(double mouseX, double mouseY, int button) {
} }
public void playAnimationFrame(long elapsed) {
if (this.animation != null) {
this.animation.tick(this.curve.apply(Math.min(1.0, (double) elapsed / this.animation.duration)), this);
}
}
public Element dimensions(int width, int height) { public Element dimensions(int width, int height) {
this.width = width; this.width = width;
this.height = height; this.height = height;
@@ -4,13 +4,15 @@ public class ElementContext {
public int mouseX, mouseY; public int mouseX, mouseY;
public int elementWidth, elementHeight; public int elementWidth, elementHeight;
public float elementScale; public float elementScale;
public long elapsed;
public ElementContext(int mouseX, int mouseY, Element e) { public ElementContext(long elapsed, int mouseX, int mouseY, Element e) {
this.mouseX = mouseX - e.x; this.mouseX = mouseX - e.x;
this.mouseY = mouseY - e.y; this.mouseY = mouseY - e.y;
this.elementWidth = e.width; this.elementWidth = e.width;
this.elementHeight = e.height; this.elementHeight = e.height;
this.elementScale = e.scale; this.elementScale = e.scale;
this.elapsed = elapsed;
} }
public boolean isHovering() { public boolean isHovering() {
@@ -1,11 +1,11 @@
package org.saturnclient.ui2; package org.saturnclient.ui2;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.saturnclient.saturnclient.mixin.DrawContextAccessor; import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
import org.saturnclient.ui2.anim.Animation;
import org.saturnclient.ui2.anim.SlideXAbsolute;
import org.saturnclient.ui2.components.ElementRenderer; import org.saturnclient.ui2.components.ElementRenderer;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@@ -23,6 +23,7 @@ public abstract class SaturnScreen extends Screen {
private final Pool pool = new Pool(3); private final Pool pool = new Pool(3);
protected List<Element> elements = new ArrayList<>(); protected List<Element> elements = new ArrayList<>();
protected float backgroundOpacity = 1.0f; protected float backgroundOpacity = 1.0f;
protected Instant start = null;
public int blurDuration = 700; public int blurDuration = 700;
public float blurProgress = 0.0f; public float blurProgress = 0.0f;
public int backgroundBlur = 10; public int backgroundBlur = 10;
@@ -41,36 +42,7 @@ public abstract class SaturnScreen extends Screen {
} }
public void draw(Element element) { public void draw(Element element) {
synchronized (elements) { ElementRenderer.draw(elements, element);
elements.add(element);
}
if (element.animation != null) {
element.animation.init(element);
Animation.execute((Float progress) -> {
element.animation.tick(progress, element);
}, element.animation.duration);
}
if (element.duration != null) {
new Thread(() -> {
try {
Animation fadeOut = new SlideXAbsolute(700, -(width - element.x));
Thread.sleep(element.duration);
fadeOut.init(element);
Animation.executeSync((Float progress) -> {
fadeOut.tick(progress, element);
}, fadeOut.duration);
synchronized (elements) {
elements.remove(element);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
} }
@Override @Override
@@ -79,10 +51,6 @@ public abstract class SaturnScreen extends Screen {
width *= 2; width *= 2;
height *= 2; height *= 2;
ui(); // abstraction to render the saturn ui and also render extra stuff here ui(); // abstraction to render the saturn ui and also render extra stuff here
Animation.execute((Float progress) -> {
blurProgress = progress;
}, blurDuration);
} }
public abstract void ui(); public abstract void ui();
@@ -90,6 +58,11 @@ public abstract class SaturnScreen extends Screen {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) { public void render(DrawContext context, int mouseX, int mouseY, float delta) {
if (start == null) {
start = Instant.now();
}
long elapsed = Duration.between(start, Instant.now()).toMillis();
mouseX *= 2; mouseX *= 2;
mouseY *= 2; mouseY *= 2;
@@ -114,7 +87,7 @@ public abstract class SaturnScreen extends Screen {
renderScope.matrices.scale(0.5f, 0.5f, 0.5f); renderScope.matrices.scale(0.5f, 0.5f, 0.5f);
ElementRenderer.render(new ArrayList<>(elements), renderScope, mouseX, mouseY); ElementRenderer.render(new ArrayList<>(elements), elapsed, renderScope, mouseX, mouseY);
renderScope.matrices.pop(); renderScope.matrices.pop();
} }
@@ -1,8 +1,5 @@
package org.saturnclient.ui2.anim; package org.saturnclient.ui2.anim;
import java.util.function.Consumer;
import java.util.function.Function;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
public abstract class Animation { public abstract class Animation {
@@ -12,64 +9,7 @@ public abstract class Animation {
this.duration = duration; this.duration = duration;
} }
public abstract void tick(float progress, Element element); public abstract void tick(double progress, Element element);
public abstract void init(Element element); public abstract void init(Element element);
public static void execute(Consumer<Float> tick, int duration) {
new Thread(() -> {
Function<Double, Double> curveFunction = Curve::easeInOutCubic;
float progress = 0.0f;
double incremental = (1000 - duration) / 10000.0;
for (int i = 0; i <= 100; i++) {
float newProgress = (float) (double) curveFunction.apply(i / 100.0);
while (Math.abs(newProgress - progress) > 1e-6) {
if (progress < newProgress) {
progress += incremental;
if (progress > newProgress) progress = newProgress;
} else {
progress -= incremental;
if (progress < newProgress) progress = newProgress;
}
tick.accept(newProgress);
try {
Thread.sleep(duration / 100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
public static void executeSync(Consumer<Float> tick, int duration) {
Function<Double, Double> curveFunction = Curve::easeInOutCubic;
float progress = 0.0f;
double incremental = (1000 - duration) / 10000.0;
for (int i = 0; i <= 100; i++) {
float newProgress = (float) (double) curveFunction.apply(i / 100.0);
while (Math.abs(newProgress - progress) > 1e-6) {
if (progress < newProgress) {
progress += incremental;
if (progress > newProgress) progress = newProgress;
} else {
progress -= incremental;
if (progress < newProgress) progress = newProgress;
}
tick.accept(newProgress);
try {
Thread.sleep(duration / 100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
} }
@@ -10,8 +10,8 @@ public class Fade extends Animation {
} }
@Override @Override
public void tick(float progress, Element element) { public void tick(double progress, Element element) {
element.opacity = targetOpacity * progress; element.opacity = targetOpacity * (float) progress;
} }
@Override @Override
@@ -10,8 +10,8 @@ public class FadeOut extends Animation {
} }
@Override @Override
public void tick(float progress, Element element) { public void tick(double progress, Element element) {
element.opacity = targetOpacity - progress; element.opacity = targetOpacity - (float) progress;
} }
@Override @Override
@@ -24,7 +24,7 @@ public class SlideX extends Animation {
} }
@Override @Override
public void tick(float progress, Element element) { public void tick(double progress, Element element) {
element.x = elementX + (int) (offset * progress); element.x = elementX + (int) (offset * progress);
} }
@@ -24,7 +24,7 @@ public class SlideXAbsolute extends Animation {
} }
@Override @Override
public void tick(float progress, Element element) { public void tick(double progress, Element element) {
element.x = elementX + (int) (offset * progress); element.x = elementX + (int) (offset * progress);
} }
@@ -26,7 +26,7 @@ public class SlideY extends Animation {
} }
@Override @Override
public void tick(float progress, Element element) { public void tick(double progress, Element element) {
element.y = elementY + (int) (offset * progress); element.y = elementY + (int) (offset * progress);
} }
@@ -7,7 +7,6 @@ import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.Utils; import org.saturnclient.ui2.Utils;
import org.saturnclient.ui2.anim.Animation;
public class ElementRenderer { public class ElementRenderer {
public static void draw(List<Element> elements, Element element) { public static void draw(List<Element> elements, Element element) {
@@ -17,20 +16,18 @@ public class ElementRenderer {
if (element.animation != null) { if (element.animation != null) {
element.animation.init(element); element.animation.init(element);
Animation.execute((Float progress) -> {
element.animation.tick(progress, element);
}, element.animation.duration);
} }
} }
public static void render(List<Element> elements, RenderScope renderScope, int mouseX, int mouseY) { public static void render(List<Element> elements, long elapsed, RenderScope renderScope, int mouseX, int mouseY) {
for (Element element : elements) { for (Element element : elements) {
element.playAnimationFrame(elapsed);
renderScope.matrices.push(); renderScope.matrices.push();
renderScope.setOpacity(element.opacity); renderScope.setOpacity(element.opacity);
renderScope.matrices.translate(element.x, element.y, 0); renderScope.matrices.translate(element.x, element.y, 0);
renderScope.matrices.scale(element.scale, element.scale, 1.0f); renderScope.matrices.scale(element.scale, element.scale, 1.0f);
element.render(renderScope, new ElementContext(mouseX, mouseY, element)); element.render(renderScope, new ElementContext(elapsed, mouseX, mouseY, element));
renderScope.matrices.pop(); renderScope.matrices.pop();
renderScope.setRenderLayer(null); renderScope.setRenderLayer(null);
} }
@@ -36,7 +36,7 @@ public class Scroll extends Element {
renderScope.matrices.push(); renderScope.matrices.push();
renderScope.matrices.translate(padding, -scroll + padding, 0); renderScope.matrices.translate(padding, -scroll + padding, 0);
ElementRenderer.render(children, renderScope, ElementRenderer.render(children, ctx.elapsed, renderScope,
ctx.mouseX - padding, ctx.mouseX - padding,
ctx.mouseY - padding + scroll); ctx.mouseY - padding + scroll);