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

asd-v

v1.0.0

Published

ASD-V 数据可视化画布

Downloads

136

Readme

allsense-canvas 重构说明(src 引擎化设计)

AllSense Canvas:在不修改 core/ 的前提下,于 src/ 目录实现引擎化重构。目标是将“数据、事件、节点、渲染、动画、请求(网络)”等能力抽象为独立引擎与清晰接口,降低耦合,提升测试与演进效率。


重构目标

  • 保持对外 API 简洁稳定(CanvasApp 门面),逐步替换内部实现为引擎化结构。
  • 引擎化分层:数据引擎、事件引擎、节点引擎、渲染引擎、动画引擎、请求引擎(可按需补充线条引擎)。
  • 强类型与安全:限制运行时代码字符串,建议沙箱/白名单。
  • 高性能:图层化、增量刷新、离屏渲染与缓存。
  • 可测试:单元/集成测试覆盖核心路径。

现有 core 架构摘要(参考对象)

根路径:core/src/

  • canvas/:主渲染与交互编排(canvas.ts)、图片层(canvasImage.ts)、模板与网格/标尺(canvasTemplate.ts)、放大镜与离屏(magnifierCanvas.tsoffscreen.ts)。
  • pen/:模型与绘制(model.tsrender.ts)、文本/箭头/几何工具(text.tsarrow.tsutils.ts)。
  • store/:全局状态与注册(store.tsglobal.ts)。
  • event/:事件模型与动作(event.ts)。
  • diagrams/:内置图形与路径(包含 svgPath.ts、各类形状、连线)。
  • utils/:通用工具(克隆、颜色、对象、数学、URL、JetLinks 等网络适配)。
  • 其他:options.ts、主题/消息/对话框/提示等辅助模块。

核心问题(重构关注):

  • 职责集中在门面/大模块,渲染/交互/网络/事件强耦合,难以单测与增量演进。
  • render.ts 体量巨大,计算与绘制交织,影响可维护性。
  • 运行时代码字符串(线条动画、socket 回调等)存在安全与类型风险。

引擎映射设计(src/engine)

本仓库已在 src/engine/ 下建立基础骨架与聚合入口:

src/engine/
  data/      // 数据引擎:集中管理 pens、选择集、CRUD
  event/     // 事件引擎:封装 EventBus,统一 on/off/emit
  render/    // 渲染引擎:主绘制流程,后续接入 Registry 的绘制
  node/      // 节点引擎:节点类图形的创建、更新、旋转、缩放与命中
  line/      // 线条引擎(可选):线条创建、更新与命中(路径路由预留)
  index.ts   // 聚合导出

顶层导出:src/index.ts 已包含 export * from './engine'

1) 数据引擎(Data Engine)

  • 核心职责:
    • 管理 pensselectedIdspathsguides 等数据;
    • 提供 CRUD 与批量操作;
    • 与历史记录协作(pushHistory/undo/redo 由 HistoryService 维护,DataEngine 只专注数据)。
  • 对应 core:store/store.ts + data.ts 的数据模型。
  • 接口建议:
    • init(store), add/update/remove/getById/list, select/selected
  • 迁移要点:ID→Pen 索引、calculative 运行态缓存收敛、选区与区域刷新协作。

2) 事件引擎(Event Engine)

  • 核心职责:
    • 封装 EventBus,提供统一 on/off/emit
    • 承接事件动作路由(后续可将 event.ts 的动作表以服务方式实现)。
  • 对应 core:store/store.tsemitterevent/event.ts
  • 安全建议:事件动作涉及 JS 代码时引入沙箱/白名单;校验类型与数据范围。

