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

device-marker-canvas

v1.0.1

Published

A Vue.js component for marking devices on a canvas

Readme

DeviceMarkerCanvas 组件使用说明

本组件是一个vue2组件,用于在平面图上进行设备标记与查看,支持拖拽添加、选中拖动、等比自适应、DPR高清绘制、查看/编辑两种模式切换。

特性概览

  • 等比自适应:图片以 contain 方式适配画布,始终完整展示并居中。
  • 高清绘制:自动根据 devicePixelRatio 放大画布像素,保证清晰度。
  • 拖拽交互:从右侧设备列表拖入画布以添加标记;编辑模式下可选中拖动标记。
  • 防重复添加:同一设备仅允许添加一次(可按需扩展)。
  • 事件丰富:图片加载、画布缩放、标记新增/移动等事件上报,方便与业务联动。
  • 响应式:监听容器尺寸与窗口变化,实时重算画布尺寸与图片适配。

## 基础用法示例
```vue
<template>
  <div class="page">
    <DeviceMarkerCanvas
      :backageImageSrc="planUrl"
      :existingMarkers="lockedMarkers"
      :deviceList="devices"
      :mode="pageMode"
      :moveIconFillColor="'#e6a23c'"
      :moveEndIconFillColor="'#409eff'"
      :disabledIconFillColor="'#67c23a'"
      @image-loaded="onImageLoaded"
      @canvas-resized="onCanvasResized"
      @marker-added="onMarkerAdded"
      @marker-moved="onMarkerMoved"
    />
  </div>
</template>

<script>
export default {
  data() {
    return {
      pageMode: 'edit', // 'edit' | 'view'
      planUrl: 'https://picsum.photos/800/500',
      // 只读标记(不可编辑,绿色展示)
      lockedMarkers: [
        { id: 'lk1', deviceId: 'D-100', deviceName: '摄像头', x: 1200, y: 800 },
      ],
      // 可拖拽设备列表(右侧)
      devices: [
        { id: 'D-101', name: '烟感', icon: 'icon-smoke' },
        { id: 'D-102', name: '摄像头', icon: 'icon-camera' },
      ],
    };
  },
  methods: {
    onImageLoaded(meta) {
      console.log('图片尺寸:', meta); // { naturalWidth, naturalHeight }
    },
    onCanvasResized(meta) {
      console.log('画布尺寸:', meta); // { width, height, dpr }
    },
    onMarkerAdded(marker) {
      console.log('新增标记:', marker);
      // 可在此将标记保存到后端
    },
    onMarkerMoved(marker /*, allMarkers*/ ) {
      console.log('移动标记:', marker);
      // 可在此持久化保存移动后的坐标
    },
  },
};
</script>

<style scoped>
.page { height: 100vh; display: flex; }
</style>

属性(Props)

  • backageImageSrc (String, required)
    • 平面图图片地址。
  • existingMarkers (Array, default: [])
    • 已存在的只读标记列表(不可编辑,默认绿色)。
  • deviceList (Array, default: [])
    • 可拖拽设备列表,作为右侧设备面板的数据源。
  • mode (String, default: 'edit', oneOf: 'edit' | 'view')
    • 工作模式:edit 可编辑、view 查看。
  • moveIconFillColor (String, default: '#e6a23c')
    • 选中/移动中的标记填充色。
  • moveEndIconFillColor (String, default: '#409eff')
    • 非选中(新增集合中的)标记填充色。
  • disabledIconFillColor (String, default: '#67c23a')
    • 只读标记(existingMarkers)填充色。

事件(Emits)

  • image-loaded -> { naturalWidth, naturalHeight }
    • 背景图片加载完成时触发。
  • canvas-resized -> { width, height, dpr }
    • 画布因容器变化重算后触发。
  • marker-added -> marker
    • 从设备列表拖入画布后触发,返回新增标记对象。
  • marker-moved -> marker[, allMarkers]
    • 编辑模式下拖动标记释放鼠标后触发。

数据结构

  • 设备(deviceList item)
interface DeviceItem {
  id: string;          // 设备唯一ID
  name: string;        // 设备名称(用于标注)
  icon?: string;       // 可选,右侧面板的图标class
}
  • 标记(existingMarkers 与 新增标记)
interface MarkerItem {
  id: string;          // 标记ID(组件对新增标记会生成随机ID)
  deviceId: string;    // 设备ID(来自 deviceList.id)
  deviceName: string;  // 设备名称(来自 deviceList.name)
  x: number;           // 世界坐标(以图片原始像素为单位)
  y: number;           // 世界坐标(以图片原始像素为单位)
}

交互说明

  • 拖拽新增
    • 从右侧设备面板拖动某设备图标到画布放开,即在鼠标所指位置添加此设备标记。
    • 同一设备默认仅允许添加一次(内部用 addedDeviceIds 约束)。
  • 选中/拖动(编辑模式)
    • 在画布上点击已有“新增标记”可选中;按住拖动以移动位置;释放触发 marker-moved
    • 只读标记 existingMarkers 不可编辑,仅展示。

自适应与坐标体系

  • 画布尺寸:
    • 组件实时读取容器尺寸,设置 CSS 尺寸与像素尺寸(乘以 DPR)。
  • 图片等比适配:
    • fitImage() 使用 contain 策略计算 scale/offsetX/offsetY,使图片完整且居中显示。
  • 坐标转换:
    • 世界 → 屏幕:screen = world * scale + offset
    • 屏幕 → 世界:world = (screen - offset) / scale
    • 组件内部所有存储坐标为“世界坐标”(基于图片原始尺寸),因此不受缩放影响,便于持久化。

样式与布局

  • 组件根容器:device-marker-container,包含:
    • 左侧/顶部:floor-plan-section(画布所在区域,flex居中)。
    • 右侧/底部:device-list-section(设备面板,仅在 mode !== 'view' 时渲染)。
  • 查看模式 view:根容器会添加 is-view 类,移除边框与外间隙,确保画布居中且铺满可用空间。

效果图

性能建议

  • 大量标记时可考虑:
    • 仅在数据变更时重绘;
    • 将只读标记缓存到离屏 canvas 再一次性绘制;
    • 降低重绘频率(resize 防抖)。

常见问题

  1. 平面图不铺满或不居中?
  • 请确保外层容器可伸缩,组件已在 view 模式下移除外间隙与右边框,同时在 mode 切换时会重新计算画布尺寸与图片适配。
  1. 拖入无效或设备列表不显示?
  • 请确保 mode === 'edit';并检查外层是否给了足够高度与可见区域;设备列表由 deviceList 决定。
  1. 为什么坐标值很大?
  • 坐标为图片原始像素(世界坐标),便于缩放不失真、存储一致。渲染时会转换到屏幕坐标。

二次开发指引

  • 更换图标形态:修改 drawMarker 或将其拆分为图标绘制与标签绘制两个方法;
  • 自定义命中半径:调整 hitTest 中半径 r
  • 允许重复添加:去掉 addedDeviceIds 约束或基于设备类型而非ID限制。

如需更多示例或联调帮助,可在页面中监听上述事件并接入后端保存逻辑。