feat(projects): add project scope
This commit is contained in:
BIN
app/assets/images/projects/top-screen/scope-badge.png
Normal file
BIN
app/assets/images/projects/top-screen/scope-badge.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@@ -1,7 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import SCOPE_BADGE_IMAGE from "~/assets/images/projects/top-screen/scope-badge.png";
|
||||||
|
|
||||||
const store = useProjectsStore();
|
const store = useProjectsStore();
|
||||||
|
|
||||||
const projectPreviews = useImages(...store.projects.map((x) => x.preview));
|
const [scopeBadgeImage, ...projectPreviews] = useImages(
|
||||||
|
SCOPE_BADGE_IMAGE,
|
||||||
|
...store.projects.map((x) => x.preview),
|
||||||
|
);
|
||||||
|
|
||||||
const getBounds = () => ({
|
const getBounds = () => ({
|
||||||
startIndex: Math.max(store.currentProject - 1, 0),
|
startIndex: Math.max(store.currentProject - 1, 0),
|
||||||
@@ -72,6 +77,7 @@ useRender((ctx) => {
|
|||||||
const x = baseX + 256 * offsetFromCenter + store.offsetX * (256 / 69);
|
const x = baseX + 256 * offsetFromCenter + store.offsetX * (256 / 69);
|
||||||
const y = (192 - previewImage.height) / 2 + 10;
|
const y = (192 - previewImage.height) / 2 + 10;
|
||||||
|
|
||||||
|
// game
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.translate(x, y);
|
ctx.translate(x, y);
|
||||||
|
|
||||||
@@ -89,6 +95,7 @@ useRender((ctx) => {
|
|||||||
ctx.drawImage(previewImage, 0, 0);
|
ctx.drawImage(previewImage, 0, 0);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
||||||
|
// technologies
|
||||||
const project = store.projects[i]!;
|
const project = store.projects[i]!;
|
||||||
if (project.technologies && project.technologies.length > 0) {
|
if (project.technologies && project.technologies.length > 0) {
|
||||||
const badgeY = previewImage.height - BADGE_HEIGHT - BADGE_MARGIN;
|
const badgeY = previewImage.height - BADGE_HEIGHT - BADGE_MARGIN;
|
||||||
@@ -96,6 +103,15 @@ useRender((ctx) => {
|
|||||||
drawTechBadges(ctx, project.technologies, badgeX, badgeY);
|
drawTechBadges(ctx, project.technologies, badgeX, badgeY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scope
|
||||||
|
ctx.translate(previewImage.width - scopeBadgeImage!.width + 3, -2);
|
||||||
|
ctx.drawImage(scopeBadgeImage!, 0, 0);
|
||||||
|
|
||||||
|
ctx.font = "7px NDS7";
|
||||||
|
ctx.fillStyle = "#000000";
|
||||||
|
ctx.textBaseline = "top";
|
||||||
|
fillTextCentered(ctx, project.scope.toUpperCase(), 0, -5, 40);
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,55 +30,53 @@ export const useContactStore = defineStore("contact", {
|
|||||||
animateIntro() {
|
animateIntro() {
|
||||||
this.isIntro = true;
|
this.isIntro = true;
|
||||||
|
|
||||||
const start = 2;
|
const timeline = gsap.timeline({
|
||||||
|
onComplete: () => {
|
||||||
|
this.isIntro = false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
gsap.fromTo(
|
timeline
|
||||||
this.intro,
|
.fromTo(
|
||||||
{
|
this.intro,
|
||||||
stage1Opacity: 0,
|
{
|
||||||
titleY: SCREEN_HEIGHT,
|
stage1Opacity: 0,
|
||||||
},
|
titleY: SCREEN_HEIGHT,
|
||||||
{
|
|
||||||
stage1Opacity: 1,
|
|
||||||
titleY: SCREEN_HEIGHT - 23,
|
|
||||||
duration: 0.1,
|
|
||||||
delay: start,
|
|
||||||
ease: "none",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
gsap.fromTo(
|
|
||||||
this.intro,
|
|
||||||
{
|
|
||||||
stage2Opacity: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
stage2Opacity: 1,
|
|
||||||
duration: 0.1,
|
|
||||||
delay: start + 0.15,
|
|
||||||
ease: "none",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
gsap.fromTo(
|
|
||||||
this.intro,
|
|
||||||
{
|
|
||||||
stage3Opacity: 0,
|
|
||||||
topBarY: -20,
|
|
||||||
bottomBarY: SCREEN_HEIGHT - 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
stage3Opacity: 1,
|
|
||||||
topBarY: 0,
|
|
||||||
bottomBarY: SCREEN_HEIGHT - 24,
|
|
||||||
duration: 0.1,
|
|
||||||
delay: start + 0.15 + 0.15,
|
|
||||||
ease: "none",
|
|
||||||
onComplete: () => {
|
|
||||||
this.isIntro = false;
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
);
|
stage1Opacity: 1,
|
||||||
|
titleY: SCREEN_HEIGHT - 23,
|
||||||
|
duration: 0.1,
|
||||||
|
ease: "none",
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
.fromTo(
|
||||||
|
this.intro,
|
||||||
|
{ stage2Opacity: 0 },
|
||||||
|
{
|
||||||
|
stage2Opacity: 1,
|
||||||
|
duration: 0.1,
|
||||||
|
ease: "none",
|
||||||
|
},
|
||||||
|
2.15,
|
||||||
|
)
|
||||||
|
.fromTo(
|
||||||
|
this.intro,
|
||||||
|
{
|
||||||
|
stage3Opacity: 0,
|
||||||
|
topBarY: -20,
|
||||||
|
bottomBarY: SCREEN_HEIGHT - 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stage3Opacity: 1,
|
||||||
|
topBarY: 0,
|
||||||
|
bottomBarY: SCREEN_HEIGHT - 24,
|
||||||
|
duration: 0.1,
|
||||||
|
ease: "none",
|
||||||
|
},
|
||||||
|
2.3,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
pushNotification(content: string) {
|
pushNotification(content: string) {
|
||||||
@@ -94,49 +92,46 @@ export const useContactStore = defineStore("contact", {
|
|||||||
animateOutro() {
|
animateOutro() {
|
||||||
this.isOutro = true;
|
this.isOutro = true;
|
||||||
|
|
||||||
gsap.fromTo(
|
const timeline = gsap.timeline({
|
||||||
this.outro,
|
onComplete: () => {
|
||||||
{
|
setTimeout(() => {
|
||||||
stage1Opacity: 1,
|
this.isOutro = false;
|
||||||
|
navigateTo("/");
|
||||||
|
}, 2000);
|
||||||
},
|
},
|
||||||
{
|
});
|
||||||
stage1Opacity: 0,
|
|
||||||
duration: 0.2,
|
|
||||||
ease: "none",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
gsap.fromTo(
|
timeline
|
||||||
this.outro,
|
.fromTo(
|
||||||
{
|
this.outro,
|
||||||
stage2Opacity: 1,
|
{ stage1Opacity: 1 },
|
||||||
},
|
{
|
||||||
{
|
stage1Opacity: 0,
|
||||||
stage2Opacity: 0,
|
duration: 0.2,
|
||||||
duration: 0.25,
|
ease: "none",
|
||||||
delay: 0.25,
|
|
||||||
ease: "none",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
gsap.fromTo(
|
|
||||||
this.outro,
|
|
||||||
{
|
|
||||||
stage3Opacity: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
stage3Opacity: 0,
|
|
||||||
duration: 0.3,
|
|
||||||
delay: 0.5,
|
|
||||||
ease: "none",
|
|
||||||
onComplete: () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.isOutro = false;
|
|
||||||
navigateTo("/");
|
|
||||||
}, 2000);
|
|
||||||
},
|
},
|
||||||
},
|
0,
|
||||||
);
|
)
|
||||||
|
.fromTo(
|
||||||
|
this.outro,
|
||||||
|
{ stage2Opacity: 1 },
|
||||||
|
{
|
||||||
|
stage2Opacity: 0,
|
||||||
|
duration: 0.25,
|
||||||
|
ease: "none",
|
||||||
|
},
|
||||||
|
0.25,
|
||||||
|
)
|
||||||
|
.fromTo(
|
||||||
|
this.outro,
|
||||||
|
{ stage3Opacity: 1 },
|
||||||
|
{
|
||||||
|
stage3Opacity: 0,
|
||||||
|
duration: 0.3,
|
||||||
|
ease: "none",
|
||||||
|
},
|
||||||
|
0.5,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,74 +24,72 @@ export const useHomeStore = defineStore("home", {
|
|||||||
|
|
||||||
this.isIntro = true;
|
this.isIntro = true;
|
||||||
|
|
||||||
const start = 0.5;
|
const timeline = gsap.timeline({
|
||||||
const duration = 0.5;
|
onComplete: () => {
|
||||||
const delay = 0.35;
|
this.isIntro = false;
|
||||||
|
if (!appStore.booted) appStore.booted = true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
gsap.fromTo(
|
timeline
|
||||||
this.intro,
|
.fromTo(
|
||||||
{ stage1Opacity: 0 },
|
this.intro,
|
||||||
{
|
{ stage1Opacity: 0 },
|
||||||
stage1Opacity: 1,
|
{
|
||||||
duration,
|
stage1Opacity: 1,
|
||||||
delay: start,
|
duration: 0.5,
|
||||||
ease: "none",
|
ease: "none",
|
||||||
onComplete: () => {
|
|
||||||
this.isIntro = false;
|
|
||||||
if (!appStore.booted) appStore.booted = true;
|
|
||||||
},
|
},
|
||||||
},
|
0.5,
|
||||||
);
|
)
|
||||||
|
.fromTo(
|
||||||
gsap.fromTo(
|
this.intro,
|
||||||
this.intro,
|
{ statusBarY: -20 },
|
||||||
{ statusBarY: -20 },
|
{
|
||||||
{
|
statusBarY: 0,
|
||||||
statusBarY: 0,
|
duration: 0.15,
|
||||||
duration: duration - delay,
|
ease: "none",
|
||||||
delay: start + delay,
|
},
|
||||||
ease: "none",
|
0.85,
|
||||||
},
|
);
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
animateOutro(to: "contact" | "projects" | "settings") {
|
animateOutro(to: "contact" | "projects" | "settings") {
|
||||||
this.isOutro = true;
|
this.isOutro = true;
|
||||||
this.outro.animateTop = to !== "settings";
|
this.outro.animateTop = to !== "settings";
|
||||||
|
|
||||||
const duration = 0.4;
|
const timeline = gsap.timeline({
|
||||||
const delay = 0.08;
|
onComplete: () => {
|
||||||
|
this.isOutro = true;
|
||||||
gsap.fromTo(
|
navigateTo(`/${to}`);
|
||||||
this.outro,
|
|
||||||
{
|
|
||||||
buttonOffsetY: 0,
|
|
||||||
stage1Opacity: 1,
|
|
||||||
},
|
},
|
||||||
{
|
});
|
||||||
buttonOffsetY: -200,
|
|
||||||
stage1Opacity: 0,
|
timeline
|
||||||
duration,
|
.fromTo(
|
||||||
delay,
|
this.outro,
|
||||||
ease: "none",
|
{ stage2Opacity: 1 },
|
||||||
onComplete: () => {
|
{
|
||||||
this.isOutro = true;
|
stage2Opacity: 0,
|
||||||
navigateTo(`/${to}`);
|
duration: 0.16,
|
||||||
|
ease: "none",
|
||||||
},
|
},
|
||||||
},
|
0,
|
||||||
);
|
)
|
||||||
|
.fromTo(
|
||||||
gsap.fromTo(
|
this.outro,
|
||||||
this.outro,
|
{
|
||||||
{
|
buttonOffsetY: 0,
|
||||||
stage2Opacity: 1,
|
stage1Opacity: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stage2Opacity: 0,
|
buttonOffsetY: -200,
|
||||||
ease: "none",
|
stage1Opacity: 0,
|
||||||
duration: duration / 2 - delay,
|
duration: 0.4,
|
||||||
},
|
ease: "none",
|
||||||
);
|
},
|
||||||
|
0.08,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export const useProjectsStore = defineStore("projects", {
|
|||||||
preview: string;
|
preview: string;
|
||||||
url: string | null;
|
url: string | null;
|
||||||
technologies: string[];
|
technologies: string[];
|
||||||
|
scope: "hobby" | "work";
|
||||||
body: MarkdownBody;
|
body: MarkdownBody;
|
||||||
}[],
|
}[],
|
||||||
currentProject: 0,
|
currentProject: 0,
|
||||||
@@ -92,6 +93,7 @@ export const useProjectsStore = defineStore("projects", {
|
|||||||
preview: `/images/projects/${id}/preview.webp`,
|
preview: `/images/projects/${id}/preview.webp`,
|
||||||
url: project.url,
|
url: project.url,
|
||||||
technologies: project.technologies,
|
technologies: project.technologies,
|
||||||
|
scope: project.scope,
|
||||||
body: simplifyMarkdownAST(project.body),
|
body: simplifyMarkdownAST(project.body),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export default defineContentConfig({
|
|||||||
url: z.url().nullable(),
|
url: z.url().nullable(),
|
||||||
order: z.number(),
|
order: z.number(),
|
||||||
technologies: z.array(z.string()),
|
technologies: z.array(z.string()),
|
||||||
|
scope: z.enum(["hobby", "work"]),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ description: Biobleud - Automated Excel imports\nfor an ERP system
|
|||||||
url: null
|
url: null
|
||||||
order: 100
|
order: 100
|
||||||
technologies: [VBA]
|
technologies: [VBA]
|
||||||
|
scope: work
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ description: LBF Bot - Custom Discord bot for\na gaming group
|
|||||||
url: https://github.com/pihkaal/lbf-bot
|
url: https://github.com/pihkaal/lbf-bot
|
||||||
order: 50
|
order: 50
|
||||||
technologies: [Node, TypeScript]
|
technologies: [Node, TypeScript]
|
||||||
|
scope: hobby
|
||||||
---
|
---
|
||||||
|
|
||||||
a
|
a
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ description: lilou.cat - My cat's website
|
|||||||
url: https://lilou.cat
|
url: https://lilou.cat
|
||||||
order: 40
|
order: 40
|
||||||
technologies: [HTML, Go]
|
technologies: [HTML, Go]
|
||||||
|
scope: hobby
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ description: pihkaal.me - my personal website
|
|||||||
url: https://pihkaal.me
|
url: https://pihkaal.me
|
||||||
order: 10
|
order: 10
|
||||||
technologies: [Nuxt, TypeScript]
|
technologies: [Nuxt, TypeScript]
|
||||||
|
scope: hobby
|
||||||
---
|
---
|
||||||
|
|
||||||
# pihkaal.me
|
# pihkaal.me
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
description: Raylib Speedruns - Collection of simple\nRaylib setups in multiple languages
|
description: Raylib Speedruns - Collection of simple\nRaylib setups in multiple languages
|
||||||
url: https://github.com/pihkaal/raylib-speedruns
|
url: https://github.com/pihkaal/raylib-speedruns
|
||||||
order: 60
|
order: 60
|
||||||
|
scope: hobby
|
||||||
---
|
---
|
||||||
|
|
||||||
# Raylib Speedruns
|
# Raylib Speedruns
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ description: S3PWeb - Intership and\napprenticeship
|
|||||||
url: null
|
url: null
|
||||||
order: 80
|
order: 80
|
||||||
technologies: [Node, StencilJS, TypeScript]
|
technologies: [Node, StencilJS, TypeScript]
|
||||||
|
scope: work
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ description: Simple QR - Simple QR code generator\nwith straightforward API
|
|||||||
url: https://simple-qr.com
|
url: https://simple-qr.com
|
||||||
order: 30
|
order: 30
|
||||||
technologies: [Nuxt, TypeScript]
|
technologies: [Nuxt, TypeScript]
|
||||||
|
scope: hobby
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ description: tlock - fully customizable and cross-\nplatform terminal based cloc
|
|||||||
url: https://github.com/pihkaal/tlock
|
url: https://github.com/pihkaal/tlock
|
||||||
order: 20
|
order: 20
|
||||||
technologies: [Rust]
|
technologies: [Rust]
|
||||||
|
scope: hobby
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user