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

@hzab/hk-video

v0.0.2

Published

海康 Web 视频插件(VideoWebPlugin **V1.5.2**)的函数式 React 封装,配合 TypeScript 全量类型。

Readme

HikVideoPlayer

海康 Web 视频插件(VideoWebPlugin V1.5.2)的函数式 React 封装,配合 TypeScript 全量类型。


特性

  • 函数式组件 + Hooks 实现:完全替代旧的类组件
  • 完整的 TS 类型:覆盖 SDK 接口、回调消息、Props、Ref,避免裸 any
  • 常量分层FIXED_*(SDK 强制写死,禁止修改)、DEFAULT_*(可被 Props 覆盖)
  • 位置自适应:自动跟踪容器尺寸 / 页面滚动 / 视口大小变化;支持 autoFitParent 撑满父容器(详见 位置自适应
  • 命令式 API:通过 ref 暴露 20+ 个方法,覆盖预览、回放、抓图、布局、全屏、字符叠加等
  • 强类型回调:8 类 SDK 消息(窗口选中 / 播放状态 / 抓图 / 录像 / 全屏 / 布局 / 双击 / 时间轴)按类型分发为独立的 onXxx props

前置条件

  1. 客户端已安装 VideoWebPlugin.exe(V1.5.2 及以上)。安装目录通常为 C:\Program Files (x86)\VideoWebPlugin,安装后系统会注册唤醒协议 VideoWebPlugin://, 并在后台运行 WebControl.exe

  2. 页面已加载 SDK 资源(推荐放在 public/index.html 或入口 HTML):

    <script src="/jsencrypt.min.js"></script>          <!-- RSA 公钥加密 -->
    <script src="/jsWebControl-1.0.0.min.js"></script> <!-- WebControl 主库 -->

    加载后会向 window 上挂 WebControlJSEncrypt 两个全局对象,组件内 通过 declare global 已声明类型,IDE 不会再报"未定义"。

  3. 平台信息appKey / secret / ip):

  • 直接通过 Props 传入(最高优先级)
  • 通过 HikVideoPlayerProvider 注入子树(推荐用于多处使用同一平台)
  • 通过 configureHikVideoPlayer() 全局配置一次(兼容老项目 bootstrap)
  • 兜底从 window 读取(默认键路径 projectInfo.videoHk,可改、可关)
  • 详见下文 平台配置(三层模型)

快速上手

import HikVideoPlayer from "@/components/HikVideoPlayer";

export default function Demo() {
  return (
    <HikVideoPlayer
      cameraCodes={["c633ef048fe141e1ac6dbeb36aaf21d3"]}
      mode="preview"
      layout="1x1"
      width={800}
      height={450}
    />
  );
}

cameraCodes 数组下标 +1 即对应播放窗口序号(wndId),所以传 4 个 code + layout="2x2" 就能填满四宫格。


Props 详解

基本播放

| 属性 | 类型 | 默认值 | 说明 | |---|---|---|---| | cameraCodes | string[] | [] | 监控点编号列表,按下标自动分配窗口 | | mode | "preview" \| "playback" | "preview" | 播放模式 | | layout | HikLayout | "1x1" | 画面布局,支持 1x1 ~ 5x5 / 1+5 / 4+9 等 | | autoFitParent | boolean | false | 是否撑满父容器并随父容器尺寸自动校正;开启后 width/height 可省略 | | width / height | number | — | 容器尺寸(单位 px);autoFitParent=false必填 | | style / className | — | — | 标准 React 样式入口 |

平台信息

这里只列 Props 入口,完整解析顺序见 平台配置(三层模型)

| 属性 | 默认值 | 说明 | |---|---|---| | appKey / secret / ip | 由三层配置合并 | 平台凭证 | | getSecret | — | 异步 / 同步获取 secret 的函数,优先于 secret,适合走后端代理 | | port | 443 | HTTPS 端口 | | enableHTTPS | 1 | 目前仅支持 HTTPS |