3) 节点引擎(Node Engine)

  • 核心职责:
    • 节点类图形的创建/更新、旋转/缩放、命中判定;
    • 锚点计算、文本布局(可与渲染引擎协商接口以分离计算与绘制)。
  • 对应 core:pen/model.tspen/utils.tsrect/*diagrams/* 部分逻辑。
  • 接口建议:create/update/rotate/resize/hitTest(后续可加 anchors(pen)layoutText(pen))。

4) 线条引擎(Line Engine,建议加入)

  • 核心职责:
    • 线条几何(直线/折线/曲线);
    • 路由与端点吸附;
    • 箭头/标注/标签与命中;
    • 与动画引擎协同(线条动画)。
  • 对应 core:diagrams/line/*pen/arrow.tspen/utils.tssvgPath.ts
  • 接口建议:create/update/hitTest(后续:route(line)anchors(line))。

5) 渲染引擎(Render Engine)

  • 核心职责:
    • 图层管理:模板层/底图层/主层/顶图层/图片层;
    • 计算与绘制:worldRect、Path2D/Canvas 二路绘制;
    • 增量刷新与区域重绘(PatchFlags);
    • 离屏与放大镜;
    • 选择框、锚点与覆盖层 UI。
  • 对应 core:canvas/*pen/render.ts
  • 集成建议:与 RegistryService 协作,从注册表查询绘制函数;与 Node/Line 引擎分离几何计算。

6) 动画引擎(Animation Engine)

  • 核心职责:
    • 帧驱动(队列 + requestAnimationFrame);
    • 线条动画注册与执行(函数或安全代码字符串);
    • 与渲染引擎低耦合协作(只触发需要重绘的区域)。
  • 对应 core:pen/render.ts 的动画段落、lineAnimateDraws 相关功能。
  • 安全建议:尽量函数注册,若保留代码字符串,必须沙箱化并限定上下文。

7) 请求引擎(Request/Network Engine)

  • 核心职责:
    • 统一封装网络协议:HTTP/WebSocket/MQTT/SSE/SQL/IoT/JetLinks;
    • 连接/订阅/心跳/重连/鉴权;
    • 消息归一化(数据点映射到 setDatas/setValue(s)),与 BindingService 协作。
  • 对应 core:utils/jetLinks.tsutils/url.ts、以及 core.ts 中网络相关方法。
  • 接口建议:init(store), connect(opts), disconnect(), subscribe(topic, handler), send(channel, payload)
  • 安全建议:Token/鉴权隔离;错误与超时统一上报;模板变量替换必须校验。

与 Services 的协作关系

  • RenderService 调用 RenderEngine.render(),并逐步将选择层/锚点绘制迁移到引擎内部;
  • InteractionService 将几何操作委托 NodeEngine/LineEngine,自身只做事件编排与状态维护;
  • HistoryServiceDataEngine 协作,统一历史入口;
  • RegistryService 作为绘制/锚点/动画注册中心,渲染/动画引擎按需查询;
  • ThemeService/AnimationService/NetworkService/BindingService 通过 Store 与事件引擎交互,保持低耦合。

迁移路线(建议增量)

  1. 注入并初始化五/六大引擎(含 LineEngine 可选),门面 CanvasApp 保持对外 API 不变;
  2. 迁移 services/render/* 的选择层与锚点到 RenderEngine
  3. 拆分 services/interaction/* 的命中/旋转/缩放到 NodeEngine/LineEngine
  4. 将 CRUD 与选区操作集中到 DataEngine,历史走 HistoryService
  5. 收敛注册点到 RegistryService,绘制由 RenderEngine 查询执行;
  6. 引入 Animation Engine 的统一帧驱动与线条动画注册;
  7. 引入 Request Engine 统一网络协议与消息归一化,联动 Binding;
  8. 单测覆盖:命中与变换、历史合并、增量渲染、事件派发、网络消息映射与错误路径。

风险与测试策略

  • 风险:职责迁移期间渲染/交互耦合断裂,线条路由与动画回归;
  • 缓解:先搭骨架与适配层,逐模块迁移;保持旧 API 的门面代理;
  • 测试:
    • 单测:Data/Node/Line 命中与变换、History 合并、Animation 帧时序;
    • 集成:Render 层区域刷新与覆盖层 UI;Network 消息到绑定到渲染的闭环;
    • 回归:示例工程作为验收清单。

接口草案(TypeScript 简版)

export interface IDataEngine {
  init(store: Store): void;
  add(pen: Pen): void; update(pen: Pen): void; remove(id: string): void;
  getById(id: string): Pen | undefined; list(): Pen[];
  select(ids: string[]): void; selected(): string[];
}

export interface IEventEngine {
  init(store: Store): void;
  on(type: string, handler: (...args: any[]) => void): void;
  off(type: string, handler?: (...args: any[]) => void): void;
  emit(type: string, payload?: any): void;
}

export interface INodeEngine {
  init(store: Store): void;
  create(pen: Pen): void; update(pen: Pen): void;
  rotate(id: string, rad: number): void; resize(id: string, w: number, h: number): void;
  hitTest(x: number, y: number): string | undefined;
}

export interface ILineEngine {
  init(store: Store): void;
  create(pen: Pen): void; update(pen: Pen): void;
  hitTest(x: number, y: number): string | undefined;
}

export interface IRenderEngine {
  // 在指定容器中初始化并创建基础画布(不建立观察器)
  init(store: Store, holder?: HTMLElement): void;
  // 创建并附着 Canvas 到容器,负责首帧渲染与自适应(推荐)
  attach(store: Store, holder: HTMLElement, options?: Options): void;
  // 获取当前使用的 Canvas
  getCanvas(): HTMLCanvasElement | undefined;
  // 渲染/尺寸更新与销毁
  render(): void;
  resize(): void;
  updateContainerSize(opts: Partial<Options>): void;
  destroy(): void;
}

export interface IRequestEngine {
  init(store: Store): void;
  connect(opts: any): Promise<void>;
  disconnect(): Promise<void>;
  subscribe(topic: string, handler: (msg: any) => void): void;
  send(channel: string, payload: any): Promise<void>;
}

使用示例(初始化)

import { createStore } from './src/store/Store';
import { DataEngine, EventEngine, RenderEngine, NodeEngine, LineEngine } from './src/engine';

const store = createStore();
const data = new DataEngine();
const events = new EventEngine();
const render = new RenderEngine();
const nodes = new NodeEngine();
const lines = new LineEngine();

data.init(store); events.init(store); render.attach(store, holderEl);
nodes.init(store); lines.init(store);

data.add({ id: 'rect1', name: 'rect', x: 100, y: 100, width: 120, height: 80 });
render.render();

注意事项

  • “根据 core 的架构,重构在 src 里进行”:不要改动 core/ 源码;仅参考其实现与 API 约定。
  • SVG 拖拽命中需显式 width/height(仅 d 路径无法命中盒模型)。
  • DOM 组件交互(上层/下层)通过 pointerEventsdomLayer/domZSplit 控制;
  • 高级能力(旋转枢轴/吸附/对齐)建议由 Node/Render 引擎协作实现(几何接口 + 可视 UI)。

最新架构要点(职责与用法)

  • CanvasApp:仅组装与调度;若传入 holder,委托 RenderEngine.attach() 完成创建、挂载与自适应;setOptions() 内部转发到 render.updateContainerSize();保留 mount(holder) 用于在外部控制的容器中初始化但不建立观察器的场景。
  • RenderEngine:负责创建/附着 canvas、监听容器尺寸变化(ResizeObserver),在变更时更新 canvas.width/height 并调用 resize() + render();订阅 store.emitter.on('render') 并通过 requestAnimationFrame 合并高频重绘(requestRender);提供 destroy() 清理观察器与移除 canvas
  • 渲染层级与 DOM/Canvas 叠层:详见 src/engine/render/README.md。通过 LayerManager 统一管理分层根(z=2)与顶层覆盖 Canvas(z=3),每个 zIndex 一组,组内复用 Canvas 绘制该层的 Canvas 图元,同时承载该层的 DOM 图元。
  • Pass 管线:内置按序执行的渲染 Pass(背景→网格→标尺→置底线→主图元→选择→悬停→调试),支持 registerPass 扩展,Pass 类型包含 canvas/dom/top 三类,分别对应主/分组画布、分层根(DOM)、顶层覆盖画布。
  • 事件驱动:图片加载、业务动作等通过 store.emitter.emit('render') 触发;AnimationEngine 周期性发出 tick 事件(当前渲染未直接订阅,后续动画更新可通过触发 render 合帧)。
  • 图片与 SVG:
    • 图片(name: 'image')支持地址字段 src/image/url,运行时缓存到 calculative.imgonload/onerror 触发 render;跨域导出可在数据中设置 crossOrigin: 'anonymous'
    • SVG(name: 'svg' + d)使用 calculative.path2d: Path2D 缓存与 ensureSvgBounds 计算边界;明确 ctx.fill(path, 'nonzero') 以避免运行时重载歧义。

用法示例(直接使用 RenderEngine)

const store = createStore();
const render = new RenderEngine();
const holderEl = document.getElementById('app')!;
render.attach(store, holderEl, { background: '#fff', width: 800, height: 500 });
render.render();

用法示例(使用 CanvasApp 门面)

const holder = document.getElementById('app')!;
const app = new CanvasApp(holder, { background: '#f8f9fb', width: 800, height: 500 });
app.node.createNode({ id: 'n1', x: 100, y: 80, width: 120, height: 60 });
app.render.render();
// 动态更新容器尺寸(由引擎观察器驱动到画布)
app.setOptions({ width: 900, height: 560 });

说明:根据 core 架构,所有改造均在 src 完成;core/ 不做改动,仅作为参考。

图形类型要点(数据结构与渲染约束)

  • ImagePen
    • 必填:idname: 'image'、定位与尺寸(x/y/width/height),以及图片地址之一(src/image/url)。
    • 可选:rotatezIndexcrossOrigin
    • 渲染:加载中显示占位(虚线矩形),加载完成自动重绘(emit('render'))。
  • SvgPen
    • 必填:idname: 'svg'd 路径;建议显式 width/height 以便命中与布局;
    • 渲染:Path2D 缓存与边界计算,填充采用 'nonzero' 规则;路径缓存类型不合法时自动重建。

如需进一步细化“CanvasApp 注入引擎的示例实现”和“引擎/服务依赖图”,可在本 README 中继续补充。

核心 → 引擎映射详表

为贯彻“根据 core 的架构,重构项目在 src 里面”的原则,以下将 core/src 的主要模块映射到 src 下的六大引擎(Data、Event、Render、Node、Line、Animation、Request)以及基础 Store/模型。

| Core 路径 | 引擎/模块(src) | 职责对应 | 迁移建议 | | --- | --- | --- | --- | | core/src/store | src/store/Store.tssrc/engine/data | 数据容器、历史与选择集 | 将历史完整策略迁移到 Store,CRUD 与选择统一由 DataEngine 管理 | | core/src/data.ts | src/engine/datasrc/models/types.ts | 数据模型与操作 | 将 PenOptionsCanvasData 类型收敛到 models/types.ts;操作迁移至 DataEngine | | core/src/event | src/engine/event | 事件总线与订阅 | 保留 on/off/emit 接口语义,统一暴露为 EventEngine | | core/src/canvas | src/engine/render(协作 node/line) | 画布绘制、网格背景、标尺等 | 将绘制主循环并入 RenderEngine;命中/拾取由 NodeEngine/LineEngine 提供 | | core/src/pen | src/engine/nodesrc/engine/line | 图元基础、节点/连线 | 依据图元类型拆分到节点与线引擎,通用几何与样式由 RenderEngine 使用 | | core/src/pointcore/src/rect | src/models/types.tsnode/line | 基础几何类型与计算 | 点与矩形类型迁移至 models/types.ts;相关几何算法进入 node/line 实现或共享工具 | | core/src/options.ts | src/models/types.tssrc/store/Store.ts | 画布选项与样式 | 统一选项至 OptionsStore.options 作为引擎消费入口 | | core/src/animation | src/engine/animation | 动画循环与时间线 | 将 tick、关键帧与过渡迁移至动画引擎,广播 tick 事件驱动渲染 | | core/src/scroll | src/engine/rendersrc/store/Store.tssrc/engine/event | 平移缩放、滚轮拖拽 | 视图变换由 RenderEngineStore.data.scale/origin 管理;交互事件通过 EventEngine | | core/src/diagrams | src/engine/node(扩展)、src/engine/render | 特定业务图元与绘制 | 作为节点引擎的扩展模块注册;渲染细节在 RenderEngine 插件化 | | core/src/dialogcore/src/popconfirmcore/src/tooltipcore/src/title | src/engine/render(DOM层)或独立 UI 服务 | 浮层与 UI 组件 | 走 DOM 叠层(Pen.domLayer/Options.domZSplit)或抽象为独立 UI 服务对接 | | core/src/map | src/engine/render(扩展)+ src/engine/request | 地图渲染与数据拉取 | 将瓦片/矢量请求适配到 RequestEngine,绘制层扩展在 RenderEngine | | core/src/message | src/engine/event | 应用级消息 | 统一并入事件总线;必要时区分频道/命名空间 | | core/src/theme.ts | src/engine/rendersrc/store/Store.ts | 主题与样式变量 | 主题选项进入 Store.options;渲染消费在 RenderEngine | | core/src/utils | 建议新增 src/shared/utils | 通用工具方法 | 按需迁移到共享工具,避免引擎间重复实现 | | core/index.ts | src/engine/index.tssrc/index.ts | 聚合导出 | 顶层导出由 src/index.ts 提供;引擎聚合在 src/engine/index.ts |

补充说明:

  • DOM 叠层策略:若图元需要在 Canvas 上方/下方渲染,使用 Pen.domLayerOptions.domZSplit 协调;相关实现位于 RenderEngine
  • 历史与撤销重做:完整策略(合并、分组、跨引擎操作)在迁移时应统一落入 StoreDataEngine。当前为最小实现。
  • 选择与命中:选择集在 DataEngine;命中测试由 NodeEngine/LineEngine 提供,渲染在 RenderEngine 完成视觉反馈。

引擎注入与挂载示例

以下示例展示如何在应用中注入引擎并完成挂载与渲染、交互、动画联动:

import { CanvasApp } from './src';

// 1) 初始化 App 与引擎
const app = new CanvasApp();

// 可选:配置画布选项(背景、网格、对齐等)
app.store.options = {
  background: '#ffffff',
  grid: true,
  gridSize: 20,
  domZSplit: 1000,
};

// 2) 挂载到 DOM 中的 Canvas 与可选 Holder(用于 DOM 叠层)
const canvas = document.getElementById('canvas') as HTMLCanvasElement;
const holder = document.getElementById('holder') as HTMLElement | null;
app.mount(canvas, holder || undefined);

// 3) 数据与图元操作:创建节点与线
app.node.createNode({ id: 'n1', x: 100, y: 80, width: 120, height: 60, name: '矩形' });
app.line.createLine({ id: 'l1', x1: 100, y1: 80, x2: 240, y2: 140, name: '连线' });

// 4) 渲染一次
app.render.render();

// 5) 事件与动画联动:每帧重绘(可替换为脏矩形策略)
app.event.on('tick', () => {
  app.render.render();
});
app.animation.start();

// 6) 命中测试(示例:鼠标点选)
canvas.addEventListener('click', (e) => {
  const rect = canvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  const hitNodeId = app.node.hitTestPoint(x, y);
  const hitLineId = app.line.hitTest(x, y);
  if (hitNodeId) app.data.select([hitNodeId]);
  else if (hitLineId) app.data.select([hitLineId]);
  else app.data.select([]);
});

// 7) 网络请求(示例)
async function loadPens() {
  const res = await app.request.get('/api/pens');
  if (res.ok) {
    const pens = await res.json();
    for (const p of pens) {
      if (p.x1 !== undefined) app.line.createLine(p);
      else app.node.createNode(p);
    }
    app.render.render();
  }
}

依赖注入扩展示例(替换默认渲染引擎)

如需替换某个引擎(例如自定义渲染),可在构造后进行注入并重新初始化:

import { CanvasApp } from './src';
import type { IRenderEngine } from './src/engine/render';

class CustomRender implements IRenderEngine {
  private base!: IRenderEngine; // 可复用默认实现作为降级
  attach(store, holder, options?) {
    // 在指定容器中创建并附着画布,建立自适应与上下文,订阅 store/event
  }
  render() {
    // 自定义绘制流程(网格、节点、线、DOM 叠层)
  }
}

const app = new CanvasApp();
const canvas = document.getElementById('canvas') as HTMLCanvasElement;
const holder = document.getElementById('holder') as HTMLElement;

app.render = new CustomRender();
app.render.attach(app.store, holder);
app.event.on('tick', () => app.render.render());
app.animation.start();

以上映射与示例为建立统一的引擎层入口,后续迁移时请按模块逐步替换 core 中对应实现,确保:

  • 数据模型与历史在 Store/DataEngine 统一;
  • 事件在 EventEngine 统一;
  • 渲染在 RenderEngine 统一,命中/几何在 Node/LineEngine
  • 动画与请求分别在 Animation/RequestEngine
  • 所有改造均在 src 目录下进行,以便与现有服务协作并减少耦合。