增加全功能editor页面

This commit is contained in:
ymk
2025-08-01 17:04:17 +08:00
parent c2bb89f7bd
commit f54550631c
18 changed files with 290 additions and 48 deletions

View File

@@ -8,6 +8,14 @@ export default function Footer() {
<footer className="w-full border-t backdrop-blur-sm bg-background/95 ">
<Flex justify={"between"} align={"center"} direction={"column"} gap={"2"} p="2">
<Flex justify={"center"} gap={"4"}>
<Link
href="/editor"
className="text-sm text-muted-foreground hover:text-primary"
>
Editor
</Link>
<Link
href="/do-not-write-on-this-page"
className="text-sm text-muted-foreground hover:text-primary"

View File

@@ -1,12 +1,12 @@
"use client";
import { Flex, Box } from "@radix-ui/themes";
import { Flex, Box, Link } from "@radix-ui/themes";
import BackgroundSelector, {
BackgroundProp,
} from "./common/BackgroundSelector";
import PreviewToolbar from "./common/PreviewToolbar";
import TextSetting, { FontNames, FontWeights, TextProp } from "./common/TextSetting";
import { useState } from "react";
import { useTranslations } from "next-intl";
import TextSetting, { TextProp } from "./common/TextSetting";
/**
*
@@ -16,17 +16,12 @@ export default function Page({ textProp, backgroundProp }: { textProp: TextProp
const t = useTranslations("TextEditor");
const [background, setBackground] = useState<BackgroundProp>(backgroundProp || {
const [background, setBackground] = useState<BackgroundProp>({
type: "color",
color: "#c4b1b1",
image: null,
});
const [text, setText] = useState<TextProp>(textProp || {
text: t("defaultText"),
color: "#8e86fe",
font: FontNames[0],
weight: FontWeights[0],
});
const [text, setText] = useState<TextProp>(TextProp.default(t("defaultText")));
return (
<Flex gap={"2"}>
@@ -38,9 +33,9 @@ export default function Page({ textProp, backgroundProp }: { textProp: TextProp
<TextSetting text={text} setText={setText} />
</Flex>
<Box className="w-2/3" >
<Flex className="w-2/3" direction={"column"} justify={"between"}>
<PreviewToolbar background={background} text={text} />
</Box>
</Flex>
</Flex>
);
}

View File

@@ -6,23 +6,23 @@ import { Box, Flex, Link, Strong, Text } from "@radix-ui/themes";
export default function Header() {
const t = useTranslations("Index");
return (
<header className="w-full py-2">
<header className="w-full py-2 border-b-1 ">
<Flex justify="center" gap="9" align="center">
<Box >
<Box className="w-1/4 text-center" >
<a href="/" >
<Text size="6" color="iris"><Strong>{t("appName")}</Strong></Text>
</a>
</Box >
<Flex gap={"4"} justify={"between"} align={"center"}>
<Flex gap={"4"} justify={"center"} align={"center"} className="w-1/2">
<Link
href="/do-not-write-on-this-page"
href="/editor"
>
Do Not Write On This Page
Editor
</Link>
</Flex>
<Flex align="center" gap="4">
<Flex align="center" gap="4" className="w-1/4">
<LanguageSwitcher />
<ModeToggle />
</Flex>

View File

@@ -0,0 +1,46 @@
"use client";
import { Flex, Box, Link } from "@radix-ui/themes";
import BackgroundSelector, {
BackgroundProp,
} from "./common/BackgroundSelector";
import PreviewToolbar from "./common/PreviewToolbar";
import SimpleTextSetting from "./common/SimpleTextSetting";
import { useState } from "react";
import { useTranslations } from "next-intl";
import { TextProp } from "./common/TextSetting";
/**
* 简易工具
* @returns
*/
export default function Page({ textProp, backgroundProp }: { textProp: TextProp | undefined, backgroundProp: BackgroundProp | undefined }) {
const t = useTranslations("TextEditor");
const tIndex = useTranslations("Index");
const [background, setBackground] = useState<BackgroundProp>(backgroundProp || {
type: "color",
color: "#c4b1b1",
image: null,
});
const [text, setText] = useState<TextProp>(textProp || TextProp.default(t("defaultText")));
return (
<Flex gap={"2"}>
<Flex gap={"2"} direction={"column"} className="w-1/3">
<BackgroundSelector
background={background}
setBackground={setBackground}
/>
<SimpleTextSetting text={text} setText={setText} />
</Flex>
<Flex className="w-2/3" direction={"column"} justify={"between"}>
<PreviewToolbar background={background} text={text} />
<Box className="text-center"> <Link href="./editor">{tIndex("toolMore")}?</Link> </Box>
</Flex>
</Flex>
);
}

View File

@@ -3,9 +3,9 @@ import { useState, useRef, useEffect, } from "react";
import { useTranslations } from "next-intl";
import { Eye, Download } from "lucide-react";
import { BackgroundProp } from "./BackgroundSelector";
import { TextProp } from "./TextSetting";
import { Text, Flex } from "@radix-ui/themes";
import { getPicture, resize, init as threeInit, updateBackground, updateTextProps } from "./ThreeTools";
import { TextProp } from "./TextSetting";
const Sizes = [
"1920x1080",

View File

@@ -0,0 +1,68 @@
import { Flex, Heading } from "@radix-ui/themes";
import { useTranslations } from "next-intl";
import { FontNames, FontWeights, TextProp } from "./TextSetting";
export default function TextSetting({
text,
setText,
}: {
text: TextProp;
setText: (text: TextProp) => void;
}) {
const t = useTranslations("TextEditor");
return (
<Flex className="p-4 border rounded-lg " gap={"3"} direction={"column"}>
<Heading size={"3"} className="font-medium text-lg" >{t("title")}</Heading>
<textarea
value={text.text}
onChange={e => setText({ ...text, text: e.target.value })}
className="w-full p-3 border rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
rows={4}
/>
<div className="space-y-1">
<label className="block text-sm text-muted-foreground">
{t("textColor")}
</label>
<input
type="color"
value={text.color}
onChange={e => setText({ ...text, color: e.target.value })}
className="w-full h-10 rounded-md cursor-pointer"
/>
</div>
<div className="space-y-1">
<label className="block text-sm text-muted-foreground">
{t("fontFamily")}
</label>
<select
value={text.font}
onChange={(e) => setText({ ...text, font: e.target.value })}
className="w-full p-2 border rounded-md"
>
{FontNames.map((name) => (
<option key={name} value={name}>
{name}
</option>
))}
</select>
</div>
<div className="space-y-2">
<label className="block text-sm text-muted-foreground">
{t("fontWeight")}
</label>
<select
value={text.weight}
onChange={(e) => setText({ ...text, weight: e.target.value })}
className="w-full p-2 border rounded-md"
>
{FontWeights.map((weight) => (
<option key={weight} value={weight}>
{weight}
</option>
))}
</select>
</div>
</Flex>
);
}

View File

@@ -1,24 +1,63 @@
import { Flex, Heading } from "@radix-ui/themes";
import { useTranslations } from "next-intl";
export interface TextProp {
text: string;
color: string;
font: string;
weight: string;
}
export const FontWeights = ["regular", "bold"];
export const FontNames = ["gentilis", "helvetiker", "optimer", "Noto_Sans_SC_zh", "Alibaba_PuHuiTi_3.0_zh"];
export function getFontPath(fontName: string, fontWeight: String) {
if (fontName != "noto_sans_zh") {
return `/fonts/${fontName}_${fontWeight}.typeface.json`;
} else {
fontWeight = fontWeight.charAt(0).toUpperCase() + fontWeight.slice(1);
return `https://fast3dtest.mysoul.fun/Noto_Sans_SC_${fontWeight}.json`;
type FontFrom = "local" | "upload";
export class TextProp {
text: string
color: string
fontFrom: FontFrom
font: string
weight: string
constructor(
text: string,
color: string,
fontFrom: FontFrom,
font: string,
weight: string) {
this.text = text;
this.color = color;
this.fontFrom = fontFrom;
this.font = font;
this.weight = weight;
}
static default(text: string): TextProp {
let font = FontNames[0];
if (containsChinese(text)) {
font = "Alibaba_PuHuiTi_3.0_zh";
}
return {
text,
color: "#8e86fe",
font,
weight: FontWeights[0],
fontFrom: "local",
}
}
}
export const FontWeights = ["regular", "bold"];
export const FontNames = ["gentilis", "helvetiker", "optimer", "noto_sans_zh"];
function containsChinese(str: string) {
return /[\u4e00-\u9fa5]/.test(str);
}
export function getFontPath(fontName: string, fontWeight: String) {
if (!fontName.endsWith("zh")) {
return `/fonts/${fontName}_${fontWeight}.typeface.json`;
} else {
fontWeight = fontWeight.charAt(0).toUpperCase() + fontWeight.slice(1);
let font = fontName.slice(0, -3);
return `https://fast3dtest.mysoul.fun/${font}_${fontWeight}.json`;
}
}
export default function TextSetting({
text,

View File

@@ -1,11 +1,11 @@
import * as THREE from "three";
import { getFontPath, TextProp } from "./TextSetting";
import { BackgroundProp } from "./BackgroundSelector";
import { Font, FontLoader } from "three/addons/loaders/FontLoader.js";
import { TextGeometry } from "three/addons/geometries/TextGeometry.js";
THREE.Cache.enabled = true;
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { TextProp, getFontPath } from "./TextSetting";
let camera: THREE.PerspectiveCamera,
scene: THREE.Scene,