npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dcg-overseas/cartesian

v0.1.4

Published

Cartesian axes & math↔canvas coordinate transform for react-konva

Readme

@dcg-overseas/cartesian

react-konva 提供的笛卡尔坐标系基础组件:坐标轴渲染、数学坐标↔画布像素双向变换,以及一套可选的画板状态机(点 / 线段 / 自由笔触 / 选中 / 橡皮擦 / 撤销)。

包负责绘图的操作逻辑;消费者负责工具栏 UI、调色板、形状选择。颜色和形状作为受控 props 传入。


安装

pnpm add @dcg-overseas/cartesian konva react-konva

reactreact-domkonvareact-konva 是 peer 依赖(>=18)。


两种使用模式

包支持两种模式,按需选择。

模式 A — 完整画板(推荐)

<CartesianProvider> + <CartesianBoard> 得到一个可交互的完整坐标平面:坐标轴、绘图、选中、历史撤销,全部内置。消费者只需提供工具栏 UI 和调色板。

模式 B — 仅坐标轴(headless)

<CartesianAxes> + useCartesianTransform()。只要坐标轴显示和坐标变换,其他内容你自己在 Stage 里画。


模式 A:完整画板

快速示例

import { useState } from 'react'
import {
  CartesianProvider,
  CartesianBoard,
  useCartesianContext,
} from '@dcg-overseas/cartesian'
import type { ToolMode, PointShape } from '@dcg-overseas/cartesian'

function MyPlane() {
  const [tool, setTool] = useState<ToolMode>('select')
  const [pointShape, setPointShape] = useState<PointShape>('circle')
  const [color, setColor] = useState('#ef4444')

  return (
    <CartesianProvider
      grid="10x10"
      tool={tool}
      pointShape={pointShape}
      color={color}
    >
      {/* The Board fills its parent — wrap it in a sized container */}
      <div style={{ width: 420, height: 420 }}>
        <CartesianBoard />
      </div>
      <MyToolbar onToolChange={setTool} />
    </CartesianProvider>
  )
}

function MyToolbar({ onToolChange }: { onToolChange: (t: ToolMode) => void }) {
  const { canDelete, canUndo, deleteSelected, undo, reset } = useCartesianContext()
  return (
    <div>
      <button onClick={() => onToolChange('point')}>点</button>
      <button onClick={() => onToolChange('pen')}>笔</button>
      <button onClick={() => onToolChange('line')}>直线</button>
      <button onClick={() => onToolChange('eraser')}>橡皮擦</button>
      <button onClick={deleteSelected} disabled={!canDelete}>删除</button>
      <button onClick={undo} disabled={!canUndo}>撤销</button>
      <button onClick={reset}>重置</button>
    </div>
  )
}

工具行为

| 工具 | 行为 | |---|---| | select | 点击元素切换选中态(支持多选)。点空白处取消所有选中。 | | point | 点击落点。光标跟随显示半透明 ghost 预览。使用 pointShape + color。 | | pen | 按下-拖动-松开画自由笔触(自带平滑)。使用 color。 | | line | 第一次点击落起点,第二次落终点。期间显示虚线预览。使用 color。 | | eraser | 悬停元素时高亮,点击删除。 |

切换工具会自动清理进行中的状态(选中、line 锚点、未结束的笔触)。

<CartesianProvider> props

| Prop | 类型 | 必填 | 说明 | |---|---|---|---| | grid | GridKey \| GridSpec | ✓ | 坐标范围。见下方 grid。 | | tool | ToolMode | ✓ | 当前激活工具。消费者受控。 | | pointShape | PointShape | ✓ | 添加新点时使用的形状。 | | color | string | ✓ | 新元素的十六进制颜色。 | | axesTheme | CartesianTheme | | 覆盖坐标轴外观,见 坐标轴主题。 | | boardTheme | CartesianBoardTheme | | 覆盖画板外观,见 画板主题。 | | children | ReactNode | ✓ | 任何放在 context 内的内容(board + 工具栏)。 |