取流默认值(可被运行时 ref 调用覆盖)

| 属性 | 默认值 | 说明 | |---|---|---| | defaultStreamMode | 0 | 0-主码流 / 1-子码流 | | defaultTransMode | 1 | 0-UDP / 1-TCP | | defaultGpuMode | 0 | 0-关闭 / 1-启用 GPU 硬解 | | defaultRecordLocation | 0 | 0-中心存储 / 1-设备存储 |

回放专用

| 属性 | 说明 | |---|---| | startTime / endTime | 支持 Date / 毫秒时间戳 / "yyyy-MM-dd HH:mm:ss" 字符串 |

UI

| 属性 | 默认值 | 说明 | |---|---|---| | showToolbar | 1 | 工具栏开关 | | showSmart | 0 | 智能信息(移动侦测线框等) | | buttonIDs | "" | 上 / 下工具条按钮 ID,逗号分隔;空串=全部默认 | | toolBarButtonIDs | "" | 工具栏按钮 ID,逗号分隔 | | snapDir | "D:\\SnapDir" | 抓图保存路径 | | videoDir | "D:\\VideoDir" | 紧急录像 / 剪辑保存路径 | | language | "zh_CN" | 多语言:zh_CN / en_US | | levelType | — | 时间轴级别(仅回放) |

服务 / 重连

| 属性 | 默认值 | 说明 | |---|---|---| | servicePortStart / servicePortEnd | 15900 / 15909 | WebControl 服务端口范围(建议使用默认值) | | reconnectTimes | 0 | 0 无限 / >0 次数 / <0 不重连 | | reconnectDuration | 5 | 重连间隔,秒 | | maxRetry | 3 | 插件实例化失败的最大重试次数 |

位置跟踪

| 属性 | 默认值 | 说明 | |---|---|---| | trackPosition | false | 开启后会用 rAF 监测容器位置变化,自动同步插件窗口;用于可拖拽 Modal / 浮窗等场景,详见 可拖拽 Modal 接入 |

事件回调

| 属性 | payload | 说明 | |---|---|---| | onReady | — | 插件创建 + 初始化完成 | | onError | string | 初始化 / 启动失败 | | onDisconnect | (normal: boolean) | 服务断开(true 正常 / false 异常) | | onWindowSelected | HikWindowSelectedMsg | 窗口选中(type=1) | | onPlayStatus | HikPlayStatusMsg | 预览/回放状态(type=2) | | onSnapshot | HikSnapshotMsg | 抓图结果(type=3) | | onRecord | HikRecordMsg | 紧急录像 / 剪辑结果(type=4) | | onFullScreenChange | (isFullScreen: boolean) | 全屏变化(type=5) | | onLayoutChange | HikLayoutChangeMsg | 布局切换(type=6) | | onWindowDblClick | HikWindowDblClickMsg | 双击事件(type=7) | | onTimeBarLevelChange | HikTimeBarLevelMsg | 时间轴级别变化(type=8,仅回放) |


Ref 命令式 API

import { useRef } from "react";
import HikVideoPlayer, { type HikVideoPlayerRef } from "@/components/HikVideoPlayer";

const ref = useRef<HikVideoPlayerRef>(null);

ref.current?.snapShot();                                // 抓图
ref.current?.setFullScreen();                           // 进入全屏
ref.current?.setLayout("2x2");                          // 切换布局
ref.current?.drawOSD({ text: "温度: 50℃", x: 10, y: 10 });
ref.current?.cutPart(0, 0, 200, 100);                   // 弹窗弹出前手动扣掉一块
ref.current?.repairPart(0, 0, 200, 100);                // 弹窗关闭后还原
ref.current?.destroy();                                 // 主动释放

完整列表:

