溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么實(shí)現(xiàn)Web端自定義截屏

發(fā)布時(shí)間:2021-10-19 09:47:24 來源:億速云 閱讀:117 作者:iii 欄目:web開發(fā)

這篇文章主要講解了“怎么實(shí)現(xiàn)Web端自定義截屏”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么實(shí)現(xiàn)Web端自定義截屏”吧!

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

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

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

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

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

{ - "vue": "^3.0.0-0", - "vue-class-component": "^8.0.0-0" }

創(chuàng)建DOM

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

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

import toolbar from "@/lib/config/Toolbar"; import { toolbarType } from "@/lib/type/ComponentType"; import { toolClickEvent } from "@/lib/split-methods/ToolClickEvent"; import { setBrushSize } from "@/lib/common-methords/SetBrushSize"; import { selectColor } from "@/lib/common-methords/SelectColor"; import { getColor } from "@/lib/common-methords/GetColor";  export default class CreateDom {   // 截圖區(qū)域canvas容器   private readonly screenShortController: HTMLCanvasElement;   // 截圖工具欄容器   private readonly toolController: HTMLDivElement;   // 繪制選項(xiàng)頂部ico容器   private readonly optionIcoController: HTMLDivElement;   // 畫筆繪制選項(xiàng)容器   private readonly optionController: HTMLDivElement;   // 文字工具輸入容器   private readonly textInputController: HTMLDivElement;    // 截圖工具欄圖標(biāo)   private readonly toolbar: Array<toolbarType>;        constructor() {     this.screenShortController = document.createElement("canvas");     this.toolController = document.createElement("div");     this.optionIcoController = document.createElement("div");     this.optionController = document.createElement("div");     this.textInputController = document.createElement("div");     // 為所有dom設(shè)置id     this.setAllControllerId();     // 為畫筆繪制選項(xiàng)角標(biāo)設(shè)置class     this.setOptionIcoClassName();     this.toolbar = toolbar;     // 渲染工具欄     this.setToolBarIco();     // 渲染畫筆相關(guān)選項(xiàng)     this.setBrushSelectPanel();     // 渲染文本輸入     this.setTextInputPanel();     // 渲染頁(yè)面     this.setDomToBody();     // 隱藏所有dom     this.hiddenAllDom();   }      /** 其他代碼省略 **/    }

插件入口文件

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

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

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

import CreateDom from "@/lib/main-entrance/CreateDom"; // 導(dǎo)入截圖所需樣式 import "@/assets/scss/screen-short.scss"; import InitData from "@/lib/main-entrance/InitData"; import {   cutOutBoxBorder,   drawCutOutBoxReturnType,   movePositionType,   positionInfoType,   zoomCutOutBoxReturnType } from "@/lib/type/ComponentType"; import { drawMasking } from "@/lib/split-methods/DrawMasking"; import { fixedData, nonNegativeData } from "@/lib/common-methords/FixedData"; import { drawPencil, initPencil } from "@/lib/split-methods/DrawPencil"; import { drawText } from "@/lib/split-methods/DrawText"; import { drawRectangle } from "@/lib/split-methods/DrawRectangle"; import { drawCircle } from "@/lib/split-methods/DrawCircle"; import { drawLineArrow } from "@/lib/split-methods/DrawLineArrow"; import { drawMosaic } from "@/lib/split-methods/DrawMosaic"; import { drawCutOutBox } from "@/lib/split-methods/DrawCutOutBox"; import { zoomCutOutBoxPosition } from "@/lib/common-methords/ZoomCutOutBoxPosition"; import { saveBorderArrInfo } from "@/lib/common-methords/SaveBorderArrInfo"; import { calculateToolLocation } from "@/lib/split-methods/CalculateToolLocation";  export default class ScreenShort {   // 當(dāng)前實(shí)例的響應(yīng)式data數(shù)據(jù)   private readonly data: InitData;    // video容器用于存放屏幕MediaStream流   private readonly videoController: HTMLVideoElement;   // 截圖區(qū)域canvas容器   private readonly screenShortController: HTMLCanvasElement | null;   // 截圖工具欄dom   private readonly toolController: HTMLDivElement | null;   // 截圖圖片存放容器   private readonly screenShortImageController: HTMLCanvasElement;   // 截圖區(qū)域畫布   private screenShortCanvas: CanvasRenderingContext2D | undefined;   // 文本區(qū)域dom   private readonly textInputController: HTMLDivElement | null;   //  截圖工具欄畫筆選項(xiàng)dom   private optionController: HTMLDivElement | null;   private optionIcoController: HTMLDivElement | null;   // 圖形位置參數(shù)   private drawGraphPosition: positionInfoType = {     startX: 0,     startY: 0,     width: 0,     height: 0   };   // 臨時(shí)圖形位置參數(shù)   private tempGraphPosition: positionInfoType = {     startX: 0,     startY: 0,     width: 0,     height: 0   };   // 裁剪框邊框節(jié)點(diǎn)坐標(biāo)事件   private cutOutBoxBorderArr: Array<cutOutBoxBorder> = [];   // 當(dāng)前操作的邊框節(jié)點(diǎn)   private borderOption: number | null = null;    // 點(diǎn)擊裁剪框時(shí)的鼠標(biāo)坐標(biāo)   private movePosition: movePositionType = {     moveStartX: 0,     moveStartY: 0   };    // 鼠標(biāo)點(diǎn)擊狀態(tài)   private clickFlag = false;   private fontSize = 17;   // 最大可撤銷次數(shù)   private maxUndoNum = 15;   // 馬賽克涂抹區(qū)域大小   private degreeOfBlur = 5;    // 文本輸入框位置   private textInputPosition: { mouseX: number; mouseY: number } = {     mouseX: 0,     mouseY: 0   };   constructor() {     // 創(chuàng)建dom     new CreateDom();     this.videoController = document.createElement("video");     this.videoController.autoplay = true;     this.screenShortImageController = document.createElement("canvas");     // 實(shí)例化響應(yīng)式data     this.data = new InitData();     // 獲取截圖區(qū)域canvas容器     this.screenShortController = this.data.getScreenShortController() as HTMLCanvasElement | null;     this.toolController = this.data.getToolController() as HTMLDivElement | null;     this.textInputController = this.data.getTextInputController() as HTMLDivElement | null;     this.optionController = this.data.getOptionController() as HTMLDivElement | null;     this.optionIcoController = this.data.getOptionIcoController() as HTMLDivElement | null;     this.load();   }      /** 其他代碼省略 **/ }

對(duì)外暴露default屬性

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

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

module.exports = {     // 自定義webpack配置   configureWebpack: {     output: {       // 對(duì)外暴露default屬性       libraryExport: "default"     }   } }

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

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

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

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

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

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

  • 使用getDisplayMedia來捕獲屏幕,得到MediaStream流

  • 將得到的MediaStream流輸出到video標(biāo)簽中

  • 使用canvas將video標(biāo)簽中的內(nèi)容繪制到canvas容器中

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

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

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

// 加載截圖組件   private load() {     // 設(shè)置截圖區(qū)域canvas寬高     this.data.setScreenShortInfo(window.innerWidth, window.innerHeight);     // 設(shè)置截圖圖片存放容器寬高     this.screenShortImageController.width = window.innerWidth;     this.screenShortImageController.height = window.innerHeight;     // 顯示截圖區(qū)域容器     this.data.showScreenShortPanel();     // 截取整個(gè)屏幕     this.screenShot();   }    // 開始捕捉屏幕   private startCapture = async () => {     let captureStream = null;      try {       // eslint-disable-next-line @typescript-eslint/ban-ts-ignore       // @ts-ignore       // 捕獲屏幕       captureStream = await navigator.mediaDevices.getDisplayMedia();       // 將MediaStream輸出至video標(biāo)簽       this.videoController.srcObject = captureStream;     } catch (err) {       throw "瀏覽器不支持webrtc" + err;     }     return captureStream;   };    // 停止捕捉屏幕   private stopCapture = () => {     const srcObject = this.videoController.srcObject;     if (srcObject && "getTracks" in srcObject) {       const tracks = srcObject.getTracks();       tracks.forEach(track => track.stop());       this.videoController.srcObject = null;     }   };    // 截屏   private screenShot = () => {     // 開始捕捉屏幕     this.startCapture().then(() => {       setTimeout(() => {         // 獲取截圖區(qū)域canvas容器畫布         const context = this.screenShortController?.getContext("2d");         if (context == null || this.screenShortController == null) return;          // 賦值截圖區(qū)域canvas畫布         this.screenShortCanvas = context;         // 繪制蒙層         drawMasking(context);         // 將獲取到的屏幕截圖繪制到圖片容器里         this.screenShortImageController           .getContext("2d")           ?.drawImage(             this.videoController,             0,             0,             this.screenShortImageController?.width,             this.screenShortImageController?.height           );         // 添加監(jiān)聽         this.screenShortController?.addEventListener(           "mousedown",           this.mouseDownEvent         );         this.screenShortController?.addEventListener(           "mousemove",           this.mouseMoveEvent         );         this.screenShortController?.addEventListener(           "mouseup",           this.mouseUpEvent         );         // 停止捕捉屏幕         this.stopCapture();       }, 300);     });   };

感謝各位的閱讀,以上就是“怎么實(shí)現(xiàn)Web端自定義截屏”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)怎么實(shí)現(xiàn)Web端自定義截屏這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI