updated perspective code

This commit is contained in:
liyunze 2025-06-21 22:01:23 +10:00
parent b1a6ef4237
commit d2f28d2b7e
4 changed files with 107 additions and 294 deletions

View file

@ -8,8 +8,9 @@ interface Tag {
} }
interface Props { interface Props {
image: ImageMetadata; position?: "left" | "right";
alt: string; image?: ImageMetadata;
alt?: string;
href: string; href: string;
title: string; title: string;
description: string; description: string;
@ -25,35 +26,50 @@ const {
description, description,
label = "View Repository", label = "View Repository",
tags = [], tags = [],
position = "left",
} = Astro.props; } = Astro.props;
const leftProject = position == "left";
--- ---
<div <div
class="flex h-fit w-full flex-col justify-center px-10 lg:flex-row lg:px-20" class="flex h-fit w-full flex-col justify-center px-10 lg:px-20"
class:list={leftProject ? "lg:flex-row" : "lg:flex-row-reverse"}
> >
<div <div
class="image-container relative mb-5 flex aspect-video w-full [perspective:200px] lg:mr-5 lg:mb-0 lg:ml-auto lg:w-1/2" class="image-container relative mb-5 flex aspect-video w-full [perspective:200px] lg:mr-5 lg:mb-0 lg:ml-auto lg:w-1/2"
> >
<slot />
{
image && alt &&
(
<Image <Image
src={image} src={image}
alt={alt} alt={alt}
class=`absolute max-w-full class={`absolute max-w-full
aspect-video aspect-video
top-1/2 left-1/2 opacity-40
[transform:rotateY(5deg)_rotateX(0deg)_translateZ(-50px)_translateX(-45%)_translateY(-50%)] opacity-40 border-2 border-solid border-white rounded-xl`}
border-2 border-solid border-white rounded-xl` class:list={leftProject
? "[transform:translateX(50px)_translateY(5px)_rotateY(5deg)_rotateX(0deg)_translateZ(-50px)]"
: "[transform:translateZ(-50px)_rotateY(-3deg)]"}
width="700" width="700"
/> />
<Image <Image
src={image} src={image}
alt={alt} alt={alt}
width="700" width="700"
class=`absolute max-w-full class={`absolute max-w-full
aspect-video aspect-video
top-1/2 left-1/2 opacity-100
[transform:rotateY(5deg)_rotateX(0deg)_translateZ(-50px)_translateX(-43%)_translateY(-52%)] opacity-100 border-2 border-solid border-white rounded-xl`}
border-2 border-solid border-white rounded-xl` class:list={leftProject
? "[transform:rotateY(5deg)_rotateX(0deg)_translateX(60px)_translateZ(-50px)]"
: "[transform:translateZ(-50px)_rotateY(-3deg)]"}
/> />
)
}
<div <div
class="visit-div absolute top-1/2 left-1/2 aspect-video w-[700px] max-w-full [transform:translateX(-50%)_translateY(-50%)] rounded-xl border-2 border-solid border-white bg-[rgba(0,0,0,0.5)] opacity-0" class="visit-div absolute top-1/2 left-1/2 aspect-video w-[700px] max-w-full [transform:translateX(-50%)_translateY(-50%)] rounded-xl border-2 border-solid border-white bg-[rgba(0,0,0,0.5)] opacity-0"
> >
@ -74,18 +90,24 @@ const {
> >
</div> </div>
<div <div
class="flex h-fit w-full flex-col items-center justify-center lg:h-[340px] lg:w-1/2 lg:items-start lg:px-10" class="flex h-fit w-full flex-col items-center justify-center lg:h-[340px] lg:w-1/2 lg:px-10"
class:list={leftProject ? "lg:items-start" : "lg:items-end"}
>
<h2
class="mb-5 text-center text-3xl font-bold"
class:list={leftProject ? "lg:text-left" : "lg:text-right"}
> >
<h2 class="mb-5 text-center text-3xl font-bold lg:text-left">
{title} {title}
</h2> </h2>
<p <p
class="max-w-lg text-center text-xl text-balance lg:max-w-none lg:text-left" class="max-w-lg text-center text-xl text-balance lg:max-w-none"
class:list={leftProject ? "lg:text-left" : "lg:text-right"}
> >
{description} {description}
</p> </p>
<div <div
class="mt-5 flex w-full flex-row flex-wrap justify-center lg:justify-start" class="mt-5 flex w-full flex-row flex-wrap justify-center"
class:list={leftProject ? "lg:justify-start" : "lg:justify-end"}
> >
{ {
tags.map((tag) => ( tags.map((tag) => (
@ -110,8 +132,7 @@ const {
} }
@media (min-width: 1025px) { @media (min-width: 1025px) {
.image-container:hover > img { .image-container:hover > img {
transform: rotateY(0deg) rotateX(0deg) translateZ(0px) transform: rotateY(0deg) rotateX(0deg) translateZ(0px);
translateX(-50%) translateY(-50%);
} }
.image-container:hover > .visit-div { .image-container:hover > .visit-div {

View file

@ -1,121 +0,0 @@
---
import { Image } from "astro:assets";
interface Tag {
text: string;
textColor: string;
bgColor: string;
}
interface Props {
image: ImageMetadata;
alt: string;
href: string;
title: string;
description: string;
label?: string;
tags?: Tag[];
}
const {
image,
alt,
href,
title,
description,
label = "View Repository",
tags,
} = Astro.props;
---
<div
class="mt-10 flex h-fit w-full flex-col-reverse justify-center px-10 lg:flex-row lg:px-16"
>
<div
class="flex h-fit w-full flex-col items-center justify-center lg:h-[340px] lg:w-1/2 lg:items-end lg:px-10"
>
<h2 class="mb-5 text-center text-3xl font-bold lg:text-right">
{title}
</h2>
<p
class="max-w-lg text-center text-xl text-balance lg:max-w-none lg:text-right"
>
{description}
</p>
<div
class="mt-5 flex w-full flex-row flex-wrap justify-center lg:justify-end"
>
{
tags?.map((tag) => (
<div
class="my-1 mr-1 w-fit rounded-full px-3 py-1 transition-colors duration-300"
style={`
background-color: ${tag.bgColor || "black"};
color: ${tag.textColor || "white"};
`}
>
{tag.text}
</div>
))
}
</div>
</div>
<div class="mb-10 flex items-center justify-center lg:hidden">
<a
href={href}
target="_blank"
class="rounded-xl bg-blue-600 px-5 py-3 font-bold opacity-100 transition-colors duration-300 hover:bg-white hover:text-blue-500"
>{label}</a
>
</div>
<div
class="image-container relative mb-5 flex aspect-video w-full [perspective:200px] lg:mr-5 lg:mb-0 lg:ml-auto lg:w-1/2"
>
<Image
src={image}
alt={alt}
class=`absolute max-w-full
top-1/2 left-1/2 [transform:translateX(-48%)_translateY(-48%)_translateZ(-50px)_rotateY(-3deg)] opacity-40
border-2 border-solid border-white rounded-xl`
`
width="700"
/>
<Image
src={image}
alt={alt}
width="700"
class=`absolute max-w-full
top-1/2 left-1/2
[transform:translateX(-50%)_translateY(-50%)_translateZ(-50px)_rotateY(-3deg)] opacity-100
border-2 border-solid border-white rounded-xl`
/>
<div
class="visit-div absolute top-1/2 left-1/2 aspect-video w-[700px] max-w-full [transform:translateX(-50%)_translateY(-50%)] rounded-xl border-2 border-solid border-white bg-[rgba(0,0,0,0.5)] opacity-0"
>
<a
href="https://github.com/liyunze-coding/TwitchPlaysX"
target="_blank"
class="absolute top-1/2 left-1/2 hidden [transform:translateX(-50%)_translateY(-50%)] rounded-xl bg-blue-600 px-5 py-3 font-bold opacity-100 transition-colors duration-300 hover:bg-white hover:text-blue-500 lg:block"
>{label}</a
>
</div>
</div>
</div>
<style>
.image-container img {
transition: transform 0.5s ease-in-out;
}
@media (min-width: 1025px) {
.image-container:hover > img {
transform: rotateY(0deg) rotateX(0deg) translateZ(0px)
translateX(-50%) translateY(-50%);
}
.image-container:hover > .visit-div {
opacity: 100%;
transition: opacity 0.3s ease-in-out;
transition-delay: 0.3s;
}
}
</style>

View file

@ -3,8 +3,7 @@ import TaskWidget from "./TaskWidget.svelte";
import CoworkingGuide from "../images/projects/coworking-guide.webp"; import CoworkingGuide from "../images/projects/coworking-guide.webp";
import Wnrs from "../images/projects/wnrs.webp"; import Wnrs from "../images/projects/wnrs.webp";
import TwitchPlays from "../images/projects/twitchplays.webp"; import TwitchPlays from "../images/projects/twitchplays.webp";
import ProjectLeft from "./ProjectLeft.astro"; import Project from "./Project.astro";
import ProjectRight from "./ProjectRight.astro";
--- ---
<div <div
@ -12,7 +11,8 @@ import ProjectRight from "./ProjectRight.astro";
class="flex h-fit flex-col items-center [perspective:1000px]" class="flex h-fit flex-col items-center [perspective:1000px]"
> >
<div class="w-full max-w-[1440px]"> <div class="w-full max-w-[1440px]">
<ProjectLeft <Project
position="left"
image={CoworkingGuide} image={CoworkingGuide}
alt="coworking guide" alt="coworking guide"
href="https://coworking-guide.vercel.app/" href="https://coworking-guide.vercel.app/"
@ -37,55 +37,39 @@ import ProjectRight from "./ProjectRight.astro";
}, },
]} ]}
/> />
<div <Project
class="mt-10 flex h-fit w-full flex-col-reverse justify-center px-10 lg:flex-row lg:px-16" position="right"
> href="https://github.com/liyunze-coding/chat-task-tic-overlay-infinity"
<div title="Chat-task-tic widget"
class="flex h-fit w-full flex-col items-center justify-center lg:h-[340px] lg:w-1/2 lg:items-end lg:px-10" label="View Repository"
> description={`
<h2 class="mb-5 text-center text-3xl font-bold lg:text-right">
Chat-Task-Tic Widget
</h2>
<p
class="max-w-lg text-center text-xl text-balance lg:max-w-none lg:text-right"
>
Used by 100+ Twitch streamers, allow viewers and streamers Used by 100+ Twitch streamers, allow viewers and streamers
to add their own tasks to the widget for accountability! to add their own tasks to the widget for accountability!
Easy to setup and the customization possibilities are Easy to setup and the customization possibilities are
endless! endless!`}
</p>
<div
class="mt-5 flex w-full flex-row flex-wrap justify-center lg:justify-end"
> >
<div <TaskWidget
class="bg-html my-1 mr-1 w-fit rounded-full px-3 py-1 text-white transition-colors duration-300" classList={`
> widget
HTML [perspective:200px] absolute [transform:translateX(-10px)_translateY(5px)_translateZ(-40px)_rotateY(-3deg)]
</div> w-full h-full
<div opacity-10
class="my-1 mr-1 w-fit rounded-full bg-blue-700 px-3 py-1 text-white transition-colors duration-300" `}
> client:visible
CSS />
</div> <TaskWidget
<div classList={`
class="my-1 mr-1 w-fit rounded-full bg-yellow-400 px-3 py-1 text-black transition-colors duration-300" widget
> [perspective:200px] absolute
Javascript [transform:translateX(-20px)_translateZ(-40px)_rotateY(-3deg)]
</div> w-full h-full
</div> hover:[transform:translateX(0)_translateY(0)_translateZ(0)_rotateY(0)]`}
</div> client:visible
<div class="mb-10 flex items-center justify-center lg:hidden"> />
<a </Project>
href="https://github.com/liyunze-coding/chat-task-tic-overlay-infinity"
target="_blank"
class="rounded-xl bg-blue-600 px-5 py-3 font-bold opacity-100 transition-colors duration-300 hover:bg-white hover:text-blue-600"
>View repository</a
>
</div>
<TaskWidget client:visible />
</div>
<ProjectLeft <Project
position="left"
image={Wnrs} image={Wnrs}
alt="We're not really strangers" alt="We're not really strangers"
href="https://liyunze-coding.github.io/wnrs/" href="https://liyunze-coding.github.io/wnrs/"
@ -105,7 +89,8 @@ import ProjectRight from "./ProjectRight.astro";
}, },
]} ]}
/> />
<ProjectRight <Project
position="right"
image={TwitchPlays} image={TwitchPlays}
alt="Twitch Plays X" alt="Twitch Plays X"
href="https://github.com/liyunze-coding/TwitchPlaysX" href="https://github.com/liyunze-coding/TwitchPlaysX"
@ -130,14 +115,17 @@ import ProjectRight from "./ProjectRight.astro";
</div> </div>
</div> </div>
<style> <style is:global>
.image-container img { .image-container img,
.image-container .widget {
transition: transform 0.5s ease-in-out; transition: transform 0.5s ease-in-out;
} }
@media (min-width: 1025px) { @media (min-width: 1025px) {
.image-container:hover > img { .image-container:hover > img,
transform: rotateY(0deg) rotateX(0deg) translateZ(0px) .image-container:hover .widget {
translateX(-50%) translateY(-50%); transform: rotateY(0deg) rotateX(0deg) translateZ(0px) translateX(0)
translateY(0);
} }
.image-container:hover > .visit-div { .image-container:hover > .visit-div {

View file

@ -113,17 +113,17 @@
task: "anki flashcards", task: "anki flashcards",
}, },
]; ];
interface Props {
classList: string;
}
let { classList }: Props = $props();
</script> </script>
<div <div class={`${classList} aspect-video`}>
class="image-container relative mr-0 mb-3 ml-0 aspect-video [perspective:200px] md:mr-5 lg:mr-0 lg:mb-0 lg:ml-1 lg:w-1/2"
>
<!-- task bot --> <!-- task bot -->
<div <div class="task-widget absolute flex h-[99%] w-full flex-col">
class="task-widget absolute top-1/2 left-1/2 flex
h-[99%] w-full
[transform:translateX(-50%)_translateY(-49%)_translateZ(-50px)_rotateY(-3deg)] flex-col opacity-30"
>
<div <div
class="flex w-full flex-row items-center justify-between class="flex w-full flex-row items-center justify-between
rounded-lg rounded-lg
@ -160,84 +160,9 @@
</div> </div>
</div> </div>
</div> </div>
<div
class="task-widget absolute top-1/2 left-1/2
flex h-[99%]
w-full [transform:translateX(-51%)_translateY(-50%)_translateZ(-50px)_rotateY(-3deg)] flex-col"
>
<div
class="flex w-full flex-row items-center justify-between
rounded-lg
border-2 border-solid
border-white bg-black
px-5 py-2 font-['Fredoka',_sans-serif] text-xl
text-white"
>
<span>Hover here!</span>
<span>0/100+</span>
</div>
<div
class="scroll-parent relative h-full overflow-hidden font-['Fredoka',_sans-serif]"
>
<div class="scroll-element primary absolute w-full px-1">
{#each streamers as { streamer, task }}
<div
class="my-2 w-full overflow-hidden rounded-md bg-[#404040] px-5 py-1
overflow-ellipsis whitespace-nowrap text-white"
>
O {streamer} : {task}
</div>
{/each}
</div>
<div class="scroll-element secondary absolute w-full px-1">
{#each streamers as { streamer, task }}
<div
class="my-2 w-full overflow-hidden rounded-md bg-[#404040] px-5 py-1
overflow-ellipsis whitespace-nowrap text-white"
>
O {streamer} : {task}
</div>
{/each}
</div>
</div>
</div>
<div
class="visit-div absolute top-1/2 left-1/2 h-full w-full
[transform:translateX(-50%)_translateY(-50%)]
overflow-hidden rounded-md border-2
border-solid border-white bg-[rgba(0,0,0,0.5)]
opacity-0"
>
<a
href="https://github.com/liyunze-coding/chat-task-tic-overlay-infinity"
target="_blank"
class="absolute top-1/2 left-1/2 hidden
[transform:translateX(-50%)_translateY(-50%)] rounded-xl bg-blue-500 px-5 py-3
font-bold
opacity-100 transition-colors
duration-300 hover:bg-white
hover:text-blue-500 lg:block">View repository</a
>
</div>
</div> </div>
<style> <style>
.image-container .task-widget {
transition: transform 0.5s ease-in-out;
}
@media (min-width: 1025px) {
.image-container:hover > .task-widget {
transform: rotateY(0deg) rotateX(0deg) translateZ(0px)
translateX(-50%) translateY(-50%);
}
.image-container:hover > .visit-div {
opacity: 100%;
transition: opacity 0.3s ease-in-out;
transition-delay: 0.3s;
}
}
.primary { .primary {
animation: primary 20s linear infinite; animation: primary 20s linear infinite;
} }