| 方法 | 说明 | |---|---| | startPreview(item) / startMultiPreview(list) | 单点 / 批量预览 | | startPlayback(item) / startMultiPlayback(list) | 单点 / 批量回放 | | stopAllPreview() / stopAllPlayback() | 停止全部 | | stopMultiPlay(wndIds) | 按窗口序号批量停止 | | setLayout(layout) / getLayout() | 设置 / 获取布局 | | snapShot(arg?) | 抓图(不传 arg 则按选中窗口、默认路径) | | drawOSD(arg) | 画面字符叠加 | | setFullScreen() / exitFullScreen() | 进入 / 退出全屏 | | setTimeBarLevel(level) | 设置时间轴级别(回放) | | getVersion() | 获取插件版本号 | | setAuthInfo(list) | 多平台认证信息 | | resize(w, h) | 手动 JS_Resize | | syncPosition() | 立即同步位置 / 大小 / 可见性(手动改变容器后用) | | hide() / show() | 显隐插件窗口 | | cutPart(l, t, w, h) / repairPart(l, t, w, h) | 手动扣 / 还原插件窗口区域 | | destroy() | 主动销毁 | | getInstance() | 拿到原始 WebControl 实例(兜底,谨慎使用) |


平台配置(三层模型)

为了让本组件能作为通用 npm 包独立分发,平台信息(appKey / secret / ip / port / enableHTTPS)采用四层叠加 + 单字段合并

Props(最高) > Provider(Context) > 全局 configure() > window 兜底(最低)

单字段合并 = 高层为 undefined / null / "" 时回退到下一层,所以混用毫无负担。

1) Props(推荐用于一次性 / 临时覆盖)

<HikVideoPlayer appKey="xxx" secret="yyy" ip="10.0.0.1" port={443} />

2) Provider(推荐用于「同一棵树多处共用」)

import { HikVideoPlayerProvider } from "@/components/HikVideoPlayer";

<HikVideoPlayerProvider config={{ appKey, ip, getSecret: () => fetch('/api/hik/secret').then(r => r.text()) }}>
  <App />
</HikVideoPlayerProvider>

3) 全局 configureHikVideoPlayer()(兼容老项目 bootstrap)

// src/main.ts / bootstrap.ts,应用入口处调用一次
import { configureHikVideoPlayer } from "@/components/HikVideoPlayer";

configureHikVideoPlayer({
  appKey: import.meta.env.VITE_HIK_APP_KEY,
  ip: import.meta.env.VITE_HIK_IP,
  port: 443,
  getSecret: () => fetch('/api/hik/secret').then(r => r.text()),
});

4) window 兜底(默认开启,可配置可关闭)

为兼容历史项目「把配置挂到 window.projectInfo.videoHk 上」的约定,组件默认开启此兜底:

// 老项目入口仍可以维持原有写法
window.projectInfo = {
  videoHk: { appkey: 'xxx', secret: 'yyy', ip: '10.0.0.1' }
};

字段命名同时兼容 SDK 风格(小写)和组件风格(驼峰):appkey / appKey 都能识别。

自定义键路径完全关闭 兜底:

// 改用其他键路径
configureHikVideoPlayer({ windowKey: 'myApp.hik' });
// 或彻底关闭
configureHikVideoPlayer({ windowKey: null });

关于 secret 安全

直接把 secret 明文挂在 window 或 props 上,可能被第三方脚本读取。生产推荐使用 getSecret 走后端代理:

<HikVideoPlayer
  appKey="xxx"
  ip="10.0.0.1"
  getSecret={async () => (await fetch('/api/hik/secret')).text()}
/>

存在 getSecret 时会忽略 secret 字段。


可拖拽 Modal / 浮窗接入

由于海康插件窗口是 WebControl.exe 在浏览器之上绘制的顶层 OS 窗口

  • 它不受浏览器 z-index 影响(永远置顶)
  • 它的位置同步是异步 IPC,快速拖动时会有 1~2 帧拖影

组件已内置「容器尺寸 / 滚动」监听,但 ResizeObserver 不感知 transform 位移——可拖拽 Modal 通常用 transform: translate(x, y) 改位置,所以默认情况下插件窗口不会跟随移动。

