跳到正文
册页

图示

DiagramMinimap - 缩略图

在一个比窗口更大的东西里,你现在在哪。

用法

什么时候用它

和 DiagramCanvas 搭配。视口矩形是从画布自己的 view 推出来的,从不另存一份——一张跟它的疆域对不上的地图,比没有地图更糟。

来自@misoto22/folio/diagrams

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

说明

在一个比窗口更大的东西里,你现在在哪。

同时是两样东西,而且两样都必须有:整张图形的一个缩影,以及一个矩形,标出框此刻正盖在它的哪一部分上。光有缩影回答的是「那里有什么」;矩形回答的是「那我在哪」——后者才是一个刚平移过两次的读者真正的问题。

矩形是推导出来的,从不另存。它的位置来自画布自己的 view——就是画布正在用来做变换的那三个数——除以地图的缩放比,再夹进那块板子里:一个已经平移到图形边缘之外的视口,有一半是压在空白纸上的,把矩形画到那外面去,等于声称这张地图延伸到了它并没有延伸到的地方。另存一份「视口在哪」,正是一张缩略图开始和它所映照的东西对不上的方式;而一张对不上的地图,比没有地图更糟。

在图形有尺寸之前,什么都不画。content 通常是量出来的,而一次测量的第一个值是零——那样缩放比会是 1,缩影会变成图形左上角那一块的原尺寸,矩形盖在它上面什么也不意味着。空着一帧,才是「还没到」的老实版本。

点击是重新居中,不是跳转。onSeek 报出的是内容坐标系里的一个点,那正是画布 centerOn 接受的东西。缩略图自己不移动任何东西:它对视图没有权柄,它只说读者指了哪儿。拖动会一直寻位,而且只有起点落在地图上的拖动才算——一个在别处按下、再拖过来的按键,不是这个组件该回答的手势。

结构

DiagramMinimap anatomy
部件说明
地图框必填一块定宽的底——width 没另说就是 200px——高度跟着画面自己的宽高比走,并以 group 的身份命名,免得它成为图旁边一个没有标签的盒子。
缩影必填children,套上一层 width 除以 content.width 的 CSS scale,锚在左上角,对辅助技术隐藏,对指针透明。只有 content 有了尺寸才画——宽度为 0 时缩放比会是 1,缩影就成了画面左上角那块原尺寸的部分。它只回答一眼扫过去「那里有什么」,别的什么都不回答。
视口矩形必填一个带 accent 底色的矩形,由画布的 view 和地图的缩放比算出来,裁在这块底之内——所以一个平移到画面之外的视口,不会再声称地图还往更远处延伸——并设了几个像素的下限,这样深度放大时屏幕上仍然剩得下点东西可看。它每次渲染都重新推导,从不另存一份。
定位层盖在地图上的那层透明的东西,把一次按下、或者一次起点就落在它身上的拖动,换算成内容坐标系里的一个点,交给 onSeek。在别处按下、再拖过它的按键,什么也不会移动。地图自己也一样——它对视图没有任何话语权。

实践建议

推荐

  • 喂给它画布自己的 onViewChange。那个矩形就是这三个数字的算术,所以这样接起来的地图不可能和它所映照的那个框对不上——而另存一份「视口在哪」的副本,恰恰就是对不上的由来。
  • content 要按画面天然的 CSS 尺寸量。这里的一切都是按 width 除以 content.width 缩放的,所以一个错的 content 宽度,会把缩影和矩形按同一个错误的系数一起缩放:地图看起来照样合理,指的却是画面上错误的那一块。宽度为 0 则改画一块空底,而一次还没落地的测量,本来就该长这个样子。
  • 框有多大,交给画布来说。DiagramCanvas 发出的每一个 view 都带着它当时量出来的那个框,所以把 onViewChange 透传过去就够了——frame 这个属性是留给这个组件无从得知的框的,而一个手写的框,正是让那个矩形开始说谎的唯一一个数。
  • onSeek 直接接到画布句柄的 centerOn 上。那个点送来时就在内容坐标系里,而 centerOn 本来接收的就是这个空间,所以重新居中不需要你自己做任何换算。

避免

  • 它只认指针,而且对辅助技术隐藏:没有 Tab 停靠点,没有按键,缩影也有意什么都不发布,因为它映照的那张图已经发布过了。要到远处的角落去,键盘的路径仍然是画布的方向键,所以缩略图绝不能是抵达某个角落的唯一途径。
  • 缩影是同一份标记缩小后的样子,不是一张简化的画——每一个标签都跟着一起缩下去,所以一张宽图缩到 200px 就是一个形状,不是一份能读的副本。它回答你在哪;某个节点写了什么,那是图的活。

示例

default

只有当框比画作更小时,一张地图才值得画出来,所以 frame 承载的是画布自己以 CSS 像素计的盒子,content 承载的是图形的自然尺寸。那个矩形是每次变化时从画布的 view 推导出来的、不是另存的,这正是为什么两者永远不会对不上。在地图上点击或拖动会报出画作坐标系里的一个点,而移动的动作由 centerOn 来做——缩略图对视图没有权柄,它只说读者指了哪儿。

Request path5 elements and 4 relationships.EXTERNALBrowserCLOUDCloudFrontSERVICEAPIDATAPostgresQUEUESQS

5 elements and 4 relationships.

  • Browser (external)
  • CloudFront (cloud)
  • API (backend)
  • Postgres (database)
  • SQS (messagebus)
  • Browser → CloudFront
  • CloudFront → API
  • API → Postgres
  • API → SQS

Drag to pan. Plus and minus zoom, zero resets, the arrow keys pan.

what the rectangle is

同一张地图的三份,除了 view 之外别无差异。那个矩形是拿那三个数算出来的——框左上角对应的内容点,是取负的偏移量除以缩放比,再换算到地图上——所以「视口在哪」没有第二份副本,也就没有什么需要同步。放大会让这个矩形变小、而不是移动,而这正是读者想要的那个读法:它说明的是他们此刻看到的是整体的多大一部分。这三个都没有传 onSeek,所以它们是读数而不是控件,指针也保持它平常的光标。

At rest
Panned right
Zoomed to 200%

when a minimap earns its place

两种情形并排放着。左边,框是一扇窗,望着一个四倍于它面积的东西,所以那个矩形圈出了一部分,地图回答了「你在哪」;右边,框已经装下了整幅画作,矩形从边到边盖满了地图,而它的每一个像素说的都是读者本来就已经看见的那一件事。只在第一种情形下渲染缩略图——在第二种情形下,它是这张图的第二份绘制,花掉一次渲染,却什么也没告诉任何人。

400 × 190 of 900 × 340
900 × 340 of 900 × 340

无障碍

  • 缩略图是 aria-hidden 的。它映照的那张图已经发布了自己的摘要,再来一份会把整张图念两遍。