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"
> >
<Image <slot />
src={image} {
alt={alt} image && alt &&
class=`absolute max-w-full (
aspect-video <Image
top-1/2 left-1/2 src={image}
[transform:rotateY(5deg)_rotateX(0deg)_translateZ(-50px)_translateX(-45%)_translateY(-50%)] opacity-40 alt={alt}
border-2 border-solid border-white rounded-xl` class={`absolute max-w-full
width="700" aspect-video
/> opacity-40
<Image border-2 border-solid border-white rounded-xl`}
src={image} class:list={leftProject
alt={alt} ? "[transform:translateX(50px)_translateY(5px)_rotateY(5deg)_rotateX(0deg)_translateZ(-50px)]"
width="700" : "[transform:translateZ(-50px)_rotateY(-3deg)]"}
class=`absolute max-w-full width="700"
aspect-video />
top-1/2 left-1/2 <Image
[transform:rotateY(5deg)_rotateX(0deg)_translateZ(-50px)_translateX(-43%)_translateY(-52%)] opacity-100 src={image}
border-2 border-solid border-white rounded-xl` alt={alt}
/> width="700"
class={`absolute max-w-full
aspect-video
opacity-100
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 lg:text-left"> <h2
class="mb-5 text-center text-3xl font-bold"
class:list={leftProject ? "lg:text-left" : "lg:text-right"}
>
{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"
title="Chat-task-tic widget"
label="View Repository"
description={`
Used by 100+ Twitch streamers, allow viewers and streamers
to add their own tasks to the widget for accountability!
Easy to setup and the customization possibilities are
endless!`}
> >
<div <TaskWidget
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" classList={`
> widget
<h2 class="mb-5 text-center text-3xl font-bold lg:text-right"> [perspective:200px] absolute [transform:translateX(-10px)_translateY(5px)_translateZ(-40px)_rotateY(-3deg)]
Chat-Task-Tic Widget w-full h-full
</h2> opacity-10
<p `}
class="max-w-lg text-center text-xl text-balance lg:max-w-none lg:text-right" client:visible
> />
Used by 100+ Twitch streamers, allow viewers and streamers <TaskWidget
to add their own tasks to the widget for accountability! classList={`
Easy to setup and the customization possibilities are widget
endless! [perspective:200px] absolute
</p> [transform:translateX(-20px)_translateZ(-40px)_rotateY(-3deg)]
<div w-full h-full
class="mt-5 flex w-full flex-row flex-wrap justify-center lg:justify-end" hover:[transform:translateX(0)_translateY(0)_translateZ(0)_rotateY(0)]`}
> client:visible
<div />
class="bg-html my-1 mr-1 w-fit rounded-full px-3 py-1 text-white transition-colors duration-300" </Project>
>
HTML
</div>
<div
class="my-1 mr-1 w-fit rounded-full bg-blue-700 px-3 py-1 text-white transition-colors duration-300"
>
CSS
</div>
<div
class="my-1 mr-1 w-fit rounded-full bg-yellow-400 px-3 py-1 text-black transition-colors duration-300"
>
Javascript
</div>
</div>
</div>
<div class="mb-10 flex items-center justify-center lg:hidden">
<a
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,24 +113,24 @@
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
border-2 border-solid border-2 border-solid
border-white bg-black border-white bg-black
px-5 py-2 font-['Fredoka',_sans-serif] text-xl px-5 py-2 font-['Fredoka',_sans-serif] text-xl
text-white" text-white"
> >
<span>Hover here!</span> <span>Hover here!</span>
<span>0/100+</span> <span>0/100+</span>
@ -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;
} }