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

@wtsml/doodle

v1.0.4

Published

一个高性能的 deepzoom 绘图标注插件。它可以轻松实现数十万可交互图形的同屏渲染。

Downloads

284

Readme

Doodle

Doodle 是一个高性能的 deepzoom 绘图标注插件。它可以轻松实现数十万可交互图形的同屏渲染。

在线体验

Demo 地址:https://wt-sml.github.io/doodle/

安装

pnpm install @wtsml/doodle

使用

import { createDoodle } from "@wtsml/doodle"

// 创建doodle实例
const doodle = createDoodle({
  viewer, // openseadragon viewer
  // 监听添加 shape 事件
  onAdd: (shape) => {
    doodle.addShape(shape)
  },
  // 监听删除 shape 事件
  onRemove: (shape) => {
    doodle.removeShape(shape)
  },
  // 监听更新 shape 事件
  onUpdate: (shape) => {
    doodle.updateShape(shape)
  },
  // 监听选中 shape 事件
  onSelect: (shape) => {
    console.log("选中了shape", shape)
  },
  // 监听取消选中 shape 事件
  onCancelSelect: (shape) => {
    console.log("取消选中了shape", shape)
  },
})

// 添加图形
doodle.addShapes(shapes) // shape的数据结构参考后面 “shape对象结构示例” 部分

属性

tools

支持的绘图工具枚举。

其默认值为:

// 绘图工具列表
tools = {
  move: "move", // 移动
  rect: "rect", // 矩形
  polygon: "polygon", // 多边形
  circle: "circle", // 圆
  ellipse: "ellipse", // 椭圆
  path: "path", // 路径
  closed_path: "closed_path", // 闭合路径
  line: "line", // 直线
  arrow_line: "arrow_line", // 箭头直线
  point: "point", // 点
}

conf

type : object

使用 createDoodle 创建 doodle 实例时的入参。

conf 里应该至少包含 viewer 参数,viewer 是 openseadragon 的画布实例。

pixiApp

type : object

doodle 底层使用 pixi.js 实现,pixiApp 是 pixi 的实例。

mode

type : string

doodle 当前所处的绘图模式,可以通过 setMode 方法切换绘图模式。

viewer

type : object

openseadragon 的画布实例。

shapes

type : object[]

shape 列表,所有绘制的图形都保存在这里。

数据结构参考后面 “shape 对象结构示例” 部分。

可以通过 getShapes API 获取它的克隆副本。

bounds

type : object[]

所有 shape 的边界列表。doodle 为了更高的性能,为每个 shape 都保存了边界信息,方便后续的计算。

数据结构为:

{
  minX: 0,
  minY: 0,
  maxX: 0,
  maxY: 0,
  id: null,
}

scale

type : number

缩放信息。

translate

type : object

平移信息。

数据结构为:

{
  x: 0,
  y: 0,
}

strokeWidth

type : number

图形线宽。默认值为 2。

defaultColor

type : string

图形默认颜色。默认值为 #FF0000 红色。可以通过 setDefaultColor API 修改。

brushColor

type : string

画笔颜色。默认值为 #FF0000 红色。可以通过 setBrushColor API 修改。

hitRadius

type : number

光标的碰撞检测半径。默认值为 5。

anchorRadius

type : number

锚点半径。默认值为 5。

pointRadius

type : number

点半径。默认值为 6。

tempShape

type : object

临时 shape,新增和编辑图形时,临时创建的 shape 副本。

hoverShape

type : object

鼠标悬浮的 shape。

mouse

type : object

鼠标相关信息。 其结构为:

// 鼠标
mouse = {
  x: 0, // 视口x
  y: 0, // 视口y
  dx: 0, // 画布x
  dy: 0, // 画布y
  isPressed: false, // 是否按下
}

readonly

type : boolean

全局只读模式。只读模式下,shape 无法被选中。可以通过 setReadOnly API 切换。

如果想要精确控制某个 shape 的只读模式。可以直接修改该 shape 的 readonly 属性。

API

setMode(mode: string): void

设置绘图模式,其入参合法值应为 tools 枚举中的值。

setPan(pan: boolean): void

设置画布是否允许拖动。

addShapes(shapes: object[]): void

添加图形(批量)。

