您好,登錄后才能下訂單哦!
這篇文章主要講解了“怎么實(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)注!
免責(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)容。