產(chǎn)品要求添加的功能,本來功能是用戶上傳圖片圈驼,添加新需求是可以點擊button截屏然后編輯(隱藏敏感信息,高亮和裁剪)望几。參考的是google的截屏. gif沒顯示的是選擇截屏的選項, 已添加截圖
中間遇到了很多問題绩脆,記錄下。
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只截取當前網(wǎng)頁。需要引入第三方庫楼誓。上代碼
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等主守,現(xiàn)代瀏覽器也基本支持。所以選擇了這個榄融。代碼
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黑色涎才,
遇到的問題
畫布大小
開始畫的時候發(fā)現(xiàn)畫的路徑和鼠標的路徑不一樣,這里就有個canvas的clientWidth/clientHeight 和width/height
canvas顯示的大小和畫布實際大小不一樣力九,所以一定要記得設(shè)置畫布大小耍铜,如果兩個尺寸不一樣的話在畫的時候還要進行坐標轉(zhuǎn)換
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的實現(xiàn)
highlight是要把圖片加一個遮罩層然后高亮部分不加。這塊邏輯包括遮罩還得是canvas跌前。
canvas畫一個和畫布大小一樣的矩形棕兼, stroke圖片的遮罩顏色,需要高亮的部分clear一下就實現(xiàn)了舒萎,遮罩加個flag程储,如果有了就不再重復加了
清除path
畫圖開始記錄開始坐標,在畫圖的過程中怎么清楚上一步的路徑也是問題臂寝,因為我們在畫的過程中要實時顯示當前選中的區(qū)域章鲤,也就是在這個過程中一直在畫
想了一個比較好現(xiàn)實也比較切合實際的辦法就是在當前畫布層之上再加一個畫布層,每次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;
? ? }
保存
保存的時候在創(chuàng)建一個畫布將圖片和畫布的路徑層畫到上面然后保存就可以了
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())
? ? }
效果