Files
pihkaal-me/app/components/Projects/BottomScreen/Buttons.vue

206 lines
4.9 KiB
Vue

<script setup lang="ts">
import gsap from "gsap";
const { onRender, onClick } = useScreen();
const store = useProjectsStore();
const { assets } = useAssets();
const CLICK_RADIUS = 22;
const BUTTONS = {
prev: {
position: [36, 100],
image: assets.images.projects.bottomScreen.prevPressed,
},
quit: {
position: [88, 156],
image: assets.images.projects.bottomScreen.quitPressed,
},
link: {
position: [168, 156],
image: assets.images.projects.bottomScreen.linkPressed,
},
next: {
position: [220, 100],
image: assets.images.projects.bottomScreen.nextPressed,
},
} as const satisfies Record<
string,
{
position: Point;
image: {
draw: (ctx: CanvasRenderingContext2D, x: number, y: number) => void;
};
}
>;
type ButtonType = keyof typeof BUTTONS;
type ButtonAnimation = {
type: ButtonType;
position: Point;
showButton: boolean;
showSmallCircle: boolean;
showBigCircle: boolean;
};
let currentAnimation: ButtonAnimation | null = null;
const circleContains = (
[cx, cy]: Point,
[x, y]: Point,
radius: number,
): boolean => Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2)) < radius;
const SMALL_CIRCLE_DURATION = 0.07;
const BIG_CIRCLE_DURATION = 0.09;
const POST_DURATION = 0.07;
const startButtonAnimation = (type: ButtonType) => {
const anim: ButtonAnimation = {
type,
position: BUTTONS[type].position,
showButton: true,
showSmallCircle: true,
showBigCircle: false,
};
currentAnimation = anim;
gsap
.timeline()
.to(anim, { duration: SMALL_CIRCLE_DURATION })
.call(() => {
anim.showSmallCircle = false;
anim.showBigCircle = true;
})
.to(anim, { duration: BIG_CIRCLE_DURATION })
.call(() => {
anim.showBigCircle = false;
})
.to(anim, { duration: POST_DURATION })
.call(() => {
currentAnimation = null;
});
};
onClick((x, y) => {
if (
currentAnimation ||
store.isIntro ||
store.isOutro ||
store.showConfirmationPopup
)
return;
if (
circleContains(BUTTONS.prev.position, [x, y], CLICK_RADIUS) &&
store.currentProject > 0
) {
startButtonAnimation("prev");
store.scrollProjects("left");
} else if (
circleContains(BUTTONS.next.position, [x, y], CLICK_RADIUS) &&
store.currentProject < store.projects.length - 1
) {
startButtonAnimation("next");
store.scrollProjects("right");
} else if (circleContains(BUTTONS.quit.position, [x, y], CLICK_RADIUS)) {
startButtonAnimation("quit");
store.animateOutro();
} else if (circleContains(BUTTONS.link.position, [x, y], CLICK_RADIUS)) {
startButtonAnimation("link");
setTimeout(
() => (store.showConfirmationPopup = true),
1000 * (SMALL_CIRCLE_DURATION + BIG_CIRCLE_DURATION + POST_DURATION),
);
}
});
onRender((ctx) => {
// Draw disabled buttons
if (store.currentProject === 0) {
assets.images.projects.bottomScreen.prevDisabled.draw(
ctx,
BUTTONS.prev.position[0] - 14,
BUTTONS.prev.position[1] - 14,
);
}
if (store.currentProject === store.projects.length - 1) {
assets.images.projects.bottomScreen.nextDisabled.draw(
ctx,
BUTTONS.next.position[0] - 14,
BUTTONS.next.position[1] - 14,
);
}
if (currentAnimation?.showButton) {
const image = BUTTONS[currentAnimation.type].image;
image.draw(
ctx,
currentAnimation.position[0] - 14,
currentAnimation.position[1] - 14,
);
}
if (currentAnimation?.showSmallCircle) {
assets.images.projects.bottomScreen.circleSmall.draw(
ctx,
currentAnimation.position[0] - 28,
currentAnimation.position[1] - 28,
);
}
if (currentAnimation?.showBigCircle) {
assets.images.projects.bottomScreen.circleBig.draw(
ctx,
currentAnimation.position[0] - 44,
currentAnimation.position[1] - 44,
);
}
ctx.fillStyle = `rgba(0, 0, 0, ${store.isIntro ? store.intro.fadeOpacity : store.isOutro ? store.outro.fadeOpacity : 0})`;
ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
});
useKeyDown((key) => {
if (
currentAnimation ||
store.isIntro ||
store.isOutro ||
store.showConfirmationPopup
)
return;
switch (key) {
case "NDS_LEFT":
if (store.currentProject > 0) {
startButtonAnimation("prev");
store.scrollProjects("left");
}
break;
case "NDS_RIGHT":
if (store.currentProject < store.projects.length - 1) {
startButtonAnimation("next");
store.scrollProjects("right");
}
break;
case "NDS_B":
startButtonAnimation("quit");
store.animateOutro();
break;
case "NDS_A":
case "NDS_START":
startButtonAnimation("link");
setTimeout(
() => (store.showConfirmationPopup = true),
1000 * (SMALL_CIRCLE_DURATION + BIG_CIRCLE_DURATION + POST_DURATION),
);
break;
}
});
defineOptions({
render: () => null,
});
</script>