方案 A(推荐):trackPosition 自动跟随

直接打开 trackPosition,组件内部会挂一个 rAF 循环,每帧对比容器位置,发现变化立刻同步:

import { Modal } from "antd";
import HikVideoPlayer from "@/components/HikVideoPlayer";

<Modal open={open} maskClosable={false} /* 启用可拖拽逻辑 */>
  <HikVideoPlayer
    trackPosition         // ← 关键:开启位置跟踪
    cameraCodes={[code]}
    width={800}
    height={450}
  />
</Modal>

性能开销很小:未变化时只调用一次 getBoundingClientRect,不触发任何 SDK 调用。

方案 B:手动在 onDrag 里同步(不想常驻 rAF)

const playerRef = useRef<HikVideoPlayerRef>(null);

<DraggableModal
  onDrag={() => playerRef.current?.syncPosition()}
  onDragEnd={() => playerRef.current?.syncPosition()}
>
  <HikVideoPlayer ref={playerRef} ... />
</DraggableModal>

方案 C:拖动期间 hide / 拖动结束 show(无拖影)

如果不在意拖动过程中的画面,这是体验最一致的做法:

<DraggableModal
  onDragStart={() => playerRef.current?.hide()}
  onDragEnd={() => {
    playerRef.current?.show();
    playerRef.current?.syncPosition();
  }}
/>

关闭动画建议

UI 库的 Modal 关闭带 200ms 缩放动画,插件窗口同步不上时会看到画面「滞留」一下再消失。建议关闭前先调 hide()

const handleClose = () => {
  playerRef.current?.hide();
  setOpen(false);
};

位置自适应

要解决的问题:旧版类组件存在一个长期痛点—— 插件窗口是 WebControl.exe 在浏览器之上绘制的顶层窗口,并不是一个 <video> 标签, 因此既不会跟随 DIV 自动滚动 / 缩放,也不会被父容器的 overflow: hidden 裁剪。 一旦页面滚动或窗口变化,插件就会"飘"出容器、甚至覆盖到浏览器边框外。

这一版组件已通过以下机制把这个问题解决:

  1. 尺寸 / 滚动监听:组件内置
  • ResizeObserver 监听容器尺寸变化
  • window 上的 resize + 捕获阶段 scroll(捕获所有可滚动祖先的滚动事件)
  • width / height props 变化时主动校正(固定尺寸模式)
  1. autoFitParent 父容器适配(可选):
  • 开启后容器样式为 width/height: 100%,插件窗口尺寸由 DOM 实测值驱动
  • 父容器尺寸变化时,子元素随之变化,ResizeObserver 自动触发 JS_Resize
  • 要求父容器具有明确的宽高(或 flex 子项等可计算尺寸的布局)
  1. requestAnimationFrame 合帧:同一帧内的多次事件合并为 1 次 syncPluginWindow, 避免高频滚动引起的卡顿
  2. syncPluginWindow 完整流程(每次触发执行):
    ① JS_Resize(w, h)                       —— 对齐位置 & 大小
    ② 完全离开视口?                          —— JS_HideWnd 整体隐藏
    ③ 进入视口?                              —— JS_ShowWnd 恢复显示
    ④ JS_RepairPartWindow(0,0,w+1,h+1)      —— 先整体还原上次的扣除
    ⑤ 计算四方向溢出(left/right/top/bottom)—— JS_CuttingPartWindow 把溢出块挖掉
    这一套是参照海康官方 demo 的 setWndCover 实现,能保证插件画面始终被严格限制在 DIV 的可见区域内。

