• 简体中文
  • QuickUIDesign

    关于 QuickUIDesign 模板

    QuickUIDesign 是一个基于 React 的 UE5(Unreal Engine 5)UI 开发模板。它提供了一套完整的工具链,使开发者能够使用现代前端技术创建高性能、交互性强的 UE5 界面。

    版本更新记录

    • v1.0.0 初始版本发布,包含核心模板结构、UE5 连接演示、资源工具链和 Rspack 构建配置。
    • v1.1.0 新增媒体资源打包支持,支持将图片、音频、视频等媒体资源打包到 HTML 文件中。
    • v1.2.0 重构为路由架构(route-template),引入 React Router v7 路由系统、framer-motion 页面过渡动画、ScreenAnchor 屏幕定位组件体系。
    • v1.2.5 新增 clickdeck-core 模块,提供可视化编辑器引擎,支持在浏览器端对页面进行所见即所得的样式/内容编辑。

    模板结构

    模板源码位于 src/ 目录下,组织结构如下:

    src/
    ├── assets/                          # 静态资源文件
    │   └── img/
    │       ├── QuickUI-A.svg            # QuickUI 标志 SVG
    │       ├── QuickUI-A-base64.txt
    │       ├── lufei.png                # 示例图片
    │       └── lufei-base64.txt
    ├── components/                      # React 组件库
    │   ├── Ohters/
    │   │   ├── DemoContent.tsx          # 鼠标穿透演示组件
    │   │   └── UEConnect-Demo/
    │   │       └── index.tsx            # UE5 连接演示组件
    │   ├── framer-motion/
    │   │   ├── animated-layout.tsx      # 动画布局包装器(motion.div)
    │   │   └── animated-outlet.tsx      # 路由出口动画器(AnimatePresence)
    │   └── screen-anchor/
    │       └── index.tsx                # 九宫格屏幕定位组件
    ├── lib/
    │   ├── data/
    │   │   └── animate-data.tsx         # framer-motion 默认动画预设
    │   └── utils.ts                     # 工具函数(cn 辅助方法)
    ├── pages/
    │   ├── route-template/              # 【当前活跃】路由架构模板
    │   │   ├── pages/
    │   │   │   ├── index.tsx            # 应用入口(MemoryRouter)
    │   │   │   ├── layout.tsx           # 根布局组件
    │   │   │   ├── error-page.tsx       # 路由错误边界
    │   │   │   ├── home/
    │   │   │   │   └── index.tsx        # 主页 - ScreenAnchor 演示
    │   │   │   └── show/
    │   │   │       └── index.tsx        # 展示页 - 图片加载模式演示
    │   │   └── router/
    │   │       └── index.tsx            # 路由配置
    │   └── template/                    # 旧版平面结构(不再活跃)
    │       ├── index.tsx
    │       └── App.tsx
    ├── styles/
    │   └── index.css                     # Tailwind CSS 指令
    └── types/
        └── @type.d.ts                    # TypeScript 类型声明
    
    public/
    ├── audios/
    │   └── light-on.mp3                 # 示例音频资源(非内联引用)
    ├── img/
    │   └── lufei.png                    # 示例图片资源(非内联引用)
    └── index.html                       # HTML 模板

    核心架构说明

    路由系统(React Router v7 + MemoryRouter)

    模板使用 createMemoryRouter,导航完全由内存路由管理。

    入口文件:src/pages/route-template/pages/index.tsx

    import React from 'react'
    import { createRoot } from 'react-dom/client'
    import { RouterProvider, createMemoryRouter } from 'react-router'
    
    import { router_path } from '@/pages/route-template/router'
    import { UEProvider } from 'ue-connect'
    
    const router = createMemoryRouter(router_path)
    
    const container = document.getElementById('root')
    const root = createRoot(container)
    root.render(
      <>
        <UEProvider>
          <RouterProvider router={router} />
        </UEProvider>
      </>
    )

    关键特性:

    • UEProvider 包裹整个路由树,确保所有子组件都能访问 UE5 连接上下文
    • createMemoryRouter 创建内存路由实例,路由配置由 router_path 数组定义
    • 路由切换通过 useNavigate() 编程式导航或 <Link> 组件完成

    路由配置:src/pages/route-template/router/index.tsx

    export const router_path = [
      {
        path: '/',
        element: <Layout Fit={false} />,
        errorElement: <ErrorPage />,
        children: [
          { path: 'show', element: <ShowPage /> },
          { path: '/', element: <HomePage /> }
        ]
      }
    ]

    路由层级说明:

    • 根路由 /:使用 Layout 作为布局组件,含自适应缩放配置
    • 子路由/ 渲染 HomePage 主页,/show 渲染 ShowPage 展示页
    • 错误边界:路由错误时渲染 ErrorPage

    页面切换动画体系

    模板集成 framer-motion v11 实现页面切换过渡动画。

    AnimatedOutlet:src/components/framer-motion/animated-outlet.tsx

    替代 React Router 的 <Outlet />,使用 AnimatePresence 包装子路由元素,使路由切换时支持进出场动画:

    import { AnimatePresence } from 'framer-motion'
    import { cloneElement } from 'react'
    import { useLocation, useOutlet } from 'react-router'
    
    export default function AnimatedOutlet() {
      const location = useLocation()
      const element = useOutlet()
      return (
        <AnimatePresence mode="wait" initial={true}>
          {element && cloneElement(element, { key: location.pathname })}
        </AnimatePresence>
      )
    }
    • 使用 mode="wait":等待当前页面退出动画完成后,再进入新页面
    • 通过 key={location.pathname} 驱动 AnimatePresence 识别路由变化

    AnimatedLayout:src/components/framer-motion/animated-layout.tsx

    页面内容动画包装器,为子元素注入 framer-motion 的 variants 动画变量:

    export default function AnimatedLayout({ animate, children }: Props) {
      return (
        <motion.div
          variants={animate ? animate : default_animate}
          initial="hidden"
          animate="enter"
          exit="exit"
          className="relative"
        >
          {children}
        </motion.div>
      )
    }
    • 每个页面根元素使用 <AnimatedLayout> 包裹以获取动画能力
    • 可传入自定义 animate 配置,默认使用 default_animate 预设

    动画预设:src/lib/data/animate-data.tsx

    export const default_animate = {
      hidden:  { y: -10, opacity: 0 },
      enter:   { y: 0, opacity: 1, transition: { duration: 0.5, type: 'easeInOut' } },
      exit:    { y: -50 + Math.floor(Math.random() * 30) + 1, opacity: 0,
                 transition: { duration: Math.random() * 0.1 + 0.5, type: 'easeInOut' } }
    }

    出场动画(exit)包含随机偏移量,使连续页面切换时的退出动效更具变化感。

    ScreenAnchor 屏幕定位组件体系

    src/components/screen-anchor/index.tsx 提供了一套九宫格定位组件,用于在 UE5 全屏 UI 中将元素精确定位到预设位置。

    AnchorGrid

    容器组件,创建相对定位的全屏容器:

    export function AnchorGrid({ children }: { children: ReactNode }) {
      return <div className="select-none relative h-screen">{children}</div>
    }

    ScreenAnchor

    定位锚点组件,将子元素固定到屏幕的九个预设位置:

    type AnchorName =
      | 'top-left' | 'top-center' | 'top-right'
      | 'center-left' | 'center' | 'center-right'
      | 'bottom-left' | 'bottom-center' | 'bottom-right'
    
    export function ScreenAnchor({ name, children, className = '' }: ScreenAnchorProps) {
      if (!children) return null
      return <div className={`${anchorStyles[name]} ${className}`}>{children}</div>
    }

    九宫格位置样式映射:

    AnchorName定位样式
    top-leftabsolute top-0 left-0
    top-centerabsolute top-0 left-1/2 -translate-x-1/2
    top-rightabsolute top-0 right-0
    center-leftabsolute top-1/2 left-0 -translate-y-1/2
    centerabsolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2
    center-rightabsolute top-1/2 right-0 -translate-y-1/2
    bottom-leftabsolute bottom-0 left-0
    bottom-centerabsolute bottom-0 left-1/2 -translate-x-1/2
    bottom-rightabsolute bottom-0 right-0

    使用示例:

    <AnchorGrid>
      <ScreenAnchor name="center">
        <DemoContent />
      </ScreenAnchor>
      <ScreenAnchor name="top-center">
        <div>顶部居中内容</div>
      </ScreenAnchor>
    </AnchorGrid>

    页面组件说明

    根布局:src/pages/route-template/pages/layout.tsx

    应用根布局组件,负责:

    • 初始化 autofit.js 自适应缩放(通过 Fit prop 控制)
    • 调用 useQuickUIEventListener('UEcallback') 启用 UE5 事件监听
    • 使用 AnimatedOutlet 渲染带动画过渡的子路由页面
    • 托管独立于路由动画的全局组件(如 UEConnect-Demo)
    export function Layout({ Fit = false }: { Fit?: boolean }) {
      useQuickUIEventListener('UEcallback', (event) => {})
      useEffect(() => {
        if (Fit)
          autofit.init({
            dw: 1920, dh: 1080, el: 'body',
            resize: true, transition: 0.25, limit: 0.3
          })
      }, [])
      return (
        <>
          <AnimatedOutlet />
          <div id="no-animation-driven">
            <UEConnectDemo />
          </div>
        </>
      )
    }

    布局分离了"动画驱动区域"(AnimatedOutlet)和"非动画驱动区域"(#no-animation-driven),确保全局悬浮组件不受页面切换动画影响。

    主页:src/pages/route-template/pages/home/index.tsx

    ScreenAnchor 定位组件的完整演示页,展示全部九个锚点位置的视觉效果。

    UE5 路由导航事件

    主页和 ShowPage 均监听来自 UE5 的路由导航事件:

    useQuickUIEventListener('RouteNavigate', (event: QuickEvent) => {
      const payload = event.payload
      if (payload.route) {
        navigate(payload.route)
      }
    })

    UE5 后端可通过发送 RouteNavigate 事件(含 route 字段)控制前端页面切换,实现双向导航。

    展示页:src/pages/route-template/pages/show/index.tsx

    展示两种图片加载模式:

    1. Base64 内联模式:将小图片转码为 Base64 字符串嵌入 HTML,无需额外加载
    2. 静态资源模式:引用 public/ 目录中的图片文件,通过相对路径加载

    两种模式的对比演示帮助开发者根据场景选择最优的图片加载策略。

    错误页面:src/pages/route-template/pages/error-page.tsx

    路由错误边界组件,捕获路由异常并展示错误信息和当前路径,提供返回首页的链接。

    UEConnect-Demo 组件:src/components/Ohters/UEConnect-Demo/index.tsx

    一个完整的参考组件,展示了 ue-connect 的核心 API 用法:

    • useInputBlocker:禁用右键菜单和 Tab 键
    • useUEContext:获取 UE5 连接状态、设备类型、鼠标状态和最近按键操作
    • useQuickUIEventSender:向 UE5 发送自定义事件
    • useQuickUIEventListener:监听来自 UE5 的事件
    • data-nohit 属性:标记元素为鼠标事件不可穿透,确保 Web UI 与 UE5 之间的事件正确路由

    该组件默认渲染在 #no-animation-driven 容器中,作为页面切换时的全局悬浮面板。

    DemoContent 组件:src/components/Ohters/DemoContent.tsx

    鼠标穿透/不可穿透的对比演示组件,展示了 data-nohit 属性的实际效果:

    • 带有 data-nohit 的红色区域:鼠标事件不会穿透到 UE5
    • 不带 data-nohit 的绿色区域:鼠标事件穿透到 UE5 层

    项目构建配置

    rspack.config.js

    项目使用 Rspack(基于 Rust 的高性能 Web 构建工具)作为核心打包器。

    入口配置:

    entry: {
      index: './src/pages/route-template/pages/index.tsx',
    }

    src/pages/route-template/pages/index.tsx 为当前模板的唯一入口。

    模块解析与别名:

    resolve: {
      extensions: ['.js', '.jsx', '.ts', '.tsx'],
      alias: {
        '@': path.resolve(__dirname, './src'),
        'ue-connect': path.resolve(__dirname, './ue-connect')
      }
    }
    • @ 别名简化源码导入路径
    • ue-connect 别名确保模块引用指向本地 ue-connect/ 目录

    txt 文件加载:

    {
      test: /\.txt$/,
      use: 'raw-loader'
    }

    支持导入 .txt 文件作为字符串(如 Base64 编码的图片数据)。

    开发服务器:

    devServer: {
      open: true,
      static: [
        { directory: path.join(__dirname, 'dist') },
        { directory: path.join(__dirname, 'public'), publicPath: '/', serveIndex: true }
      ],
      hot: true,
      historyApiFallback: true,
      port: 3000
    }

    public/ 目录中的静态资源通过 devServer 直接提供,开发阶段即可使用相对路径引用图片、音频等资源。

    ue-connect 模块

    ue-connect 是项目的核心 UE5 连接器模块,位于 ue-connect/ 目录下。它提供了一套完整的 hooks 和上下文管理工具,用于建立 React 与 UE5 之间的双向通信。 详细文档请访问 ue-connect 文档

    clickdeck-core 模块

    ClickDeck Core 是一个专为 Vibe Coding 工作流设计的浏览器端可视化编辑器核心模块。ClickDeck Core 允许开发者在浏览器中直接对页面 DOM 元素进行拖拽、修改样式与调整布局,将模糊的“界面感觉”固化为精准的“视觉标注”。
    它不仅是调试工具,更是 AI 的“眼睛”和“翻译官”——所有可视化编辑操作都会被实时转化为结构化的 AI Prompt 提示词。您只需像设计师一样“指指点点”,剩下的布局逻辑与代码实现,交给 AI 即可完成。 clickdeck-core 是 v1.2.5 新增的可视化编辑器引擎模块,位于 clickdeck-core/ 目录下。

    assets-tool 工具集

    assets-tool/ 目录包含两个构建辅助工具,用于优化 UE5 集成的最终产物体积和性能。

    工具结构

    assets-tool/
    ├── convertImageToBase64.js   # 图片转 Base64 工具
    └── merge-html.js             # HTML 内联合并工具

    convertImageToBase64

    支持图片(PNG/JPG)转换为 Base64 编码的文本文件,便于在 UE5 中直接嵌入资源。

    ⚠ 注意:QuickUI 支持两种图片资源加载方式,内联和非内联。推荐小图片资源使用 Base64 编码内联到 HTML 文件中。
    若是非内联资源,在 UE5 打包时需要额外打包图片资源,具体参考加载构建后的 HTML

    使用方式:

    # 转换单个文件
    node assets-tool/convertImageToBase64.js ./src/assets/img/lufei.png
    
    # 批量转换目录下所有图片
    node assets-tool/convertImageToBase64.js ./src/assets/img/

    对应的 npm script:

    "base64img": "node assets-tool/convertImageToBase64.js ./src/assets/img/"

    工作原理:

    • 读取图片/WASM 文件的二进制数据
    • 转换为 Base64 字符串,添加 data:image/png;base64,data:application/wasm;base64, 前缀
    • 在同目录下生成 文件名-base64.txt 文件
    • 支持的格式:.png.jpg.jpeg

    merge-html

    HTML 内联合并工具,将构建产物中引用的外部 JS、CSS、图片和字体文件全部内联到 HTML 文件中,生成一个完全自包含的 HTML 文件,适合在 UE5 的 Web 浏览器控件中加载。

    使用方式:

    node assets-tool/merge-html.js

    对应的 npm script:

    "merge-html": "node assets-tool/merge-html.js"

    使用场景:

    • rspack build 构建后,生成的外链资源 HTML 文件通过此工具合并
    • 合并得到单个自包含 HTML,可直接在 UE5 中加载,无需额外资源文件

    使用模板

    开发

    启动开发服务器:

    npm run dev

    将启动一个带有热模块替换的本地开发服务器,支持快速迭代开发。

    构建

    构建生产版本:

    npm run build
    npm run merge-html

    构建产物将输出到 dist/merged/ 目录,所有资源已内联为单个 HTML 文件,可直接在 UE5 中使用。

    UE5 集成

    构建完成后,生成的 HTML 文件可通过 QuickUI 插件的网页浏览器控件加载到 UE5 中,实现 React 与 UE5 之间的无缝双向通信。UE5 端可通过自定义事件(如 RouteNavigate)控制前端页面路由,实现 UE5 驱动的 UI 导航。