{"id":21524036,"url":"https://github.com/likaia/js-screen-shot","last_synced_at":"2025-05-14T08:07:49.292Z","repository":{"id":37745395,"uuid":"336985289","full_name":"likaia/js-screen-shot","owner":"likaia","description":"web端自定义截图插件(原生JS版)","archived":false,"fork":false,"pushed_at":"2025-05-13T15:24:31.000Z","size":2374,"stargazers_count":887,"open_issues_count":11,"forks_count":122,"subscribers_count":6,"default_branch":"pre_release","last_synced_at":"2025-05-13T15:52:39.622Z","etag":null,"topics":["electron","javascript","js-screen-shot","screenshot","typescript","web-screenshot"],"latest_commit_sha":null,"homepage":"https://www.kaisir.cn/js-screen-shot/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/likaia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-08T06:48:45.000Z","updated_at":"2025-05-13T15:24:35.000Z","dependencies_parsed_at":"2024-04-06T04:28:53.280Z","dependency_job_id":"074e7b61-53cc-4839-aae9-331e9972e783","html_url":"https://github.com/likaia/js-screen-shot","commit_stats":{"total_commits":164,"total_committers":8,"mean_commits":20.5,"dds":0.09146341463414631,"last_synced_commit":"268db1dd01a9eca444efb3cc0129cc07ad703cc3"},"previous_names":[],"tags_count":52,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/likaia%2Fjs-screen-shot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/likaia%2Fjs-screen-shot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/likaia%2Fjs-screen-shot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/likaia%2Fjs-screen-shot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/likaia","download_url":"https://codeload.github.com/likaia/js-screen-shot/tar.gz/refs/heads/pre_release","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253983787,"owners_count":21994732,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["electron","javascript","js-screen-shot","screenshot","typescript","web-screenshot"],"created_at":"2024-11-24T01:20:24.684Z","updated_at":"2025-05-14T08:07:44.277Z","avatar_url":"https://github.com/likaia.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# js-web-screen-shot · [![npm](https://img.shields.io/badge/npm-v1.9.9_rc.26-2081C1)](https://www.npmjs.com/package/js-web-screen-shot) [![yarn](https://img.shields.io/badge/yarn-v1.9.9_rc.26-F37E42)](https://yarnpkg.com/package/js-web-screen-shot) [![github](https://img.shields.io/badge/GitHub-depositary-9A9A9A)](https://github.com/likaia/js-screen-shot) [![](https://img.shields.io/github/issues/likaia/js-screen-shot)](https://github.com/likaia/js-screen-shot/issues) [![](\thttps://img.shields.io/github/forks/likaia/js-screen-shot)](https://github.com/likaia/js-screen-shot/network/members) [![](\thttps://img.shields.io/github/stars/likaia/js-screen-shot)](https://github.com/likaia/js-screen-shot/stargazers)\nweb端自定义截屏插件(原生JS版)，运行视频：[实现web端自定义截屏功能](https://www.bilibili.com/video/BV1Ey4y127cV) ,效果图如下：![截屏效果图](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/486d810877a24582aa8cf110e643c138~tplv-k3u1fbpfcp-watermark.image)\n\n## 写在前面\n关于此插件的更多介绍以及实现原理请移步：\n- [实现Web端自定义截屏](https://juejin.cn/post/6924368956950052877)\n- [实现Web端自定义截屏(JS版)](https://juejin.cn/post/6931901091445473293)\n\n\u003e 注意⚠️：本文档并非最新的，最新文档请移步[官网](https://www.kaisir.cn/js-screen-shot/)\n\n## 插件安装\n```bash\nyarn add js-web-screen-shot\n\n# or\n\nnpm install js-web-screen-shot --save\n```\n\n## 插件使用\n由于插件采用原生js编写且不依赖任何第三方库，因此它可以在任意一台支持js的设备上运行。\n\u003e 注意⚠️： 如果需要使用插件的webrtc模式或者截图写入剪切板功能，需要你的网站运行在`https`环境或者`localhost`环境。当然，也可以通过修改浏览器设置的方式实现在所有环境下都能运行。步骤如下：\n\u003e 1.打开谷歌浏览器，在地址栏输入`chrome://flags/#unsafely-treat-insecure-origin-as-secure`\n\u003e 2.在打开的界面中：下拉框选择enabled，地址填写你的项目访问路径。\n\u003e ![img.png](https://www.kaisir.cn/uploads/MarkDownImg/20230531/5e49de8f32f54f8bb972b4f472d4272e.png)\n\n### import形式使用插件\n* 在需要使用截屏插件的业务代码中导入插件\n```javascript\nimport ScreenShot from \"js-web-screen-shot\";\n```\n* 在业务代码中使用时实例化插件即可\n```javascript\nnew ScreenShot();\n```\n\u003e ⚠️注意：实例化插件时一定要等dom加载完成，否则插件无法正常工作。\n### cdn形式使用插件\n* 将插件的`dist`文件夹复制到你的项目中\n* 使用`script`标签引入dist目录下的`screenShotPlugin.umd.js`文件\n```javascript\n\u003cscript src=\"./screenShotPlugin.umd.js\"\u003e\u003c/script\u003e\n```\n* 在业务代码中使用时实例化插件即可\n```javascript\n    // 截图确认按钮回调函数\n    const callback = ({base64, cutInfo})=\u003e{\n      console.log(base64, cutInfo);\n    }\n    // 截图取消时的回调函数\n    const closeFn = ()=\u003e{\n      console.log(\"截图窗口关闭\");\n    }\n    new screenShotPlugin({enableWebRtc: true, completeCallback: callback,closeCallback: closeFn});\n```\n\u003e ⚠️注意：实例化插件时一定要等dom加载完成，否则插件无法正常工作。\n\n### electron环境下使用插件\n由于electron环境下无法直接调用webrtc来获取屏幕流，因此需要调用者自己稍作处理，具体做法如下所示：\n* 直接获取设备的窗口，主线程发送一个IPC消息handle\n```javascript\n// electron主线程\nimport { desktopCapturer, webContents } from \"electron\";\n\n// 修复electron18.0.0-beta.5 之后版本的BUG: 无法获取当前程序页面视频流\nconst selfWindws = async () =\u003e\n        await Promise.all(\n                webContents\n                        .getAllWebContents()\n                        .filter(item =\u003e {\n                          const win = BrowserWindow.fromWebContents(item);\n                          return win \u0026\u0026 win.isVisible();\n                        })\n                        .map(async item =\u003e {\n                          const win = BrowserWindow.fromWebContents(item);\n                          const thumbnail = await win?.capturePage();\n                          // 当程序窗口打开DevTool的时候  也会计入\n                          return {\n                            name:\n                                    win?.getTitle() + (item.devToolsWebContents === null ? \"\" : \"-dev\"), // 给dev窗口加上后缀\n                            id: win?.getMediaSourceId(),\n                            thumbnail,\n                            display_id: \"\",\n                            appIcon: null\n                          };\n                        })\n        );\n\n// 获取设备窗口信息\nipcMain.handle(\"IPC消息名称\", async (_event, _args) =\u003e {\n  return [\n    ...(await desktopCapturer.getSources({ types: [\"window\", \"screen\"] })),\n    ...(await selfWindws())\n  ];\n});\n```\n\n* 渲染线程(前端)发送消息封装处理(相应写法自己调整)\n```typescript\n// xxx.ts\nexport const getDesktopCapturerSource = async () =\u003e {\n  return await window.electron.ipcRenderer.invoke\u003cElectron.DesktopCapturerSource[]\u003e(\"IPC消息名称\", []);\n}\n```\n\n* 获取指定窗口的媒体流\n```typescript\n// yyy.ts\nexport function getInitStream(source: any): Promise\u003cMediaStream | null\u003e {\n    return new Promise((resolve, _reject) =\u003e {\n        // 获取指定窗口的媒体流\n        // 此处遵循的是webRTC的接口类型  暂时TS类型没有支持  只能断言成any\n        (navigator.mediaDevices as any).getUserMedia({\n            audio: false,\n            video: {\n                mandatory: {\n                    chromeMediaSource: 'desktop',\n                    chromeMediaSourceId: source.id\n                },\n            }\n        }).then((stream: MediaStream) =\u003e {\n            resolve(stream);\n        }).catch((error: any) =\u003e {\n            console.log(error);\n            resolve(null);\n        })\n    });\n}\n```\n\n* 前端调用设备窗口信息\n```typescript\nimport { getDesktopCapturerSource } from \"xxx.ts\";\nimport { getInitStream } from \"yyy.ts\";\nimport ScreenShot from \"js-web-screen-shot\";\n\nexport const doScreenShot = async ()=\u003e{\n  // 下面这两块自己考虑  \n  const sources = await getDesktopCapturerSource(); // 这里返回的是设备上的所有窗口信息\n  // 这里可以对`sources`数组下面id进行判断  找到当前的electron窗口  这里为了简单直接拿了第一个\n  const stream = await getInitStream(sources[0]);\n\n  new ScreenShot({\n    enableWebRtc: true, // 启用webrtc\n    screenFlow: stream!, // 传入屏幕流数据\n    level: 999,\n  });\n}\n```\n\u003e 感谢 [@Vanisper](https://github.com/Vanisper) 提供的在electron环境下使用本插件的兼容思路。\n\n### electron示例代码\n如果你看完上个章节的使用方法，依然不是很理解的话，这里准备了一份在electron环境下使用本插件的demo，请移步[electron-js-web-screen-shot-demo](https://github.com/Vanisper/electron-js-web-screen-shot-demo)。\n\n\n### 兼容移动端\n插件对触屏设备做了兼容处理，如果你是pc端的触屏设备可以支持webrtc模式，如果是移动端那么就只能使用html2canvas模式。\n```javascript\nimport ScreenShot from \"js-web-screen-shot\";\n\nconst config = {\n    enableWebRtc: false\n};\nconst screenShotHandler = new ScreenShot(config);\n```\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"zh-CN\"\u003e\n\u003chead\u003e\n\u003c!--禁止移动端浏览器的缩放--\u003e\n\u003cmeta name=\"viewport\" content=\"user-scalable=no\"\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n/body\u003e\n\u003c/html\u003e\n```\n\n\u003e 注意：在移动端使用时，需要在head标签里禁止浏览器的缩放行为，否则就会出现在使用撤销功能时，多次双击造成界面放大问题。\n\n\n\n\n### Vue项目下使用乱码问题\n当你vue项目中使用h2c模式进行截图时，画布左上角可能会出现一些奇怪的字符，这是由于`noscript`标签导致的，将其删除即可。\n\n### 参数说明\n截图插件有一个可选参数，它接受一个对象，对象每个key的作用如下:\n* `enableWebRtc` 是否启用webrtc，值为`boolean`类型，值为`false`则使用`html2canvas`来截图\n* `screenFlow` 设备提供的屏幕流数据(用于electron环境下自己传入的视频流数据)，需要将**enableWebRtc**属性设为`true`\n* `completeCallback` 截图完成回调函数，值为`Function`类型，最右侧的对号图标点击后会将图片的base64地址与裁剪信息回传给你定义的函数，如果不传的话则会将这些数据放到`sessionStorage`中，你可以通过下述方式拿到他：\n```javascript\nsessionStorage.getItem(\"screenShotImg\");\n```\n* `closeCallback` 截图关闭回调函数，值为`Function`类型。\n* `triggerCallback` 截图响应回调函数，值为`Function`类型，使用html2canvas截屏时，页面图片过多时响应会较慢；使用webrtc截屏时用户点了分享，该函数为响应完成后触发的事件。回调函数返回一个对象，类型为: `{code: number,msg: string, displaySurface: string | null,displayLabel: string | null}`，code为0时代表截图加载完成，displaySurface返回的的是当前选择的窗口类型，displayLabel返回的是当前选择的标签页标识，浏览器不支持时此值为null。\n* `cancelCallback` 取消分享回到函数，值为`Function`类型，使用webrtc模式截屏时，用户点了取消或者浏览器不支持时所触发的事件。回调函数返回一个对象，类型为：`{code: number,msg: string, errorInfo: string}`，code为-1时代表用户未授权或者浏览器不支持webrtc。\n* `saveCallback` 保存截图回调函数，值为`Function`类型。回调函数中返回两个参数：\n  * `code` 状态码，number类型，为0时代表保存成功\n  * `msg` 消息码，string类型。\n* `level` 截图容器层级，值为number类型。 \n* `cutBoxBdColor` 裁剪区域边框像素点颜色，值为string类型。\n* `maxUndoNum` 最大可撤销次数, 值为number类型\n* `canvasWidth` 画布宽度，值为number类型，必须与高度一起设置，单独设置无效。\n* `canvasHeight` 画布高度，值为number类型，必须与宽度一起设置，单独设置无效。\n* `position` 截图容器位置，值为`{left?: number, top?: number}`类型\n* `clickCutFullScreen` 单击截全屏启用状态,值为`boolean`类型， 默认为`false`\n* `hiddenToolIco` 需要隐藏的截图工具栏图标，值为`Object`类型，默认为`{}`。传你需要隐藏的图标名称，将值设为`true`即可，除关闭图标外，其他图标均可隐藏。可隐藏的key如下所示：\n  * `square` 矩形绘制\n  * `round` 圆形绘制\n  * `rightTop` 箭头绘制\n  * `brush` 涂鸦\n  * `mosaicPen`马赛克工具\n  * `text` 文本工具\n  * `separateLine` 分割线\n  * `save` 下载图片\n  * `undo` 撤销工具\n  * `confirm` 保存图片\n* `showScreenData` 截图组件加载完毕后，是否显示截图内容至canvas画布内，值为`boolean`类型，默认为`false`。\n* `customRightClickEvent` 自定义容器的右键点击事件，值为`Object`类型，接受2个参数：\n  * `state` 是否拦截右键点击，值为boolean类型，默认为`false`。\n  * `handleFn` 拦截后的事件处理函数，该属性为可选项，如果不传，默认行为是销毁组件。\n* `imgSrc` 截图内容，如果你已经通过其他方式获取到了屏幕内容（例如`electron`环境），那么可以将获取到的内容传入，此时插件将使用你传进来的图片，值为`string`类型（可以为图片`url`地址或者`base64`），默认为`null`。\n* `loadCrossImg` 是否加载跨域图片，值为`boolean`类型，默认为`false`。\n* `proxyUrl` 代理服务器地址，值为`string`类型，默认为\"\"\n* `screenShotDom` 需要进行截图的容器，值为`HTMLElement`类型，默认使用的是`body`。\n* `useRatioArrow` 是否使用等比例箭头, 默认为false(递增变粗的箭头)。\n* `imgAutoFit` 是否开启图片自适应, 默认为false。如果自定义了截图内容，浏览器的缩放比例不为100%时，可以设置此参数来修复图片与蒙板大小不一致的问题。\n* `cropBoxInfo` 初始裁剪框，值为`{ x: number; y: number; w: number; h: number }`类型，默认不加载。\n* `wrcReplyTime` webrtc模式捕捉屏幕时的响应时间，值为`number`类型，默认为500ms。\n* `wrcImgPosition` webrtc模式下是否需要对图像进行裁剪，值为`{ x: number; y: number; w: number; h: number }`类型，默认为不裁剪。\n* `noScroll` 截图容器是否可滚动，值为`boolean`类型，默认为`true`。\n* `maskColor` 蒙层颜色，值为`{ r: number; g: number; b: number; a: number }`类型,默认为:`{ r: 0; g: 0; b: 0; a: 0.6 }`\n* `toolPosition` 工具栏展示位置，值为`string`类型，默认为居中展示，提供三个选项：\n  * `left` 左对齐于裁剪框\n  * `center` 居中对齐于裁剪框\n  * `right` 右对齐于裁剪框\n* `writeBase64` 是否将截图内容写入剪切板，值为`boolean`类型，默认为`true`\n* `wrcWindowMode` 是否启用窗口截图模式，值为`boolean`类型，默认为`false`，即当前标签页截图。如果标签页截图的内容有滚动条或者底部有空缺，可以考虑启用此模式。\n* `hiddenScrollBar` 是否隐藏滚动条，用webrtc模式截图时chrome 112版本的浏览器在部分系统下会挤压出现滚动条，如果出现你可以尝试通过此参数来进行修复。值为`Object`类型，有4个属性：\n  * `state: boolean`; 启用状态, 默认为`false`\n  * `fillState?: boolean`; 填充状态，默认为`false`\n  * `color?: string`; 填充层颜色，滚动条隐藏后可能会出现空缺，需要进行填充，默认填充色为黑色。\n  * `fillWidth?: number`; 填充层宽度，默认为截图容器的宽度\n  * `fillHeight?: number`; 填充层高度，默认为空缺区域的高度\n\n\u003e 使用当前标签页进行截图相对而言用户体验是最好的，但是因为`chrome 112`版本的bug会造成页面内容挤压导致截取到的内容不完整，因此只能采用其他方案来解决此问题了。`wrcWindowMode`和`hiddenScrollBar`都可以解决这个问题。\n\u003e * `wrcWindowMode`方案会更完美些，但是用户授权时会出现其他的应用程序选项，用户体验会差一些\n\u003e * `hiddenScrollBar`方案还是采用标签页截图，但是会造成内容挤压，底部出现空白。\n\u003e\n\u003e 两种方案的优点与缺点讲完了，最好的办法还是希望`chrome`能在之后的版本更新中修复此问题。\n\n\n\u003e 上述类型中的`?:`为ts中的可选类型，意思为：这个key是可选的，如果需要就传，不需要就不传。\n\n\u003e imgSrc是url时，如果图片资源跨域了，必须让图片服务器允许跨域才能正常加载。同样的loadCrossImg设置为true时，图片资源跨域了也需要让图片服务器允许跨域。\n\n### 快捷键监听\n插件容器监听了三个快捷键，如下所示：\n* `Esc`，按下键盘上的esc键时，等同于点了工具栏的关闭图标。\n* `Enter`，按下键盘上的enter键时，等同于点了截图工具栏的确认图标。\n* `Ctrl/Command + z`，按下这两个组合键时，等同于点了截图工具栏的撤销图标。\n\n\n### 额外提供的API\n插件暴露了一些内部变量出来，便于调用者根据自己的需求进行修改。\n\n#### getCanvasController\n该函数用于获取截图容器的DOM，返回值为`HTMLCanvasElement`类型。\n\n示例代码：\n\n```javascript\nimport ScreenShot from \"js-web-screen-shot\";\n\nconst screenShotHandler = new ScreenShot();\nconst canvasDom = screenShotHandler.getCanvasController();\n```\n\u003e 注意：如果截图容器尚未加载完毕，获取到的内容可能为null。\n\n#### destroyComponents\n该函数用于销毁截图容器，无返回值。\n\n示例代码：\n\n```javascript\nimport ScreenShot from \"js-web-screen-shot\";\n\nconst screenShotHandler = new ScreenShot();\nscreenShotHandler.destroyComponents()\n```\n#### completeScreenshot\n该函数用于将框选区域的截图内容写入剪切版，无返回值。\n\n该方法可以跟`cropBoxInfo`参数结合起来实现指定位置的自动截图，截图内容默认写入剪切版内，如果你想拿到截取到的base64内容可以通过`completeCallback`参数拿到，或者直接从sessionStorage中获取。\n\n该回调函数中返回的参数格式如下所示：\n* base64\n* cutInfo 裁剪框位置参数\n  * startX\n  * startY\n  * width\n  * height\n\n示例代码：\n```javascript\n      const plugin = new screenShotPlugin(\n        {\n          clickCutFullScreen:true,\n          wrcWindowMode: true,\n          cropBoxInfo:{x:350, y:20, w:300, h:300},\n          completeCallback: ({base64, cutInfo}) =\u003e {\n            console.log(base64, cutInfo);\n          },\n          triggerCallback:() =\u003e {\n            // 截图组件加载完毕调用此方法来完成框选区域的截图\n            plugin.completeScreenshot()\n          }\n        });\n```\n\u003e 注意：此方法在1.9.9版本之后不再返回字符串类型的数据，而是返回的对象格式。\n\n\n### 工具栏图标定制\n如果你需要修改截图工具栏的图标，可以通过覆盖元素css类名的方式实现，插件内所有图标的css类名如下所示：\n* square 矩形绘制图标\n* round 圆型绘制图标\n* right-top 箭头绘制图标\n* brush 画笔工具\n* mosaicPen 马赛克工具\n* text 文本工具\n* save 保存\n* close 关闭\n* undo 撤销\n* confirm 确认\n\n以`square`为例，要修改它的图标，只需要将下述代码添加进你项目代码的样式中即可。\n```scss\n  .square {\n    background-image: url(\"你的图标路径\") !important;\n    \n    \u0026:hover {\n      background-image: url(\"你的图标路径\") !important;\n    }\n    \n    \u0026:active {\n      background-image: url(\"你的图标路径\") !important;\n    }\n }\n```\n\n\n## 写在最后\n至此，插件的所有使用方法就介绍完了，该插件的Vue3版本，请移步：[vue-web-screen-shot](https://www.npmjs.com/package/vue-web-screen-shot)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flikaia%2Fjs-screen-shot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flikaia%2Fjs-screen-shot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flikaia%2Fjs-screen-shot/lists"}