撑满父容器(autoFitParent

默认模式下,容器尺寸由 width / height props 固定为像素值;仅当 props 变化时容器才会 跟着变,父容器单独改变尺寸不会触发校正

若希望播放器随父容器自动伸缩,可开启 autoFitParent

<div style={{ width: 800, height: 450 }}>
  <HikVideoPlayer
    autoFitParent
    cameraCodes={["cam-code-1"]}
    mode="preview"
    layout="1x1"
  />
</div>

说明:

  • 无需传入 width / height,组件会在首次布局完成后用 DOM 实测尺寸创建插件窗口
  • 父容器 width / height 变化时,插件窗口会自动 JS_Resize 对齐
  • 父容器必须有明确尺寸;若父级高度未设置,100% 高度可能塌缩为 0
  • trackPosition 可并存:前者管尺寸,后者管 transform / 拖拽位移

弹窗 / 抽屉 / 下拉遮挡场景

插件窗口始终置顶,任何 Antd 的 Modal / Drawer / Popover 都盖不住。 组件已暴露 cutPart / repairPart,由调用方在弹层打开前主动挖掉、关闭后还原:

const playerRef = useRef<HikVideoPlayerRef>(null);

<Modal
  open={open}
  afterOpenChange={(visible) => {
    if (visible) {
      // 假设 Modal 居中宽 600 高 400
      playerRef.current?.cutPart(100, 50, 600, 400);
    } else {
      playerRef.current?.repairPart(100, 50, 600, 400);
    }
  }}
/>

如果不在乎过渡体验,最简单的做法是弹窗打开前 playerRef.current?.hide()、关闭后 show()


常量配置(constants.ts

| 分类 | 命名 | 说明 | |---|---|---| | 固定参数 FIXED_* | FIXED_SERVICE_TYPE | 固定 "window",禁止改 | | | FIXED_DLL_PATH | 固定 "./VideoPluginConnect.dll" | | | FIXED_ACTIVEX_CLSID | IE10 ActiveX 的 clsid | | | FIXED_WAKEUP_PROTOCOL | "VideoWebPlugin://" | | | FIXED_RSA_KEY_LENGTH | 1024 | | | FIXED_ENCRYPTED_FIELDS | 只对 secret 加密(多字段加密会显著拖慢初始化) | | 默认参数 DEFAULT_* | 完整列表见 constants.ts | 全部可被 Props 覆盖 |


从旧组件迁移

- import HikVisionPulgin from "@/components/videoHk";
+ import HikVideoPlayer from "@/components/HikVideoPlayer";

- <HikVisionPulgin
-   codes={[code]}
-   type="preview"
-   width={800}
-   height={450}
-   showToolbar={0}
- />
+ <HikVideoPlayer
+   cameraCodes={[code]}
+   mode="preview"
+   width={800}
+   height={450}
+   showToolbar={0}
+ />

主要 Prop 名变化:

| 旧 | 新 | |---|---| | codes | cameraCodes | | type | mode | | 无 | 新增大量类型化回调 + Ref 方法 |


演示

example/pages/videoDemo/HikVideoPlayerDemo.tsx,已默认挂在路由 /demo 下, 覆盖:

  • 单点预览 / 单点回放切换
  • 多窗口布局(2x2、3x3)
  • Ref 方法(抓图、全屏、布局切换、字符叠加)
  • 滚动 / 拖动 demo(验证位置自适应)

常见问题

Q1:页面加载后插件没出现?

  • 确认浏览器控制台有无 window.WebControl 未定义 警告
  • 确认 VideoWebPlugin.exe 已安装;可手动在终端执行 tasklist | findstr WebControl 检查 exe 是否运行
  • 检查 Props 中 appKey / secret / ip 是否齐全

Q2:浏览器开发者工具里看插件窗口是黑色? 这是预期行为。DIV 仅用作位置 / 尺寸锚点,实际画面由 exe 绘制在浏览器之上, F12 看到的"黑色"就是这个 DIV 自身的背景色(已通过 backgroundColor: "#000" 设置成黑色作占位)。

Q3:路由跳转回来后插件不显示? 通常是由于上一次 unmount 时 JS_Disconnect 尚未完成。组件已做好兜底:

  • unmount 时 JS_HideWnd + JS_Disconnect
  • destroyedRef 守卫异步链

若仍有问题,可在路由切换前手动 playerRef.current?.destroy()