image-transfer / src /app /page.tsx
william
add login
a8dfd23
/** @jsxImportSource @emotion/react */
"use client";
import Dropzone, { uploadImageAtom } from "@/components/dropzone";
import { css } from "@emotion/react";
import { Button } from "antd";
import { useAtom, useAtomValue, atom } from "jotai";
import Header from "@/components/header";
import {
DownloadOutlined,
ReloadOutlined,
SyncOutlined,
} from "@ant-design/icons";
import { useEffect, useState } from "react";
import StyleCard from "./style-card";
const STYLEs = [
{
style: "anime",
name: "Anime",
},
{
style: "3d",
name: "3D",
},
{
style: "handdrawn",
name: "Handdrawn",
},
{
style: "sketch",
name: "Sketch",
},
{
style: "artstyle",
name: "Art Style",
},
{
style: "sd-design",
name: "SD Design",
},
{
style: "sd-illustration",
name: "SD Illustrate",
},
];
const generatedImageAtom = atom<string | null>(null);
export default function Home() {
const uploadImage = useAtomValue(uploadImageAtom);
const [generatedImage, setGeneratedImage] = useAtom(generatedImageAtom);
const [loading, setLoading] = useState(false);
const [pickedStyle, setPickedStyle] = useState("anime");
useEffect(() => {
setGeneratedImage(null);
}, [uploadImage]);
const handleTransform = async () => {
if (!uploadImage) return;
setLoading(true);
const res = await fetch("/api/style-transform", {
method: "POST",
body: JSON.stringify({
style: pickedStyle,
pic: uploadImage.split(",")[1],
}),
})
.then((res) => res.json())
.finally(() => setLoading(false));
setGeneratedImage(`data:image/jpeg;base64,${res.image}`);
};
return (
<>
<main className="min-h-screen container mx-auto pb-8">
<Header />
<div className="flex flex-col items-center mx-auto px-4">
<h1 className="text-5xl font-bold text-center mt-4 mb-8">
AI Art Generator
</h1>
<div className="flex flex-col items-center gap-4 max-w-4xl">
<Dropzone />
<div className="flex justify-center w-full space-x-4 p-2 overflow-x-auto">
{STYLEs.map((style) => (
<StyleCard
key={style.name}
styleName={style.name}
selected={style.style === pickedStyle}
onClick={() => setPickedStyle(style.style)}
/>
))}
</div>
{generatedImage ? (
<div className="relative flex justify-center items-center w-[600px] h-[400px]">
<div className="absolute top-0 right-0 translate-x-[100%] flex flex-col pl-1 gap-1 text-xl text-gray-800">
<ReloadOutlined
className="cursor-pointer hover:scale-125"
onClick={() => handleTransform()}
/>
<DownloadOutlined
className="cursor-pointer hover:scale-125"
onClick={() => {
const a = document.createElement("a");
a.href = generatedImage;
a.download = "generated.jpg";
a.click();
}}
/>
</div>
{loading && (
<SyncOutlined
spin
className="absolute text-9xl text-gray-700 opacity-60"
/>
)}
<img
className="object-contain object-top w-full h-full"
src={generatedImage}
alt="generated image"
/>
</div>
) : (
<Button
className="w-[200px] h-[80px] text-2xl text-white rounded-lg"
css={css`
background: linear-gradient(
106.28deg,
rgb(133, 96, 250) 0%,
rgb(68, 211, 239) 90.81%
) !important;
`}
disabled={!uploadImage}
onClick={handleTransform}
loading={loading}
>
Generate!
</Button>
)}
</div>
</div>
</main>
</>
);
}