JS實現截屏和編輯圖片(隱藏,高亮,裁剪)

產品要求添加的功能,本來功能是用戶上傳圖片,添加新需求是可以點擊button截屏然后編輯(隱藏敏感信息,高亮和裁剪)。參考的是google的截屏. gif沒顯示的是選擇截屏的選項, 已添加截圖


google take screenshot


select what to capture

中間遇到了很多問題,記錄下。

1, 截圖

canvas

第一個問題就是截圖,最開始考慮的是htmlcanvas來截圖

https://hackernoon.com/how-to-take-screenshots-in-the-browser-using-javascript-l92k3xq7

https://www.cnblogs.com/chunying/p/17022025.html

https://blog.csdn.net/m0_56976301/article/details/127776427

但是canvas只截取當前網頁。需要引入第三方庫。上代碼

html2canvas(document.body, {

? ? ? ? ? ? ? ? ignoreElements: (el) => {

? ? ? ? ? ? ? ? ? ? if (el.classList.contains('upload')) {

? ? ? ? ? ? ? ? ? ? ? ? if (el.tagName==="DIV") {

? ? ? ? ? ? ? ? ? ? ? ? ? ? return true;

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }).then(canvas => {

? ? ? ? ? ? ? ? ? ? this.img.nativeElement.setAttribute('src', canvas.toDataURL());

? ? ? ? ? ? });

MediaDevices.getDisplayMedia()

https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getDisplayMedia

這個是web API,不需要引入庫,而且可以選擇當前電腦的屏幕,tab等,現代瀏覽器也基本支持。所以選擇了這個。代碼

async capture() {

? ? ? ? const canvas = document.createElement("canvas");

? ? ? ? const context = canvas.getContext("2d");

? ? ? ? const video: any = document.createElement("video");

? ? ? ? const gdmOptions = {

? ? ? ? ? ? video: true,

? ? ? ? ? ? preferCurrentTab:true

? ? ? ? }

? ? ? ? try {

? ? ? ? ? const mediaDevices : any = navigator.mediaDevices;

? ? ? ? ? const captureStream = await mediaDevices.getDisplayMedia(gdmOptions);

? ? ? ? ? video.srcObject = captureStream;

? ? ? ? ? console.dir(video);

? ? ? ? ? console.dir(captureStream);

? ? ? ? ? await video.play();

? ? ? ? ? canvas.width = video.videoWidth;

? ? ? ? ? canvas.height = video.videoHeight;

? ? ? ? ? context.drawImage(video, 0, 0, canvas.width, canvas.height);

? ? ? ? ? let size = canvas.toDataURL().length*3/4;

? ? ? ? // ? let s = size > 1048576 ? (size/1048576).toFixed(2) + 'MB' : (size/1024).toFixed(1) + 'KB';

? ? ? ? ? this.editImage = true;

? ? ? ? ? setTimeout(() => {

? ? ? ? ? ? // console.log(this.can);

? ? ? ? ? ? this.can.nativeElement.setAttribute('src', canvas.toDataURL());

? ? ? ? ? }, 50);

? ? ? ? ? captureStream.getTracks().forEach(track => track.stop());

? ? ? ? } catch (err) {

? ? ? ? ? console.error("Error: " + err);

? ? ? ? }

? ? ? };

編輯

重要的就是編輯的邏輯

肯定要用canvas畫圖

https://baijiahao.baidu.com/s?id=1733267341500955304&wfr=spider&for=pc

布局

圖片層,然后在圖片層之上畫布層,畫圖在畫布層做,保存在合并

?<div style="display: inline-block; position: relative;">

? ? ? <img id="can" (dragstart)="stopDrag()" #can/>

? ? ? <canvas style="position: absolute; width: 100%; height: 100%; left: 0; top: 0;" id="canvas" #canvas></canvas>

? ? ? <div id="drawState" #drawState style="width: 100%; height: 100%; position: absolute;left: 0;top: 0;" (mousedown)="canvasStart($event)" (mousemove)="canvasDraw($event)" (mouseup)="canvasDone($event)" (mouseleave)="canvasDone($event)"></div>

? ? </div>

hide就是畫矩形然后stroke黑色,

遇到的問題

畫布大小

開始畫的時候發現畫的路徑和鼠標的路徑不一樣,這里就有個canvas的clientWidth/clientHeight 和width/height

canvas顯示的大小和畫布實際大小不一樣,所以一定要記得設置畫布大小,如果兩個尺寸不一樣的話在畫的時候還要進行坐標轉換

InitCanvasInstance() {

? ? ? ? if (!this.ctx) {

? ? ? ? ? ? let canvas: any = this.canvas.nativeElement;

? ? ? ? ? ? let img:any = this.img.nativeElement;

? ? ? ? ? ? this.canvasRatio.width = can.naturalWidth/canvas.clientWidth;

? ? ? ? ? ? this.canvasRatio.height = can.naturalHeight/canvas.clientHeight?

? ? ? ? ? ? canvas.width = can.naturalWidth;

? ? ? ? ? ? canvas.height = can.naturalHeight;

? ? ? ? ? ? this.ctx = canvas.getContext('2d');

? ? ? ? ? ? this.strokeColor = "#0072a3";

? ? ? ? }

highlight的實現

highlight是要把圖片加一個遮罩層然后高亮部分不加。這塊邏輯包括遮罩還得是canvas。

canvas畫一個和畫布大小一樣的矩形, stroke圖片的遮罩顏色,需要高亮的部分clear一下就實現了,遮罩加個flag,如果有了就不再重復加了

清除path

畫圖開始記錄開始坐標,在畫圖的過程中怎么清楚上一步的路徑也是問題,因為我們在畫的過程中要實時顯示當前選中的區域,也就是在這個過程中一直在畫

想了一個比較好現實也比較切合實際的辦法就是在當前畫布層之上再加一個畫布層,每次mousemove的時候都把這個畫布層全部clear,鼠標up的時候把最后這一步的path畫到下面畫布上,這個畫布層clear

start

canvasStart (){? ? ? ? ? ?

? ? ? ? ? this.drawing = true;?

? ? ? ? ? ? let canvas: any = this.canvas.nativeElement;

? ? ? ? ? ? this.startCoordinate = {x: e.offsetX, y: e.offsetY};

? ? ? ? ? ? this.endCoordinate = {x: e.offsetX, y: e.offsetY};

? ? ? ? ? ? let statusCanvas = document.createElement('canvas');

? ? ? ? ? ? statusCanvas.width = canvas.width

? ? ? ? ? ? statusCanvas.height = canvas.width;

? ? ? ? ? ? this.statusCtx = statusCanvas.getContext('2d');

? ? ? ? ? ? this.drawState.nativeElement.appendChild(statusCanvas);

}

drawing

canvasDraw(e: MouseEvent) {

? ? ? ? ? ? let canvas: any = this.canvas.nativeElement;

? ? ? ? ? ? this.statusCtx.beginPath();

? ? ? ? ? ? this.statusCtx.clearRect(0, 0, canvas.width, canvas.height);

? ? ? ? ? ? this.statusCtx.strokeStyle = this.strokeColor;

? ? ? ? ? ? this.statusCtx.strokeRect(this.startCoordinate.x*this.canvasRatio.width, this.startCoordinate.y*this.canvasRatio.height , (e.offsetX-this.startCoordinate.x)*this.canvasRatio.width, (e.offsetY-this.startCoordinate.y)*this.canvasRatio.height);

? ? ? ? ? ? this.statusCtx.closePath();

? ? }

done

canvasDone(e: MouseEvent) {

? ? ? ? ? ? let canvas: any = this.canvas.nativeElement;

? ? ? ? ? ? this.statusCtx.beginPath();

? ? ? ? ? ? this.statusCtx.clearRect(0, 0, canvas.width, canvas.height);

? ? ? ? ? ? this.statusCtx.closePath();

? ? ? ? ? ? this.ctx.beginPath();

? ? ? ? ? ? if (this.type === 'hide') {

? ? ? ? ? ? ? ? this.ctx.fillStyle = this.fillColor;

? ? ? ? ? ? ? ? this.ctx.fillRect(this.startCoordinate.x*this.canvasRatio.width, this.startCoordinate.y*this.canvasRatio.height, (e.offsetX-this.startCoordinate.x)*this.canvasRatio.width, (e.offsetY-this.startCoordinate.y)*this.canvasRatio.height);

? ? ? ? ? ? }

? ? ? ? ? ? if (this.type === 'highlight') {

? ? ? ? ? ? ? ? this.ctx.strokeStyle = this.strokeColor;

? ? ? ? ? ? ? ? this.ctx.clearRect(this.startCoordinate.x*this.canvasRatio.width, this.startCoordinate.y*this.canvasRatio.height, (e.offsetX-this.startCoordinate.x)*this.canvasRatio.width, (e.offsetY-this.startCoordinate.y)*this.canvasRatio.height);

? ? ? ? ? ? ? ? this.ctx.strokeRect(this.startCoordinate.x*this.canvasRatio.width, this.startCoordinate.y*this.canvasRatio.height , (e.offsetX-this.startCoordinate.x)*this.canvasRatio.width, (e.offsetY-this.startCoordinate.y)*this.canvasRatio.height);

? ? ? ? ? ? }

? ? ? ? ? ? this.ctx.closePath();

? ? ? ? ? ? let statusCanvas: HTMLElement = this.drawState.nativeElement;

? ? ? ? ? ? for (let i = statusCanvas.childNodes.length - 1; i >= 0; i -- ) {

? ? ? ? ? ? ? ? statusCanvas.removeChild(statusCanvas.childNodes[i]);

? ? ? ? ? ? }

? ? ? ? this.drawing = false;

? ? }

保存

保存的時候在創建一個畫布將圖片和畫布的路徑層畫到上面然后保存就可以了

saveScreenshot() {

? ? ? ? ? ? let canvas: any = this.canvas.nativeElement;

? ? ? ? ? ? let newCanvas = document.createElement('canvas');

? ? ? ? ? ? let can: any = this.can.nativeElement;

? ? ? ? ? ? newCanvas.width = can.naturalWidth;

? ? ? ? ? ? newCanvas.height = can.naturalHeight;

? ? ? ? ? ? let newCtx = newCanvas.getContext('2d');

? ? ? ? ? ? var image = new Image();

? ? ? ? ? ? image.src = can['src'];

? ? ? ? ? ? newCtx.drawImage(image, 0, 0, newCanvas.width, newCanvas.height);

? ? ? ? ? ? newCtx.drawImage(canvas, 0, 0, newCanvas.width, newCanvas.height);

? ? ? ? ? ? this.clear();? ?// 取消,clear,或者done的時候記得清除畫布就可以了

? ? ? ? ? ? document.getElementById('scr').setAttribute('src', newCanvas.toDataURL())

? ? }

效果


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內容