支持无背景、颜色图片混合背景
This commit is contained in:
@@ -17,7 +17,6 @@ export default function Page({ textProp, backgroundProp }: { textProp: TextProp
|
||||
const t = useTranslations("TextEditor");
|
||||
|
||||
backgroundProp = backgroundProp || {
|
||||
type: "color",
|
||||
color: "#c4b1b1",
|
||||
image: null,
|
||||
} satisfies BackgroundProp;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Box, Text, Flex, Heading, Section, Radio, RadioGroup } from "@radix-ui/themes";
|
||||
'use client'
|
||||
import { Box, Checkbox, Flex, Heading } from "@radix-ui/themes";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
export type BackgroundType = "color" | "image";
|
||||
export interface BackgroundProp {
|
||||
type: BackgroundType;
|
||||
color: string;
|
||||
color: string | null;
|
||||
image: string | null;
|
||||
}
|
||||
export default function BackgroundSelector({
|
||||
@@ -14,13 +15,53 @@ export default function BackgroundSelector({
|
||||
setBackground: (bg: BackgroundProp) => void;
|
||||
}) {
|
||||
const t = useTranslations("BackgoundSetting");
|
||||
const types: BackgroundType[] = [];
|
||||
|
||||
if (background.color) {
|
||||
types.push("color");
|
||||
}
|
||||
|
||||
if (background.image) {
|
||||
types.push("image");
|
||||
}
|
||||
|
||||
const [backgroundType, setBackgroundType] = useState<BackgroundType[]>(types);
|
||||
const [color, setColor] = useState<string | null>(background.color);
|
||||
const [image, setImage] = useState<string | null>(background.image);
|
||||
|
||||
const handleBackgroundTypeChange = (value: BackgroundType) => {
|
||||
let newTypes: BackgroundType[];
|
||||
if (backgroundType.includes(value)) {
|
||||
backgroundType.splice(backgroundType.indexOf(value), 1);
|
||||
newTypes = backgroundType;
|
||||
} else {
|
||||
newTypes = [...backgroundType, value];
|
||||
}
|
||||
|
||||
setBackgroundType(newTypes);
|
||||
|
||||
if (newTypes.includes("color")) {
|
||||
background.color = color;
|
||||
} else {
|
||||
background.color = null;
|
||||
}
|
||||
if (newTypes.includes("image")) {
|
||||
background.image = image;
|
||||
} else {
|
||||
background.image = null;
|
||||
}
|
||||
|
||||
setBackground({ ...background });
|
||||
}
|
||||
|
||||
const handleColorChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setBackground({
|
||||
type: "color",
|
||||
color: e.target.value,
|
||||
image: background.image,
|
||||
});
|
||||
setColor(e.target.value);
|
||||
if (backgroundType.includes("color")) {
|
||||
setBackground({
|
||||
...background,
|
||||
color: e.target.value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -29,11 +70,13 @@ export default function BackgroundSelector({
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
const result = event.target?.result as string;
|
||||
setBackground({
|
||||
type: "image",
|
||||
image: result,
|
||||
color: background.color,
|
||||
});
|
||||
setImage(result);
|
||||
if (backgroundType.includes("image")) {
|
||||
setBackground({
|
||||
...background,
|
||||
image: result,
|
||||
});
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
@@ -42,52 +85,47 @@ export default function BackgroundSelector({
|
||||
return (
|
||||
<Box className="p-4 border rounded-lg min-w-64">
|
||||
<Heading as="h2" size="4" className="font-medium text-lg">{t("title")}</Heading>
|
||||
<Flex gap={"2"} p="2">
|
||||
|
||||
<RadioGroup.Root value={background.type} name="backgroundType" orientation="horizontal" onValueChange={(value) => setBackground({ ...background, type: value as BackgroundType })}>
|
||||
<RadioGroup.Item value="color">{t("colorOption")}</RadioGroup.Item>
|
||||
<RadioGroup.Item value="image">{t("imageOption")}</RadioGroup.Item>
|
||||
</RadioGroup.Root>
|
||||
|
||||
</Flex>
|
||||
|
||||
<Box className="w-full">
|
||||
{background.type === "color" && (
|
||||
<Flex gap={"6"} p="2">
|
||||
<Flex gap={"2"} p="2" direction={"column"}>
|
||||
<Flex gap="2" align={"center"}>
|
||||
<Checkbox checked={backgroundType.includes("color")} onClick={(e) => handleBackgroundTypeChange("color")} className="cursor-pointer" />
|
||||
<Heading as="h3" size={"3"}>{t("colorOption")}</Heading>
|
||||
<Flex gap={"4"} >
|
||||
<input
|
||||
type="color"
|
||||
id="color-picker"
|
||||
value={background.color}
|
||||
value={color || "black"}
|
||||
onChange={handleColorChange}
|
||||
className="w-1/3 h-10 rounded-md cursor-pointer"
|
||||
/>
|
||||
|
||||
<input
|
||||
{color && (<input
|
||||
type="text"
|
||||
value={background.color}
|
||||
value={color}
|
||||
onChange={handleColorChange}
|
||||
className="w-1/3 h-10 rounded-md cursor-pointer pl-4"
|
||||
/>
|
||||
|
||||
className="w-1/2 h-10 rounded-md cursor-pointer pl-4"
|
||||
/>)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
)}
|
||||
|
||||
{background.type === "image" && (
|
||||
<input
|
||||
type="file"
|
||||
id="file-upload"
|
||||
accept="image/*"
|
||||
onChange={handleImageUpload}
|
||||
className="block w-full text-sm text-muted-foreground
|
||||
<Flex gap="2" align={"center"}>
|
||||
<Checkbox checked={backgroundType.includes("image")} onClick={(e) => handleBackgroundTypeChange("image")} className="cursor-pointer" />
|
||||
<Heading as="h3" size={"3"}>{t("imageOption")}</Heading>
|
||||
<Flex gap={"4"} >
|
||||
<input
|
||||
type="file"
|
||||
id="file-upload"
|
||||
accept="image/*"
|
||||
onChange={handleImageUpload}
|
||||
className="block w-full text-sm text-muted-foreground
|
||||
file:mr-4 file:py-2 file:px-4
|
||||
file:rounded-md file:border-0
|
||||
file:text-sm file:font-semibold
|
||||
file:bg-primary file:text-primary-foreground
|
||||
hover:file:bg-primary/90"
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ export default function PreviewToolbar({
|
||||
const handleShare = () => {
|
||||
setShareError(null);
|
||||
setShareLink(null);
|
||||
if (background.type == "image" && background.image) {
|
||||
if (background.image) {
|
||||
setShareError(t("shareErrorNotSupportDesc"));
|
||||
return;
|
||||
}
|
||||
@@ -173,8 +173,8 @@ export default function PreviewToolbar({
|
||||
|
||||
<AspectRatio ratio={AspectRatios[aspectRadio]}>
|
||||
<canvas ref={container} className="w-full border border-gray-300" style={{
|
||||
// backgroundColor: background.type === "color" ? background.color : "none",
|
||||
backgroundImage: (background.type === "image" && background.image) ? `url(${background.image})` : "none",
|
||||
backgroundColor: background.color ? `${background.color}` : "rgba(0,0,0,0)",
|
||||
backgroundImage: (background.image) ? `url(${background.image})` : "none",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundSize: "contain",
|
||||
backgroundPosition: "center",
|
||||
@@ -209,7 +209,6 @@ export default function PreviewToolbar({
|
||||
<AlertDialog.Content maxWidth="450px">
|
||||
<AlertDialog.Title>{t("share")}</AlertDialog.Title>
|
||||
{!shareError ? (<AlertDialog.Description size="2">
|
||||
|
||||
{t("shareSuccessDesc")} !
|
||||
<br />
|
||||
<Code className="mt-2">
|
||||
|
||||
@@ -213,19 +213,22 @@ function setGradient(
|
||||
if (dir == "l2r") {
|
||||
const maxX = geo.boundingBox!.max.x;
|
||||
const minX = geo.boundingBox!.min.x;
|
||||
const color = new THREE.Color();
|
||||
for (let i = 0; i < position.count; i++) {
|
||||
const x = position.getX(i);
|
||||
const t = (x - minX) / (maxX - minX); // 归一化
|
||||
const color = new THREE.Color().lerpColors(startColor, endColor, t);
|
||||
color.lerpColors(startColor, endColor, t);
|
||||
colorss.push(color.r, color.g, color.b);
|
||||
}
|
||||
} else if (dir == "t2b") {
|
||||
const maxY = geo.boundingBox!.max.y;
|
||||
const minY = geo.boundingBox!.min.y;
|
||||
const color = new THREE.Color();
|
||||
|
||||
for (let i = 0; i < position.count; i++) {
|
||||
const y = position.getY(i);
|
||||
const t = (y - minY) / (maxY - minY); // 归一化
|
||||
const color = new THREE.Color().lerpColors(startColor, endColor, 1.0 - t);
|
||||
color.lerpColors(startColor, endColor, 1.0 - t);
|
||||
colorss.push(color.r, color.g, color.b);
|
||||
}
|
||||
}
|
||||
@@ -292,11 +295,11 @@ async function loadFont(textProps: TextProp) {
|
||||
}
|
||||
|
||||
export function updateBackground(bg: BackgroundProp) {
|
||||
if (bg.type === "color") {
|
||||
scene.background = new THREE.Color(bg.color);
|
||||
} else {
|
||||
scene.background = null;
|
||||
}
|
||||
// if (bg.color) {
|
||||
// scene.background = new THREE.Color(bg.color);
|
||||
// } else {
|
||||
// scene.background = null;
|
||||
// }
|
||||
}
|
||||
|
||||
// function createGradientTexture(colors: string[]) {
|
||||
|
||||
Reference in New Issue
Block a user