addShape(shape: object): void

添加图形(单个)。

removeShapes(shapes: object[]): void

删除图形(批量)。

removeShape(shape: object): void

删除图形(单个)。

updateShapes(shapes: object[]): void

更新图形(批量)。

updateShape(shape: object): void

更新图形(单个)。

selectShape(shape: object): void

选择图形。该方法不会触发 onSelect 事件。

cancelSelectShape(): void

取消选择图形。该方法不会触发 onCancelSelect 事件。

clear(): void

清空所有的 shape。

destroy(): void

销毁 doodle 实例。

getScale(): number

获取缩放比例。

getShapes(): object[]

获取所有图形的克隆副本。

setDefaultColor(color: string): void

设置默认颜色。

setBrushColor(color: string): void

设置画笔颜色。

setReadOnly: (readonly: boolean) => void

设置全局只读模式。

如果想要精确控制某个 shape 的只读模式。可以直接修改该 shape 的 readonly 属性。

moveToShape(shape: object, immediately: boolean): void

移动视野至指定图形。

如果 immediatelytrue 则立即移动,没有过渡动画。默认为 false,有过渡动画。

事件

onAdd: (shape: object) => void

添加 shape 事件。

onRemove: (shape: object) => void

删除 shape 事件。

onUpdate: (shape: object) => void

更新 shape 事件。

onSelect: (shape: object) => void

选中 shape 事件。

onCancelSelect: (shape: object) => void

取消选中 shape 事件。

shape 对象结构示例

