最近,我遇到了一个的需求:设计并实现一个具备拖拽和缩放功能的画布,这在许多设计软件中是一个常见的功能,比如蓝湖等。在这篇文章中,我将分享一种简单而高效的方法来构建这样的画布功能。

画布将具备以下核心功能:

all.gif

在线演示

为了让您更直观地体验这个拖拽缩放画布的功能,我已经将其完整实现并部署在StackBlitz平台上。您可以通过以下链接直接访问并尝试这个交互式的画布示例:

UnoCSS Simple Canvas - StackBlitz

点击上面的链接,您将能够看到画布的实际效果,并可以编辑代码来探索其工作原理。

此外,如果您想要嵌入这个示例到您的项目中,以下链接提供了一个嵌入式的版本:

https://stackblitz.com/edit/vitejs-vite-cgbb74hd?embed=1&file=src%2FApp.tsx

实现

概览

在深入实现细节之前,让我们先来审视一下整体的布局代码,这是构建我们可拖拽缩放画布的基础。

import { useRef } from 'react'

interface PageItem {
  title: string
  rect: {
    x: number
    y: number
    width: number
    height: number
  }
}

// 定义页面元素的数组,每个元素包含标题和位置尺寸信息
const pages: PageItem[] = [
  {
    title: 'Page 1',
    rect: { x: 200, y: 100, width: 200, height: 150 },
  },
  {
    title: 'Page 2',
    rect: { x: 500, y: 300, width: 200, height: 150 },
  },
  {
    title: 'Page 3',
    rect: { x: 800, y: 200, width: 200, height: 150 },
  },
  {
    title: 'Page 4',
    rect: { x: 350, y: 500, width: 200, height: 150 },
  },
]

function App() {
  const canvasRef = useRef<HTMLDivElement>(null)

  return (
    <div ref={canvasRef} className='w-full h-full' style={{ transformOrigin: '0 0' }}>
      {pages.map(page => (
        <div
          key={page.title}
          className='absolute bg-white rounded-lg shadow-lg p-4 border border-gray-200 cursor-pointer select-none'
          style={{
            top: page.rect.y,
            left: page.rect.x,
            width: page.rect.width,
            height: page.rect.height,
          }}
        >
          <h3 className='text-lg font-semibold text-gray-800 mb-2'>{page.title}</h3>
          <p className='text-gray-600'>Some content here...</p>
        </div>
      ))}
    </div>
  )
}