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