feat(settings/touchScreen/tapTap): intro and outro animations
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useLocalStorage } from "@vueuse/core";
|
||||
import gsap from "gsap";
|
||||
|
||||
const store = useSettingsStore();
|
||||
const achievements = useAchievementsStore();
|
||||
@@ -65,6 +66,58 @@ const highScore = useLocalStorage("taptap_high_score", 0);
|
||||
let score = 0;
|
||||
let isNewBest = false;
|
||||
|
||||
const AREA_FADE_DURATION = 0.2;
|
||||
const SCORE_OFFSET = -20;
|
||||
const SCORE_DURATION = 0.15;
|
||||
|
||||
const animation = reactive({
|
||||
areaOpacity: 0,
|
||||
circlesOpacity: 1,
|
||||
scoreOffsetY: SCORE_OFFSET,
|
||||
});
|
||||
|
||||
const animateIntro = async () => {
|
||||
await gsap
|
||||
.timeline()
|
||||
.to(
|
||||
animation,
|
||||
{ areaOpacity: 1, duration: AREA_FADE_DURATION, ease: "none" },
|
||||
0,
|
||||
)
|
||||
.to(
|
||||
animation,
|
||||
{ scoreOffsetY: 0, duration: SCORE_DURATION, ease: "none" },
|
||||
AREA_FADE_DURATION,
|
||||
);
|
||||
};
|
||||
|
||||
const animateOutro = async () => {
|
||||
targetX = 0;
|
||||
targetY = LOGICAL_HEIGHT * 2 - 20;
|
||||
|
||||
await gsap
|
||||
.timeline()
|
||||
.to(
|
||||
animation,
|
||||
{ areaOpacity: 0, duration: AREA_FADE_DURATION, ease: "none" },
|
||||
0,
|
||||
)
|
||||
.to(
|
||||
animation,
|
||||
{ circlesOpacity: 0, duration: AREA_FADE_DURATION, ease: "none" },
|
||||
0,
|
||||
)
|
||||
.to(
|
||||
animation,
|
||||
{ scoreOffsetY: SCORE_OFFSET, duration: SCORE_DURATION, ease: "none" },
|
||||
0,
|
||||
);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
animateIntro();
|
||||
});
|
||||
|
||||
const handleActivateB = () => {
|
||||
if (state.value === "playing") {
|
||||
state.value = "paused";
|
||||
@@ -72,8 +125,9 @@ const handleActivateB = () => {
|
||||
text: $t("settings.touchScreen.tapTap.quitConfirmation"),
|
||||
bLabel: $t("common.no"),
|
||||
aLabel: $t("common.yes"),
|
||||
onClosed: (choice) => {
|
||||
onClosed: async (choice) => {
|
||||
if (choice === "confirm") {
|
||||
await animateOutro();
|
||||
store.closeSubMenu();
|
||||
} else {
|
||||
state.value = "playing";
|
||||
@@ -81,11 +135,11 @@ const handleActivateB = () => {
|
||||
},
|
||||
});
|
||||
} else {
|
||||
store.closeSubMenu();
|
||||
animateOutro().then(() => store.closeSubMenu());
|
||||
}
|
||||
};
|
||||
|
||||
const handleActivateA = () => {
|
||||
const handleActivateA = async () => {
|
||||
if (state.value === "playing") {
|
||||
state.value = "paused";
|
||||
confirmationModal.open({
|
||||
@@ -100,6 +154,13 @@ const handleActivateA = () => {
|
||||
}
|
||||
},
|
||||
});
|
||||
} else if (state.value === "waiting") {
|
||||
await gsap.to(animation, {
|
||||
areaOpacity: 0,
|
||||
duration: AREA_FADE_DURATION,
|
||||
ease: "none",
|
||||
});
|
||||
resetGame();
|
||||
} else {
|
||||
resetGame();
|
||||
}
|
||||
@@ -163,10 +224,11 @@ const showDeathScreen = () => {
|
||||
text,
|
||||
bLabel: $t("common.quit"),
|
||||
aLabel: $t("common.restart"),
|
||||
onClosed: (choice) => {
|
||||
onClosed: async (choice) => {
|
||||
if (choice === "confirm") {
|
||||
resetGame();
|
||||
} else {
|
||||
await animateOutro();
|
||||
store.closeSubMenu();
|
||||
}
|
||||
},
|
||||
@@ -254,6 +316,8 @@ onRender((ctx) => {
|
||||
|
||||
// draw start instructions
|
||||
if (state.value === "waiting") {
|
||||
ctx.save();
|
||||
ctx.globalAlpha = animation.areaOpacity;
|
||||
ctx.fillStyle = "#fbfbfb";
|
||||
ctx.textBaseline = "top";
|
||||
ctx.fillRect(32, 112, 191, 31);
|
||||
@@ -267,11 +331,13 @@ onRender((ctx) => {
|
||||
123,
|
||||
191,
|
||||
);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
if (state.value !== "waiting") {
|
||||
// draw circles and rings
|
||||
for (const circle of circles) {
|
||||
ctx.globalAlpha = animation.circlesOpacity;
|
||||
ctx.fillStyle = "#fb0000";
|
||||
fillCirclePixelated(ctx, circle.x, circle.y, circle.radius);
|
||||
|
||||
@@ -280,7 +346,7 @@ onRender((ctx) => {
|
||||
}
|
||||
|
||||
for (const ring of rings) {
|
||||
ctx.globalAlpha = ring.alpha;
|
||||
ctx.globalAlpha = ring.alpha * animation.circlesOpacity;
|
||||
ctx.fillStyle = "#fb0000";
|
||||
strokeCirclePixelated(
|
||||
ctx,
|
||||
@@ -310,25 +376,26 @@ onRender((ctx) => {
|
||||
}, 0);
|
||||
|
||||
onRender((ctx) => {
|
||||
ctx.translate(0, animation.scoreOffsetY);
|
||||
drawButton(
|
||||
ctx,
|
||||
$t("settings.touchScreen.tapTap.score", { score }),
|
||||
10,
|
||||
3,
|
||||
2,
|
||||
88,
|
||||
);
|
||||
drawButton(
|
||||
ctx,
|
||||
$t("settings.touchScreen.tapTap.best", { best: highScore.value }),
|
||||
108,
|
||||
3,
|
||||
2,
|
||||
88,
|
||||
);
|
||||
drawButton(
|
||||
ctx,
|
||||
lives > 0 ? ICONS.HEART.repeat(lives) : ICONS.SAD,
|
||||
206,
|
||||
3,
|
||||
2,
|
||||
40,
|
||||
);
|
||||
}, 110);
|
||||
|
||||
Reference in New Issue
Block a user