Les fonctionnalités de ce composant incluent :
Recadrage de l'image (faites glisser la zone de recadrage et modifiez la taille de la zone de recadrage) ;
Mosaïque d'images (dessiner une mosaïque, une mosaïque claire);
Aperçu de l'image, restauration de l'image (retour à l'image originale, retour à l'image traitée) ;
Téléchargement d'image (obtenir une signature, télécharger une image).
2. Logique de base2.1 Recadrage d'image
Obtenez la position de la zone de recadrage (rectangle) par rapport à la toile (en haut à gauche), ainsi que la hauteur et la largeur de la zone de recadrage. Obtenez (getImageData) l'objet image (ImageData) à la position correspondante du canevas. Effacez la toile de toile. Dessinez l'objet image (ImageData) obtenu par (putImageData) à la position correspondante du canevas. Générez une image d’aperçu.
2.2 Mosaïque d'images
Le dessin en mosaïque consiste à redessiner la zone centrée sur le tracé de la souris (largeur du pinceau) dans d'autres couleurs. Le résultat général est que les couleurs environnantes seront similaires.
Méthode de sélection des couleurs :
1) Par exemple, si vous disposez des coordonnées (x, y) d'un point traversé par la souris, définissez un rectangle avec les coordonnées du coin supérieur gauche (x, y), 30px de large et 30px de haut. Nous divisons la largeur et la hauteur du rectangle par 5 (divisé en 5 parties, personnalisables en n parties), il y a donc maintenant 25 petites grilles de 6px. Chaque petite grille a une largeur et une hauteur de 6px.
2) Ensuite, nous obtenons aléatoirement une petite grille et obtenons (getImageData) l'objet image (ImageData) de cette petite grille ; puis obtenons aléatoirement la couleur (rgba : ImageD) d'un certain pixel (largeur 1px, hauteur 1px) sur celle-ci. objet image ata.data[0], ImageData.data[1], ImageData.data[2], ImageData.data[3]); Enfin, nous définissons la couleur de chaque pixel de la première petite grille de 6x6px sur color .
3) Pour les couleurs des 24 autres petites grilles, suivez simplement 2 étapes.
2.3 Mosaïque claire
Nous devons comprendre un problème, qu’il s’agisse de dessiner une mosaïque ou de nettoyer une mosaïque, l’essence est de dessiner une image. Nous avons dessiné la mosaïque à un certain emplacement. Lorsque nous l'effaçons, nous dessinons à nouveau l'objet image d'origine à l'emplacement actuel. L'effet de nettoyage est obtenu. Par conséquent, nous devons sauvegarder une toile qui est exactement la même que l'image d'origine. Lors de l'effacement, nous devons obtenir l'image à la position correspondante sur la toile de sauvegarde et la dessiner à la position de la mosaïque.
2.4 Aperçu de l'image
L'aperçu de l'image consiste à obtenir la zone du cadre de recadrage et à obtenir les objets de l'image dans la zone. Dessinez-le ensuite sur la toile.
2.5 Restaurer l'image à l'image d'origine
Effacez la toile et dessinez à nouveau l'image originale
2.6 Restaurer l'image manipulée
L'aperçu consiste à enregistrer l'objet image du canevas (ImageData), à effacer le canevas et à dessiner l'objet image enregistré sur le canevas.
2.7 Téléchargement d'images
Obtenez le chemin de l'image du canevas (toDataURL) et convertissez l'image base64 obtenue en un objet File. à télécharger.
3. Le code complet est le suivant :
<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 gauche vers le haut @mousedown.stop=startResize($event,0)></div> <div class=canvas-minBox vers le haut @mousedown.stop=startResize($event,1)></div> <div class=canvas-minBox à droite @mousedown.stop=startResize($event,2)></div> <div class=canvas- minBox à droite @mousedown.stop=startResize($event,3)></div> <div class=canvas-minBox à droite vers le bas @mousedown.stop=startResize($event,4)></div> <div class=canvas-minBox vers le bas @mousedown.stop=startResize($event,5)></div> <div class=canvas-minBox vers le bas @mousedown.stop=startResize($event,6)></ div div> <div class=canvas-minBox gauche @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> <!-- Toile de sauvegarde--> <classe canvas=canvas-copy ref=canvasCopy :width=canvasWidth :height=canvasHeight></canvas> <div class=canvas-btns> <button v-if=backBtn @click=clipBack>Retour</button> <button :class={active:btnIndex==0} @click= sourceImg>Image originale</button> <button :class={active:btnIndex==1} @click=paintRectReady :disabled=isDisabled>Mosaïque</button> <button :class={active:btnIndex==2} @click=paintRectClearReady :disabled=isDisabled>Gomme</button> <button :class={active:btnIndex==3 } @click=clipReady :disabled=isDisabled>Recadrer</button> <button :class={active:btnIndex==4} @click=clipPosition>Aperçu</button> <button @click=getSignature>Télécharger</button> <button class=close @click=canvasClose()>x</button> <!-- <div class=paint-size v-if=isMaClear || isMa> <span>Taille du pinceau</span> <input :defaultValue=maSize v-model=maSize max=100 min=1 type=range> <span class=size-num>{{maSize}}</span> </div> --> </div> </div></template><script>importer axios depuis axios ; importer md5 depuis js-md5 ;importer la demande depuis ../../axios/config;export default { props: [imgUrl], data() { return { resizeFX: , movePrev: , canvasWidth : 800, // Largeur du canevas canvasHeight : 600, // Chargement de la hauteur du canevas : false, isDrop : false, // Recadrage isMa : false, // Mosaïque maSize : 30, // Taille de la mosaïque isMaClear : false, // Effacer la mosaïque backBtn : false, // Le bouton Retour isDisabled : false, // Désactiver le bouton btnIndex : 0, //Bouton actuel mouseX:'', // Position de la souris mouseY:'', clipEle: , // Élément de zone de recadrage canvasDataSession: , // Informations sur le canevas avant l'aperçu du canevas: , // Canvas ctx: , // Contexte du canevas canvasCopy : , // copier le canevas ctxCopy : , // copier le contexte du canevas uploadOption : { // Chemin des paramètres de téléchargement d'image : , stratégie : , signature : , nom d'utilisateur : } }; }, Mounted() { this.clipEle = this.$refs[canvas-mainBox]; this.canvasCopy; = this.$refs[canvasCopy]; this.ctxCopy = this.canvasCopy.getContext(2d); this.draw( ); Créer une 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 calcule la position de la zone de recadrage (coordonnée supérieure gauche) clipPosition() { this.isDisabled = true; this.backBtn = true; this.isMa = false; this.isMaClear = false; this.btnIndex = 4; //Position du canevas var canvasPx = this.canvas.offsetLeft, canvasPy = this.canvas.offsetTop; if (this.isDrop) { // Position de la zone de recadrage var clipPx = this.clipEle.offsetLeft, clipPy = this.clipEle.offsetTop, x = clipPx - canvasPx, y = clipPy - canvasPy, w = this.clipEle.offsetWidth, h = this.clipEle.offsetHeight, // Centre la position de l'image d'aperçuX = 400 - this.clipEle.offsetWidth / 2, positionY = 300 - this.clipEle.offsetHeight / 2; } else { // Pas de zone de recadrage, enregistre l'image complète var x = 0, y = 0, w = this.canvas.offsetWidth , h = this.canvas.offsetHeight, // Centre l'image d'aperçu positionX = 0, positionY = 0 } var imageData = this.ctx.getImageData(x, y, w, h); this.canvasDataSession = this.ctx.getImageData( 0, 0, this.canvasWidth, this.canvasHeight ); canvasWidth, this.canvasHeight); this.ctx.putImageData(imageData, positionX, positionY); this.clipEle.style.display = none; this.canvasCopy.style.display = none; }, // Retour à l'état de pré-aperçu clipBack() { this.btnIndex = -1; = false ; this.isDrop = false; this.ctx.putImageData(this.canvasDataSession, 0, 0); block; }, // Image originale sourceImg() { this.isDisabled = false; this.btnIndex = 0; this.isMa = false; this.isMaClear = false; = 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 }; .imgUrl + '?time=' + new Date().valueOf(); this.canvasCopy.style.display = block }, // Obtenez la signature getSignature() { // Image Canvas base64 vers l'objet File 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); (n--) { u8arr[n] = bstr.charCodeAt(n); } var obj = new Blob([u8arr], { type: mime }), time = new Date().toGMTString(), formData = new FormData(); formData.append(file, obj); // Récupère le suffixe du fichier var suffix = formData.get(file).type.split(/)[1]; req .get(/carsource-api/upyun/sign, { suffixe : suffixe }) .then(response => { if (response.data.code === 0) { this.uploadOption.path = réponse.data.data.path; formData.append(policy, réponse.data.data.policy); formData.append (autorisation, réponse.data.data.signature); this.updateImg (formData); } }) .catch (function (error) {} }), // Télécharger updateImg (formData) { axios ({ url : http://v0.api.upyun.com/tmp-img, méthode : POST, données : formData }).then(response => { if (response.data.code == 200) { this.$message.success(image modifiée avec succès); this.canvasClose(upload, réponse.data.url.slice(4) } }); Déplacement du zoom de la boîte 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); this.stopMove); }, moveDiv(e) { // Mosaïque if (this.isMa) { this.paintRect(e); } // Effacer la mosaïque if (this.isMaClear) { this.paintRectClear(e); } // Recadrer 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({ gauche : ox + chaX + px, top : oy + chaY + px }); this.movePrev = [e.pageX, e.pageY]; } }, resizeDiv(e) { e.preventDefault(); e.stopPropagation(); // Récupère la distance de l'élément dont la taille doit être modifiée par rapport à la page var targetDiv = $(#canvas-mainBox), offsetArr = targetDiv.offset(); = targetDiv.width() , eleSHeight = targetDiv.height(), ox = parseFloat(targetDiv.css(left)), oy = parseFloat(targetDiv.css(top)); // Récupère la position de la souris et compare-la avec le décalage initial de l'élément, var chaX = e.pageX - offsetArr.left, chaY = e.pageY - offsetArr.top; this.resizeFX) { case 0 : //Si la distance de déplacement est proche de la largeur ou de la hauteur, aucun changement n'est effectué si (chaX >= eleSWidth - 10 || chaY >= eleSHeight - 10) { return; } // Obtenez la différence de position (moi), définissez d'abord la largeur et la hauteur, puis définissez la position // Largeur et hauteur d'origine + ((moi) *-1), position d'origine + (moi) targetDiv.css({ largeur : eleSWidth + chaX * -1 + px, hauteur : eleSHeight + chaY * -1 + px, gauche : ox + chaX + px, haut : oy + chaY + px }); break; case 1 : //Si la distance de déplacement est proche de la largeur ou de la hauteur, aucun changement n'est effectué if (chaY >= eleSHeight - 10) { return } // Obtenez la différence de position (me), définissez la largeur et height d'abord, puis définissez la position // Largeur et hauteur d'origine + ((me) *-1), position d'origine + (me) targetDiv.css({ hauteur : eleSHeight + chaY * -1 + px, top : oy + chaY + px }); break; case 2 : //Si la distance de déplacement est proche de la largeur ou de la hauteur, aucun changement n'est effectué if (chaX <= 10 || chaY >= eleSHeight - 10) { return } // Récupère la position; différence (moi), Définissez d'abord la largeur et la hauteur, définissez la position // hauteur d'origine + ((moi) *-1), largeur d'origine + ((moi)), position d'origine + (moi) targetDiv.css({ width : chaX + px, height : eleSHeight + chaY * -1 + px, top : oy + chaY + px }); break; cas 3 : //Si la distance de déplacement est proche de la largeur ou de la hauteur, aucun changement n'est effectué if (chaX <= 10 ) { return; } // Obtenez la différence de position (me), définissez d'abord la largeur et la hauteur, puis définissez la position // Largeur et hauteur d'origine + ((me) *-1), position d'origine + (me) targetDiv .css({ largeur : chaX + px }); break; cas 4 : //Si la distance de déplacement est proche de la largeur ou de la hauteur, aucun changement n'est effectué if (chaX <= 10 || chaY <= 10) { return } // Récupère la position; différence (moi), Définissez d'abord la largeur et la hauteur, puis définissez la position // Largeur et hauteur d'origine + ((moi) *-1), position d'origine + (moi) targetDiv.css({ width: chaX + px, height : chaY + px }); break; case 5 : //Si la distance de déplacement est proche de la largeur ou de la hauteur, aucun changement ne sera effectué if (chaY <= 10) { return } // Récupère la différence de position (me), définit la largeur et la hauteur. d'abord, puis définissez la position // Largeur et hauteur d'origine + ((me) *-1), position d'origine + (me) targetDiv.css({ height: chaY + px }); la distance de déplacement est proche de la largeur ou de la hauteur, aucune action ne sera effectuée si (chaX. >= eleSWidth - 10 || chaY <= 10) { return; } // Obtenez la différence de position (moi), définissez d'abord la largeur et la hauteur, puis définissez la position // Largeur et hauteur d'origine + ((me) * -1), Position d'origine + (moi) targetDiv.css({ width: eleSWidth + chaX * -1 + px, height: chaY + px, left: ox + chaX + px }); 7 : //Si la distance de déplacement est proche de la largeur ou de la hauteur, aucun changement ne sera effectué if (chaX >= eleSWidth - 10) { return; } // Obtenez la différence de position (me), définissez d'abord la largeur et la hauteur , puis définissez la position // Largeur et hauteur d'origine + ((me) *-1), position d'origine + (me) targetDiv.css({ width: eleSWidth + chaX * -1 + px, left: ox + chaX + px }); break ; par défaut : break ; } }, // découpage clipReady() { this.btnIndex = 3 ; this.isDrop = true ; this.isMaClear = false }, // mosaïque paintRectReady() ; .btnIndex = 1; ceci.isMa = vrai; ceci.isDrop = faux; ceci.isMaClear = faux }, // Eraser paintRectClearReady() { this.btnIndex = 2; this.isMa = false; this.isDrop = false; this.isMaClear = true }, // Dessiner une mosaïque paintRect(e) { var offT = this.canvas.offsetTop, / / Distance depuis le haut L = this.canvas.offsetLeft, // Distance depuis la gauche 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; //Le degré de mosaïque, plus le nombre est grand, plus il est flou var num = 6; canvas var stepW = w/ num; var stepH = h/num; //Voici les pixels du canevas de boucle for(var i=0;i<stepH;i++){ for(var j=0;j<stepW; j++){ //Obtenir la couleur aléatoire d'un petit carré. Il s'agit de la couleur var obtenue à partir de la position aléatoire du petit carré = this.getXY(oImg,j*num+Math.floor(Math.random()*num),i *num +Math.floor(Math.random()*num)); //Voici les pixels du petit carré circulaire, for(var k=0;k<num;k++){ for(var l=0; l< num;l++){ //Définit la couleur du petit carré 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.hauteur; var d = obj.data; var couleur = []; d[4*(y*w+x)]; w+x)+1]; couleur[2] = d[4*(y*w+x)+2]; couleur[3] = d[4*(y*w+x)+3]; ; }, setXY(obj,x,y,color){ var w = obj.largeur; var h = obj.hauteur; var d = obj.data d[4*(y*w+x)] = couleur[0]; d[4*(y*w+x)+1] = couleur[1]; d[4*(y*w+x)+2] = couleur[2]; d[4*(y*w+x)+3] = color[3]; }, // Effacer la mosaïque paintRectClear(e) { var offT = this.canvasCopy.offsetTop, // Distance du haut offL = this.canvasCopy .offsetLeft, // Distance vers la gauche x = e.clientX, y = e.clientY, // Récupère les données d'image à cette position de l'image d'origine imageData = this.ctxCopy.getImageData( x - offL, y - offT, this.maSize, this.maSize ); this.ctx.putImageData(imageData, x - offL, y - offT }, // Ferme le canevas canvasClose(type); , url) { this.$emit(isShowImgChange, type, url);</script><style); scoped>.canvas-clip { position : fixe ; haut : 0 ; bas : 0 ; droite : 0 ; arrière-plan : #000;}.canvas-mainBox { position : largeur absolue ; 400 px ; hauteur : 300 px ; gauche : 50 % ; haut : 50 % ; marge-gauche : -200 px ; marge supérieure : -150 px ; 1px solide #FFF ; curseur : déplacer ; z-index : 9009 ;}.canvas-minBox { position : absolue ; hauteur : 8px ; arrière-plan : #FFF ;}.left-up { top : -4px ; : -4px; curseur : nw-resize;}.up { haut : -4px gauche : 50 % ; curseur : -4px ; n-resize;}.right-up { top : -4px ; droite : -4px ; curseur : ne-resize ;}.right { top : 50 % ; marge-top : -4px ; -resize;}.right-down { bas : -4px ; droite : -4px ; curseur : se-resize;}.down { bas : -4px gauche : 50 % ; marge-gauche : -4px ; curseur : s-resize ;}.left-down { bas : -4px gauche : -4px ; curseur : sw-resize ;}.left { haut : 50 % ; ; gauche : -4px ; curseur : w-resize;}.canvas-btns { position : fixe : 50px ; z-index : 9003 ;}.canvas-btns bouton { display : inline-blovk ; arrière-plan : vert ; curseur : bordure : aucun ; hauteur : 30 px ; hauteur de la ligne : 30px ; taille de police : 15 px ;}.canvas-btns bouton.active { arrière-plan : rgb(32, 230, 32);}.canvas-btns bouton.close { background : rgb(230, 72, 32);}.canvas-copy { position : haut : 50 % à gauche : 50 % ; marge-haut : -300 px ; marge-gauche : -400px ; z-index : 9007 ;}.canvas-mosatic { position : haut : 50 % ; 50 % ; marge supérieure : -300 px ; marge gauche : -400 px ; z-index : 9009 ;}.zone-canvas { position : haut : 50 % ; gauche : 50 % ; marge gauche : -400 px ; z-index : 9008 ;}.paint-size{ marge-top : 20 px ; 13 px ; couleur : #FFF ; hauteur : 30 px ; hauteur de ligne : 30 px ; texte-align : droite ;}.paint-size input{ alignement vertical : milieu ; arrière-plan : vert ;}.paint-size .size-num{ affichage : bloc en ligne ; largeur : 15 px ;}.hoverClear{ curseur : url('./paint.png'),auto;}.hoverPaint{ curseur : url('./paint.png'),auto;}</style>
4. Les rendus sont les suivants :
RésumerCe qui précède est ce que l'éditeur vous présente sur la base du canevas Html5 pour réaliser les fonctions de recadrage et de mosaïque et la fonction de téléchargement dans le cloud. J'espère que cela vous sera utile. Si vous avez des questions, laissez-moi un message et l'éditeur le fera. je vous répondrai à temps. Je tiens également à remercier tout le monde pour votre soutien au site d'arts martiaux VeVb !
Si vous pensez que cet article vous est utile, n'hésitez pas à le réimprimer, veuillez indiquer la source, merci !