@visx/marker 使用指南:用 React 组件为 SVG 图形添加箭头、圆点与十字标记
visx/marker 使用指南用 React 组件为 SVG 图形添加箭头、圆点与十字标记【免费下载链接】visx visx | visualization components项目地址: https://gitcode.com/gh_mirrors/vi/visxvisx/marker 是 visx 可视化组件库中专用于生成SVGmarker图形对象的包。在 SVG 中marker标记是一类依附于path、line、polyline或polygon元素的图形对象最常见的形态就是折线/路径端点处的箭头。本指南将以 packages/visx-marker/Readme.md 为骨架结合包内源码与测试讲解visx/marker的安装方式、底层Marker组件的全部可配置属性以及内置的 5 种预制标记组件箭头、圆、十字、叉号、竖线各自的几何计算原理与使用场景读完即可在任意 visx 图表中为连线/路径加上精确可控的端点标记。什么是 SVG marker为什么需要 visx/marker按照 SVG 规范marker元素是一种可复用的图形模板它本身不直接出现在画布上而是通过marker-start、marker-mid、marker-end这三个引用属性挂接到路径类元素上从而在路径的起点、每个中间顶点、终点处绘制箭头、圆点等图形。visx/marker正是把这一底层 SVG 能力封装成 React 组件每个组件渲染一个defs包裹的marker你只需在路径元素上通过url(#id)引用即可例如marker-endurl(#arrow)。该包对外导出统一的入口见 src/index.tsexport { default as Marker } from ./markers/Marker; export { default as MarkerArrow } from ./markers/Arrow; export { default as MarkerCross } from ./markers/Cross; export { default as MarkerX } from ./markers/X; export { default as MarkerCircle } from ./markers/Circle; export { default as MarkerLine } from ./markers/Line; export type { MarkerProps, MarkerComponentProps } from ./markers/Marker;其中Marker是通用底层组件可自定义内部图形其余 5 个为开箱即用的预制标记。安装在项目中安装该包即可使用peer 依赖为 React 18 或 19见 package.jsonnpm install --save visx/marker由于包在package.json中声明了sideEffects: false且同时提供 CommonJSlib/与 ES Moduleesm/两种产物可以安全地被摇树tree-shaking打包器只会把实际用到的标记组件打进产物。通用 Marker 组件与全部可配置属性Marker是包裹原生marker的最小封装源码位于 src/markers/Marker.tsx。它始终在defs中输出marker其余任意 SVG 属性如fill、stroke、orient都会透传给原生元素。属性类型默认值说明idstring必填marker的唯一 id必须保证页面内全局唯一因为后续通过url(#id)引用sizenumber—用于计算 marker 内容包围盒尺寸的数值预制组件用它推导markerWidth/markerHeight/refX/refYmarkerWidthstring \| number3marker 视口宽度markerHeightstring \| number3marker 视口高度markerUnitsstringuserSpaceOnUsemarker 坐标系统userSpaceOnUse表示按用户坐标不受描边宽度影响strokeWidth表示按引用它的路径的描边宽度缩放refXstring \| number—标记图形在 marker 坐标系中的 x 参考点对齐到路径顶点refYstring \| number—标记图形在 marker 坐标系中的 y 参考点strokeWidthnumber—描边宽度源码注释明确指出被约束为number类型因为要参与包围盒数值计算childrenReactNode必填marker 内容通常是path、line、polyline或polygon需要特别留意strokeWidth的类型约束其余属性都允许string | number唯独strokeWidth只能是numbersrc/markers/Marker.tsx这是因为预制组件要用它做加减乘除的包围盒计算字符串无法参与运算。最基础的用法是自定义内容import { Marker } from visx/marker; function Chart() { return ( svg width{400} height{300} {/* Marker 自带 defs无需再包裹一层 */} Marker idcustom-diamond markerWidth{10} markerHeight{10} refX{5} refY{5} markerUnitsuserSpaceOnUse polygon points5,0 10,5 5,10 0,5 filltomato / /Marker polyline points20,60 120,60 120,180 220,180 fillnone strokesteelblue strokeWidth{2} markerEndurl(#custom-diamond) / /svg ); }5 个预制标记组件与它们的几何计算所有预制组件都遵循同一套设计模式接收size与strokeWidth两个关键数值在渲染前计算出marker的视口尺寸与参考点并设置markerUnitsstrokeWidth即随引用元素的描边宽度缩放与合适的orient。下面逐一拆解。MarkerArrow路径端点箭头源码见 src/markers/Arrow.tsx默认size 9、strokeWidth 1。它的核心计算const max size strokeWidth * 2; // 视口边长 size 2 倍描边 const midX size; // 参考点 x size const midY max / 2; // 参考点 y 视口高的一半 const points 0 0, ${size} ${size / 2}, 0 ${size}; // 箭头 polyline即用一个polyline0 0 → size, size/2 → 0 size勾勒出朝右的 V 形箭头内部用g transform{translate(strokeWidth, strokeWidth)}为描边留出 1 倍线宽的边距。orientauto让箭头自动沿路径方向旋转。测试 test/Arrow.test.tsx 验证了尺寸计算size8、strokeWidth1时markerWidth/markerHeight均为10、refX8、refY5、points0 0, 8 4, 0 8。使用示例import { MarkerArrow } from visx/marker; MarkerArrow idarrow size{9} strokeWidth{1.5} strokerebeccapurple / Line x1{0} y1{80} x2{200} y2{80} strokerebeccapurple strokeWidth{1.5} markerEndurl(#arrow) /MarkerCircle圆点标记源码见 src/markers/Circle.tsx默认size 9、strokeWidth 1。计算逻辑const diameter size * 2; // 直径 2 * size const bounds diameter strokeWidth; // 包围盒边长 直径 描边 const mid bounds / 2; // 圆心 包围盒中心渲染一个r{size}的circle圆心放在包围盒正中心refY mid、refX 0orientauto-start-reverse意味着它既可以做起点标记也可以做终点标记自动区分方向。适合做散点路径上每个数据点的落点强调。MarkerCross 与 MarkerX十字与叉号两者都位于 src/markers/Cross.tsxX 只是其特殊形态。MarkerCross默认size 9、strokeWidth 1const bounds size strokeWidth; // 包围盒边长 size 描边 const mid size / 2; // 参考点 中心 const points 0 ${mid}, ${mid} ${mid}, ${mid} 0, ${mid} ${size}, ${mid} ${mid}, ${size} ${mid};即一笔画出一个正十字上、右、下、左四段折线。而MarkerX的实现极其简洁src/markers/X.tsxexport default function MarkerX(props: MarkerComponentProps) { return Cross orient{45} {...props} /; }它只是把十字绕中心旋转 45° 得到叉号——用orient{45}传入一个固定数字而非auto从而让标记始终保持 45° 倾角不随路径方向旋转。MarkerLine竖向短线标记源码见 src/markers/Line.tsx它用rect画一条竖线而不是 polylineconst max Math.max(size, strokeWidth * 2); // 视口宽 max(size, 2*描边) const midX max / 2; // 参考点 x 视口宽一半 const midY size / 2; // 参考点 y size 一半值得注意它的着色方式fill{fill || stroke}、strokenone也就是说颜色既可以用fill传入也可以直接用stroke传入此时把描边色当作填充色使用适合在路径末端画一条与线同色的短刻度线。在 visx 图表中的组合方式预制标记组件都可以和 visx 其他包自由组合。例如配合 visx/shape 的LinePath使用把MarkerArrow放进defs再把markerEnd透传给LinePath内部的path即可让折线/曲线的终点带上箭头import { LinePath } from visx/shape; import { MarkerArrow } from visx/marker; svg width{500} height{300} MarkerArrow idline-arrow size{8} strokeWidth{1.2} strokeorange / LinePath data{data} x{(d) xScale(d.x)} y{(d) yScale(d.y)} strokeorange strokeWidth{1.2} markerEndurl(#line-arrow) / /svg在 React 中markerEnd这类带连字符的 SVG 属性可以直接以 JSX 属性名书写React 会正确渲染为marker-end。因为Marker自带defs包裹多个标记之间互不干扰只要保证id全局唯一即可。关键设计要点与注意事项id 必须全局唯一Marker的id属性是必填的且整个页面内不能与其他元素的 id 冲突否则url(#id)引用会指向错误的元素。markerUnits的两种模式通用Marker默认userSpaceOnUse固定像素尺寸而 5 个预制组件统一使用strokeWidth标记会随引用路径的描边宽度成比例缩放视觉上更协调。orient决定旋转行为auto让标记沿路径方向自动旋转MarkerArrow、MarkerCross、MarkerLineauto-start-reverse额外支持起点/终点双向MarkerCircle传数字则固定角度MarkerX的 45°。size与strokeWidth是几何计算入口从源码可以看到预制组件的视口宽高与参考点全部由这两个数值推导因此调整大小时推荐只改size与strokeWidth而不是直接覆盖markerWidth/refX等计算出的属性。包围盒显式计算的必要性strokeWidth被刻意约束为number类型Marker.tsx正是为了保证这些加减乘除计算在运行时不出错测试 test/Arrow.test.tsx 也把这一套计算作为断言对象确保渲染出的markerWidth、markerHeight、refX、refY与预期完全一致。至此从安装、通用Marker的属性体系到 5 个预制标记各自的几何推导与组合用法visx/marker的全部能力都已覆盖。将它用于 visx 图表的路径端点、散点强调或刻度装饰都能以极少的样板代码获得精确可控的 SVG 标记。【免费下载链接】visx visx | visualization components项目地址: https://gitcode.com/gh_mirrors/vi/visx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考