Provider 没有 width / height props。<CartesianBoard> 会自动填满它的父容器,由父容器的 CSS(px / %、flex、grid、vw 等)决定画布尺寸。

<CartesianBoard> props

无。组件从 context 读取所有状态。它会填满最近的父容器,所以你需要给父容器一个明确的尺寸:

<div style={{ width: 420, height: 420 }}>
  <CartesianBoard />
</div>

// 或响应式
<div style={{ flex: 1, height: '60vh' }}>
  <CartesianBoard />
</div>

useCartesianContext() 返回值

<CartesianProvider> 任何后代组件都能读取的只读状态 + 操作函数。

{
  // 身份信息(透传 props)
  width, height, tool, pointShape, color,
  transform: CartesianTransform,
  axesTheme?: CartesianTheme,
  boardTheme: Required<CartesianBoardTheme>,

  // 只读状态
  elements: DrawnElement[],
  selectedIds: ReadonlySet<string>,
  hoveredId: string | null,
  lineStart: { mx, my } | null,           // line 工具的第一次点击锚点
  pointerMath: { mx, my } | null,         // 当前光标的数学坐标
  activeStroke: number[] | null,          // 进行中笔触的画布坐标点列

  // 操作(在你的工具栏里调用)
  canDelete: boolean,
  canUndo: boolean,
  deleteSelected: () => void,
  undo: () => void,
  reset: () => void,

  // 内部 — 由 <CartesianBoard> 使用,消费者通常不需要
  onStageMouseDown, onStageMouseMove, onStageMouseUp,
}

绘图模型

type ToolMode = 'select' | 'point' | 'pen' | 'line' | 'eraser'

type PointShape = 'circle' | 'triangle' | 'square' | 'star'

type DrawnPoint  = { id, type: 'point';  mx, my, shape, color }
type DrawnStroke = { id, type: 'stroke'; points: number[];  color }   // [mx, my, mx, my, ...]
type DrawnLine   = { id, type: 'line';   x1, y1, x2, y2,    color }

type DrawnElement = DrawnPoint | DrawnStroke | DrawnLine

所有数学坐标(mx, my, x1...)是数学空间值(即网格的 min..max 范围),不是画布像素。包内部通过 transform 自动换算。


模式 B:仅坐标轴

快速示例

import { Stage, Layer } from 'react-konva'
import { CartesianAxes, useCartesianTransform } from '@dcg-overseas/cartesian'

function MyChart() {
  const W = 420, H = 420
  const t = useCartesianTransform({ width: W, height: H, grid: '10x10' })

  return (
    <Stage width={W} height={H}>
      <Layer listening={false}>
        <CartesianAxes width={W} height={H} grid="10x10" />
      </Layer>
      <Layer>
        {/* 你自己的元素,用 t.toCanvasX / t.toCanvasY 定位 */}
      </Layer>
    </Stage>
  )
}

<CartesianAxes width height grid theme?>

输出一个 Konva <Group>,包含网格线、刻度数字、O / x / y 轴标记。放在任意 <Layer> 内。

useCartesianTransform({ width, height, grid })

返回 memoized 的 CartesianTransform

{
  min, max, step, width, height,
  ticks: number[],                  // 预计算,浮点安全
  toCanvasX: (mx) => number,
  toCanvasY: (my) => number,
  toMathX:   (cx) => number,
  toMathY:   (cy) => number,
}

memo 依赖被拆成原子(width / height / min / max / step),所以即使每次都传字面量 { min, max, step },也不会让 transform 反复重建。

createCartesianTransform({ width, height, grid })

形状一致的纯函数 —— 不带 React,不调 useMemowidthheightrangestep 非正时会 throw。可在 worker、测试、React 树外的地方使用。


grid prop

可以是内置预设 key,也可以是自定义 spec。

// 内置预设(GRID_OPTIONS 列出所有 key)
grid="10x10"   // -5..5,    step 1
grid="20x20"   // -10..10,  step 2
grid="30x30"   // -15..15,  step 5

