自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

實(shí)現(xiàn)Web端自定義截屏(原生JS版)

開(kāi)發(fā) 前端
前幾天我發(fā)布了一個(gè)web端自定義截圖的插件,在使用過(guò)程中有開(kāi)發(fā)者反饋這個(gè)插件無(wú)法在vue2項(xiàng)目中使用,于是,我就開(kāi)始找問(wèn)題,發(fā)現(xiàn)我的插件是基于Vue3的開(kāi)發(fā)的,由于Vue3的插件和Vue2的插件完全不兼容,因此插件也就只能在Vue3項(xiàng)目中使用。

[[381668]]

本文轉(zhuǎn)載自微信公眾號(hào)「神奇的程序員K」,作者神奇的程序員K。轉(zhuǎn)載本文請(qǐng)聯(lián)系神奇的程序員K公眾號(hào)。

前言

前幾天我發(fā)布了一個(gè)web端自定義截圖的插件,在使用過(guò)程中有開(kāi)發(fā)者反饋這個(gè)插件無(wú)法在vue2項(xiàng)目中使用,于是,我就開(kāi)始找問(wèn)題,發(fā)現(xiàn)我的插件是基于Vue3的開(kāi)發(fā)的,由于Vue3的插件和Vue2的插件完全不兼容,因此插件也就只能在Vue3項(xiàng)目中使用。

經(jīng)過(guò)一番考慮后,我決定用原生js來(lái)重構(gòu)這個(gè)插件,讓其不依賴(lài)任何庫(kù),這樣它就能運(yùn)行在任意一臺(tái)支持js的設(shè)備上,本文就跟大家分享下我重構(gòu)這個(gè)插件的過(guò)程,歡迎各位感興趣的開(kāi)發(fā)者閱讀本文。

運(yùn)行結(jié)果視頻:(請(qǐng)看原文)

使用Vue實(shí)現(xiàn)Web端的自定義截屏,效果如視頻所示,文章,教程,體驗(yàn)地址明天和大家分享[壞笑] #Vue #截屏 #自定義截屏 #Web前端

寫(xiě)在前面

本文不講解插件的具體實(shí)現(xiàn)思路,對(duì)插件實(shí)現(xiàn)思路感興趣的開(kāi)發(fā)者請(qǐng)移步:實(shí)現(xiàn)Web端自定義截屏

搭建開(kāi)發(fā)環(huán)境

我想使用ts、scss、eslint、prettier來(lái)提升插件的可維護(hù)性,又嫌麻煩,不想手動(dòng)配置webpack環(huán)境,于是我決定使用Vue CLI來(lái)搭建插件開(kāi)發(fā)環(huán)境。

本文不細(xì)講Vue CLI搭建插件開(kāi)發(fā)環(huán)境的過(guò)程,對(duì)此感興趣的開(kāi)發(fā)者請(qǐng)移步:使用CLI開(kāi)發(fā)一個(gè)Vue3的npm庫(kù)。

移除vue相關(guān)依賴(lài)

我們搭建好插件的開(kāi)發(fā)環(huán)境后,CLI默認(rèn)會(huì)在package.json中添加Vue的相關(guān)包,我們的插件不會(huì)依賴(lài)于vue,因此我們把它刪除即可。

  1. "vue""^3.0.0-0"
  2. "vue-class-component""^8.0.0-0" 

創(chuàng)建DOM

為了方便開(kāi)發(fā)者使用dom,這里選擇使用js動(dòng)態(tài)來(lái)創(chuàng)建dom,最后將其掛載到body中,在vue3版本的截圖插件中,我們可以使用vue組件來(lái)輔助我們,這里我們就要基于組件來(lái)使用js來(lái)創(chuàng)建對(duì)應(yīng)的dom,為其綁定對(duì)應(yīng)的事件。

