跳到正文
册页

图示

DiagramCanvas - 图画布

一个能让比它更大的画在里面平移和缩放的框。

用法

什么时候用它

任何超尺寸的图形——一张 SVG、一张图片、一张折不下来的表。它不知道什么是节点,这正是它能复用的原因。

来自@misoto22/folio/diagrams

TSX
import { DiagramCanvas } from '@misoto22/folio/diagrams'

说明

一个能让比它更大的画在里面移动的框。

拖拽平移,用控件或者 ⌘/Ctrl 加滚轮缩放,按一个键复位。就这些——它刻意是一个视口而不是一个图编辑器:这里没有任何东西知道什么是节点,所以它对任何超尺寸的图形都成立,一张 SVG、一张图片、一张折不下来的表都行。

这里有三个不那么显然的决定。

不带修饰键的滚轮滚的是页面,不是图。一个把滚轮吞掉的画布是个滚动陷阱:读者一路往下刷文章,撞上这张图,页面就毫无理由地不动了。缩放需要修饰键——那本来也是各处平台通用的缩放手势,而且触控板的捏合发的就是它。

变换加在包裹层上,不加在内容上。子元素保留自己的坐标空间,所以里面的图仍然可以被测量、被导出、被任何遍历它的东西读取。直接缩放子元素会让它内部每一次 getBoundingClientRect 都成为谎言。

键盘优先,这个框本身是真正的 Tab 停靠点。+ / - / 0 和方向键移动视图;框会接收焦点,这些键才按得下去。一个只回应拖拽的画布,是一个有一半读者操作不了的画布。

结构

DiagramCanvas anatomy
部件说明
外框必填最外面那个盒子:固定高度——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 停靠点,所以加号、减号、零和方向键完全不用指针也能干同样的活。这个图形自己的标题、图例和卡片在这里被关掉了,因为这块画布装的是画,不是一篇文章。

Request path4 elements and 3 relationships.EXTERNALBrowserCLOUDCloudFrontCDNSERVICEAPIFastAPIDATAPostgres

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 是够到它们的唯一途径。当页面本身已经有一条工具栏、两套缩放按钮会为同一份活打架时,就把内置控件关掉。关掉之后又什么都不提供,缩放就只剩下修饰键加滚轮和键盘,而这两样读者都没有任何办法发现。

Request path3 elements and 2 relationships.CLOUDCloudFrontCDNSERVICEAPIFastAPIDATAPostgres

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——画布是一个视口,对节点一无所知,所以一个想跳到某个节点的调用方,必须自己知道它被画在哪儿。

Request path4 elements and 3 relationships.EXTERNALBrowserCLOUDCloudFrontSERVICEAPIDATAPostgres

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.

100% at 0, 0

键盘操作

DiagramCanvas keyboard interactions
按键作用
+=以框的中心为原点放大。
-缩小。
0同时把缩放和偏移复位。
←→↑↓平移。按住 Shift 每次走得更远。

无障碍

  • 这个框本身是真正的 Tab 停靠点,键盘控制才按得下去。
  • 不带修饰键的滚轮滚的是页面。缩放需要平台修饰键,所以这个画布永远不会变成文章中间的滚动陷阱。