// 自定义 — 支持小数 step(不会丢末端刻度)
grid={{ min: 0, max: 1, step: 0.1 }}

GRID_OPTIONS

导出的预设数组 { key, label, min, max, step }[],用它渲染网格选择器。

resolveGrid(grid)

辅助函数:把 GridKey | GridSpec 解析为纯 { min, max, step }。未知 key 时 throw。


主题定制

坐标轴主题 (CartesianTheme)

<CartesianProvider
  axesTheme={{
    // 轴颜色 — 优先级见下方
    axisColor:  '#0ea5e9',   // 两条轴的统一 fallback
    xAxisColor: '#3b82f6',   // 仅 x 轴(水平零线),覆盖 axisColor
    yAxisColor: '#ef4444',   // 仅 y 轴(垂直零线),覆盖 axisColor

    // 其他样式
    gridColor:         '#e5e7eb',
    labelColor:        '#374151',
    fontSize:          9,        // 刻度数字字号
    axisLabelFontSize: 11,       // O / x / y 标签字号
    axisStrokeWidth:   1.5,
    gridStrokeWidth:   1,
  }}
  ...
>

颜色优先级xAxisColor || axisColor || 默认值yAxisColor 同理)。两轴想同色,只设 axisColor;想分别上色,设 xAxisColor / yAxisColor

画板主题 (CartesianBoardTheme)

<CartesianProvider
  boardTheme={{
    background:     '#fff',      // 画布底色
    selectionColor: '#f97316',   // 选中态描边色
    hoverColor:     '#fbbf24',   // 橡皮擦悬停描边色
    pointSize:      8,           // 点的画布像素大小
  }}
  ...
>

导出清单

| 名称 | 类型 | 用途 | |---|---|---| | CartesianProvider | 组件 | 持有绘图状态,注入 context。 | | CartesianBoard | 组件 | 渲染 Stage + 坐标轴 + 绘制元素 + 预览。 | | CartesianAxes | 组件 | 仅坐标轴 (Konva Group),headless 模式用。 | | PointShapeNode | 组件 | 单个 PointShape 的 Konva 节点;消费者可在缩略图/工具栏预览中复用。 | | useCartesianTransform | Hook | memoized 数学↔画布坐标变换。 | | useCartesianContext | Hook | 在 <CartesianProvider> 内读取状态和操作。 | | createCartesianTransform | 函数 | CartesianTransform 的纯函数工厂。 | | resolveGrid | 函数 | GridKey \| GridSpecGridSpec。 | | GRID_OPTIONS | 常量 | 内置预设列表。 |

类型

GridKeyGridSpecGridConfigCartesianTransformCartesianThemeCartesianAxesPropsCartesianBoardThemeCartesianProviderPropsCartesianContextValueToolModePointShapeDrawnPointDrawnStrokeDrawnLineDrawnElement


关于响应式尺寸

Konva 渲染到 <canvas> 时使用确切的像素尺寸 —— 没有 SVG viewBox 那种等比缩放。

完整画板模式(Provider + Board)

<CartesianBoard> 内部用 ResizeObserver 测自身外层 div,并自动把测得的物理像素同步给 Provider。消费者只需要给 Board 的父容器一个 CSS 尺寸(px / rem / %、flex 子项、grid 单元、vw / vh 等都行),剩下的不用管:

<div style={{ flex: 1, height: '60vh' }}>
  <CartesianBoard />
</div>

Headless 模式(CartesianAxes)

仅坐标轴的 headless 用法不带自动测量 —— 你自己决定 <Stage>width / height,传给 <CartesianAxes> 就行。

关于 postcss-pxtorem / postcss-px-to-viewport

不引入任何 CSS,也不输出带 px 的 inline 样式(width: '100%'height: '100%'touchAction: 'none' 都不是 px),所以消费者侧的 px 转换插件完全不用关心这个包 —— 它们扫不到也无须转换。


License

MIT