部分實(shí)現(xiàn)代碼如下,完整代碼請(qǐng)移步:CreateDom.ts

  1. import toolbar from "@/lib/config/Toolbar"
  2. import { toolbarType } from "@/lib/type/ComponentType"
  3. import { toolClickEvent } from "@/lib/split-methods/ToolClickEvent"
  4. import { setBrushSize } from "@/lib/common-methords/SetBrushSize"
  5. import { selectColor } from "@/lib/common-methords/SelectColor"
  6. import { getColor } from "@/lib/common-methords/GetColor"
  7.  
  8. export default class CreateDom { 
  9.   // 截圖區(qū)域canvas容器 
  10.   private readonly screenShortController: HTMLCanvasElement; 
  11.   // 截圖工具欄容器 
  12.   private readonly toolController: HTMLDivElement; 
  13.   // 繪制選項(xiàng)頂部ico容器 
  14.   private readonly optionIcoController: HTMLDivElement; 
  15.   // 畫(huà)筆繪制選項(xiàng)容器 
  16.   private readonly optionController: HTMLDivElement; 
  17.   // 文字工具輸入容器 
  18.   private readonly textInputController: HTMLDivElement; 
  19.  
  20.   // 截圖工具欄圖標(biāo) 
  21.   private readonly toolbar: Array<toolbarType>; 
  22.    
  23.     constructor() { 
  24.     this.screenShortController = document.createElement("canvas"); 
  25.     this.toolController = document.createElement("div"); 
  26.     this.optionIcoController = document.createElement("div"); 
  27.     this.optionController = document.createElement("div"); 
  28.     this.textInputController = document.createElement("div"); 
  29.     // 為所有dom設(shè)置id 
  30.     this.setAllControllerId(); 
  31.     // 為畫(huà)筆繪制選項(xiàng)角標(biāo)設(shè)置class 
  32.     this.setOptionIcoClassName(); 
  33.     this.toolbar = toolbar; 
  34.     // 渲染工具欄 
  35.     this.setToolBarIco(); 
  36.     // 渲染畫(huà)筆相關(guān)選項(xiàng) 
  37.     this.setBrushSelectPanel(); 
  38.     // 渲染文本輸入 
  39.     this.setTextInputPanel(); 
  40.     // 渲染頁(yè)面 
  41.     this.setDomToBody(); 
  42.     // 隱藏所有dom 
  43.     this.hiddenAllDom(); 
  44.   } 
  45.    
  46.   /** 其他代碼省略 **/ 
  47.    

插件入口文件

在開(kāi)發(fā)vue插件時(shí)我們需要暴露一個(gè)install方法,由于此處我們不需要依賴(lài)vue,我們就無(wú)需暴露install方法,我的預(yù)想效果是:用戶(hù)在使用我插件時(shí),直接實(shí)例化插件就能正常運(yùn)行。

因此,我們默認(rèn)暴露出一個(gè)class,無(wú)論是使用script標(biāo)簽引入插件,還是在其他js框架里使用import來(lái)引入插件,都只需要在使用時(shí)new一下即可。

部分代碼如下,完整代碼請(qǐng)移步:main.ts

  1. import CreateDom from "@/lib/main-entrance/CreateDom"
  2. // 導(dǎo)入截圖所需樣式 
  3. import "@/assets/scss/screen-short.scss"
  4. import InitData from "@/lib/main-entrance/InitData"
  5. import { 
  6.   cutOutBoxBorder, 
  7.   drawCutOutBoxReturnType, 
  8.   movePositionType, 
  9.   positionInfoType, 
  10.   zoomCutOutBoxReturnType 
  11. from "@/lib/type/ComponentType"
  12. import { drawMasking } from "@/lib/split-methods/DrawMasking"
  13. import { fixedData, nonNegativeData } from "@/lib/common-methords/FixedData"
  14. import { drawPencil, initPencil } from "@/lib/split-methods/DrawPencil"
  15. import { drawText } from "@/lib/split-methods/DrawText"
  16. import { drawRectangle } from "@/lib/split-methods/DrawRectangle"
  17. import { drawCircle } from "@/lib/split-methods/DrawCircle"
  18. import { drawLineArrow } from "@/lib/split-methods/DrawLineArrow"
  19. import { drawMosaic } from "@/lib/split-methods/DrawMosaic"
  20. import { drawCutOutBox } from "@/lib/split-methods/DrawCutOutBox"
  21. import { zoomCutOutBoxPosition } from "@/lib/common-methords/ZoomCutOutBoxPosition"
  22. import { saveBorderArrInfo } from "@/lib/common-methords/SaveBorderArrInfo"
  23. import { calculateToolLocation } from "@/lib/split-methods/CalculateToolLocation"
  24.  
  25. export default class ScreenShort { 
  26.   // 當(dāng)前實(shí)例的響應(yīng)式data數(shù)據(jù) 
  27.   private readonly data: InitData; 
  28.  
  29.   // video容器用于存放屏幕MediaStream流 
  30.   private readonly videoController: HTMLVideoElement; 
  31.   // 截圖區(qū)域canvas容器 
  32.   private readonly screenShortController: HTMLCanvasElement | null
  33.   // 截圖工具欄dom 
  34.   private readonly toolController: HTMLDivElement | null
  35.   // 截圖圖片存放容器 
  36.   private readonly screenShortImageController: HTMLCanvasElement; 
  37.   // 截圖區(qū)域畫(huà)布 
  38.   private screenShortCanvas: CanvasRenderingContext2D | undefined; 
  39.   // 文本區(qū)域dom 
  40.   private readonly textInputController: HTMLDivElement | null
  41.   //  截圖工具欄畫(huà)筆選項(xiàng)dom 
  42.   private optionController: HTMLDivElement | null
  43.   private optionIcoController: HTMLDivElement | null
  44.   // 圖形位置參數(shù) 
  45.   private drawGraphPosition: positionInfoType = { 
  46.     startX: 0, 
  47.     startY: 0, 
  48.     width: 0, 
  49.     height: 0 
  50.   }; 
  51.   // 臨時(shí)圖形位置參數(shù) 
  52.   private tempGraphPosition: positionInfoType = { 
  53.     startX: 0, 
  54.     startY: 0, 
  55.     width: 0, 
  56.     height: 0 
  57.   }; 
  58.   // 裁剪框邊框節(jié)點(diǎn)坐標(biāo)事件 
  59.   private cutOutBoxBorderArr: Array<cutOutBoxBorder> = []; 
  60.   // 當(dāng)前操作的邊框節(jié)點(diǎn) 
  61.   private borderOption: number | null = null
  62.  
  63.   // 點(diǎn)擊裁剪框時(shí)的鼠標(biāo)坐標(biāo) 
  64.   private movePosition: movePositionType = { 
  65.     moveStartX: 0, 
  66.     moveStartY: 0 
  67.   }; 
  68.  
  69.   // 鼠標(biāo)點(diǎn)擊狀態(tài) 
  70.   private clickFlag = false
  71.   private fontSize = 17; 
  72.   // 最大可撤銷(xiāo)次數(shù) 
  73.   private maxUndoNum = 15; 
  74.   // 馬賽克涂抹區(qū)域大小 
  75.   private degreeOfBlur = 5; 
  76.  
  77.   // 文本輸入框位置 
  78.   private textInputPosition: { mouseX: number; mouseY: number } = { 
  79.     mouseX: 0, 
  80.     mouseY: 0 
  81.   }; 
  82.   constructor() { 
  83.     // 創(chuàng)建dom 
  84.     new CreateDom(); 
  85.     this.videoController = document.createElement("video"); 
  86.     this.videoController.autoplay = true
  87.     this.screenShortImageController = document.createElement("canvas"); 
  88.     // 實(shí)例化響應(yīng)式data 
  89.     this.data = new InitData(); 
  90.     // 獲取截圖區(qū)域canvas容器 
  91.     this.screenShortController = this.data.getScreenShortController() as HTMLCanvasElement | null
  92.     this.toolController = this.data.getToolController() as HTMLDivElement | null
  93.     this.textInputController = this.data.getTextInputController() as HTMLDivElement | null
  94.     this.optionController = this.data.getOptionController() as HTMLDivElement | null
  95.     this.optionIcoController = this.data.getOptionIcoController() as HTMLDivElement | null
  96.     this.load(); 
  97.   } 
  98.    
  99.   /** 其他代碼省略 **/ 

對(duì)外暴露default屬性

做完上述配置后我們的插件開(kāi)發(fā)環(huán)境就搭建好了,我執(zhí)行build命令打包插件后,在vue2項(xiàng)目中使用import形式正常運(yùn)行,在使用script標(biāo)簽時(shí)引入時(shí)卻報(bào)錯(cuò)了,于是我將暴露出來(lái)的screenShotPlugin變量打印出來(lái)后發(fā)現(xiàn)他還有個(gè)default屬性,default屬性才是我們插件暴露出來(lái)的東西。

求助了下我朋友@_Dreams找到了解決方案,需要配置下webpack中的output.libraryExport屬性,我們的插件是使用Vue CLI開(kāi)發(fā)的,有關(guān)webpack的配置需要在需要在vue.config.js中進(jìn)行配置,代碼如下:

  1. module.exports = { 
  2.     // 自定義webpack配置 
  3.   configureWebpack: { 
  4.     output: { 
  5.       // 對(duì)外暴露default屬性 
  6.       libraryExport: "default" 
  7.     } 
  8.   } 

這一塊的配置在Vue CLI文檔中也有被提到,感興趣的開(kāi)發(fā)者請(qǐng)移步:build-targets.html#vue-vs-js-ts-entry-files

使用webrtc截取整個(gè)屏幕

插件一開(kāi)始使用的是html2canvas來(lái)將dom轉(zhuǎn)換為canvas的,因?yàn)樗闅v整個(gè)body中的dom,然后再轉(zhuǎn)換成canvas,而且圖片還不能跨域,如果頁(yè)面中圖片一多,它會(huì)變得非常慢。

在上一篇文章的評(píng)論區(qū)中有位開(kāi)發(fā)者 @名字什么的都不重要 建議我使用webrtc來(lái)替代html2canvas,于是我就看了下webrtc的相關(guān)文檔,最終實(shí)現(xiàn)了截屏功能,它截取出來(lái)的東西更精確、性能更好,不存在卡頓問(wèn)題也不存在css問(wèn)題,而且它把選擇權(quán)交給了用戶(hù),讓用戶(hù)決定來(lái)共享屏幕的那一部分內(nèi)容。

實(shí)現(xiàn)思路

接下來(lái)就跟大家分享下我的實(shí)現(xiàn)思路:

  • 使用getDisplayMedia來(lái)捕獲屏幕,得到MediaStream流
  • 將得到的MediaStream流輸出到video標(biāo)簽中
  • 使用canvas將video標(biāo)簽中的內(nèi)容繪制到canvas容器中

有關(guān)getDisplayMedia的具體用法,請(qǐng)移步:使用屏幕捕獲API

實(shí)現(xiàn)代碼

接下來(lái),我們來(lái)看下具體的實(shí)現(xiàn)代碼,完整代碼請(qǐng)移步:main.ts

  1. // 加載截圖組件 
  2.   private load() { 
  3.     // 設(shè)置截圖區(qū)域canvas寬高 
  4.     this.data.setScreenShortInfo(window.innerWidth, window.innerHeight); 
  5.     // 設(shè)置截圖圖片存放容器寬高 
  6.     this.screenShortImageController.width = window.innerWidth; 
  7.     this.screenShortImageController.height = window.innerHeight; 
  8.     // 顯示截圖區(qū)域容器 
  9.     this.data.showScreenShortPanel(); 
  10.     // 截取整個(gè)屏幕 
  11.     this.screenShot(); 
  12.   } 
  13.  
  14.   // 開(kāi)始捕捉屏幕 
  15.   private startCapture = async () => { 
  16.     let captureStream = null
  17.  
  18.     try { 
  19.       // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 
  20.       // @ts-ignore 
  21.       // 捕獲屏幕 
  22.       captureStream = await navigator.mediaDevices.getDisplayMedia(); 
  23.       // 將MediaStream輸出至video標(biāo)簽 
  24.       this.videoController.srcObject = captureStream; 
  25.     } catch (err) { 
  26.       throw "瀏覽器不支持webrtc" + err; 
  27.     } 
  28.     return captureStream; 
  29.   }; 
  30.  
  31.   // 停止捕捉屏幕 
  32.   private stopCapture = () => { 
  33.     const srcObject = this.videoController.srcObject; 
  34.     if (srcObject && "getTracks" in srcObject) { 
  35.       const tracks = srcObject.getTracks(); 
  36.       tracks.forEach(track => track.stop()); 
  37.       this.videoController.srcObject = null
  38.     } 
  39.   }; 
  40.  
  41.   // 截屏 
  42.   private screenShot = () => { 
  43.     // 開(kāi)始捕捉屏幕 
  44.     this.startCapture().then(() => { 
  45.       setTimeout(() => { 
  46.         // 獲取截圖區(qū)域canvas容器畫(huà)布 
  47.         const context = this.screenShortController?.getContext("2d"); 
  48.         if (context == null || this.screenShortController == nullreturn
  49.  
  50.         // 賦值截圖區(qū)域canvas畫(huà)布 
  51.         this.screenShortCanvas = context; 
  52.         // 繪制蒙層 
  53.         drawMasking(context); 
  54.         // 將獲取到的屏幕截圖繪制到圖片容器里 
  55.         this.screenShortImageController 
  56.           .getContext("2d"
  57.           ?.drawImage( 
  58.             this.videoController, 
  59.             0, 
  60.             0, 
  61.             this.screenShortImageController?.width, 
  62.             this.screenShortImageController?.height 
  63.           ); 
  64.         // 添加監(jiān)聽(tīng) 
  65.         this.screenShortController?.addEventListener( 
  66.           "mousedown"
  67.           this.mouseDownEvent 
  68.         ); 
  69.         this.screenShortController?.addEventListener( 
  70.           "mousemove"
  71.           this.mouseMoveEvent 
  72.         ); 
  73.         this.screenShortController?.addEventListener( 
  74.           "mouseup"
  75.           this.mouseUpEvent 
  76.         ); 
  77.         // 停止捕捉屏幕 
  78.         this.stopCapture(); 
  79.       }, 300); 
  80.     }); 
  81.   }; 

插件地址

至此,插件的實(shí)現(xiàn)過(guò)程就分享完畢了。

  • 插件在線體驗(yàn)地址:chat-system
  • 插件GitHub倉(cāng)庫(kù)地址:screen-shot
  • 開(kāi)源項(xiàng)目地址:chat-system-github

 

責(zé)任編輯:武曉燕 來(lái)源: 神奇的程序員k
相關(guān)推薦

2023-03-26 08:41:37

2021-11-04 09:37:31

Android截屏實(shí)現(xiàn)方式監(jiān)聽(tīng)截屏

2021-09-15 10:19:15

鴻蒙HarmonyOS應(yīng)用

2009-09-07 22:00:15

LINQ自定義

2022-02-21 15:16:30

HarmonyOS鴻蒙操作系統(tǒng)

2021-11-01 10:21:36

鴻蒙HarmonyOS應(yīng)用

2022-02-16 16:09:12

鴻蒙游戲操作系統(tǒng)

2022-09-09 14:47:50

CircleArkUI

2015-02-12 15:33:43

微信SDK

2022-12-07 08:56:27

SpringMVC核心組件

2013-01-09 17:22:38

Android開(kāi)發(fā)Camera

2022-04-01 15:59:22

SQLPostgreSQL審計(jì)

2009-09-03 13:34:03

.NET自定義控件

2022-03-01 16:09:06

OpenHarmon鴻蒙單選組件

2023-01-03 07:40:27

自定義滑塊組件

2015-07-29 10:31:16

Java緩存算法

2009-06-17 16:00:03

Hibernate自定

2023-10-24 13:48:50

自定義注解舉值驗(yàn)證

2020-12-21 16:35:51

JavaScript網(wǎng)頁(yè)截屏代碼

2022-05-18 07:44:13

自定義菜單前端
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)