mirror of
https://github.com/JuLi0n21/homepage.git
synced 2026-04-19 15:20:05 +00:00
add client side shuffel so every page refresh it stays fresh
This commit is contained in:
5
.prettierrc
Normal file
5
.prettierrc
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"plugins": ["prettier-plugin-astro"],
|
||||
"overrides":
|
||||
[{ "files": "*.astro", "*.md", "*.mdx", "options": { "parser": "astro" } }],
|
||||
}
|
||||
47
package-lock.json
generated
47
package-lock.json
generated
@@ -13,9 +13,12 @@
|
||||
"@astrojs/rss": "^4.0.15",
|
||||
"@astrojs/sitemap": "^3.7.0",
|
||||
"astro": "^5.17.1",
|
||||
"prettier": "^3.8.1",
|
||||
"sharp": "^0.34.3",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "3.8.1",
|
||||
"prettier-plugin-astro": "0.14.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@astrojs/check": {
|
||||
@@ -5005,6 +5008,21 @@
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-astro": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-astro/-/prettier-plugin-astro-0.14.1.tgz",
|
||||
"integrity": "sha512-RiBETaaP9veVstE4vUwSIcdATj6dKmXljouXc/DDNwBSPTp8FRkLGDSGFClKsAFeeg+13SB0Z1JZvbD76bigJw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "^2.9.1",
|
||||
"prettier": "^3.0.0",
|
||||
"sass-formatter": "^0.7.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prismjs": {
|
||||
"version": "1.30.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
|
||||
@@ -5447,6 +5465,23 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/s.color": {
|
||||
"version": "0.0.15",
|
||||
"resolved": "https://registry.npmjs.org/s.color/-/s.color-0.0.15.tgz",
|
||||
"integrity": "sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sass-formatter": {
|
||||
"version": "0.7.9",
|
||||
"resolved": "https://registry.npmjs.org/sass-formatter/-/sass-formatter-0.7.9.tgz",
|
||||
"integrity": "sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"suf-log": "^2.5.3"
|
||||
}
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz",
|
||||
@@ -5681,6 +5716,16 @@
|
||||
"inline-style-parser": "0.2.7"
|
||||
}
|
||||
},
|
||||
"node_modules/suf-log": {
|
||||
"version": "2.5.3",
|
||||
"resolved": "https://registry.npmjs.org/suf-log/-/suf-log-2.5.3.tgz",
|
||||
"integrity": "sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"s.color": "0.0.15"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz",
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
"@astrojs/rss": "^4.0.15",
|
||||
"@astrojs/sitemap": "^3.7.0",
|
||||
"astro": "^5.17.1",
|
||||
"prettier": "^3.8.1",
|
||||
"sharp": "^0.34.3",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "3.8.1",
|
||||
"prettier-plugin-astro": "0.14.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
// Import the global.css file here so that it is included on
|
||||
// all pages through the use of the <BaseHead /> component.
|
||||
import '@styles/global.css';
|
||||
import type { ImageMetadata } from 'astro';
|
||||
import { SITE_TITLE } from '@ptypes/consts.ts';
|
||||
import "@styles/global.css";
|
||||
import type { ImageMetadata } from "astro";
|
||||
import { SITE_TITLE } from "@ptypes/consts.ts";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
image?: ImageMetadata;
|
||||
title: string;
|
||||
description: string;
|
||||
image?: ImageMetadata;
|
||||
}
|
||||
|
||||
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
||||
@@ -23,16 +23,28 @@ const { title, description } = Astro.props;
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title={SITE_TITLE}
|
||||
href={new URL('rss.xml', Astro.site)}
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title={SITE_TITLE}
|
||||
href={new URL("rss.xml", Astro.site)}
|
||||
/>
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
|
||||
<!-- Font preloads -->
|
||||
<link rel="preload" href="/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin />
|
||||
<link rel="preload" href="/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin />
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/atkinson-regular.woff"
|
||||
as="font"
|
||||
type="font/woff"
|
||||
crossorigin
|
||||
/>
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/atkinson-bold.woff"
|
||||
as="font"
|
||||
type="font/woff"
|
||||
crossorigin
|
||||
/>
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href={canonicalURL} />
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
---
|
||||
interface Props {
|
||||
date: Date;
|
||||
date: Date;
|
||||
}
|
||||
|
||||
const { date } = Astro.props;
|
||||
---
|
||||
|
||||
<time datetime={date.toISOString()}>
|
||||
{
|
||||
date.toLocaleDateString('en-us', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})
|
||||
}
|
||||
{
|
||||
date.toLocaleDateString("en-us", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
})
|
||||
}
|
||||
</time>
|
||||
|
||||
@@ -1,46 +1,49 @@
|
||||
---
|
||||
import { SITE_TITLE } from '@ptypes/consts.ts';
|
||||
import HeaderLink from '@components/HeaderLink.astro';
|
||||
import '@styles/indexHoverCss.css';
|
||||
import { getCollection } from 'astro:content';
|
||||
import { SITE_TITLE } from "@ptypes/consts.ts";
|
||||
import HeaderLink from "@components/HeaderLink.astro";
|
||||
import "@styles/indexHoverCss.css";
|
||||
import { getCollection } from "astro:content";
|
||||
|
||||
const personalPosts = (await getCollection('personal')).sort(
|
||||
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||
const personalPosts = (await getCollection("personal")).sort(
|
||||
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||
);
|
||||
const uniPosts = (await getCollection('uni')).sort(
|
||||
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||
const uniPosts = (await getCollection("uni")).sort(
|
||||
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||
);
|
||||
---
|
||||
<!DOCTYPE html>
|
||||
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<h2><a href="/">{SITE_TITLE}</a></h2>
|
||||
<ul>
|
||||
<h4>Personal</h4>
|
||||
{
|
||||
personalPosts.map((post) => (
|
||||
<li>
|
||||
<HeaderLink href={`/blog/personal/${post.id}/`}>{post.data.title}</HeaderLink>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
<hr>
|
||||
<h4>University</h4>
|
||||
<h6>(Group Projects)</h6>
|
||||
{
|
||||
uniPosts.map((post) => (
|
||||
<li>
|
||||
<HeaderLink href={`/blog/university/${post.id}/`}>{post.data.title}</HeaderLink>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</body>
|
||||
<head>
|
||||
<style></style>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<h2><a href="/">{SITE_TITLE}</a></h2>
|
||||
<ul>
|
||||
<h4>Personal</h4>
|
||||
{
|
||||
personalPosts.map((post) => (
|
||||
<li>
|
||||
<HeaderLink href={`/blog/personal/${post.id}/`}>
|
||||
{post.data.title}
|
||||
</HeaderLink>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
<hr />
|
||||
<h4>University</h4>
|
||||
<h6>(Group Projects)</h6>
|
||||
{
|
||||
uniPosts.map((post) => (
|
||||
<li>
|
||||
<HeaderLink href={`/blog/university/${post.id}/`}>
|
||||
{post.data.title}
|
||||
</HeaderLink>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
---
|
||||
import type { HTMLAttributes } from 'astro/types';
|
||||
import type { HTMLAttributes } from "astro/types";
|
||||
|
||||
type Props = HTMLAttributes<'a'>;
|
||||
type Props = HTMLAttributes<"a">;
|
||||
|
||||
const { href, class: className, ...props } = Astro.props;
|
||||
const pathname = Astro.url.pathname.replace(import.meta.env.BASE_URL, '');
|
||||
const pathname = Astro.url.pathname.replace(import.meta.env.BASE_URL, "");
|
||||
const subpath = pathname.match(/[^\/]+/g);
|
||||
const isActive = href === pathname || href === '/' + (subpath?.[0] || '');
|
||||
const isActive = href === pathname || href === "/" + (subpath?.[0] || "");
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<a href={href} class:list={[className, { active: isActive }]} {...props}>
|
||||
<slot />
|
||||
</a>
|
||||
</body>
|
||||
<head> </head>
|
||||
<body>
|
||||
<a href={href} class:list={[className, { active: isActive }]} {...props}>
|
||||
<slot />
|
||||
</a>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,52 +1,58 @@
|
||||
---
|
||||
import BaseHead from '@components/BaseHead.astro';
|
||||
import Header from '@components/Header.astro';
|
||||
import GithubIcon from '@assets/github.svg'
|
||||
import BaseHead from "@components/BaseHead.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import GithubIcon from "@assets/github.svg";
|
||||
|
||||
const { title, description, gitLink } = Astro.props;
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<BaseHead title={title} description={description} />
|
||||
<!-- To add meta data here later -->
|
||||
<style>
|
||||
body {
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
main { display: flex;
|
||||
flex-direction: row;
|
||||
align-items: start;
|
||||
justify-content: space-between;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
<head>
|
||||
<BaseHead title={title} description={description} />
|
||||
<!-- To add meta data here later -->
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: start;
|
||||
justify-content: space-between;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
width: 0;
|
||||
padding: 1cm;
|
||||
margin: 1cm;
|
||||
}
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
width: 0;
|
||||
padding: 1cm;
|
||||
margin: 1cm;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<Header />
|
||||
<div class="wrapper">
|
||||
<a href={gitLink ?? 'https://github.com/juli0n21'} style="position: relative;">
|
||||
<GithubIcon style="position: absolute; right:0;" width="50px" height="50px" />
|
||||
</a>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</body>
|
||||
<body>
|
||||
<main>
|
||||
<Header />
|
||||
<div class="wrapper">
|
||||
<a
|
||||
href={gitLink ?? "https://github.com/juli0n21"}
|
||||
style="position: relative;"
|
||||
>
|
||||
<GithubIcon
|
||||
style="position: absolute; right:0;"
|
||||
width="50px"
|
||||
height="50px"
|
||||
/>
|
||||
</a>
|
||||
<slot />
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
import { getCollection, render } from 'astro:content';
|
||||
import BlogPost from '@layouts/BlogPost.astro';
|
||||
import { getCollection, render } from "astro:content";
|
||||
import BlogPost from "@layouts/BlogPost.astro";
|
||||
import type {
|
||||
InferGetStaticParamsType,
|
||||
InferGetStaticPropsType,
|
||||
@@ -8,11 +8,11 @@ import type {
|
||||
} from "astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getCollection('personal');
|
||||
return posts.map(post => ({
|
||||
params: { id: post.id },
|
||||
props: { post },
|
||||
}));
|
||||
const posts = await getCollection("personal");
|
||||
return posts.map((post) => ({
|
||||
params: { id: post.id },
|
||||
props: { post },
|
||||
}));
|
||||
}
|
||||
|
||||
getStaticPaths satisfies GetStaticPaths;
|
||||
@@ -25,5 +25,5 @@ const { Content } = await render(post);
|
||||
---
|
||||
|
||||
<BlogPost {...post.data}>
|
||||
<Content />
|
||||
<Content />
|
||||
</BlogPost>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
import { getCollection, render } from 'astro:content';
|
||||
import BlogPost from '@layouts/BlogPost.astro';
|
||||
import { getCollection, render } from "astro:content";
|
||||
import BlogPost from "@layouts/BlogPost.astro";
|
||||
import type {
|
||||
InferGetStaticParamsType,
|
||||
InferGetStaticPropsType,
|
||||
@@ -8,11 +8,11 @@ import type {
|
||||
} from "astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getCollection('uni');
|
||||
return posts.map(post => ({
|
||||
params: { id: post.id },
|
||||
props: { post },
|
||||
}));
|
||||
const posts = await getCollection("uni");
|
||||
return posts.map((post) => ({
|
||||
params: { id: post.id },
|
||||
props: { post },
|
||||
}));
|
||||
}
|
||||
|
||||
getStaticPaths satisfies GetStaticPaths;
|
||||
@@ -25,5 +25,5 @@ const { Content } = await render(post);
|
||||
---
|
||||
|
||||
<BlogPost {...post.data}>
|
||||
<Content />
|
||||
<Content />
|
||||
</BlogPost>
|
||||
|
||||
@@ -1,143 +1,182 @@
|
||||
---
|
||||
import BaseHead from '@components/BaseHead.astro';
|
||||
import Header from '@components/Header.astro';
|
||||
import { SITE_DESCRIPTION, SITE_TITLE } from '@ptypes/consts.ts';
|
||||
import BaseHead from "@components/BaseHead.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import { SITE_DESCRIPTION, SITE_TITLE } from "@ptypes/consts.ts";
|
||||
|
||||
let top: string[] = [];
|
||||
let bottom: string[] = [];
|
||||
const api: string =
|
||||
"https://fshare.kami.boo/api/server/folder/cmm9jbqvt00025omwu5cnpw9i";
|
||||
const response = await fetch(api);
|
||||
|
||||
let shuffled: string[] = [];
|
||||
let images: string[] = [];
|
||||
const api: string = 'https://fshare.kami.boo/api/server/folder/cmm9jbqvt00025omwu5cnpw9i';
|
||||
const response = await fetch(api)
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const files = data.files.map((f: Response) => `https://fshare.kami.boo/raw${f.url.substring(2)}`).sort(() => Math.random() - .5) as string[];
|
||||
const middle = Math.round(files.length / 2);
|
||||
shuffled = files.slice(0,middle);
|
||||
images = files.slice(middle);
|
||||
} else {
|
||||
const errorBody = await response.text();
|
||||
console.error("FAILED TO FETCH", response.status, response.statusText, errorBody);
|
||||
process.exit(1);
|
||||
}
|
||||
const data = await response.json();
|
||||
const files = data.files.map(
|
||||
(f: Response) => `https://fshare.kami.boo/raw${f.url.substring(2)}`,
|
||||
) as string[];
|
||||
|
||||
const middle = Math.round(files.length / 2);
|
||||
top = files.slice(0, middle);
|
||||
bottom = files.slice(middle);
|
||||
} else {
|
||||
const errorBody = await response.text();
|
||||
console.error(
|
||||
"FAILED TO FETCH",
|
||||
response.status,
|
||||
response.statusText,
|
||||
errorBody,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
|
||||
<style>
|
||||
.layout {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
<head>
|
||||
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
|
||||
<style>
|
||||
.layout {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
z-index: 10;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: white;
|
||||
}
|
||||
.sidebar {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
z-index: 10;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.scrolling-gallery {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
margin: 1cm;
|
||||
}
|
||||
.scrolling-gallery {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
margin: 1cm;
|
||||
}
|
||||
|
||||
main {
|
||||
background: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
main {
|
||||
background: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gallery-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4rem;
|
||||
transform: rotate(-8deg) scale(1.3);
|
||||
}
|
||||
.gallery-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4rem;
|
||||
transform: rotate(-8deg) scale(1.3);
|
||||
}
|
||||
|
||||
.scroll-track {
|
||||
display: flex;
|
||||
gap: 3rem;
|
||||
width: max-content;
|
||||
animation: infiniteScroll 500s linear infinite;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
.scroll-track {
|
||||
display: flex;
|
||||
gap: 3rem;
|
||||
width: max-content;
|
||||
animation: infiniteScroll 500s linear infinite;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
.track-reverse {
|
||||
animation: infiniteScrollReverse 600s linear infinite;
|
||||
}
|
||||
.track-reverse {
|
||||
animation: infiniteScrollReverse 600s linear infinite;
|
||||
}
|
||||
|
||||
.tilted-container {
|
||||
width: 220px;
|
||||
height: 280px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
transition: transform 0.3s ease;
|
||||
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
|
||||
}
|
||||
.tilted-container {
|
||||
width: 220px;
|
||||
height: 280px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
transition: transform 0.3s ease;
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.tilted-container:hover {
|
||||
transform: scale(1.10);
|
||||
z-index: 1;
|
||||
}
|
||||
.tilted-container:hover {
|
||||
transform: scale(1.1);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tilted-container img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
.tilted-container img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes infiniteScroll {
|
||||
0% { transform: translateX(0%); }
|
||||
100% { transform: translateX(-50%); }
|
||||
}
|
||||
@keyframes infiniteScroll {
|
||||
0% {
|
||||
transform: translateX(0%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes infiniteScrollReverse {
|
||||
0% { transform: translateX(0%); }
|
||||
100% { transform: translateX(50%) }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="layout">
|
||||
<aside class="sidebar">
|
||||
<Header />
|
||||
</aside>
|
||||
@keyframes infiniteScrollReverse {
|
||||
0% {
|
||||
transform: translateX(0%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(50%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="layout">
|
||||
<aside class="sidebar">
|
||||
<Header />
|
||||
</aside>
|
||||
|
||||
<main class="scrolling-gallery">
|
||||
<div class="gallery-wrapper">
|
||||
<div class="scroll-track">
|
||||
{[...images, ...images].map((src) => (
|
||||
<a class="tilted-container" href={src} target="_blank">
|
||||
<img src={src} loading="lazy" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
{
|
||||
<main class="scrolling-gallery">
|
||||
<div class="gallery-wrapper">
|
||||
<div id="top-images" class="scroll-track">
|
||||
{
|
||||
[...top, ...top].map((src) => (
|
||||
<a class="tilted-container" href={src} target="_blank">
|
||||
<img src={src} loading="lazy" />
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
{}
|
||||
<div id="bottom-images" class="scroll-track track-reverse">
|
||||
{
|
||||
[...bottom, ...bottom].map((src) => (
|
||||
<a class="tilted-container" href={src} target="_blank">
|
||||
<img src={src} loading="lazy" />
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<script>
|
||||
function shuffleImages(selector: string) {
|
||||
const track = document.querySelector(selector);
|
||||
if (!track) return;
|
||||
|
||||
}
|
||||
<div class="scroll-track track-reverse">
|
||||
{[...shuffled,...shuffled].map((src) => (
|
||||
<a class="tilted-container" href={src} target="_blank">
|
||||
<img src={src} loading="lazy" />
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
const items = Array.from(track.children);
|
||||
const shuffled = items.sort(() => Math.random() - 0.5);
|
||||
|
||||
track.innerHTML = "";
|
||||
|
||||
const fragment = document.createDocumentFragment();
|
||||
[...shuffled, ...shuffled].forEach((item) => {
|
||||
fragment.appendChild(item.cloneNode(true));
|
||||
});
|
||||
|
||||
track.appendChild(fragment);
|
||||
}
|
||||
|
||||
shuffleImages("#top-images");
|
||||
shuffleImages("#bottom-images");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user