图示
DiagramCanvas - 图画布
一个能让比它更大的画在里面平移和缩放的框。
用法
什么时候用它
来自@misoto22/folio/diagrams
import { DiagramCanvas } from '@misoto22/folio/diagrams'说明
一个能让比它更大的画在里面移动的框。
拖拽平移,用控件或者 ⌘/Ctrl 加滚轮缩放,按一个键复位。就这些——它刻意是一个视口而不是一个图编辑器:这里没有任何东西知道什么是节点,所以它对任何超尺寸的图形都成立,一张 SVG、一张图片、一张折不下来的表都行。
这里有三个不那么显然的决定。
不带修饰键的滚轮滚的是页面,不是图。一个把滚轮吞掉的画布是个滚动陷阱:读者一路往下刷文章,撞上这张图,页面就毫无理由地不动了。缩放需要修饰键——那本来也是各处平台通用的缩放手势,而且触控板的捏合发的就是它。
变换加在包裹层上,不加在内容上。子元素保留自己的坐标空间,所以里面的图仍然可以被测量、被导出、被任何遍历它的东西读取。直接缩放子元素会让它内部每一次 getBoundingClientRect 都成为谎言。
键盘优先,这个框本身是真正的 Tab 停靠点。+ / - / 0 和方向键移动视图;框会接收焦点,这些键才按得下去。一个只回应拖拽的画布,是一个有一半读者操作不了的画布。
结构
| 部件 | 说明 |
|---|---|
| 外框必填 | 最外面那个盒子:固定高度——height 没另说就是 24rem——一条细线、图示底色,以及 overflow hidden。它就是那扇窗,而画本身比它更大。 |
| 视口必填 | 框里那一层可聚焦的东西:带名字的 role="group"、tabIndex 0、拖拽处理、按键处理,以及画在框内侧的聚焦环。它就是那个 Tab 停靠点,也是那些按键根本按得下去的原因。 |
| 舞台必填 | translate 和 scale 作用在的那层包裹元素。transform 只落在这层包裹上,从不落到子元素上,所以里面那张图保住了自己的坐标空间,在它内部量出来的每一个尺寸都还是真的。 |
| 缩放控件 | 钉在底部角落的那一簇:缩小、当前百分比——它同时也是复位按钮,按它做的事命名——以及放大。controls={false} 会去掉这一簇,键盘那套留着。 |
| 键盘提示必填 | 一段视觉上隐藏、由 aria-describedby 引用的文字,说明这个框可以拖,以及哪些键负责平移、缩放和复位。屏幕上没有任何东西承载这句话。 |
实践建议
推荐
- 给它起个名字。label 是这个 group 的无障碍名称,默认值是 "Diagram canvas",所以同一页上的两个画布,在各自拿到自己的名字之前播报出来是同一句话。
- 用 heading={false}、legend="hidden" 和 cards={false} 把里面那张图剥到只剩画面。框里的东西是一起平移、一起缩放的,所以留着的标题会离它所命名的那张图越走越远,而图例恰恰在读者放大到想查它的那一刻离开了这个框。
- 要移动视图就走 ref——zoomIn、zoomOut、reset、centerOn——而不是把子元素按新的尺寸重渲一遍。centerOn 接收的是内容自身坐标系里的一个点,而这正是缩略图回报的东西。
- 把 onViewChange 直接交给一张缩略图。每一个 view 除了缩放比和偏移,还带着它当时量出来的那个框,而这几项加起来就是一个视口矩形全部的算术——其中那个框,是画布之外任何东西都量不到的那个数。
避免
- 没有任何东西会夹住平移。偏移就是拖拽或者方向键留下的那个值,所以画面可以被整个推到框外去;0 键和复位按钮就是回来的全部路径,而 controls={false} 又不给替代品,等于把其中属于指针的那一半也拿走了。
- 缩放止步于 0.35。比框大出三倍上下的图,缩不到能整张看全的程度,所以框的尺寸得按图来定——缩略图回答的是你在哪,从来不是那里有什么。
- 滚轮被放过了,拇指没有:这个框把 touch-action 设成了 none,所以在里面拖动的手指是在平移图,永远不会滚动页面。文章里一块通栏的画布,对触屏读者来说是一条只能绕过去、不能穿过去的带子——这就是把它的高度做得矮一点、旁边留出页面的理由。
示例
default
一张比给它的地方更宽的图形,装在一个可以围着它移动的框里。拖动是平移,角落那块板负责缩放,而它两个按钮之间那个百分比是重置;普通滚轮刻意留给页面,所以只有 Command 或 Control 加滚轮才缩放。这个框是一个真的 tab 停靠点,所以加号、减号、零和方向键完全不用指针也能干同样的活。这个图形自己的标题、图例和卡片在这里被关掉了,因为这块画布装的是画,不是一篇文章。
4 elements and 3 relationships.
- Browser (external)
- CloudFront (cloud) — CDN
- API (backend) — FastAPI
- Postgres (database)
- Browser → CloudFront: HTTPS
- CloudFront → API
- API → Postgres: SQL
Drag to pan. Plus and minus zoom, zero resets, the arrow keys pan.
your own controls
controls={false} 会拿掉角落那块板,而它原本做的一切都得在别处补回来:zoomIn、zoomOut 和 reset 挂在那个 handle 上,而 ref 是够到它们的唯一途径。当页面本身已经有一条工具栏、两套缩放按钮会为同一份活打架时,就把内置控件关掉。关掉之后又什么都不提供,缩放就只剩下修饰键加滚轮和键盘,而这两样读者都没有任何办法发现。
3 elements and 2 relationships.
- CloudFront (cloud) — CDN
- API (backend) — FastAPI
- Postgres (database)
- CloudFront → API: HTTPS
- API → Postgres: SQL
Drag to pan. Plus and minus zoom, zero resets, the arrow keys pan.
following the view
onViewChange 会在每一次平移、缩放和重置时触发,带着画布正在用来做变换的那同样三个数,而一个百分比读数、一张缩略图,或者一条深入图内的链接,都是拿它搭出来的。centerOn 是反方向的,它收的是画作自身坐标空间里的一个点,不是一个节点 id——画布是一个视口,对节点一无所知,所以一个想跳到某个节点的调用方,必须自己知道它被画在哪儿。
4 elements and 3 relationships.
- Browser (external)
- CloudFront (cloud)
- API (backend)
- Postgres (database)
- Browser → CloudFront
- CloudFront → API
- API → Postgres: SQL
Drag to pan. Plus and minus zoom, zero resets, the arrow keys pan.
键盘操作
| 按键 | 作用 |
|---|---|
| += | 以框的中心为原点放大。 |
| - | 缩小。 |
| 0 | 同时把缩放和偏移复位。 |
| ←→↑↓ | 平移。按住 Shift 每次走得更远。 |
无障碍
- 这个框本身是真正的 Tab 停靠点,键盘控制才按得下去。
- 不带修饰键的滚轮滚的是页面。缩放需要平台修饰键,所以这个画布永远不会变成文章中间的滚动陷阱。