export const defaultShapes = [
  // 矩形
  {
    id: "uLi2gbqSx6sX2a40GiYzr",
    type: "rect",
    // pos 结构为 [x, y, width, height],表示矩形左上角的点坐标x,y与矩形的宽高width,height
    pos: [1428, 2067, 1384, 969],
    color: "#0000ff",
    // readonly: true, // 单独控制该shape是否只读
  },
  // 多边形
  {
    id: "pPLz_t2P4ph0U9QWyGhxK",
    type: "polygon",
    // pos 结构为 [x, y, x, y, x, y,...],表示多边形各个点的坐标x,y的循环
    pos: [
      4654, 2049, 4178, 2596, 4628, 3098, 5465, 3116, 5924, 2587, 5395, 2058,
    ],
    color: "#e74c3c",
  },
  // 圆
  {
    id: "CkJbGZOs1VE68YAzPUGtf",
    type: "circle",
    // pos 结构为 [x, y, r],x,y为圆心坐标,r为半径
    pos: [8366, 2605, 608],
    color: "#4cd137",
  },
  // 椭圆
  {
    id: "DOvh2VnbUDFX97ZUtak4V",
    type: "ellipse",
    // pos 结构为 [x, y, rx, ry],x,y为圆心坐标,rx为横轴半径,ry为纵轴半径
    pos: [12064, 2746, 1185, 581],
    color: "#3271ae",
  },
  // 直线
  {
    id: "de3_sHY_w3EL76TeKYRZn",
    type: "line",
    // pos 结构为 [x, y, x, y],分别为起点和终点的x,y坐标
    pos: [8260, 4447, 9238, 6272],
    color: "#1abc9c",
  },
  // 箭头直线
  {
    id: "Jta8DSQvx40n9KXZ7ELyY",
    type: "arrow_line",
    // pos 结构为 [x, y, x, y],分别为起点和终点的x,y坐标
    pos: [11442, 6166, 12809, 4544],
    color: "#9c88ff",
  },
  // 点
  {
    id: "v5uPfeRhZFdBMA-Y16D7L",
    type: "point",
    // pos 结构为 [x, y],表示点的x,y坐标
    pos: [2098, 7603],
    color: "#00ff00",
  },
  // 路径
  {
    id: "YPb7Wk4eQAkNPCKm-kj7C",
    type: "path",
    // pos 结构为 [x, y, x, y, x, y,...],表示路径各个点的坐标x,y的循环
    pos: [
      1877, 4632, 1868, 4632, 1860, 4632, 1816, 4641, 1754, 4659, 1692, 4668,
      1648, 4685, 1604, 4694, 1525, 4729, 1445, 4765, 1392, 4809, 1357, 4861,
      1339, 4906, 1339, 4950, 1339, 4994, 1339, 5038, 1366, 5100, 1392, 5144,
      1428, 5196, 1454, 5241, 1481, 5285, 1489, 5338, 1498, 5390, 1498, 5461,
      1436, 5523, 1366, 5593, 1278, 5664, 1216, 5743, 1181, 5796, 1172, 5849,
      1172, 5902, 1207, 5955, 1269, 6008, 1339, 6052, 1419, 6104, 1516, 6140,
      1630, 6157, 1833, 6157, 2133, 6157, 2397, 6166, 2565, 6166, 2653, 6157,
      2741, 6078, 2829, 5955, 2891, 5858, 2909, 5761, 2909, 5673, 2900, 5584,
      2865, 5487, 2829, 5426, 2785, 5382, 2759, 5355, 2750, 5338, 2732, 5302,
      2732, 5276, 2741, 5249, 2776, 5214, 2829, 5152, 2882, 5082, 2926, 5011,
      2962, 4941, 2970, 4861, 2970, 4791, 2970, 4729, 2944, 4668, 2926, 4615,
      2900, 4579, 2882, 4544, 2856, 4526, 2812, 4500, 2768, 4474, 2715, 4465,
      2644, 4465, 2556, 4465, 2477, 4465, 2415, 4474, 2380, 4482, 2371, 4491,
    ],
    color: "#e18a3b",
  },
  // 闭合路径
  {
    id: "QDKEJhCju3gY_ClCOhp9H",
    type: "closed_path",
    // pos 结构为 [x, y, x, y, x, y,...],表示路径各个点的坐标x,y的循环
    pos: [
      4980, 4350, 4980, 4359, 4936, 4385, 4848, 4421, 4716, 4465, 4592, 4509,
      4496, 4553, 4416, 4597, 4372, 4668, 4363, 4738, 4407, 4835, 4513, 4923,
      4645, 5011, 4778, 5108, 4883, 5188, 4919, 5223, 4927, 5258, 4927, 5311,
      4883, 5373, 4804, 5426, 4716, 5487, 4637, 5549, 4548, 5602, 4496, 5664,
      4478, 5699, 4478, 5743, 4504, 5787, 4557, 5849, 4637, 5937, 4769, 6016,
      4892, 6078, 4972, 6113, 4980, 6122, 4980, 6131, 4954, 6166, 4927, 6210,
      4919, 6237, 4927, 6246, 4954, 6272, 5016, 6298, 5121, 6351, 5227, 6387,
      5342, 6395, 5465, 6395, 5580, 6395, 5703, 6378, 5844, 6351, 6003, 6298,
      6109, 6263, 6197, 6210, 6285, 6149, 6356, 6069, 6426, 5981, 6470, 5902,
      6505, 5814, 6514, 5743, 6523, 5673, 6541, 5602, 6541, 5531, 6541, 5461,
      6550, 5390, 6550, 5329, 6532, 5276, 6532, 5214, 6523, 5161, 6514, 5117,
      6505, 5055, 6497, 5011, 6497, 4950, 6488, 4897, 6470, 4844, 6461, 4800,
      6453, 4756, 6444, 4694, 6435, 4650, 6417, 4606, 6409, 4562, 6391, 4526,
      6364, 4482, 6329, 4447, 6294, 4412, 6259, 4385, 6206, 4368, 6144, 4359,
      6109, 4341, 6074, 4341, 6012, 4350, 5959, 4385, 5906, 4438, 5862, 4474,
      5827, 4500, 5791, 4509, 5747, 4509, 5721, 4509, 5694, 4509, 5686, 4509,
      5677, 4500, 5650, 4447, 5633, 4394, 5615, 4333, 5597, 4271, 5580, 4218,
      5571, 4147, 5553, 4095, 5545, 4059, 5545, 4042, 5527, 4024, 5509, 4015,
      5492, 3998, 5465, 3989, 5430, 3989, 5395, 3980, 5377, 3980, 5351, 3980,
      5324, 3980, 5315, 3980, 5307, 3980,
    ],
    color: "#fedc5e",
  },
]

注意事项

  • 在 vue 项目中,为了最佳性能,请使用 markRaw 将 doodle 实例设为非响应式对象,例如 state.doodle = markRaw(doodle)

贡献