5分钟,用 CSS Matrix 做一个可自由滑动的 IDE 画布容器

前言

大家好,我是馋嘴的猫。在上一篇React-Draggable的实战教程中,我们学会了如何实现 IDE 的拖动面板效果。

在这一篇文章中,我们将会继续学习另外一个 IDE 的高频交互操作:画布滑动

相信开发同学在实现前端需求时,对 figma 应该不陌生。

在使用 figma 的时候,我们可以通过鼠标滚轮或者触控板来实现画布的滑动,这种交互方式非常的流畅,也对画布容器放置多画布,提供了更好的支持,完美支持画布区域比屏幕更高更宽的场景。

今天,我们将会借助 CSS 的 matrix 属性,在页面实现上述效果,请跟随下文,请一起学起来吧~

需求

通过 CSS 的 matrix 属性,在 Next.js 项目,实现一个可滑动的 IDE 画布。

使用框架

  1. Next.js
  2. Tailwindcss

仓库地址

点此查看

在线演示地址

点此查看

实现步骤

  1. 我们这次的教程,会以上篇 React-Draggable 的工程为基础工程,所以,需先 clone 下此 demo 的仓库,重命名目录,安装依赖,并执行 dev 服务,命令如下所示:
bash 复制代码
git clone git@github.com:rqzheng2015/react-draggable-nextjs-demo.git
mv react-draggable-nextjs-demo react-matrix-scroll-demo
cd react-matrix-scroll-demo
pnpm i
pnpm dev
  1. 打开 app/components/canvas_container.tsx, 修改画布为固定宽高 375*667, 并将外层容器加上 overflow:hidden 样式,以及加上 ref 引用。
tsx 复制代码
'use client'

import { useCallback, useEffect, useRef, useState } from 'react'

const CANVAS_WIDTH = 375
const CANVAS_HEIGHT = 667

export default function CanvasContainer() {
    const containerRef = useRef<HTMLDivElement | null>(null) // 创建一个ref
    // 省略其他原有代码
    return (
        <div className={'flex-1 overflow-hidden relative p-[16px]'} ref={containerRef}>            <div
              className={'bg-white m-auto relative flex items-center justify-center'}
              style={{
                width: `${CANVAS_WIDTH}px`,
                height: `${CANVAS_HEIGHT}px`,
              }}
            >
                <p className={'text-[20px]'}>{`This is project's home page.`}</p>
            </div>
        </div>
    )
}

此时可以看到画布已经是固定宽高了,但是因为 footer 高度太高,会阻拦画布的正常显示。

所以,我们接下来,要为画布容器添加滑动支持,使得画布可以正常显示出来~

  1. 在 div 容器下,新增一层 matrix 样式层,供后面画布滑动使用。
tsx 复制代码
const [position, setPosition] = useState({ x: 0, y: 0 })
return (
    <div className={'flex-1 overflow-hidden relative p-[16px]'} ref={containerRef}>
      {/* 新增的 div 容器 */}
      <div
        className={'w-full relative'}
        style={{
          transform: `matrix(1, 0, 0, 1, ${position.x}, ${position.y})`,
        }}
      >
        <div
          className={'bg-white m-auto relative flex items-center justify-center'}
          style={{
            width: `${CANVAS_WIDTH}px`,
            height: `${CANVAS_HEIGHT}px`,
          }}
        >
          <p className={'text-[20px]'}>{`This is project's home page.`}</p>
        </div>
      </div>
    </div>
)

在这一步骤中,我们正式引入了 matrix 样式,并使用 matrix(1, 0, 0, 1, ${position.x}, ${position.y}) 来实现。

matrix的文档可点此查看

那这一段样式,和 matrix 属性 ,以及画布如何实现滑动,它们三者,又有什么关联呢,且听我细细解说:

a. matrix函数接受六个参数,分别是:abcdef。这些参数可以创建一个复杂的2D转换。

b. matrix(a, b, c, d, e, f)的效果等同于应用以下变换:

tsx 复制代码
newx = a * oldx + c * oldy + e
newy = b * oldx + d * oldy + f

