v3
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
"use client";
|
||||
|
||||
import { Fragment } from "react";
|
||||
|
||||
import CurvyRect from "@/components/shared/layout/curvy-rect";
|
||||
|
||||
import CenterStar from "./_svg/CenterStar";
|
||||
|
||||
export default function HomeHeroBackground() {
|
||||
return (
|
||||
<div className="overlay contain-layout pointer-events-none lg-max:hidden">
|
||||
<div className="top-100 h-[calc(100%-99px)] border-border-faint border-y w-full left-0 absolute" />
|
||||
|
||||
<div className="cw-[1314px] z-[105] absolute top-0 border-x border-border-faint h-full">
|
||||
<div className="text-mono-x-small font-mono text-black-alpha-12 select-none">
|
||||
<div className="absolute top-111 -left-1 w-102 text-center">
|
||||
{" "}
|
||||
[ 200 OK ]{" "}
|
||||
</div>
|
||||
<div className="absolute bottom-10 -left-1 w-102 text-center">
|
||||
{" "}
|
||||
[ .JSON ]{" "}
|
||||
</div>
|
||||
|
||||
<div className="absolute top-111 -right-1 w-102 text-center">
|
||||
{" "}
|
||||
[ SCRAPE ]{" "}
|
||||
</div>
|
||||
<div className="absolute bottom-10 -right-1 w-102 text-center">
|
||||
{" "}
|
||||
[ .MD ]{" "}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="top-302 h-1 left-0 bg-border-faint w-303 absolute" />
|
||||
<div className="top-403 h-1 left-0 bg-border-faint w-303 absolute" />
|
||||
<div className="top-504 h-1 left-100 bg-border-faint w-203 absolute" />
|
||||
|
||||
<div className="top-302 h-1 right-0 bg-border-faint w-303 absolute" />
|
||||
<div className="top-403 h-1 right-0 bg-border-faint w-303 absolute" />
|
||||
<div className="top-504 h-1 right-100 bg-border-faint w-203 absolute" />
|
||||
|
||||
{Array.from({ length: 2 }, (_, i) => (
|
||||
<Fragment key={i}>
|
||||
<CurvyRect
|
||||
bottomLeft={i === 1}
|
||||
bottomRight={i === 0}
|
||||
className="w-101 h-[calc(100%-99px)] top-100 absolute"
|
||||
style={{ [i === 0 ? "left" : "right"]: -101 }}
|
||||
/>
|
||||
|
||||
<CurvyRect
|
||||
className="w-102 h-203 top-100 absolute"
|
||||
style={{ [i === 0 ? "left" : "right"]: -1 }}
|
||||
allSides
|
||||
/>
|
||||
<CurvyRect
|
||||
className="size-102 top-302 absolute"
|
||||
style={{ [i === 0 ? "left" : "right"]: -1 }}
|
||||
allSides
|
||||
/>
|
||||
<CurvyRect
|
||||
className="w-102 h-203 top-403 absolute"
|
||||
style={{ [i === 0 ? "left" : "right"]: -1 }}
|
||||
allSides
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="cw-[910px] absolute top-100 border-x border-border-faint h-[calc(100%-99px)]" />
|
||||
<div className="cw-[708px] absolute top-100 border-x border-border-faint h-[calc(100%-99px)]">
|
||||
<CenterStar className="absolute top-77 -right-24 z-[1]" />
|
||||
<CenterStar className="absolute top-77 -left-24 z-[1]" />
|
||||
</div>
|
||||
|
||||
<CurvyRect
|
||||
className="cw-[708px] absolute top-100 h-[calc(100%-99px)]"
|
||||
bottom
|
||||
/>
|
||||
|
||||
<div className="cw-[506px] absolute top-100 border-x border-border-faint h-102" />
|
||||
<div className="cw-[304px] absolute top-100 border-x border-border-faint h-102" />
|
||||
<div className="cw-[102px] absolute top-100 border-x border-border-faint h-102" />
|
||||
|
||||
<div className="top-201 h-1 bg-border-faint cw-[1112px] absolute" />
|
||||
|
||||
<div className="cw-[1112px] absolute top-0 h-full">
|
||||
<CurvyRect className="w-full absolute top-full h-100 left-0" top />
|
||||
<CurvyRect
|
||||
className="w-100 absolute top-full h-100 -left-99"
|
||||
topRight
|
||||
/>
|
||||
<CurvyRect
|
||||
className="w-100 absolute top-full h-100 -right-99"
|
||||
topLeft
|
||||
/>
|
||||
|
||||
{Array.from({ length: 5 }, (_, i) => (
|
||||
<Fragment key={i}>
|
||||
<CurvyRect
|
||||
className="size-102 absolute left-0"
|
||||
style={{
|
||||
top: 100 + i * 101,
|
||||
}}
|
||||
allSides
|
||||
/>
|
||||
|
||||
<CurvyRect
|
||||
className="size-102 absolute right-0"
|
||||
style={{
|
||||
top: 100 + i * 101,
|
||||
}}
|
||||
allSides
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
|
||||
<CurvyRect
|
||||
className="size-102 absolute left-101 top-100"
|
||||
bottomLeft
|
||||
top
|
||||
/>
|
||||
<CurvyRect
|
||||
className="size-102 absolute left-101 top-201"
|
||||
bottom
|
||||
topLeft
|
||||
/>
|
||||
|
||||
<CurvyRect
|
||||
className="size-102 absolute right-101 top-100"
|
||||
bottomRight
|
||||
top
|
||||
/>
|
||||
<CurvyRect
|
||||
className="size-102 absolute right-101 top-201"
|
||||
bottom
|
||||
topRight
|
||||
/>
|
||||
|
||||
{Array.from({ length: 3 }, (_, i) => (
|
||||
<Fragment key={i}>
|
||||
<CurvyRect
|
||||
className="size-102 absolute left-101"
|
||||
style={{
|
||||
top: 302 + i * 101,
|
||||
}}
|
||||
allSides
|
||||
/>
|
||||
|
||||
<CurvyRect
|
||||
className="size-102 absolute right-101"
|
||||
style={{
|
||||
top: 302 + i * 101,
|
||||
}}
|
||||
allSides
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
|
||||
<CurvyRect
|
||||
className="size-102 absolute top-100 left-202"
|
||||
bottomRight
|
||||
top
|
||||
/>
|
||||
|
||||
{Array.from({ length: 5 }, (_, i) => (
|
||||
<CurvyRect
|
||||
className="size-102 absolute top-100"
|
||||
key={i}
|
||||
style={{ left: 303 + i * 101 }}
|
||||
allSides
|
||||
/>
|
||||
))}
|
||||
|
||||
<CurvyRect
|
||||
className="size-102 absolute top-100 right-202"
|
||||
bottomLeft
|
||||
top
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { Connector } from "@/components/shared/layout/curvy-rect";
|
||||
import {
|
||||
useHeaderContext,
|
||||
useHeaderHeight,
|
||||
} from "@/components/shared/header/HeaderContext";
|
||||
import { cn } from "@/utils/cn";
|
||||
|
||||
export const BackgroundOuterPiece = () => {
|
||||
const [noRender, setNoRender] = useState(false);
|
||||
const { dropdownContent } = useHeaderContext();
|
||||
const { headerHeight } = useHeaderHeight();
|
||||
|
||||
useEffect(() => {
|
||||
const heroContent = document.getElementById("hero-content");
|
||||
if (!heroContent) {
|
||||
// If hero-content doesn't exist, don't render the background piece
|
||||
setNoRender(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const heroContentHeight = heroContent.clientHeight;
|
||||
|
||||
const onScroll = () => {
|
||||
setNoRender(window.scrollY > heroContentHeight - 120);
|
||||
};
|
||||
|
||||
onScroll();
|
||||
|
||||
window.addEventListener("scroll", onScroll);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("scroll", onScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"cw-[1335px] transition-all z-[105] absolute top-0 flex justify-between h-[calc(100%+21px)] duration-[200ms] pointer-events-none",
|
||||
{ "opacity-0": noRender || dropdownContent || !headerHeight },
|
||||
)}
|
||||
style={{
|
||||
paddingTop: headerHeight - 10,
|
||||
}}
|
||||
>
|
||||
<div className="h-[3000px] w-[calc(100%-21px)] left-[10.5px] absolute bottom-21 border-x border-border-faint" />
|
||||
|
||||
<Connector className="sticky" style={{ top: headerHeight - 10 }} />
|
||||
<Connector className="sticky" style={{ top: headerHeight - 10 }} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
export default function CenterStar({
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
fill="none"
|
||||
height="47"
|
||||
viewBox="0 0 47 47"
|
||||
width="47"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M24 18C24 21.3137 26.6863 24 30 24H34V25H30C26.6863 25 24 27.6863 24 31V35H23V31C23 27.6863 20.3137 25 17 25H13V24H17C20.3137 24 23 21.3137 23 18V14H24V18Z"
|
||||
fill="var(--heat-100)"
|
||||
fillOpacity="1"
|
||||
/>
|
||||
<circle cx="23.5" cy="23.5" r="23" stroke="#EDEDED" strokeOpacity="1" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import Link from "next/link";
|
||||
|
||||
export default function HomeHeroBadge() {
|
||||
return (
|
||||
<Link
|
||||
className="p-4 rounded-full flex w-max mx-auto mb-12 lg:mb-16 items-center relative inside-border before:border-border-faint group"
|
||||
href="#"
|
||||
onClick={(e) => e.preventDefault()}
|
||||
>
|
||||
<div className="px-8 text-label-x-small">Website Builder</div>
|
||||
|
||||
<div className="p-1">
|
||||
<div className="size-18 bg-accent-black flex-center rounded-full group-hover:bg-heat-100 transition-all group-hover:w-30">
|
||||
<svg
|
||||
fill="none"
|
||||
height="8"
|
||||
viewBox="0 0 10 8"
|
||||
width="10"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
className="transition-all -translate-x-2 group-hover:translate-x-0"
|
||||
d="M6 1L9 4L6 7"
|
||||
stroke="white"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.25"
|
||||
/>
|
||||
|
||||
<path
|
||||
className="transition-all -translate-x-3 group-hover:translate-x-0 scale-x-[0] group-hover:scale-x-[1] origin-right"
|
||||
d="M1 4L9 4"
|
||||
stroke="white"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.25"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import Link from "next/link";
|
||||
|
||||
import { Connector } from "@/components/shared/layout/curvy-rect";
|
||||
import HeroFlame from "@/components/shared/effects/flame/hero-flame";
|
||||
|
||||
import HomeHeroBackground from "./Background/Background";
|
||||
import { BackgroundOuterPiece } from "./Background/BackgroundOuterPiece";
|
||||
import HomeHeroBadge from "./Badge/Badge";
|
||||
import HomeHeroPixi from "./Pixi/Pixi";
|
||||
import HomeHeroTitle from "./Title/Title";
|
||||
import HeroInput from "../hero-input/HeroInput";
|
||||
import HeroScraping from "../hero-scraping/HeroScraping";
|
||||
|
||||
export default function HomeHero() {
|
||||
return (
|
||||
<section className="overflow-x-clip" id="home-hero">
|
||||
<div
|
||||
className="pt-28 lg:pt-254 lg:-mt-100 pb-115 relative"
|
||||
id="hero-content"
|
||||
>
|
||||
<HomeHeroPixi />
|
||||
<HeroFlame />
|
||||
|
||||
<BackgroundOuterPiece />
|
||||
|
||||
<HomeHeroBackground />
|
||||
|
||||
<div className="relative container px-16">
|
||||
<HomeHeroBadge />
|
||||
<HomeHeroTitle />
|
||||
|
||||
<p className="text-center text-body-large">
|
||||
Power your AI apps with clean data crawled
|
||||
<br className="lg-max:hidden" />
|
||||
from any website.
|
||||
<Link
|
||||
className="bg-black-alpha-4 hover:bg-black-alpha-6 lg:ml-4 rounded-6 px-8 lg:px-6 text-label-large lg-max:py-2 h-30 lg:h-24 block lg-max:mt-8 lg-max:mx-auto lg-max:w-max lg:inline-block gap-4 transition-all"
|
||||
href="https://github.com/firecrawl/firecrawl"
|
||||
target="_blank"
|
||||
>
|
||||
It's also open source.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container lg:contents !p-16 relative -mt-90">
|
||||
<div className="absolute top-0 left-[calc(50%-50vw)] w-screen h-1 bg-border-faint lg:hidden" />
|
||||
<div className="absolute bottom-0 left-[calc(50%-50vw)] w-screen h-1 bg-border-faint lg:hidden" />
|
||||
|
||||
<Connector className="-top-10 -left-[10.5px] lg:hidden" />
|
||||
<Connector className="-top-10 -right-[10.5px] lg:hidden" />
|
||||
<Connector className="-bottom-10 -left-[10.5px] lg:hidden" />
|
||||
<Connector className="-bottom-10 -right-[10.5px] lg:hidden" />
|
||||
|
||||
<HeroInput />
|
||||
</div>
|
||||
|
||||
<HeroScraping />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
"use client";
|
||||
|
||||
import { Suspense, lazy, useState, useEffect } from "react";
|
||||
|
||||
const Pixi = lazy(() => import("@/components/shared/pixi/Pixi"));
|
||||
import features from "./tickers/features";
|
||||
|
||||
function PixiContent() {
|
||||
return (
|
||||
<Pixi
|
||||
canvasAttrs={{
|
||||
className: "cw-[1314px] h-506 absolute top-100 lg-max:hidden",
|
||||
}}
|
||||
fps={Infinity}
|
||||
initOptions={{ backgroundAlpha: 0 }}
|
||||
smartStop={false}
|
||||
tickers={[features]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default function HomeHeroPixi() {
|
||||
const [hasError, setHasError] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const handleError = (e: ErrorEvent) => {
|
||||
if (e.message.includes('pixi') || e.message.includes('ChunkLoadError')) {
|
||||
setHasError(true);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('error', handleError);
|
||||
return () => window.removeEventListener('error', handleError);
|
||||
}, []);
|
||||
|
||||
if (hasError) {
|
||||
// Return empty div as fallback if Pixi fails to load
|
||||
return <div className="cw-[1314px] h-506 absolute top-100 lg-max:hidden" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={<div className="cw-[1314px] h-506 absolute top-100 lg-max:hidden" />}>
|
||||
<PixiContent />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import { Ticker } from "@/components/shared/pixi/Pixi";
|
||||
import PixiAssetManager from "@/components/shared/pixi/PixiAssetManager";
|
||||
import { RenderTexture, Sprite, Text } from "pixi.js";
|
||||
|
||||
// Add more contrast to the ASCII_CHARS and ensure 'X' is used
|
||||
// const ASCII_CHARS = [' ', '.', ':', '-', '=', '+', 'X'];
|
||||
const ASCII_CHARS = ' .":,-_^=+';
|
||||
|
||||
function getAsciiChar(luminance: number) {
|
||||
if (luminance < 50) return " ";
|
||||
|
||||
const norm = Math.max(0, Math.min(1, (luminance - 16) / (250 - 16)));
|
||||
const skewed = Math.pow(norm, 1.5);
|
||||
|
||||
const minIdx = 1;
|
||||
const maxIdx = ASCII_CHARS.length - 1;
|
||||
const idx = minIdx + Math.floor(skewed * (maxIdx - minIdx + 1));
|
||||
const safeIdx = Math.max(minIdx, Math.min(maxIdx, idx));
|
||||
|
||||
return ASCII_CHARS[safeIdx];
|
||||
}
|
||||
|
||||
// Sprinkle logic is now a no-op, as getAsciiChar handles the randomness
|
||||
function sprinkleAscii(line: string) {
|
||||
return line;
|
||||
}
|
||||
|
||||
const tickAscii: Ticker = async ({ app, canvas }) => {
|
||||
const textures = await Promise.all(
|
||||
Array.from({ length: 150 }, async (_, i) => {
|
||||
const texture = await PixiAssetManager.load(
|
||||
`/Arşiv/FAQ Demo/FAQ_${i.toString().padStart(5, "0")}.png`,
|
||||
);
|
||||
|
||||
return texture!;
|
||||
}),
|
||||
);
|
||||
|
||||
const width = canvas.clientWidth;
|
||||
const height = canvas.clientHeight;
|
||||
|
||||
const sprites = textures.map((texture) => new Sprite(texture));
|
||||
|
||||
sprites.forEach((sprite) => {
|
||||
sprite.width = width;
|
||||
sprite.height = height;
|
||||
sprite.x = 0;
|
||||
sprite.y = 0;
|
||||
|
||||
app.stage.addChild(sprite);
|
||||
sprite.alpha = 0;
|
||||
});
|
||||
|
||||
// Render the texture to a renderTexture to extract pixels
|
||||
const renderTexture = RenderTexture.create({ width, height });
|
||||
|
||||
let ascii = "";
|
||||
|
||||
const asciiText = new Text({
|
||||
text: ascii,
|
||||
style: {
|
||||
fontFamily: "monospace",
|
||||
fontSize: 8,
|
||||
fill: 0x000,
|
||||
align: "left",
|
||||
lineHeight: 8,
|
||||
whiteSpace: "pre",
|
||||
},
|
||||
});
|
||||
asciiText.alpha = 0.2;
|
||||
asciiText.x = 0;
|
||||
asciiText.y = 0;
|
||||
|
||||
const variants: string[] = [];
|
||||
|
||||
const render = (index: number) => {
|
||||
ascii = "";
|
||||
const sprite = sprites[index];
|
||||
|
||||
sprites.forEach((sprite) => {
|
||||
sprite.alpha = 0;
|
||||
});
|
||||
|
||||
sprite.alpha = 1;
|
||||
app.renderer.render({ container: sprite, target: renderTexture });
|
||||
sprite.alpha = 0;
|
||||
|
||||
const pixels = app.renderer.extract.pixels(renderTexture).pixels;
|
||||
|
||||
const charWidth = 4.81640625;
|
||||
|
||||
for (let y = 0; y < height; y += 8) {
|
||||
let line = "";
|
||||
|
||||
for (let x = 0; x < width; x += charWidth) {
|
||||
let totalLum = 0;
|
||||
let count = 0;
|
||||
|
||||
for (let dy = 0; dy < 8; dy++) {
|
||||
for (let dx = 0; dx < 4; dx++) {
|
||||
const px = Math.floor(x + dx);
|
||||
const py = Math.floor(y + dy);
|
||||
if (px >= width || py >= height) continue;
|
||||
const idx = (py * width + px) * 4;
|
||||
const r = pixels[idx];
|
||||
const g = pixels[idx + 1];
|
||||
const b = pixels[idx + 2];
|
||||
const lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
totalLum += lum;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
const avgLum = count ? totalLum / count : 0;
|
||||
line += getAsciiChar(avgLum);
|
||||
}
|
||||
ascii += sprinkleAscii(line) + "\n";
|
||||
}
|
||||
|
||||
variants[index] = ascii;
|
||||
|
||||
asciiText.text = ascii;
|
||||
};
|
||||
|
||||
app.stage.addChild(asciiText);
|
||||
|
||||
for (let i = 0; i < sprites.length; i++) {
|
||||
render(i);
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
|
||||
//@ts-expect-error - safeAdd method exists on extended ticker
|
||||
app.ticker.safeAdd(() => {
|
||||
i++;
|
||||
if (i >= sprites.length) i = 0;
|
||||
|
||||
render(i);
|
||||
});
|
||||
};
|
||||
|
||||
export default tickAscii;
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Ticker } from "@/components/shared/pixi/Pixi";
|
||||
|
||||
import AnimatedRect from "./components/AnimatedRect";
|
||||
import BlinkingContainer from "./components/BlinkingContainer";
|
||||
import crawl from "./crawl";
|
||||
import mapping from "./mapping";
|
||||
import scrape from "./scrape";
|
||||
import search from "./search";
|
||||
|
||||
type Props = Parameters<Ticker>[0] & {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export const CELL_SIZE = 80;
|
||||
|
||||
export const MAIN_COLOR = 0xe6e6e6;
|
||||
|
||||
const animations = [scrape, mapping, search, crawl];
|
||||
|
||||
let lastActive = -1;
|
||||
|
||||
export default function cell(props: Props) {
|
||||
const blinkingContainer = BlinkingContainer({
|
||||
x: props.x + 10,
|
||||
y: props.y + 10,
|
||||
app: props.app,
|
||||
});
|
||||
|
||||
const anchorGraphic = AnimatedRect({
|
||||
app: props.app,
|
||||
x: CELL_SIZE / 2,
|
||||
y: CELL_SIZE / 2,
|
||||
width: 4,
|
||||
height: 4,
|
||||
radius: 10,
|
||||
color: MAIN_COLOR,
|
||||
});
|
||||
|
||||
blinkingContainer.container.addChild(anchorGraphic.graphic);
|
||||
|
||||
props.app.stage.addChild(blinkingContainer.container);
|
||||
|
||||
let running = false;
|
||||
|
||||
return {
|
||||
trigger: async () => {
|
||||
if (running) return;
|
||||
|
||||
running = true;
|
||||
|
||||
lastActive = (lastActive + 1) % animations.length;
|
||||
|
||||
const fn = animations[lastActive];
|
||||
|
||||
await fn({
|
||||
...props,
|
||||
blinkingContainer,
|
||||
anchorGraphic,
|
||||
});
|
||||
|
||||
running = false;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Ticker } from "@/components/shared/pixi/Pixi";
|
||||
import AnimatedRect from "./components/AnimatedRect";
|
||||
|
||||
type Props = Parameters<Ticker>[0] & {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export default function cellReveal(props: Props) {
|
||||
const graphic = AnimatedRect({
|
||||
app: props.app,
|
||||
x: props.x + 0.5,
|
||||
y: props.y + 0.5,
|
||||
width: 101,
|
||||
height: 101,
|
||||
radius: 0,
|
||||
alpha: 0,
|
||||
color: 0x000,
|
||||
centering: false,
|
||||
});
|
||||
|
||||
props.app.stage.addChild(graphic.graphic);
|
||||
|
||||
return {
|
||||
trigger: async () => {
|
||||
let cycleCount = 0;
|
||||
|
||||
const cycle = async () => {
|
||||
await graphic.animate(
|
||||
{
|
||||
alpha: Math.random() * 0.04,
|
||||
},
|
||||
{
|
||||
ease: "linear",
|
||||
duration: 0.03,
|
||||
},
|
||||
);
|
||||
|
||||
if (cycleCount < 5) {
|
||||
cycleCount += 1;
|
||||
cycle();
|
||||
} else {
|
||||
await graphic.animate({ alpha: 0 });
|
||||
graphic.graphic.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
cycle();
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
|
||||
import { AnimationOptions, cubicBezier } from "motion";
|
||||
import { Application, Container, Graphics, Sprite } from "pixi.js";
|
||||
|
||||
import { isDestroyed } from "@/components/shared/pixi/utils";
|
||||
|
||||
type Props = {
|
||||
app: Application;
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
radius: number;
|
||||
color: number;
|
||||
scale?: number;
|
||||
rotation?: number;
|
||||
type?: "rect" | "arc" | "container" | Sprite;
|
||||
animationConfig?: AnimationOptions;
|
||||
alpha?: number;
|
||||
|
||||
centering?: boolean;
|
||||
};
|
||||
|
||||
export type IAnimatedRect = ReturnType<typeof AnimatedRect>;
|
||||
|
||||
export default function AnimatedRect(props: Props) {
|
||||
const graphic = (() => {
|
||||
if (props.type === "container") return new Container();
|
||||
if (props.type instanceof Sprite) return props.type;
|
||||
|
||||
return new Graphics();
|
||||
})();
|
||||
|
||||
props.alpha ??= 1;
|
||||
props.scale ??= 1;
|
||||
props.centering ??= true;
|
||||
props.rotation ??= 0;
|
||||
|
||||
const p = {
|
||||
...props,
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
if (isDestroyed(props.app) || graphic.destroyed) return;
|
||||
|
||||
graphic.scale.set(p.scale!);
|
||||
graphic.alpha = p.alpha!;
|
||||
graphic.rotation = p.rotation!;
|
||||
|
||||
if (!(graphic instanceof Graphics)) {
|
||||
if (graphic instanceof Sprite) {
|
||||
graphic.x = p.x;
|
||||
graphic.y = p.y;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const g = graphic as Graphics;
|
||||
|
||||
g.clear();
|
||||
|
||||
if (p.type !== "arc") {
|
||||
g.roundRect(
|
||||
p.centering ? p.x - p.width / 2 : p.x,
|
||||
p.centering ? p.y - p.height / 2 : p.y,
|
||||
p.width,
|
||||
p.height,
|
||||
p.radius,
|
||||
);
|
||||
} else {
|
||||
g.arc(p.x, p.y, p.width / 2, 0, Math.PI * 2);
|
||||
}
|
||||
|
||||
g.fill({ color: p.color });
|
||||
};
|
||||
|
||||
render();
|
||||
|
||||
p.animationConfig ??= {
|
||||
duration: 0.4,
|
||||
ease: cubicBezier(0.83, 0, 0.17, 1),
|
||||
};
|
||||
|
||||
return {
|
||||
defaultProps: props,
|
||||
currentProps: p,
|
||||
graphic,
|
||||
setStyle: (style: Partial<Props>) => {
|
||||
Object.assign(p, style);
|
||||
|
||||
render();
|
||||
},
|
||||
render,
|
||||
animate: (renderProps: Partial<Props>, settings?: AnimationOptions) =>
|
||||
(props.app as any).animate(p, renderProps, {
|
||||
...p.animationConfig,
|
||||
...settings,
|
||||
onUpdate: render,
|
||||
}),
|
||||
reset: () => (props.app as any).animate(p, props, { onUpdate: render }),
|
||||
};
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
|
||||
import { Application, Graphics } from "pixi.js";
|
||||
|
||||
import { CELL_SIZE } from "@/components/app/(home)/sections/hero/Pixi/tickers/features/cell";
|
||||
|
||||
import AnimatedRect from "./AnimatedRect";
|
||||
|
||||
export type IBlinkingContainer = ReturnType<typeof BlinkingContainer>;
|
||||
|
||||
export default function BlinkingContainer({
|
||||
x,
|
||||
y,
|
||||
app,
|
||||
}: {
|
||||
x: number;
|
||||
y: number;
|
||||
app: Application;
|
||||
}) {
|
||||
const animatedRect = AnimatedRect({
|
||||
app,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: CELL_SIZE,
|
||||
height: CELL_SIZE,
|
||||
radius: 0,
|
||||
color: 0xededed,
|
||||
type: "container",
|
||||
});
|
||||
|
||||
animatedRect.graphic.pivot.set(CELL_SIZE / 2, CELL_SIZE / 2);
|
||||
|
||||
animatedRect.graphic.x = x + CELL_SIZE / 2;
|
||||
animatedRect.graphic.y = y + CELL_SIZE / 2;
|
||||
|
||||
animatedRect.graphic.addChild(
|
||||
new Graphics()
|
||||
.rect(0, 0, CELL_SIZE, CELL_SIZE)
|
||||
.fill({ color: "#EDEDED", alpha: 0 }),
|
||||
);
|
||||
|
||||
const blinkLayer = new Graphics()
|
||||
.rect(0, 0, CELL_SIZE, CELL_SIZE)
|
||||
.fill({ color: "#F9F9F9" });
|
||||
|
||||
blinkLayer.zIndex = 1;
|
||||
blinkLayer.alpha = 0;
|
||||
|
||||
animatedRect.graphic.addChild(blinkLayer);
|
||||
|
||||
return {
|
||||
container: animatedRect.graphic,
|
||||
animate: animatedRect.animate,
|
||||
reset: animatedRect.reset,
|
||||
shrink: async () => {
|
||||
await animatedRect.animate({ scale: 0.92 });
|
||||
|
||||
animatedRect.animate({ scale: 1 });
|
||||
},
|
||||
blink: ({ delay = 0 }: { delay?: number } = {}) => {
|
||||
(app as any)
|
||||
.animate(0, 0.32, {
|
||||
repeatType: "reverse",
|
||||
repeat: 2,
|
||||
delay,
|
||||
duration: 0.065,
|
||||
ease: "linear",
|
||||
onUpdate: (value: any) => {
|
||||
blinkLayer.alpha = value as number;
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
(app as any).animate(0.32, 0, {
|
||||
duration: 0.065,
|
||||
ease: "linear",
|
||||
onUpdate: (value: any) => {
|
||||
blinkLayer.alpha = value as number;
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { MAIN_COLOR } from "@/components/app/(home)/sections/hero/Pixi/tickers/features/cell";
|
||||
|
||||
import AnimatedRect from "./AnimatedRect";
|
||||
|
||||
export default function Dot(
|
||||
props: Pick<
|
||||
Parameters<typeof AnimatedRect>[0],
|
||||
"x" | "y" | "app" | "animationConfig"
|
||||
>,
|
||||
) {
|
||||
return AnimatedRect({
|
||||
...props,
|
||||
width: 2,
|
||||
height: 2,
|
||||
radius: 10,
|
||||
color: MAIN_COLOR,
|
||||
type: "arc",
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
import { animate } from "motion";
|
||||
|
||||
import { Ticker } from "@/components/shared/pixi/Pixi";
|
||||
import { sleep } from "@/utils/sleep";
|
||||
|
||||
import { CELL_SIZE, MAIN_COLOR } from "./cell";
|
||||
import AnimatedRect, { IAnimatedRect } from "./components/AnimatedRect";
|
||||
import { IBlinkingContainer } from "./components/BlinkingContainer";
|
||||
import Dot from "./components/Dot";
|
||||
|
||||
type Props = Parameters<Ticker>[0] & {
|
||||
x: number;
|
||||
y: number;
|
||||
blinkingContainer: IBlinkingContainer;
|
||||
anchorGraphic: IAnimatedRect;
|
||||
};
|
||||
|
||||
export default async function crawl(props: Props) {
|
||||
const rects = Array.from({ length: 6 }, () => {
|
||||
return AnimatedRect({
|
||||
app: props.app,
|
||||
x: CELL_SIZE / 2,
|
||||
y: CELL_SIZE / 2,
|
||||
width: 8,
|
||||
height: 8,
|
||||
radius: 0,
|
||||
color: MAIN_COLOR,
|
||||
});
|
||||
});
|
||||
|
||||
const dots = Array.from({ length: 16 }, () => {
|
||||
return Dot({
|
||||
x: CELL_SIZE / 2,
|
||||
y: CELL_SIZE / 2,
|
||||
app: props.app,
|
||||
});
|
||||
});
|
||||
|
||||
dots.forEach((dot) =>
|
||||
props.blinkingContainer.container.addChild(dot.graphic),
|
||||
);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
/* Step 1: Reveal the main square, reveal the corner dots */
|
||||
await Promise.all(
|
||||
[
|
||||
dots[0].animate({ x: 30, y: 30 }, { delay: 0.2 }),
|
||||
dots[1].animate({ x: CELL_SIZE - 30, y: 30 }, { delay: 0.2 }),
|
||||
dots[2].animate({ x: 30, y: CELL_SIZE - 30 }, { delay: 0.2 }),
|
||||
dots[3].animate({ x: CELL_SIZE - 30, y: CELL_SIZE - 30 }, { delay: 0.2 }),
|
||||
|
||||
props.anchorGraphic.animate({
|
||||
radius: 0,
|
||||
width: 12,
|
||||
height: 12,
|
||||
}),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
rects.forEach((rect) =>
|
||||
props.blinkingContainer.container.addChild(rect.graphic),
|
||||
);
|
||||
|
||||
rects.unshift(props.anchorGraphic);
|
||||
|
||||
await sleep(500);
|
||||
props.blinkingContainer.blink({ delay: 0.3 });
|
||||
await props.blinkingContainer.shrink();
|
||||
|
||||
let spriteOverlay: IAnimatedRect | null = null;
|
||||
|
||||
// Use fallback rectangle instead of trying to load missing image
|
||||
spriteOverlay = AnimatedRect({
|
||||
x: 13,
|
||||
y: 39,
|
||||
color: MAIN_COLOR,
|
||||
width: 54,
|
||||
height: 34,
|
||||
app: props.app,
|
||||
radius: 4,
|
||||
centering: false,
|
||||
});
|
||||
|
||||
spriteOverlay.graphic.zIndex = -1;
|
||||
props.blinkingContainer.container.addChild(spriteOverlay.graphic);
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
spriteOverlay?.animate({ height: 23, y: 50 }),
|
||||
|
||||
rects[0].animate({ width: 16, height: 16, y: 34 }),
|
||||
rects.slice(1, 4).map((rect) => rect.animate({ x: 24, y: 50 })),
|
||||
rects.slice(4, 8).map((rect) => rect.animate({ x: 56, y: 50 })),
|
||||
|
||||
dots[0].animate({ x: 28, y: 22 }),
|
||||
dots[1].animate({ x: 52, y: 22 }),
|
||||
dots[2].animate({ x: 16, y: 58 }),
|
||||
dots[3].animate({ x: 64, y: 58 }),
|
||||
|
||||
dots[4].animate({ x: 16, y: 42 }),
|
||||
dots[5].animate({ x: 64, y: 42 }),
|
||||
dots[6].animate({ x: 32, y: 58 }),
|
||||
dots[7].animate({ x: 48, y: 58 }),
|
||||
|
||||
dots.slice(8, 12).map((dot) => dot.animate({ x: 24, y: 50 })),
|
||||
dots.slice(12, 16).map((dot) => dot.animate({ x: 56, y: 50 })),
|
||||
].flat().filter(Boolean),
|
||||
);
|
||||
|
||||
await sleep(500);
|
||||
props.blinkingContainer.blink({ delay: 0.3 });
|
||||
await props.blinkingContainer.shrink();
|
||||
try {
|
||||
await Promise.all(
|
||||
[
|
||||
spriteOverlay?.animate({ height: 8, y: 58 }),
|
||||
|
||||
rects[0].animate({ y: 28 }),
|
||||
[1, 4].map((i) => rects[i].animate({ y: 44 })),
|
||||
[2, 3].map((i) => rects[i].animate({ x: 12, y: 56 })),
|
||||
[5, 6].map((i) => rects[i].animate({ x: 68, y: 56 })),
|
||||
|
||||
dots[0].animate({ y: 16 }),
|
||||
dots[1].animate({ y: 16 }),
|
||||
|
||||
dots[2].animate({ x: 4, y: 64 }),
|
||||
dots[3].animate({ x: 76, y: 64 }),
|
||||
|
||||
dots[4].animate({ x: 4, y: 48 }),
|
||||
dots[5].animate({ x: 76, y: 48 }),
|
||||
dots[6].animate({ x: 20, y: 64 }),
|
||||
dots[7].animate({ x: 60, y: 64 }),
|
||||
|
||||
dots[8].animate({ x: 16, y: 36 }),
|
||||
dots[12].animate({ x: 64, y: 36 }),
|
||||
|
||||
dots[9].animate({ x: 32, y: 52 }),
|
||||
dots[13].animate({ x: 48, y: 52 }),
|
||||
|
||||
[10, 11].map((i) => dots[i].animate({ x: 12, y: 56 })),
|
||||
[14, 15].map((i) => dots[i].animate({ x: 68, y: 56 })),
|
||||
].flat().filter(Boolean),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
await sleep(500);
|
||||
props.blinkingContainer.blink({ delay: 0.3 });
|
||||
await props.blinkingContainer.shrink();
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
spriteOverlay.animate({ height: 0, y: 66 }),
|
||||
|
||||
rects[0].animate({ y: 20 }),
|
||||
[1, 4].map((i) => rects[i].animate({ y: 36 })),
|
||||
[2, 5].map((i) => rects[i].animate({ y: 48 })),
|
||||
[3, 6].map((i) => rects[i].animate({ y: 60, x: i === 3 ? 24 : 56 })),
|
||||
|
||||
[0, 1, 4, 5, 8, 9, 12, 13].map((i) =>
|
||||
dots[i].animate({ y: dots[i].currentProps.y - 8 }),
|
||||
),
|
||||
|
||||
dots[2].animate({ x: 4, y: 56 }),
|
||||
dots[3].animate({ x: 76, y: 56 }),
|
||||
|
||||
dots[6].animate({ x: 32, y: 68 }),
|
||||
dots[7].animate({ x: 48, y: 68 }),
|
||||
|
||||
dots[10].animate({ x: 32, y: 52 }),
|
||||
dots[11].animate({ x: 16, y: 68 }),
|
||||
dots[14].animate({ x: 48, y: 52 }),
|
||||
dots[15].animate({ x: 64, y: 68 }),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
await sleep(2000);
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
rects.map((rect) =>
|
||||
rect.animate(props.anchorGraphic.defaultProps, {
|
||||
delay: Math.random() * 0.3,
|
||||
duration: 0.3,
|
||||
}),
|
||||
),
|
||||
dots.map((dot) =>
|
||||
dot.animate(dot.defaultProps, { delay: Math.random() * 0.3 }),
|
||||
),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
rects.shift();
|
||||
|
||||
rects.forEach((rect) => rect.graphic.destroy());
|
||||
dots.forEach((dot) => dot.graphic.destroy());
|
||||
spriteOverlay.graphic.destroy();
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import { Ticker } from "@/components/shared/pixi/Pixi";
|
||||
import setTimeoutOnVisible from "@/utils/set-timeout-on-visible";
|
||||
|
||||
import cell from "./cell";
|
||||
import cellReveal from "./cellReveal";
|
||||
|
||||
const CELL_GRID = [
|
||||
"-ooooooooooo-",
|
||||
"-oo-------oo-",
|
||||
"ooo-------ooo",
|
||||
"-oo-------oo-",
|
||||
"-oo-------oo-",
|
||||
];
|
||||
|
||||
const REVEAL_ANIMATION_GRID = [
|
||||
[
|
||||
"---ooooooo---",
|
||||
"--o-------o--",
|
||||
"--o-------o--",
|
||||
"--o-------o--",
|
||||
"--o-------o--",
|
||||
],
|
||||
[
|
||||
"--o-------o--",
|
||||
"-o---------o-",
|
||||
"-o---------o-",
|
||||
"-o---------o-",
|
||||
"-o---------o-",
|
||||
],
|
||||
[
|
||||
"-o---------o-",
|
||||
"-------------",
|
||||
"o-----------o",
|
||||
"-------------",
|
||||
"-------------",
|
||||
],
|
||||
[
|
||||
"-------------",
|
||||
"-------------",
|
||||
"o-----------o",
|
||||
"-------------",
|
||||
"-------------",
|
||||
],
|
||||
];
|
||||
|
||||
const features: Ticker = (params) => {
|
||||
const cells: ReturnType<typeof cell>[] = [];
|
||||
const cellReveals: {
|
||||
cell: ReturnType<typeof cellReveal>;
|
||||
row: number;
|
||||
column: number;
|
||||
}[] = [];
|
||||
|
||||
for (let i = 0; i < CELL_GRID.length; i++) {
|
||||
const row = CELL_GRID[i];
|
||||
|
||||
for (let j = 0; j < row.length; j++) {
|
||||
if (row[j] === "o") {
|
||||
cells.push(
|
||||
cell({
|
||||
...params,
|
||||
x: j * 101,
|
||||
y: i * 101,
|
||||
}),
|
||||
);
|
||||
|
||||
cellReveals.push({
|
||||
cell: cellReveal({
|
||||
...params,
|
||||
x: j * 101,
|
||||
y: i * 101,
|
||||
}),
|
||||
row: i,
|
||||
column: j,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cycle = () =>
|
||||
setTimeoutOnVisible({
|
||||
element: params.canvas,
|
||||
callback: () => {
|
||||
const cell = cells[Math.floor(Math.random() * cells.length)];
|
||||
|
||||
if (cell) {
|
||||
cell.trigger().then(() => cycle());
|
||||
}
|
||||
},
|
||||
timeout: 3000 * Math.random(),
|
||||
});
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
cycle();
|
||||
}
|
||||
|
||||
let revealIndex = -1;
|
||||
|
||||
const revealCycle = () => {
|
||||
revealIndex += 1;
|
||||
|
||||
for (let i = 0; i < REVEAL_ANIMATION_GRID[revealIndex].length; i++) {
|
||||
const row = REVEAL_ANIMATION_GRID[revealIndex][i];
|
||||
|
||||
for (let j = 0; j < row.length; j++) {
|
||||
if (row[j] === "o") {
|
||||
cellReveals
|
||||
.find((cell) => cell.row === i && cell.column === j)
|
||||
?.cell.trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (revealIndex < REVEAL_ANIMATION_GRID.length - 1) {
|
||||
setTimeout(() => {
|
||||
revealCycle();
|
||||
}, 150);
|
||||
}
|
||||
};
|
||||
|
||||
revealCycle();
|
||||
};
|
||||
|
||||
export default features;
|
||||
@@ -0,0 +1,207 @@
|
||||
import { Ticker } from "@/components/shared/pixi/Pixi";
|
||||
import { sleep } from "@/utils/sleep";
|
||||
|
||||
import { CELL_SIZE, MAIN_COLOR } from "./cell";
|
||||
import AnimatedRect, { IAnimatedRect } from "./components/AnimatedRect";
|
||||
import { IBlinkingContainer } from "./components/BlinkingContainer";
|
||||
import Dot from "./components/Dot";
|
||||
|
||||
type Props = Parameters<Ticker>[0] & {
|
||||
x: number;
|
||||
y: number;
|
||||
blinkingContainer: IBlinkingContainer;
|
||||
anchorGraphic: IAnimatedRect;
|
||||
};
|
||||
|
||||
export default async function mapping(props: Props) {
|
||||
const rects = Array.from({ length: 8 }, () => {
|
||||
return AnimatedRect({
|
||||
app: props.app,
|
||||
x: CELL_SIZE / 2,
|
||||
y: CELL_SIZE / 2,
|
||||
width: 10,
|
||||
height: 10,
|
||||
radius: 0,
|
||||
color: MAIN_COLOR,
|
||||
});
|
||||
});
|
||||
|
||||
const dots = Array.from({ length: 20 }, () => {
|
||||
return Dot({
|
||||
x: CELL_SIZE / 2,
|
||||
y: CELL_SIZE / 2,
|
||||
app: props.app,
|
||||
});
|
||||
});
|
||||
|
||||
dots.forEach((dot) =>
|
||||
props.blinkingContainer.container.addChild(dot.graphic),
|
||||
);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
await props.anchorGraphic.animate({
|
||||
radius: 0,
|
||||
width: 12,
|
||||
height: 12,
|
||||
});
|
||||
|
||||
rects.forEach((rect) =>
|
||||
props.blinkingContainer.container.addChild(rect.graphic),
|
||||
);
|
||||
|
||||
rects.unshift(props.anchorGraphic);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
props.blinkingContainer.blink({ delay: 0.1 });
|
||||
await props.blinkingContainer.shrink();
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
dots.slice(0, 16).map((dot, index) => {
|
||||
const x = 13 + (index % 4) * 18;
|
||||
const y = 13 + Math.floor(index / 4) * 18;
|
||||
|
||||
return dot.animate({ x, y });
|
||||
}),
|
||||
|
||||
rects[0].animate({ width: 10, height: 10 }),
|
||||
|
||||
rects.map((rect, index) => {
|
||||
const x = 22 + (index % 3) * 18;
|
||||
const y = 22 + Math.floor(index / 3) * 18;
|
||||
|
||||
return rect.animate({ x, y });
|
||||
}),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
await sleep(300);
|
||||
|
||||
props.blinkingContainer.blink({ delay: 0.1 });
|
||||
await props.blinkingContainer.shrink();
|
||||
|
||||
const baseDotPositions = [
|
||||
[13, 13],
|
||||
[31, 31],
|
||||
[49, 31],
|
||||
[13, 31],
|
||||
[49, 49],
|
||||
[67, 49],
|
||||
[13, 49],
|
||||
[31, 67],
|
||||
[67, 67],
|
||||
];
|
||||
|
||||
const dotPositions: string[] = [];
|
||||
|
||||
for (const [x, y] of baseDotPositions) {
|
||||
const positions = [
|
||||
{ x: x - 9, y: y - 9 },
|
||||
{ x: x + 9, y: y - 9 },
|
||||
{ x: x - 9, y: y + 9 },
|
||||
{ x: x + 9, y: y + 9 },
|
||||
];
|
||||
|
||||
for (const position of positions) {
|
||||
if (!dotPositions.includes(`${position.x},${position.y}`)) {
|
||||
dotPositions.push(`${position.x},${position.y}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
rects[0].animate({ x: 13, y: 13 }),
|
||||
rects[1].animate({ x: 31, y: 31 }),
|
||||
rects[2].animate({ x: 49, y: 31 }),
|
||||
rects[3].animate({ x: 13, y: 31 }),
|
||||
|
||||
rects[4].animate({ x: 49, y: 49 }),
|
||||
rects[5].animate({ x: 67, y: 49 }),
|
||||
|
||||
rects[6].animate({ x: 13, y: 49 }),
|
||||
rects[7].animate({ x: 31, y: 67 }),
|
||||
rects[8].animate({ x: 67, y: 67 }),
|
||||
|
||||
dots.map((dot, index) => {
|
||||
const position = dotPositions[index].split(",").map(Number);
|
||||
|
||||
return dot.animate({ x: position[0], y: position[1] });
|
||||
}),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const lines = Array.from({ length: 8 }, () => {
|
||||
return AnimatedRect({
|
||||
app: props.app,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
radius: 0,
|
||||
color: MAIN_COLOR,
|
||||
centering: false,
|
||||
animationConfig: {
|
||||
duration: 0.25,
|
||||
ease: "linear",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
lines.forEach((graphic) =>
|
||||
props.blinkingContainer.container.addChild(graphic.graphic),
|
||||
);
|
||||
|
||||
(async () => {
|
||||
lines[0].setStyle({ width: 1, height: 0, y: 18, x: 12.5 });
|
||||
await lines[0].animate({ height: 9 });
|
||||
|
||||
lines[1].setStyle({ width: 0, height: 1, y: 30.5, x: 18 });
|
||||
await lines[1].animate({ width: 9 });
|
||||
lines[2].setStyle({ width: 0, height: 1, y: 30.5, x: 36 });
|
||||
await lines[2].animate({ width: 9 });
|
||||
|
||||
lines[3].setStyle({ width: 1, height: 3, y: 36, x: 48.5 });
|
||||
await lines[3].animate({ height: 9 });
|
||||
lines[4].setStyle({ width: 0, height: 1, y: 48.5, x: 54 });
|
||||
await lines[4].animate({ width: 9 });
|
||||
})();
|
||||
|
||||
lines[5].setStyle({ width: 0, height: 1, y: 66.5, x: 62 });
|
||||
await lines[5].animate({ width: 28, x: 62 - 28 }, { duration: 0.4 });
|
||||
lines[6].setStyle({ width: 0, height: 1, y: 66.5, x: 26 });
|
||||
await lines[6].animate({ width: 13.5, x: 26 - 13.5 });
|
||||
lines[7].setStyle({ width: 1, height: 0, y: 66.5, x: 12.5 });
|
||||
await lines[7].animate({ height: 14.5, y: 66.5 - 13.5 });
|
||||
|
||||
await sleep(2000);
|
||||
|
||||
props.blinkingContainer.blink({ delay: 0.1 });
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
lines.map((line) => line.animate({ alpha: 0 })),
|
||||
|
||||
rects.map((rect) =>
|
||||
rect.animate(props.anchorGraphic.defaultProps, {
|
||||
delay: Math.random() * 0.3,
|
||||
duration: 0.3,
|
||||
}),
|
||||
),
|
||||
|
||||
dots.map((dot) =>
|
||||
dot.animate(dot.defaultProps, { delay: Math.random() * 0.3 }),
|
||||
),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
rects.shift();
|
||||
|
||||
lines.forEach((line) => line.graphic.destroy());
|
||||
rects.forEach((rect) => rect.graphic.destroy());
|
||||
dots.forEach((dot) => dot.graphic.destroy());
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
import { Ticker } from "@/components/shared/pixi/Pixi";
|
||||
import { sleep } from "@/utils/sleep";
|
||||
|
||||
import { CELL_SIZE, MAIN_COLOR } from "./cell";
|
||||
import AnimatedRect, { IAnimatedRect } from "./components/AnimatedRect";
|
||||
import { IBlinkingContainer } from "./components/BlinkingContainer";
|
||||
import Dot from "./components/Dot";
|
||||
|
||||
type Props = Parameters<Ticker>[0] & {
|
||||
x: number;
|
||||
y: number;
|
||||
blinkingContainer: IBlinkingContainer;
|
||||
anchorGraphic: IAnimatedRect;
|
||||
};
|
||||
|
||||
export default async function scrape(props: Props) {
|
||||
const rects = Array.from({ length: 15 }, () => {
|
||||
return AnimatedRect({
|
||||
app: props.app,
|
||||
x: CELL_SIZE / 2,
|
||||
y: CELL_SIZE / 2,
|
||||
width: 10,
|
||||
height: 10,
|
||||
radius: 0,
|
||||
color: MAIN_COLOR,
|
||||
});
|
||||
});
|
||||
|
||||
const dots = Array.from({ length: 25 }, () => {
|
||||
return Dot({
|
||||
x: CELL_SIZE / 2,
|
||||
y: CELL_SIZE / 2,
|
||||
app: props.app,
|
||||
});
|
||||
});
|
||||
|
||||
dots.forEach((dot) =>
|
||||
props.blinkingContainer.container.addChild(dot.graphic),
|
||||
);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
[0, 12, 13, 14].map((index) =>
|
||||
dots[index].animate({ x: 30, y: 30 }, { delay: 0.2 }),
|
||||
),
|
||||
[1, 15, 16, 17].map((index) =>
|
||||
dots[index].animate({ x: CELL_SIZE - 30, y: 30 }, { delay: 0.2 }),
|
||||
),
|
||||
[2, 18, 19, 20].map((index) =>
|
||||
dots[index].animate({ x: 30, y: CELL_SIZE - 30 }, { delay: 0.2 }),
|
||||
),
|
||||
[3, 21, 22, 23].map((index) =>
|
||||
dots[index].animate(
|
||||
{ x: CELL_SIZE - 30, y: CELL_SIZE - 30 },
|
||||
{ delay: 0.2 },
|
||||
),
|
||||
),
|
||||
|
||||
props.anchorGraphic.animate({
|
||||
radius: 0,
|
||||
width: 12,
|
||||
height: 12,
|
||||
}),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
rects.forEach((rect) =>
|
||||
props.blinkingContainer.container.addChild(rect.graphic),
|
||||
);
|
||||
|
||||
rects.unshift(props.anchorGraphic);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
props.blinkingContainer.blink({ delay: 0.1 });
|
||||
await props.blinkingContainer.shrink();
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
[0, 12, 13, 14].map((index) => dots[index].animate({ x: 22, y: 22 })),
|
||||
[1, 15, 16, 17].map((index) =>
|
||||
dots[index].animate({ x: CELL_SIZE - 22, y: 22 }),
|
||||
),
|
||||
[2, 18, 19, 20].map((index) =>
|
||||
dots[index].animate({ x: 22, y: CELL_SIZE - 22 }),
|
||||
),
|
||||
[3, 21, 22, 23].map((index) =>
|
||||
dots[index].animate({ x: CELL_SIZE - 22, y: CELL_SIZE - 22 }),
|
||||
),
|
||||
|
||||
dots[4].animate({ x: 40, y: 22 }),
|
||||
dots[5].animate({ x: 22, y: 40 }),
|
||||
dots[6].animate({ x: CELL_SIZE - 22, y: 40 }),
|
||||
dots[7].animate({ x: 40, y: 58 }),
|
||||
|
||||
dots[8].animate({ x: 40, y: 22 }),
|
||||
dots[9].animate({ x: 22, y: 40 }),
|
||||
dots[10].animate({ x: CELL_SIZE - 22, y: 40 }),
|
||||
dots[11].animate({ x: 40, y: 58 }),
|
||||
|
||||
rects[0].animate({ width: 10, height: 10 }),
|
||||
rects.slice(0, 4).map((rect) => rect.animate({ x: 31, y: 31 })),
|
||||
rects
|
||||
.slice(4, 8)
|
||||
.map((rect) => rect.animate({ x: CELL_SIZE - 31, y: 31 })),
|
||||
rects
|
||||
.slice(8, 12)
|
||||
.map((rect) => rect.animate({ x: 31, y: CELL_SIZE - 31 })),
|
||||
rects
|
||||
.slice(12, 16)
|
||||
.map((rect) => rect.animate({ x: CELL_SIZE - 31, y: CELL_SIZE - 31 })),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
await sleep(1000);
|
||||
|
||||
props.blinkingContainer.blink({ delay: 0.1 });
|
||||
await props.blinkingContainer.shrink();
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
dots[0].animate({ x: 4, y: 4 }),
|
||||
dots[1].animate({ x: CELL_SIZE - 4, y: 4 }),
|
||||
dots[2].animate({ x: 4, y: CELL_SIZE - 4 }),
|
||||
dots[3].animate({ x: CELL_SIZE - 4, y: CELL_SIZE - 4 }),
|
||||
dots[4].animate({ x: 40, y: 4 }),
|
||||
dots[5].animate({ x: 4, y: 40 }),
|
||||
dots[6].animate({ x: 76, y: 40 }),
|
||||
dots[7].animate({ x: 40, y: 76 }),
|
||||
|
||||
dots[13].animate({ x: 22, y: 4 }),
|
||||
dots[14].animate({ x: 4, y: 22 }),
|
||||
dots[16].animate({ x: 58, y: 4 }),
|
||||
dots[17].animate({ x: 76, y: 22 }),
|
||||
dots[19].animate({ x: 4, y: 58 }),
|
||||
dots[20].animate({ x: 22, y: 76 }),
|
||||
dots[22].animate({ x: 58, y: 76 }),
|
||||
dots[23].animate({ x: 76, y: 58 }),
|
||||
|
||||
rects.map((rect, index) => {
|
||||
const quadrant = Math.floor(index / 4);
|
||||
const position = index % 4;
|
||||
|
||||
const col = (position % 2 === 0 ? 1 : 2) + (quadrant % 2 === 0 ? 0 : 2);
|
||||
const row = Math.floor(position / 2) + (quadrant < 2 ? 1 : 3);
|
||||
|
||||
return rect.animate({
|
||||
x: 13 + (col - 1) * 18,
|
||||
y: 13 + (row - 1) * 18,
|
||||
});
|
||||
}),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
await sleep(1200);
|
||||
|
||||
Promise.all(
|
||||
dots.map((dot) =>
|
||||
dot.animate({ alpha: 0 }, { delay: Math.random() * 0.3 }),
|
||||
),
|
||||
);
|
||||
|
||||
await sleep(100);
|
||||
|
||||
props.blinkingContainer.blink({ delay: 0.2 });
|
||||
|
||||
const newWidths: number[] = [];
|
||||
|
||||
for (let i = 0; i < rects.length; i++) {
|
||||
if (i % 2 === 0) {
|
||||
newWidths.push(20 + Math.random() * 28);
|
||||
} else {
|
||||
const remainingSpace = 62 - newWidths[i - 1];
|
||||
newWidths.push(10 + Math.random() * remainingSpace);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
rects.map((rect, index) => {
|
||||
const y = 8 + Math.floor(index / 2) * 6 + Math.floor(index / 4) * 8;
|
||||
|
||||
return rect.animate(
|
||||
{
|
||||
y,
|
||||
x:
|
||||
(index % 2 === 0 ? 8 : newWidths[index - 1] + 10) +
|
||||
newWidths[index] / 2,
|
||||
height: 4,
|
||||
width: newWidths[index],
|
||||
},
|
||||
{
|
||||
delay: Math.random() * 0.1,
|
||||
},
|
||||
);
|
||||
}),
|
||||
]);
|
||||
|
||||
props.blinkingContainer.blink({ delay: 0.1 });
|
||||
|
||||
await sleep(2000);
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
rects.map((rect) =>
|
||||
rect.animate(props.anchorGraphic.defaultProps, {
|
||||
delay: Math.random() * 0.3,
|
||||
duration: 0.3,
|
||||
}),
|
||||
),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
rects.shift();
|
||||
|
||||
rects.forEach((rect) => rect.graphic.destroy());
|
||||
dots.forEach((dot) => dot.graphic.destroy());
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
import { Ticker } from "@/components/shared/pixi/Pixi";
|
||||
import { sleep } from "@/utils/sleep";
|
||||
|
||||
import { CELL_SIZE, MAIN_COLOR } from "./cell";
|
||||
import AnimatedRect, { IAnimatedRect } from "./components/AnimatedRect";
|
||||
import { IBlinkingContainer } from "./components/BlinkingContainer";
|
||||
import Dot from "./components/Dot";
|
||||
|
||||
type Props = Parameters<Ticker>[0] & {
|
||||
x: number;
|
||||
y: number;
|
||||
blinkingContainer: IBlinkingContainer;
|
||||
anchorGraphic: IAnimatedRect;
|
||||
};
|
||||
|
||||
export default async function search(props: Props) {
|
||||
const rects = Array.from({ length: 8 }, () => {
|
||||
return AnimatedRect({
|
||||
app: props.app,
|
||||
x: CELL_SIZE / 2,
|
||||
y: CELL_SIZE / 2,
|
||||
width: 10,
|
||||
height: 10,
|
||||
radius: 0,
|
||||
color: MAIN_COLOR,
|
||||
});
|
||||
});
|
||||
|
||||
const dots = Array.from({ length: 16 }, () => {
|
||||
return Dot({
|
||||
x: CELL_SIZE / 2,
|
||||
y: CELL_SIZE / 2,
|
||||
app: props.app,
|
||||
});
|
||||
});
|
||||
|
||||
dots.forEach((dot) =>
|
||||
props.blinkingContainer.container.addChild(dot.graphic),
|
||||
);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
await props.anchorGraphic.animate({
|
||||
radius: 0,
|
||||
width: 12,
|
||||
height: 12,
|
||||
});
|
||||
|
||||
rects.forEach((rect) =>
|
||||
props.blinkingContainer.container.addChild(rect.graphic),
|
||||
);
|
||||
|
||||
rects.unshift(props.anchorGraphic);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
props.blinkingContainer.blink({ delay: 0.1 });
|
||||
await props.blinkingContainer.shrink();
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
dots.map((dot, index) => {
|
||||
const x = 13 + (index % 4) * 18;
|
||||
const y = 13 + Math.floor(index / 4) * 18;
|
||||
|
||||
return dot.animate({ x, y });
|
||||
}),
|
||||
|
||||
rects[0].animate({ width: 10, height: 10 }),
|
||||
|
||||
rects.map((rect, index) => {
|
||||
const x = 22 + (index % 3) * 18;
|
||||
const y = 22 + Math.floor(index / 3) * 18;
|
||||
|
||||
return rect.animate({ x, y });
|
||||
}),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
await sleep(300);
|
||||
|
||||
Promise.all(
|
||||
[
|
||||
rects.map((rect) => rect.animate({ alpha: 0.68 })),
|
||||
dots.map((dot) => dot.animate({ alpha: 0.68 })),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
props.blinkingContainer.blink();
|
||||
await sleep(400);
|
||||
|
||||
for await (const rect of rects) {
|
||||
// Get the surrounding dots of this rect
|
||||
const rectX = rect.currentProps.x;
|
||||
const rectY = rect.currentProps.y;
|
||||
const surroundingDots = dots.filter((dot) => {
|
||||
const dx = Math.abs(dot.currentProps.x - rectX);
|
||||
const dy = Math.abs(dot.currentProps.y - rectY);
|
||||
|
||||
// Consider "surrounding" as adjacent horizontally, vertically, or diagonally (distance 18)
|
||||
return (
|
||||
(dx === 0 && dy === 9) ||
|
||||
(dx === 9 && dy === 0) ||
|
||||
(dx === 9 && dy === 9)
|
||||
);
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
surroundingDots.map((dot) =>
|
||||
dot.animate({ alpha: 1 }, { duration: 0.75 }),
|
||||
),
|
||||
rect.animate({ alpha: 1, width: 14, height: 14 }, { duration: 0.75 }),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
rect.animate({ alpha: 0.68, width: 10, height: 10 }, { duration: 0.75 });
|
||||
Promise.all(
|
||||
surroundingDots.map((dot) =>
|
||||
dot.animate({ alpha: 0.68 }, { duration: 0.75 }),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
rects.map((rect) =>
|
||||
rect.animate(props.anchorGraphic.defaultProps, {
|
||||
delay: Math.random() * 0.3,
|
||||
duration: 0.3,
|
||||
}),
|
||||
),
|
||||
|
||||
dots.map((dot) =>
|
||||
dot.animate(dot.defaultProps, { delay: Math.random() * 0.3 }),
|
||||
),
|
||||
].flat(),
|
||||
);
|
||||
|
||||
rects.shift();
|
||||
|
||||
rects.forEach((rect) => rect.graphic.destroy());
|
||||
dots.forEach((dot) => dot.graphic.destroy());
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
"use client";
|
||||
|
||||
// import dynamic from "next/dynamic";
|
||||
// import { useRef, useEffect, forwardRef } from "react";
|
||||
|
||||
// const originalText =
|
||||
// "";
|
||||
|
||||
type Options = {
|
||||
randomizeChance?: number;
|
||||
reversed?: boolean;
|
||||
};
|
||||
|
||||
export const encryptText = (
|
||||
text: string,
|
||||
progress: number,
|
||||
_options?: Options,
|
||||
) => {
|
||||
const options = {
|
||||
randomizeChance: 0.7,
|
||||
..._options,
|
||||
};
|
||||
|
||||
const encryptionChars = "a-zA-Z0-9*=?!";
|
||||
const skipTags = ["<br class='lg-max:hidden'>", "<span>", "</span>"];
|
||||
|
||||
// Calculate how many characters should be encrypted
|
||||
const totalChars = text.length;
|
||||
const encryptedCount = Math.floor(totalChars * (1 - progress));
|
||||
|
||||
let result = "";
|
||||
let charIndex = 1;
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const char = text[i];
|
||||
|
||||
// Check if we're at the start of a tag to skip
|
||||
let shouldSkip = false;
|
||||
|
||||
for (const tag of skipTags) {
|
||||
if (text.substring(i, i + tag.length) === tag) {
|
||||
result += tag;
|
||||
i += tag.length - 1; // -1 because loop will increment
|
||||
shouldSkip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSkip) continue;
|
||||
|
||||
// Skip spaces - keep them as is
|
||||
if (char === " ") {
|
||||
result += char;
|
||||
charIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this character should be encrypted
|
||||
if (
|
||||
options.reversed
|
||||
? charIndex < encryptedCount
|
||||
: text.length - charIndex < encryptedCount
|
||||
) {
|
||||
// 40% chance to show original character, 60% chance to encrypt
|
||||
if (Math.random() < options.randomizeChance) {
|
||||
result += char;
|
||||
} else {
|
||||
// Use random character from encryption set
|
||||
const randomIndex = Math.floor(Math.random() * encryptionChars.length);
|
||||
result += encryptionChars[randomIndex];
|
||||
}
|
||||
} else {
|
||||
// Keep original character
|
||||
result += char;
|
||||
}
|
||||
|
||||
charIndex++;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// const Wrapper = forwardRef<
|
||||
// HTMLDivElement,
|
||||
// React.HTMLAttributes<HTMLDivElement>
|
||||
// >((props, ref) => {
|
||||
// return (
|
||||
// <div className="text-title-h1 mx-auto text-center [&_span]:text-heat-100 mb-12 lg:mb-16">
|
||||
// <div {...props} className="hidden lg:contents" ref={ref} />
|
||||
// <div
|
||||
// className="lg:hidden contents"
|
||||
// dangerouslySetInnerHTML={{ __html: originalText }}
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// });
|
||||
|
||||
// Wrapper.displayName = "Wrapper";
|
||||
|
||||
// export default dynamic(() => Promise.resolve(HomeHeroTitle), {
|
||||
// ssr: false,
|
||||
// loading: () => (
|
||||
// <Wrapper
|
||||
// dangerouslySetInnerHTML={{ __html: encryptText(originalText, 0) }}
|
||||
// />
|
||||
// ),
|
||||
// });
|
||||
|
||||
// function HomeHeroTitle() {
|
||||
// const textRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (window.innerWidth < 996) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// let progress = 0;
|
||||
// let increaseProgress = -10;
|
||||
|
||||
// const animate = () => {
|
||||
// increaseProgress = (increaseProgress + 1) % 5;
|
||||
|
||||
// if (increaseProgress === 4) {
|
||||
// progress += 0.3;
|
||||
// }
|
||||
|
||||
// if (progress > 1) {
|
||||
// progress = 1;
|
||||
// textRef.current!.innerHTML = encryptText(originalText, progress);
|
||||
|
||||
// return;
|
||||
// }
|
||||
|
||||
// textRef.current!.innerHTML = encryptText(originalText, progress);
|
||||
|
||||
// const interval = 50 + progress * 20;
|
||||
// setTimeout(animate, interval);
|
||||
// };
|
||||
|
||||
// animate();
|
||||
// }, []);
|
||||
|
||||
// return (
|
||||
// <Wrapper
|
||||
// dangerouslySetInnerHTML={{ __html: encryptText(originalText, 0) }}
|
||||
// ref={textRef}
|
||||
// />
|
||||
// );
|
||||
// }
|
||||
|
||||
// import dynamic from "next/dynamic";
|
||||
// import { useRef, useEffect, forwardRef } from "react";
|
||||
|
||||
// const originalText =
|
||||
// "Turn websites into <br class='lg-max:hidden'><span>LLM-ready</span> data";
|
||||
|
||||
// type Options = {
|
||||
// randomizeChance?: number;
|
||||
// reversed?: boolean;
|
||||
// };
|
||||
|
||||
// export const encryptText = (
|
||||
// text: string,
|
||||
// progress: number,
|
||||
// _options?: Options,
|
||||
// ) => {
|
||||
// const options = {
|
||||
// randomizeChance: 0.7,
|
||||
// ..._options,
|
||||
// };
|
||||
|
||||
// const encryptionChars = "a-zA-Z0-9*=?!";
|
||||
// const skipTags = ["<br class='lg-max:hidden'>", "<span>", "</span>"];
|
||||
|
||||
// // Calculate how many characters should be encrypted
|
||||
// const totalChars = text.length;
|
||||
// const encryptedCount = Math.floor(totalChars * (1 - progress));
|
||||
|
||||
// let result = "";
|
||||
// let charIndex = 1;
|
||||
|
||||
// for (let i = 0; i < text.length; i++) {
|
||||
// const char = text[i];
|
||||
|
||||
// // Check if we're at the start of a tag to skip
|
||||
// let shouldSkip = false;
|
||||
|
||||
// for (const tag of skipTags) {
|
||||
// if (text.substring(i, i + tag.length) === tag) {
|
||||
// result += tag;
|
||||
// i += tag.length - 1; // -1 because loop will increment
|
||||
// shouldSkip = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (shouldSkip) continue;
|
||||
|
||||
// // Skip spaces - keep them as is
|
||||
// if (char === " ") {
|
||||
// result += char;
|
||||
// charIndex++;
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // If this character should be encrypted
|
||||
// if (
|
||||
// options.reversed
|
||||
// ? charIndex < encryptedCount
|
||||
// : text.length - charIndex < encryptedCount
|
||||
// ) {
|
||||
// // 40% chance to show original character, 60% chance to encrypt
|
||||
// if (Math.random() < options.randomizeChance) {
|
||||
// result += char;
|
||||
// } else {
|
||||
// // Use random character from encryption set
|
||||
// const randomIndex = Math.floor(Math.random() * encryptionChars.length);
|
||||
// result += encryptionChars[randomIndex];
|
||||
// }
|
||||
// } else {
|
||||
// // Keep original character
|
||||
// result += char;
|
||||
// }
|
||||
|
||||
// charIndex++;
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// };
|
||||
|
||||
// const Wrapper = forwardRef<
|
||||
// HTMLDivElement,
|
||||
// React.HTMLAttributes<HTMLDivElement>
|
||||
// >((props, ref) => {
|
||||
// return (
|
||||
// <div className="text-title-h1 mx-auto text-center [&_span]:text-heat-100 mb-12 lg:mb-16">
|
||||
// <div {...props} className="hidden lg:contents" ref={ref} />
|
||||
// <div
|
||||
// className="lg:hidden contents"
|
||||
// dangerouslySetInnerHTML={{ __html: originalText }}
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// });
|
||||
|
||||
// Wrapper.displayName = "Wrapper";
|
||||
|
||||
// export default dynamic(() => Promise.resolve(HomeHeroTitle), {
|
||||
// ssr: false,
|
||||
// loading: () => (
|
||||
// <Wrapper
|
||||
// dangerouslySetInnerHTML={{ __html: encryptText(originalText, 0) }}
|
||||
// />
|
||||
// ),
|
||||
// });
|
||||
|
||||
export default function HomeHeroTitle() {
|
||||
return (
|
||||
<h1 className="text-title-h1 mx-auto text-center [&_span]:text-heat-100 mb-12 lg:mb-16">
|
||||
Open Lovable <span>v3</span>
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user