最近,我遇到了一个的需求:设计并实现一个具备拖拽和缩放功能的画布,这在许多设计软件中是一个常见的功能,比如蓝湖等。在这篇文章中,我将分享一种简单而高效的方法来构建这样的画布功能。
画布将具备以下核心功能:
为了让您更直观地体验这个拖拽缩放画布的功能,我已经将其完整实现并部署在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>
)
}
transformOrigin: '0 0'
:我们给画布设置了transformOrigin
属性为'0 0'
,这确保了所有的变换(如拖拽和缩放)都是基于画布的左上角进行的,这与事件的x,y坐标系相匹配,使得逻辑更加直观。