feat(settings): number input animation

This commit is contained in:
2026-02-06 13:10:59 +01:00
parent a9d8fba359
commit 0f1407de0d

View File

@@ -1,4 +1,6 @@
<script setup lang="ts">
import gsap from "gsap";
const APP_COLOR_TO_FONT_COLOR: Record<string, string> = {
"#61829a": "#fbfbfb", // cyan
"#ba4900": "#fbe3d3", // maroon
@@ -78,6 +80,48 @@ const downImage = computed(() => {
const squareWidth = computed(() => upImage.value.rect.width);
const SLIDE_OFFSET = 96;
const SLIDE_DURATION = 0.25;
const ARROW_SLIDE_DELAY = 0.15;
const ARROW_SLIDE_DURATION = 0.15;
const animation = reactive({
offsetY: SLIDE_OFFSET,
opacity: 0,
upArrowOffsetY: ARROW_IMAGE_HEIGHT,
downArrowOffsetY: -ARROW_IMAGE_HEIGHT,
});
const animateIntro = async () => {
await gsap
.timeline()
.to(animation, { offsetY: 0, duration: SLIDE_DURATION, ease: "none" }, 0)
.to(animation, { opacity: 1, duration: SLIDE_DURATION, ease: "none" }, 0)
.to(
animation,
{ upArrowOffsetY: 0, duration: ARROW_SLIDE_DURATION, ease: "none" },
SLIDE_DURATION + ARROW_SLIDE_DELAY,
)
.to(
animation,
{ downArrowOffsetY: 0, duration: ARROW_SLIDE_DURATION, ease: "none" },
SLIDE_DURATION + ARROW_SLIDE_DELAY,
);
};
const animateOutro = async () => {
await gsap
.timeline()
.to(
animation,
{ offsetY: SLIDE_OFFSET, duration: SLIDE_DURATION, ease: "none" },
0,
)
.to(animation, { opacity: 0, duration: SLIDE_DURATION, ease: "none" }, 0);
};
defineExpose({ animateIntro, animateOutro });
const increase = () => {
const newValue = value.value + 1;
value.value = newValue > props.max ? props.min : newValue;
@@ -89,8 +133,16 @@ const decrease = () => {
};
onRender((ctx) => {
// arrow up
upImage.value.draw(ctx, props.x, Y);
ctx.globalAlpha = animation.opacity;
ctx.translate(0, animation.offsetY);
// arrow up (clipped to area above the number square)
ctx.save();
ctx.beginPath();
ctx.rect(props.x, 0, squareWidth.value, Y + ARROW_IMAGE_HEIGHT);
ctx.clip();
upImage.value.draw(ctx, props.x, Y + animation.upArrowOffsetY);
ctx.restore();
// outline
ctx.fillStyle = "#515151";
@@ -123,8 +175,22 @@ onRender((ctx) => {
Y + ARROW_IMAGE_HEIGHT + Math.floor((SQUARE_HEIGHT - 39) / 2),
);
// arrow down
downImage.value.draw(ctx, props.x, Y + ARROW_IMAGE_HEIGHT + SQUARE_HEIGHT);
// arrow down (clipped to area below the number square)
ctx.save();
ctx.beginPath();
ctx.rect(
props.x,
Y + ARROW_IMAGE_HEIGHT + SQUARE_HEIGHT,
squareWidth.value,
ARROW_IMAGE_HEIGHT,
);
ctx.clip();
downImage.value.draw(
ctx,
props.x,
Y + ARROW_IMAGE_HEIGHT + SQUARE_HEIGHT + animation.downArrowOffsetY,
);
ctx.restore();
// title
ctx.font = "10px NDS10";