Skip to content

254. 实战:3D 画廊编辑器(五)

Published:

这节我们把基本的布局完成一下。

按照这个原型图:

image.png

首先改下 Header 部分:

src/components/Header.tsx

import { Button } from 'antd'

function Header() {
  return (
    <div className="header-content">
      <h1 className="header-title">3D画廊编辑器</h1>
      <Button type="primary">预览</Button>
    </div>
  )
}

export default Header

左边是标题,右边是预览按钮。

还有 Sider 部分:

src/components/Menu.tsx

import { Menu } from 'antd'

const menuItems = [
  { key: 'gallery', label: '画廊编辑' },
  { key: 'character', label: '人物编辑' },
]

function AppSider() {
  return (
    <Menu
      mode="inline"
      defaultSelectedKeys={['gallery']}
      items={menuItems}
      style={{ height: '100%', borderRight: 0 }}
    />
  )
}

export default AppSider

有两个菜单。

然后 2D、3D 的切换按钮可以用 antd 的 Segmented 组件来做:

image.png

import { useEffect, useState } from 'react'
import { Segmented } from 'antd'
import { init3D } from './init-3d'
import { init2D } from './init-2d'

function Main() {
  useEffect(() => {
    const dom = document.getElementById('threejs-3d-container')!
    init3D(dom)
    return () => {
      dom.innerHTML = ''
    }
  }, [])

  useEffect(() => {
    const dom = document.getElementById('threejs-2d-container')!
    init2D(dom)
    return () => {
      dom.innerHTML = ''
    }
  }, [])

  const [curMode, setCurMode] = useState<'2d' | '3d'>('2d')

  return (
    <div className="main-content">
      <div
        id="threejs-3d-container"
        style={{ display: curMode === '3d' ? 'block' : 'none' }}
      />
      <div
        id="threejs-2d-container"
        style={{ display: curMode === '2d' ? 'block' : 'none' }}
      />
      <div className="mode-change-btns">
        <Segmented
          options={[
            { label: '2D', value: '2d' },
            { label: '3D', value: '3d' },
          ]}
          value={curMode}
          onChange={(value) => setCurMode(value as '2d' | '3d')}
        />
      </div>
    </div>
  )
}

export default Main

在 App.tsx 里引入下这些组件:

import { Layout } from 'antd'
import './App.scss'
import AppHeader from './components/Header'
import AppSider from './components/Menu'
import Main from './components/Main'

const { Header, Sider, Content } = Layout

function App() {
  return (
    <Layout className="gallery-editor-layout">
      <Header className="app-header">
        <AppHeader />
      </Header>
      <Layout>
        <Sider width={200} className="app-sider">
          <AppSider />
        </Sider>
        <Content className="app-content">
          <Main />
        </Content>
      </Layout>
    </Layout>
  )
}

export default App

最后改下样式 App.scss

body {
  margin: 0;
}

.gallery-editor-layout {
  min-height: 100vh;

  .app-header {
    display: flex;
    align-items: center;
    padding: 0 24px;
    background: #fff;
    border-bottom: 1px solid #000;

    .header-content {
      display: flex;
      align-items: center;
      justify-content: space-between;
      width: 100%;
    }

    .header-title {
      margin: 0;
      font-size: 18px;
      font-weight: 500;
    }
  }

  .app-sider {
    background: #fff;
    border-right: 1px solid #000;

    .ant-menu {
      margin-top: 16px;
    }
  }

  .app-content {
    position: relative;
    min-height: calc(100vh - 64px);
    background: #fff;
  }

  .main-content {
    position: relative;
    height: 100%;
    min-height: calc(100vh - 64px);

    #threejs-3d-container,
    #threejs-2d-container {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      width: 100%;
      height: 100%;
    }

    .mode-change-btns {
      position: absolute;
      z-index: 10;
      bottom: 20px;
      left: 20px;
    }
  }
}

看下效果:

2026-03-22 21.22.46.gif

这样整体布局就完成了。

参考下原型图:

image.png

案例代码上传了小册仓库

总结

这节我们修改了下整体布局,下节来做人物编辑部分。

评论