c. 在我们的代码实现:matrix(1, 0, 0, 1, ${position.x}, ${position.y}),这代表着:

  • a 和 d 是 1,所以元素的宽度和高度不会改变。
  • b 和 c 是 0,所以元素不会倾斜或旋转。
  • e 和 f 是 <math xmlns="http://www.w3.org/1998/Math/MathML"> p o s i t i o n . x 和 {position.x} 和 </math>position.x和{position.y},所以元素会沿着 X 轴和 Y 轴移动,移动的距离取决于 position.x 和 position.y 的值。

d. 因此,这个 matrix 属性,就非常满足我们今天的需求了:滑动画布

说到底,画布的滑动实现,就是让浏览器,一直根据鼠标的 X 轴和 Y 轴的滑动方向和距离,来移动画布嘛。

OK,让我们在下个步骤继续实现。

  1. 实现 handleWheel 函数,并在画布容器上挂载对 wheel 事件的监听。
tsx 复制代码
const handleWheel = useCallback((event: WheelEvent) => {
  event.preventDefault()
  const { deltaX, deltaY } = event
  const MIN_X = -CANVAS_WIDTH / 2 - 50
  const MAX_X = CANVAS_WIDTH / 2 + 50
  const MIN_Y = -CANVAS_HEIGHT / 2 - 50
  const MAX_Y = CANVAS_HEIGHT / 2 + 50
  setPosition(prev => {
    // 计算新的位置
    const newX = prev.x - deltaX
    const newY = prev.y - deltaY
    // 检查新的位置是否在范围内
    const inRangeX = newX >= MIN_X && newX <= MAX_X
    const inRangeY = newY >= MIN_Y && newY <= MAX_Y

    return {
      x: inRangeX ? newX : newX < MIN_X ? MIN_X : MAX_X,
      y: inRangeY ? newY : newY < MIN_Y ? MIN_Y : MAX_Y,
    }
  })
}, [])

useEffect(() => {
  const container = containerRef.current
  if (container) {
    container.addEventListener('wheel', handleWheel) // 在元素上添加事件监听器
  }
  return () => {
    if (container) {
      container.removeEventListener('wheel', handleWheel) // 在元素上移除事件监听器
    }
  }
}, [containerRef.current])

上面的代码实现了以下几个功能点: a. 为画布容器的 wheel 事件挂上了监听,这样,当用户在画布容器的区域使用鼠标或触摸板滑动,则会触发 handleWheel事件 b. 在 handleWheel 事件获取 wheel 事件的 deltaX, deltaY c. 计算 MIN_X, MAX_X, MIN_Y, MAX_Y,用于限制画布的滑动范围 d. 根据 deltaX, deltaY 计算新的位置,并检查新的位置是否在范围内,如果不在范围内,则取边界值 e. 通过 setPosition 更新画布的滑动位置 f. 画布容器通过样式 transform: matrix(1, 0, 0, 1, ${position.x}, ${position.y}) 来实现它的滑动

  1. 至此,全部流程完成,可以看下成果啦~

可以看到,画布已经可以通过鼠标滚轮或触控板来滑动了,效果非常流畅,和 figma 的效果几乎一样。

总结

在这篇教程中,我们学习了如何通过 CSS 的 matrix 属性,来实现画布容器的滑动效果。 它的好处有以下几点:

  1. 不用像传统的画布容器方案,配置一个超大宽高的 div 容器,去包含所有的子画布内容,而是直接使用原有的 flex: 1 容器,不限制宽高条件,然后配合 matrix 属性,来实现画布的滑动。
  2. 滑动动画效果流畅,和 figma 的效果几乎一样。
  3. 加入了边界范围的限制,使得画布的滑动不会离画布内容太远,减少用户交互的困惑。
  4. 代码实现简单,易于理解,易于维护。

这么强大的 matrix 属性,心动了吗?快去试一下吧~

欢迎点赞、收藏,评论~

相关推荐
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅6 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端
爱敲代码的小鱼6 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax