From cd62ab6c9d2900a4a8933ce9ffd4dc1e3d876064 Mon Sep 17 00:00:00 2001 From: Ra-Liz <2879055132@qq.com> Date: Thu, 11 Jul 2024 22:24:34 +0800 Subject: [PATCH] added skinNbg ui --- src/app/helper.tsx | 633 +++++++++++++++++++++++++-------------------- tailwind.config.ts | 5 + 2 files changed, 355 insertions(+), 283 deletions(-) diff --git a/src/app/helper.tsx b/src/app/helper.tsx index ed6482d..743b87c 100644 --- a/src/app/helper.tsx +++ b/src/app/helper.tsx @@ -13,303 +13,370 @@ import txt2Voice from "@/hooks/txt2VoiceAPI"; // fake list const opList = [ - { - id: 1, - title: "幸运转盘", - src: "http://picdown.jchysoft.com/uiIcon/xingyunzhuanpan.png", - }, - { - id: 2, - title: "领券中心", - src: "http://picdown.jchysoft.com/uiIcon/lingjuanzhongxin.png", - }, // bjt错别字将错就错版 - { - id: 3, - title: "0元抽", - src: "http://picdown.jchysoft.com/uiIcon/0yuanchou.png", - }, - { - id: 4, - title: "欧宝赏", - src: "http://picdown.jchysoft.com/uiIcon/oubanshang.png", - }, // bjt错别字 + { + id: 1, + title: "幸运转盘", + src: "http://picdown.jchysoft.com/uiIcon/xingyunzhuanpan.png", + }, + { + id: 2, + title: "领券中心", + src: "http://picdown.jchysoft.com/uiIcon/lingjuanzhongxin.png", + }, // bjt错别字将错就错版 + { + id: 3, + title: "0元抽", + src: "http://picdown.jchysoft.com/uiIcon/0yuanchou.png", + }, + { + id: 4, + title: "欧宝赏", + src: "http://picdown.jchysoft.com/uiIcon/oubanshang.png", + }, // bjt错别字 ]; //import "@/deps/live2dcubismcore.min.js" export default function Home() { - const [useVoice, setUseVoice] = useState(false); - const query = useSearchParams(); - const characterId = query.get("id"); - const token = query.get("token"); - localStorage.setItem("token", token || ""); - const { complete, completion: data, isLoading, abort } = useRequest(); - const voice2txt = (txt: string) => { - fetch("sharkapiBaseUrl/voice/txt2voice", { - method: "POST", - headers: { - "Content-Type": "application/json", - "Authorization": `${token}`, - }, - body: JSON.stringify({ - txt: txt, - }), - }) - } - function draggable(model: any) { - model.buttonMode = true; - model.on("pointerdown", (e: any) => { - model.dragging = true; - model._pointerX = e.data.global.x - model.x; - model._pointerY = e.data.global.y - model.y; - }); - model.on("pointermove", (e: any) => { - if (model.dragging) { - model.position.x = e.data.global.x - model._pointerX; - model.position.y = e.data.global.y - model._pointerY; - } - }); - model.on("pointerupoutside", () => (model.dragging = false)); - model.on("pointerup", () => (model.dragging = false)); - } - - function addFrame(model: any) { - const foreground = PIXI.Sprite.from(PIXI.Texture.WHITE); - foreground.width = model.internalModel.width; - foreground.height = model.internalModel.height; - foreground.alpha = 0.2; - - model.addChild(foreground); - } - - function addHitAreaFrames(model: any) { - try { - //@ts-ignore - const hitAreaFrames = new PIXI.live2d.HitAreaFrames(); - - model.addChild(hitAreaFrames); - - checkbox( - "Hit Area Frames", - (checked: any) => (hitAreaFrames.visible = checked) - ); - } catch (err) { } - } - - function checkbox(name: any, onChange: any) { - const id = name.replace(/\W/g, "").toLowerCase(); - - let checkbox = document.getElementById(id); - - if (!checkbox) { - const p = document.createElement("p")!; - p.innerHTML = ` `; - - document!.getElementById("control")!.appendChild(p); - checkbox = p.firstChild as HTMLElement; - } - - checkbox.addEventListener("change", () => { - //@ts-ignore - onChange(checkbox.checked); - }); - //@ts-ignore - onChange(checkbox.checked); - } - const send = (inputText: string) => { - setResponse(inputText); - if (!inputText) return; - complete(1, [], [{ - content: inputText, - role: "user", - }]); - }; - useEffect(() => { - (async () => { - if (data) { - setResponse(data); - if (typeof speak !== "undefined" && isLoading === false) { - const base64Voice = "data:audio/mp3;base64," + await txt2Voice(data, 4); - const audio = document.createElement("audio"); - audio.src = base64Voice; - audio.play(); - } else { - model!.motion("tap_body"); - } - } - })(); - - }, [data, isLoading]); - - const { start, end, text, isListening, error } = useVoice2Txt({ - lang: "cmn-Hans-CN", - continuous: false, + const [useVoice, setUseVoice] = useState(false); + const query = useSearchParams(); + const characterId = query.get("id"); + const token = query.get("token"); + localStorage.setItem("token", token || ""); + const { complete, completion: data, isLoading, abort } = useRequest(); + const voice2txt = (txt: string) => { + fetch("sharkapiBaseUrl/voice/txt2voice", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `${token}`, + }, + body: JSON.stringify({ + txt: txt, + }), }); + }; + function draggable(model: any) { + model.buttonMode = true; + model.on("pointerdown", (e: any) => { + model.dragging = true; + model._pointerX = e.data.global.x - model.x; + model._pointerY = e.data.global.y - model.y; + }); + model.on("pointermove", (e: any) => { + if (model.dragging) { + model.position.x = e.data.global.x - model._pointerX; + model.position.y = e.data.global.y - model._pointerY; + } + }); + model.on("pointerupoutside", () => (model.dragging = false)); + model.on("pointerup", () => (model.dragging = false)); + } - const { isSpeaking, speak, stop } = useTxt2Voice(); - const [inputText, setInputText] = useState(""); - const isMouthOpen = useRef(false); - const [response, setResponse] = useState("来和我聊天吧~"); - useEffect(() => { - console.log(text, error); - if (!text) return; - send(text); - // 先叉了这里防止消息展示error - // if (error) { - // setResponse(error); - // return; - // } - }, [text, error]); - const [model, setModel] = useState(); - useEffect(() => { - if (!isSpeaking) { - isMouthOpen.current = false; - } - }, [isSpeaking]); + function addFrame(model: any) { + const foreground = PIXI.Sprite.from(PIXI.Texture.WHITE); + foreground.width = model.internalModel.width; + foreground.height = model.internalModel.height; + foreground.alpha = 0.2; - useEffect(() => { - if (!isListening && !isSpeaking) { - } - }, [isListening]); + model.addChild(foreground); + } - useEffect(() => { - // expose PIXI to window so that this plugin is able to - // reference window.PIXI.Ticker to automatically update Live2D models - //@ts-ignore - typeof window !== "undefined" && (window.PIXI = PIXI); - (async function () { - const app = new PIXI.Application({ - view: document.getElementById("canvas") as HTMLCanvasElement, - backgroundAlpha: 0, - }); + function addHitAreaFrames(model: any) { + try { + //@ts-ignore + const hitAreaFrames = new PIXI.live2d.HitAreaFrames(); - const model = await Live2DModel.from( - "https://cdn.jsdelivr.net/gh/guansss/pixi-live2d-display/test/assets/haru/haru_greeter_t03.model3.json" - ); + model.addChild(hitAreaFrames); - app.stage.addChild(model); - const scaleX = (innerWidth * 0.4) / model.width; - const scaleY = (innerHeight * 0.8) / model.height; + checkbox( + "Hit Area Frames", + (checked: any) => (hitAreaFrames.visible = checked) + ); + } catch (err) {} + } - // fit the window - model.scale.set(0.3); + function checkbox(name: any, onChange: any) { + const id = name.replace(/\W/g, "").toLowerCase(); - model.y = innerHeight * 0.1; + let checkbox = document.getElementById(id); - draggable(model); - addFrame(model); - addHitAreaFrames(model); - setModel(model); - model.on("hit", (hitAreas) => { - if (hitAreas.includes("body")) { - model.motion("tap_body"); - model.motion("speak"); - } - }); - console.log("ok") - })(); - }, []); + if (!checkbox) { + const p = document.createElement("p")!; + p.innerHTML = ` `; - return ( -
- {typeof window !== "undefined" && - typeof window.Live2DCubismCore !== "undefined" && ( -
- {/* live2d */} - + document!.getElementById("control")!.appendChild(p); + checkbox = p.firstChild as HTMLElement; + } - {/* 旧叉 */} - {/*
- {response ? response : "请输入文字和我聊天吧"} -
*/} - - {/* 角色信息 */} -
-
-
昵称
-
- - {/* 消息泡泡 */} -
- {response ? response : "请输入文字和我聊天吧"} -
- - {/* 选项 */} -
- {opList.map((item) => ( -
-
- {item.title} -
-
{item.title}
-
- ))} -
- - {/* 旧叉 */} - {/*
*/} - {/* - */} - - {/* 输入框 */} -
- - {useVoice ? ( - - ) : ( - { - setInputText(e.target.value); - console.log(e.target.value); - }} - > - )} - -
-
- )} -
+ checkbox.addEventListener("change", () => { + //@ts-ignore + onChange(checkbox.checked); + }); + //@ts-ignore + onChange(checkbox.checked); + } + const send = (inputText: string) => { + setResponse(inputText); + if (!inputText) return; + complete( + 1, + [], + [ + { + content: inputText, + role: "user", + }, + ] ); + }; + useEffect(() => { + (async () => { + if (data) { + setResponse(data); + if (typeof speak !== "undefined" && isLoading === false) { + const base64Voice = + "data:audio/mp3;base64," + (await txt2Voice(data, 4)); + const audio = document.createElement("audio"); + audio.src = base64Voice; + audio.play(); + } else { + model!.motion("tap_body"); + } + } + })(); + }, [data, isLoading]); + + const { start, end, text, isListening, error } = useVoice2Txt({ + lang: "cmn-Hans-CN", + continuous: false, + }); + + const { isSpeaking, speak, stop } = useTxt2Voice(); + const [inputText, setInputText] = useState(""); + const isMouthOpen = useRef(false); + const [response, setResponse] = useState("来和我聊天吧~"); + + useEffect(() => { + console.log(text, error); + if (!text) return; + send(text); + // 先叉了这里防止消息展示error + // if (error) { + // setResponse(error); + // return; + // } + }, [text, error]); + const [model, setModel] = useState(); + + useEffect(() => { + if (!isSpeaking) { + isMouthOpen.current = false; + } + }, [isSpeaking]); + + useEffect(() => { + if (!isListening && !isSpeaking) { + } + }, [isListening]); + + useEffect(() => { + // expose PIXI to window so that this plugin is able to + // reference window.PIXI.Ticker to automatically update Live2D models + //@ts-ignore + typeof window !== "undefined" && (window.PIXI = PIXI); + (async function () { + const app = new PIXI.Application({ + view: document.getElementById("canvas") as HTMLCanvasElement, + backgroundAlpha: 0, + }); + + const model = await Live2DModel.from( + "https://cdn.jsdelivr.net/gh/guansss/pixi-live2d-display/test/assets/haru/haru_greeter_t03.model3.json" + ); + + app.stage.addChild(model); + const scaleX = (innerWidth * 0.4) / model.width; + const scaleY = (innerHeight * 0.8) / model.height; + + // fit the window + model.scale.set(0.3); + + model.y = innerHeight * 0.1; + + draggable(model); + addFrame(model); + addHitAreaFrames(model); + setModel(model); + model.on("hit", (hitAreas) => { + if (hitAreas.includes("body")) { + model.motion("tap_body"); + model.motion("speak"); + } + }); + console.log("ok"); + })(); + }, []); + + // 换肤/换背景相关 + + return ( +
+ {typeof window !== "undefined" && + typeof window.Live2DCubismCore !== "undefined" && ( +
+ {/* live2d */} + + + {/* 角色信息 */} +
+
+
昵称
+
+ + {/* 消息泡泡 */} +
+ {response ? response : "请输入文字和我聊天吧"} +
+ + {/* 右侧选项 */} +
+ {opList.map((item) => ( +
+
+ {item.title} +
+
{item.title}
+
+ ))} +
+ + {/* 皮肤/背景列表 */} + + + {/* 底部换肤 & 换背景 */} +
+ + +
+ + {/* 底部消息输入发送框 */} +
+ + {useVoice ? ( + + ) : ( + { + setInputText(e.target.value); + console.log(e.target.value); + }} + > + )} + +
+
+ )} +
+ ); } + +const fakeList = [ + { id: 1, src: "", name: "皮肤1" }, + { id: 2, src: "", name: "皮肤2" }, + { id: 3, src: "", name: "皮肤3" }, + { id: 4, src: "", name: "皮肤4" }, + { id: 5, src: "", name: "皮肤5" }, + { id: 6, src: "", name: "皮肤6" }, + { id: 7, src: "", name: "皮肤7" }, + { id: 8, src: "", name: "皮肤8" }, + { id: 9, src: "", name: "皮肤9" }, + { id: 10, src: "", name: "皮肤10" }, +]; + +type listItem = { id: number; src: string; name: string }; +export const SkinOrBgList = ({ + type, + list, +}: { + type: "skin" | "bg"; + list: listItem[]; +}) => { + return ( +
+ {list.map((item) => ( +
+ {item.name} +
+ ))} +
+ ); +}; diff --git a/tailwind.config.ts b/tailwind.config.ts index e9a0944..4924ca0 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -12,6 +12,11 @@ const config: Config = { "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", + "gradient-to-r": "linear-gradient(to right, var(--tw-gradient-stops))", + }, + colors: { + "start-color": "#FD7A61", + "end-color": "#FF934D", }, }, },