This component features include:
Image cropping (drag the cropping box and change the size of the cropping box);
Picture mosaic (draw mosaic, clear mosaic);
Image preview, image restoration (return to original image, return to processed image);
Image upload (get signature, upload image).
2. Core logic2.1 Image cropping
Get the position of the cropping box (rectangle) relative to the canvas (top left) and the height and width of the cropping box. Get (getImageData) the image object (ImageData) at the corresponding position of the canvas. Clear the canvas canvas. Draw the picture object (ImageData) obtained by (putImageData) at the corresponding position of the canvas. Generate preview image.
2.2 Picture Mosaic
Mosaic drawing is to redraw the area centered on the mouse stroke path (brush width) into other colors. The general result is that the surrounding colors will be similar.
Color picking method:
1) For example, if you have the coordinates (x, y) of a point crossed by the mouse, define a rectangle with coordinates of the upper left corner (x, y), 30px wide and 30px high. We divide the width and height of the rectangle by 5 (divided into 5 parts, which can be customized to n parts), so now there are 25 small grids of 6px. Each small grid has a width and height of 6px.
2) Then, we randomly obtain a small grid and obtain (getImageData) the image object (ImageData) of this small grid; then randomly obtain the color color (rgba: ImageD) of a certain pixel (width 1px, height 1px) on this image object ata.data[0], ImageData.data[1], ImageData.data[2], ImageData.data[3]); Finally, we set the color of each pixel of the first 6x6px small grid to color .
3) For the colors of the other 24 small grids, just follow 2 steps.
2.3 Clear mosaic
We need to understand a problem, whether it is drawing a mosaic or clearing a mosaic, the essence is to draw a picture. We drew the mosaic at a certain position. When we clear it, we draw the original image object at the current position again. The effect of cleaning is achieved. Therefore, we need to back up a canvas that is exactly the same as the original image. When clearing, we need to obtain the image at the corresponding position on the backup canvas and draw it to the position of the mosaic.
2.4 Picture preview
Image preview is to obtain the area of the cropping frame and obtain the image objects in the area. Then draw it on the canvas.
2.5 Restore the picture to the original picture
Clear the canvas and draw the original image again
2.6 Restore to the manipulated picture
Preview is to save the canvas image object (ImageData), clear the canvas, and draw the saved image object to the canvas.
2.7 Image upload
Get the (toDataURL) canvas image path and convert the obtained base64 image into a File object. to upload.
3. The complete code is as follows:
<template> <div class=canvas-clip :loading=loading> <div v-show=isDrop class=canvas-mainBox ref=canvas-mainBox id=canvas-mainBox @mousedown.stop=startMove($event) > <div class=canvas-minBox left-up @mousedown.stop=startResize($event,0)></div> <div class=canvas-minBox up @mousedown.stop=startResize($event,1)></div> <div class=canvas-minBox right-up @mousedown.stop=startResize($event,2)></div> <div class=canvas- minBox right @mousedown.stop=startResize($event,3)></div> <div class=canvas-minBox right-down @mousedown.stop=startResize($event,4)></div> <div class=canvas-minBox down @mousedown.stop=startResize($event,5)></div> <div class=canvas-minBox left-down @mousedown.stop=startResize($event,6)></div div> <div class=canvas-minBox left @mousedown.stop=startResize($event,7)></div> </div> <!-- Canvas--> <canvas class=canvas-area ref=canvas id=canvas :width=canvasWidth :height=canvasHeight @mousedown.stop=startMove($event) :class={hoverPaint:isMa,hoverClear:isMaClear} ></canvas> <!-- Backup canvas--> <canvas class=canvas-copy ref=canvasCopy :width=canvasWidth :height=canvasHeight></canvas> <div class=canvas-btns> <button v-if=backBtn @click=clipBack>Back</button> <button :class={active:btnIndex==0} @click= sourceImg>Original image</button> <button :class={active:btnIndex==1} @click=paintRectReady :disabled=isDisabled>Mosaic</button> <button :class={active:btnIndex==2} @click=paintRectClearReady :disabled=isDisabled>Eraser</button> <button :class={active:btnIndex==3 } @click=clipReady :disabled=isDisabled>Crop</button> <button :class={active:btnIndex==4} @click=clipPosition>Preview</button> <button @click=getSignature>Upload</button> <button class=close @click=canvasClose()>x</button> <!-- <div class=paint-size v-if=isMaClear || isMa> <span>Brush Size</span> <input :defaultValue=maSize v-model=maSize max=100 min=1 type=range> <span class=size-num>{{maSize}}</span> </div> --> </div> </div></template><script>import axios from axios;import md5 from js-md5 ;import req from ../../axios/config;export default { props: [imgUrl], data() { return { resizeFX: , movePrev: , canvasWidth: 800, // Canvas width canvasHeight: 600, // Canvas height loading: false, isDrop: false, // Cropping isMa: false, // Mosaic maSize: 30, // Mosaic size isMaClear: false, // Clear mosaic backBtn: false, // Return button isDisabled: false, // Disable button btnIndex: 0, //Current button mouseX:'', // Mouse position mouseY:'', clipEle: , // Cropping box element canvasDataSession: , // Canvas information before preview canvas: , // Canvas ctx: , // Canvas Context canvasCopy: , // copy canvas ctxCopy: , // copy canvas context uploadOption: { // Image upload parameters path: , policy: , signature: , username: } }; }, mounted() { this.clipEle = this.$refs[canvas-mainBox]; this.canvas = this.$refs[canvas]; this.ctx = this.canvas.getContext(2d); this.canvasCopy = this.$refs[canvasCopy]; this.ctxCopy = this.canvasCopy.getContext(2d); this.draw(); }, methods: { // Create an image draw() { var img = new Image(); img.setAttribute('crossOrigin', 'anonymous'); img.onload = () => { this.ctx.drawImage(img, 0, 0, 800, 600); this.ctxCopy.drawImage(img, 0, 0, 800, 600); }; img.src = this.imgUrl + '?time=' + new Date().valueOf(); }, //Preview calculates the position of the cropping box (upper left coordinate) clipPosition() { this.isDisabled = true; this.backBtn = true; this.isMa = false; this.isMaClear = false; this.btnIndex = 4; //Canvas position var canvasPx = this.canvas.offsetLeft, canvasPy = this.canvas.offsetTop; if (this.isDrop) { // Cropping box position var clipPx = this.clipEle.offsetLeft, clipPy = this.clipEle.offsetTop, x = clipPx - canvasPx, y = clipPy - canvasPy, w = this.clipEle.offsetWidth, h = this.clipEle.offsetHeight, // Center the preview image positionX = 400 - this.clipEle.offsetWidth / 2, positionY = 300 - this.clipEle.offsetHeight / 2; } else { // No cropping box, save the complete image var x = 0, y = 0, w = this.canvas.offsetWidth, h = this.canvas.offsetHeight, // Center the preview image positionX = 0, positionY = 0; } var imageData = this.ctx.getImageData(x, y, w, h); this.canvasDataSession = this.ctx.getImageData( 0, 0, this.canvasWidth, this.canvasHeight ); this.ctx.clearRect(0, 0, this. canvasWidth, this.canvasHeight); this.ctx.putImageData(imageData, positionX, positionY); this.clipEle.style.display = none; this.canvasCopy.style.display = none; }, // Return to the pre-preview state clipBack() { this.btnIndex = -1; this.backBtn = false; this.isDisabled = false ; this.isDrop = false; this.ctx.putImageData(this.canvasDataSession, 0, 0); this.canvasCopy.style.display = block; }, // Original image sourceImg() { this.isDisabled = false; this.btnIndex = 0; this.backBtn = false; this.isMa = false; this.isDrop = false; this.isMaClear = false; var img = new Image(); this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); img.setAttribute('crossOrigin', 'anonymous'); img.onload = () => { this.ctx.drawImage(img, 0, 0, this.canvasWidth, this.canvasHeight); }; img.src = this .imgUrl + '?time=' + new Date().valueOf(); this.canvasCopy.style.display = block; }, // Get the signature getSignature() { // Canvas image base64 to File object var dataURL = this.canvas.toDataURL(image/jpg), arr = dataURL.split(,), mime = arr[0].match(/:(. *?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } var obj = new Blob([u8arr], { type: mime }), time = new Date().toGMTString(), formData = new FormData(); formData.append(file, obj); // Get the file suffix var suffix = formData.get(file).type.split(/)[1]; req .get(/carsource-api/upyun/sign, { suffix: suffix }) .then(response => { if (response.data.code === 0) { this.uploadOption.path = response.data.data.path; formData.append(policy, response.data.data.policy); formData.append(authorization, response.data.data.signature); this.updateImg(formData); } }) .catch(function(error) {}); }, // Upload updateImg(formData) { axios({ url : http://v0.api.upyun.com/tmp-img, method: POST, data: formData }).then(response => { if (response.data.code == 200) { this.$message.success(image modified successfully); this.canvasClose(upload, response.data.url.slice(4)); } }); }, // Cropping Box zoom move startResize(e, n) { this.resizeFX = n; $(document).mousemove(this.resizeDiv); document.addEventListener(mouseup, this.stopResize); }, stopResize(e) { $(document).off(mousemove, this.resizeDiv); document.removeEventListener(mouseup, this.stopResize); }, startMove(e) { this.movePrev = [e.pageX, e.pageY]; $(document).mousemove(this.moveDiv); document.addEventListener(mouseup, this.stopMove); }, stopMove(e) { $(document).off(mousemove, this.moveDiv); document.removeEventListener(mouseup, this.stopMove); }, moveDiv(e) { // Mosaic if (this.isMa) { this.paintRect(e); } // Clear mosaic if (this.isMaClear) { this.paintRectClear(e); } // Crop if (this.isDrop) { var targetDiv = $(#canvas-mainBox), offsetArr = targetDiv.offset(); var chaX = e.pageX - this.movePrev[0], chaY = e.pageY - this.movePrev[1], ox = parseFloat(targetDiv.css(left)), oy = parseFloat(targetDiv.css(top)); targetDiv.css({ left: ox + chaX + px, top: oy + chaY + px }); this.movePrev = [e.pageX, e.pageY]; } }, resizeDiv(e) { e.preventDefault(); e.stopPropagation(); // Get the distance from the element whose size needs to be changed to the page var targetDiv = $(#canvas-mainBox), offsetArr = targetDiv.offset(); var eleSWidth = targetDiv.width() , eleSHeight = targetDiv.height(), ox = parseFloat(targetDiv.css(left)), oy = parseFloat(targetDiv.css(top)); // Get the mouse position and compare it with the initial offset of the element, var chaX = e.pageX - offsetArr.left, chaY = e.pageY - offsetArr.top; switch (this.resizeFX) { case 0: //If the movement distance is close to the width or height, no change is made if (chaX >= eleSWidth - 10 || chaY >= eleSHeight - 10) { return; } // Get the position difference (me), set the width and height first, and then set the position // Original width and height + ((me) *-1), original position + (me) targetDiv.css({ width: eleSWidth + chaX * -1 + px, height: eleSHeight + chaY * -1 + px, left: ox + chaX + px, top: oy + chaY + px }); break; case 1: //If the movement distance is close to the width or height, no change is made if (chaY >= eleSHeight - 10) { return; } // Get the position difference (me), set the width and height first, and then set the position // Original width and height + ((me) *-1), original position + (me) targetDiv.css({ height: eleSHeight + chaY * -1 + px, top: oy + chaY + px }); break; case 2: //If the moving distance is close to the width or height, no change is made if (chaX <= 10 || chaY >= eleSHeight - 10) { return; } // Get the position difference (me), First set the width and height, set the position // original height + ((me) *-1), original width + ((me)), original position + (me) targetDiv.css({ width: chaX + px, height: eleSHeight + chaY * -1 + px, top: oy + chaY + px }); break; case 3: //If the movement distance is close to the width or height, no change is made if (chaX <= 10) { return; } // Get the position difference (me), first set the width and height, and then set the position // Original width and height + ((me) *-1), original position + (me) targetDiv.css({ width: chaX + px }); break; case 4: //If the moving distance is close to the width or height, no change is made if (chaX <= 10 || chaY <= 10) { return; } // Get the position difference (me), Set the width and height first, then set the position // Original width and height + ((me) *-1), original position + (me) targetDiv.css({ width: chaX + px, height: chaY + px }); break; case 5: //If the movement distance is close to the width or height, no change will be made if (chaY <= 10) { return; } // Get the position difference (me), set the width and height first, and then set the position // Original width and height + ((me) *-1), original position + (me) targetDiv.css({ height: chaY + px }); break; case 6: //If the movement distance is close to the width or height, no action will be taken. change if (chaX >= eleSWidth - 10 || chaY <= 10) { return; } // Get the position difference (me), first set the width and height, and then set the position // Original width and height + ((me) *-1), original Position + (me) targetDiv.css({ width: eleSWidth + chaX * -1 + px, height: chaY + px, left: ox + chaX + px }); break; case 7: //If the movement distance is close to the width or height, no change will be made if (chaX >= eleSWidth - 10) { return; } // Get the position difference (me), set the width and height first, and then set the position // Original Width and height + ((me) *-1), original position + (me) targetDiv.css({ width: eleSWidth + chaX * -1 + px, left: ox + chaX + px }); break; default: break; } }, // clipReady() { this.btnIndex = 3; this.isMa = false; this.isDrop = true; this.isMaClear = false; }, // mosaic paintRectReady() { this .btnIndex = 1; this.isMa = true; this.isDrop = false; this.isMaClear = false; }, // Eraser paintRectClearReady() { this.btnIndex = 2; this.isMa = false; this.isDrop = false; this.isMaClear = true; }, // Draw mosaic paintRect(e) { var offT = this.canvas.offsetTop, // Distance from top offL = this.canvas.offsetLeft, // Distance from left x = e.clientX, y = e.clientY; if(this.mouseX - x > this.maSize/2 || x - this.mouseX > this.maSize/2 || this.mouseY - y > this.maSize/2 || y - this.mouseY > this.maSize/2){ var oImg = this.ctx.getImageData(x - offL ,y - offT,this.maSize,this.maSize); var w = oImg.width; var h = oImg.height; //The degree of mosaic, the larger the number, the blurr it is var num = 6; //Equally divided canvas var stepW = w/ num; var stepH = h/num; //Here are the pixels of the loop canvas for(var i=0;i<stepH;i++){ for(var j=0;j<stepW;j++){ //Get the random color of a small square. This is the var color obtained from the random position of the small square = this.getXY(oImg,j*num+Math.floor(Math.random()*num),i*num +Math.floor(Math.random()*num)); //Here are the pixels of the circular small square, for(var k=0;k<num;k++){ for(var l=0;l< num;l++){ //Set the color of the small square this.setXY(oImg,j*num+l,i*num+k,color); } } } } this.ctx.putImageData(oImg,x - offL ,y - offT); this.mouseX = e.clientX this.mouseY = e.clientY } }, getXY(obj,x,y){ var w = obj.width; var h = obj.height; var d = obj.data; var color = []; color[0] = d[4*(y*w+x)]; color[1] = d[4*(y* w+x)+1]; color[2] = d[4*(y*w+x)+2]; color[3] = d[4*(y*w+x)+3]; return color ; }, setXY(obj,x,y,color){ var w = obj.width; var h = obj.height; var d = obj.data; d[4*(y*w+x)] = color[0]; d[4*(y*w+x)+1] = color[1]; d[4*(y*w+x)+2] = color[2]; d[4*(y*w+x)+3] = color[3]; }, // Clear mosaic paintRectClear(e) { var offT = this.canvasCopy.offsetTop, // Distance from top offL = this.canvasCopy .offsetLeft, // Distance to the left x = e.clientX, y = e.clientY, // Get image data at this position of the original image imageData = this.ctxCopy.getImageData( x - offL, y - offT, this.maSize, this.maSize ); this.ctx.putImageData(imageData, x - offL, y - offT); }, // Close the canvas canvasClose(type, url) { this.$emit(isShowImgChange, type, url); } }};</script><style scoped>.canvas-clip { position: fixed; top: 0; bottom: 0; left: 0; right: 0; z-index: 9010; background: #000;}.canvas-mainBox { position: absolute; width: 400px; height: 300px; left: 50%; top: 50%; margin-left: -200px; margin-top: -150px; border: 1px solid #FFF; cursor: move; z-index: 9009;}.canvas-minBox { position: absolute; width: 8px; height: 8px; background: #FFF;}.left-up { top: -4px; left : -4px; cursor: nw-resize;}.up { top: -4px; left: 50%; margin-left: -4px; cursor: n-resize;}.right-up { top: -4px; right: -4px; cursor: ne-resize;}.right { top: 50%; margin-top: -4px; right: -4px; cursor: e -resize;}.right-down { bottom: -4px; right: -4px; cursor: se-resize;}.down { bottom: -4px; left: 50%; margin-left: -4px; cursor: s-resize;}.left-down { bottom: -4px; left: -4px; cursor: sw-resize;}.left { top: 50%; margin-top: -4px ; left: -4px; cursor: w-resize;}.canvas-btns { position: fixed; right: 50px; top: 30px; z-index: 9003;}.canvas-btns button { display: inline-blovk; background: green; cursor: pointer; border: none; width: 60px; height: 30px; line-height: 30px; color: #fff; font-size: 15px;}.canvas-btns button.active { background: rgb(32, 230, 32);}.canvas-btns button.close { background: rgb(230, 72, 32);}.canvas-copy { position: absolute; top: 50%; left: 50%; margin-top: -300px; margin-left: -400px; z-index: 9007;}.canvas-mosatic { position: absolute; top: 50%; left: 50%; margin-top: -300px; margin-left: -400px; z-index: 9009;}.canvas-area { position: absolute; top: 50%; left: 50%; margin-top: -300px; margin-left: -400px; z-index: 9008;}.paint-size{ margin-top: 20px; font-size: 13px; color: #FFF; height: 30px; line-height: 30px; text-align: right;}.paint-size input{ vertical-align: middle; background: green;}.paint-size .size-num{ display: inline-block; width: 15px;}.hoverClear{ cursor: url('./paint.png'),auto;}.hoverPaint{ cursor: url('./paint.png'),auto;}</style>
4. The renderings are as follows:
SummarizeThe above is what the editor introduces to you based on Html5 canvas to realize the functions of cropping and mosaic pictures and uploading pictures to the cloud. I hope it will be helpful to you. If you have any questions, please leave me a message and the editor will reply to you in time. of. I would also like to thank everyone for your support of the VeVb martial arts website!
If you think this article is helpful to you, you are welcome to reprint it, please indicate the source, thank you!