Las características de este componente incluyen:
Recorte de imagen (arrastre el cuadro de recorte y cambie el tamaño del cuadro de recorte);
Mosaico de imágenes (dibujar mosaico, borrar mosaico);
Vista previa de la imagen, restauración de la imagen (volver a la imagen original, volver a la imagen procesada);
Subir imagen (obtener firma, subir imagen).
2. Lógica central2.1 Recorte de imagen
Obtenga la posición del cuadro de recorte (rectángulo) en relación con el lienzo (arriba a la izquierda) y la altura y el ancho del cuadro de recorte. Obtenga (getImageData) el objeto de imagen (ImageData) en la posición correspondiente del lienzo. Limpia el lienzo del lienzo. Dibuje el objeto de imagen (ImageData) obtenido por (putImageData) en la posición correspondiente del lienzo. Generar imagen de vista previa.
2.2 Mosaico de imágenes
El dibujo en mosaico consiste en volver a dibujar el área centrada en el trazo del mouse (ancho del pincel) en otros colores. El resultado general es que los colores circundantes serán similares.
Método de selección de color:
1) Por ejemplo, si tienes las coordenadas (x, y) de un punto atravesado por el mouse, define un rectángulo con las coordenadas de la esquina superior izquierda (x, y), de 30px de ancho y 30px de alto. Dividimos el ancho y el alto del rectángulo por 5 (dividido en 5 partes, que se pueden personalizar en n partes), por lo que ahora hay 25 cuadrículas pequeñas de 6px. Cada pequeña cuadrícula tiene un ancho y un alto de 6px.
2) Luego, obtenemos aleatoriamente una pequeña cuadrícula y obtenemos (getImageData) el objeto de imagen (ImageData) de esta pequeña cuadrícula y luego obtenemos aleatoriamente el color (rgba: ImageD) de un determinado píxel (ancho 1px, alto 1px) en esta; objeto de imagen ata.data[0], ImageData.data[1], ImageData.data[2], ImageData.data[3] Finalmente, configuramos el color de cada píxel de la primera cuadrícula pequeña de 6x6px en color .
3) Para los colores de las otras 24 cuadrículas pequeñas, solo sigue 2 pasos.
2.3 Borrar mosaico
Necesitamos entender un problema, ya sea dibujar un mosaico o limpiar un mosaico, la esencia es hacer un dibujo. Dibujamos el mosaico en una ubicación determinada. Cuando lo borramos, volvemos a dibujar el objeto de la imagen original en la ubicación actual. Se consigue el efecto de limpieza. Por lo tanto, necesitamos hacer una copia de seguridad de un lienzo que sea exactamente igual a la imagen original. Al borrar, debemos obtener la imagen en la posición correspondiente en el lienzo de respaldo y dibujarla en la posición del mosaico.
2.4 Vista previa de la imagen
La vista previa de la imagen consiste en obtener el área del marco de recorte y obtener los objetos de la imagen en el área. Luego dibújalo en el lienzo.
2.5 Restaurar la imagen a la imagen original
Limpia el lienzo y vuelve a dibujar la imagen original.
2.6 Restaurar a la imagen manipulada
La vista previa sirve para guardar el objeto de imagen del lienzo (ImageData), borrar el lienzo y dibujar el objeto de imagen guardado en el lienzo.
2.7 Carga de imágenes
Obtenga la ruta de la imagen del lienzo (toDataURL) y convierta la imagen base64 obtenida en un objeto Archivo. para subir.
3. El código completo es el siguiente:
<plantilla> <div clase=canvas-clip :cargando=cargando> <div v-show=isDrop clase=canvas-mainBox ref=canvas-mainBox id=canvas-mainBox @mousedown.stop=startMove($event) > <div class=canvas-minBox izquierda arriba @mousedown.stop=startResize($event,0)></div> <div class=canvas-minBox arriba @mousedown.stop=startResize($event,1)></div> <div class=canvas-minBox derecha arriba @mousedown.stop=startResize($event,2)></div> <div class=canvas- minBox derecha @mousedown.stop=startResize($event,3)></div> <div class=canvas-minBox derecha abajo @mousedown.stop=startResize($event,4)></div> <div class=canvas-minBox abajo @mousedown.stop=startResize($event,5)></div> <div class=canvas-minBox izquierda abajo @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> <!-- Copia de seguridad del lienzo--> <clase de lienzo = copia de lienzo ref = copia de lienzo: ancho = ancho de lienzo :height=canvasHeight></canvas> <div class=canvas-btns> <button v-if=backBtn @click=clipBack>Back</button> <button :class={active:btnIndex==0} @click= sourceImg>Imagen original</button> <button :class={active:btnIndex==1} @click=paintRectReady :disabled=isDisabled>Mosaico</button> <button :class={active:btnIndex==2} @click=paintRectClearReady :disabled=isDisabled>Borrador</button> <button :class={active:btnIndex==3 } @click=clipReady :disabled=isDisabled>Recortar</button> <button :class={active:btnIndex==4} @click=clipPosition>Vista previa</button> <button @click=getSignature>Cargar</button> <button class=close @click=canvasClose()>x</button> <!-- <div class=paint-size v-if=isMaClear || isMa> <span>Tamaño del pincel</span> <input :defaultValue=maSize v-model=maSize max=100 min=1 tipo=rango> <span class=size-num>{{maSize}}</span> </div> --> </div> </div></template><script>importar axios desde axios;importar md5 desde js-md5 ;solicitud de importación desde ../../axios/config;exportar valor predeterminado { props: [imgUrl], data() { return { resizeFX: , movePrev: , canvasWidth: 800, // Ancho del lienzo canvasHeight: 600, // Altura del lienzo cargando: false, isDrop: false, // Recortando isMa: false, // Mosaico maSize: 30, // Tamaño del mosaico isMaClear: false, // Borrar mosaico backBtn: false, // El botón Volver está discapacitado: false, // Deshabilitar el botón btnIndex: 0, //Botón actual mouseX:'', // Posición del mouse mouseY:'', clipEle: , // Elemento del cuadro de recorte canvasDataSession: , // Información del lienzo antes de la vista previa del lienzo: , // Canvas ctx: , // Contexto del lienzo canvasCopy:, // copiar lienzo ctxCopy:, // copiar contexto de lienzo uploadOption: { // Ruta de parámetros de carga de imagen:, política:, firma:, nombre de usuario: } }; }, montado() { this.clipEle = this.$refs[canvas-mainBox]; this.canvas = this.$refs[canvas]; = this.$refs[canvasCopy]; this.ctxCopy = this.canvasCopy.getContext(2d); Crear una imagen draw() { var img = new Image(); img.setAttribute('crossOrigin', 'anonymous'); img.onload = () => { this.ctx.drawImage(img, 0, 0, 800 , 600); img.src = this.imgUrl + '?time=' + new Date().valueOf(); }, //La vista previa calcula la posición del cuadro de recorte (coordenada superior izquierda) clipPosition() { this.isDisabled = true; this.backBtn = true; this.isMa = false; this.isMaClear = false; this.btnIndex = 4; // Posición del lienzo var canvasPx = this.canvas.offsetLeft, canvasPy = this.canvas.offsetTop; if (this.isDrop) { // Posición del cuadro de recorte var clipPx = this.clipEle.offsetLeft, clipPy = this.clipEle.offsetTop, x = clipPx - canvasPx, y = clipPy - canvasPy, w = this.clipEle.offsetWidth, h = this.clipEle.offsetHeight, // Centrar la posición de la imagen de vista previaX = 400 - this.clipEle.offsetWidth / 2, positionY = 300 - this.clipEle.offsetHeight / 2; } else { // Sin cuadro de recorte, guarda la imagen completa var x = 0, y = 0, w = this.canvas.offsetWidth , h = this.canvas.offsetHeight, // Centrar la imagen de vista previa 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, posiciónX, posiciónY); this.clipEle.style.display = none; this.canvasCopy.style.display = none }, // Regresar al estado previo a la vista previa clipBack() { this.btnIndex = -1; = falso; this.isDrop = false; this.ctx.putImageData(this.canvasDataSession, 0, 0); block; }, // Imagen original sourceImg() { this.isDisabled = false; this.btnIndex = 0; this.backBtn = false; = nueva Imagen(); 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 = bloque }, // Obtenga la firma getSignature() { // Imagen de lienzo base64 al objeto de archivo 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], { tipo: mime }), hora = new Date().toGMTString(), formData = new FormData(); formData.append(file, obj); // Obtiene el sufijo del archivo var suffix = formData.get(file).type.split(/)[1]; req .get(/carsource-api/upyun/sign, { sufijo: sufijo }) .then(response => { if (response.data.code === 0) { this.uploadOption.path = respuesta.data.data.path; formData.append(política, respuesta.data.data.policy); formData.append(autorización, respuesta.data.data.signature); this.updateImg(formData); .catch(function(error) {}}, // Subir updateImg(formData) { axios({ url); : http://v0.api.upyun.com/tmp-img, método: POST, datos: formData }).entonces(respuesta => {si (response.data.code == 200) { this.$message.success(imagen modificada correctamente); this.canvasClose(carga, respuesta.data.url.slice(4)); Movimiento de zoom de cuadro startResize(e, n) { this.resizeFX = n $(document).mousemove(this.resizeDiv); document.addEventListener(mouseup, this.stopResize); }, stopResize(e) { $(documento).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) { // Mosaico si (this.isMa) { this.paintRect(e); } // Borrar mosaico if (this.isMaClear) { this.paintRectClear(e } // Recortar 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(izquierda)), oy = parseFloat(targetDiv.css(arriba)); px }); this.movePrev = [e.pageX, e.pageY] } }, resizeDiv(e) { e.preventDefault(); e.stopPropagation(); // Obtiene la distancia desde el elemento cuyo tamaño debe cambiarse a la página var targetDiv = $(#canvas-mainBox), offsetArr = targetDiv.offset(); = targetDiv.width(), eleSHeight = targetDiv.height(), ox = parseFloat(targetDiv.css(izquierda)), oy = parseFloat(targetDiv.css(top)); // Obtener la posición del mouse y compararla con el desplazamiento inicial del elemento, var chaX = e.pageX - offsetArr.left, chaY = e.pageY - offsetArr.top; this.resizeFX) { case 0: //Si la distancia de movimiento es cercana al ancho o alto, no se realiza ningún cambio if (chaX >= eleSWidth - 10 || chaY >= eleSHeight - 10) { return } // Obtenga la diferencia de posición (yo), establezca primero el ancho y el alto, y luego establezca la posición // Ancho y alto originales + ((me) *-1), posición original + (yo) targetDiv.css({ ancho: eleSWidth + chaX * -1 + px, alto: eleSHeight + chaY * -1 + px, izquierda: ox + chaX + px, arriba: oy + chaY + px }); break; case 1: // Si la distancia de movimiento está cerca del ancho o alto, no se realiza ningún cambio if (chaY >= eleSHeight - 10) { return } // Obtiene la diferencia de posición (me), establece el ancho y altura primero y luego establezca la posición // Ancho y alto originales + ((me) *-1), posición original + (me) targetDiv.css({ altura: eleSHeight + chaY * -1 + px, top: oy + chaY + px }); break; case 2: //Si la distancia de movimiento es cercana al ancho o alto, no se realiza ningún cambio if (chaX <= 10 || chaY >= eleSHeight - 10) { return } // Obtener la posición diferencia (yo), primero establezca el ancho y el alto, establezca la posición // alto original + ((yo) *-1), ancho original + ((yo)), posición original + (yo) targetDiv.css({ ancho : chaX + px, altura: eleSHeight + chaY * -1 + px, top: oy + chaY + px }); break case 3: // Si la distancia de movimiento es cercana al ancho o alto, no se realiza ningún cambio if (chaX <= 10); ) { return } // Obtenga la diferencia de posición (yo), primero establezca el ancho y el alto, y luego establezca la posición // Ancho y alto originales + ((me) *-1), posición original + (me) targetDiv .css({ ancho: chaX + px }); break; case 4: //Si la distancia de movimiento es cercana al ancho o alto, no se realiza ningún cambio if (chaX <= 10 || chaY <= 10) { return } // Obtener la posición diferencia (yo), establezca primero el ancho y el alto, luego establezca la posición // Ancho y alto originales + ((me) *-1), posición original + (me) targetDiv.css({ ancho: chaX + px, alto : chaY + px }); break; case 5: // Si la distancia de movimiento está cerca del ancho o alto, no se realizará ningún cambio if (chaY <= 10) { return } // Obtiene la diferencia de posición (yo), establece el ancho y el alto; primero, y luego establezca la posición // Ancho y alto originales + ((me) *-1), posición original + (me) targetDiv.css({ height: chaY + px }); la distancia de movimiento está cerca del ancho o alto, no se realizará ningún cambio si (chaX). >= eleSWidth - 10 || chaY <= 10) { return } // Obtenga la diferencia de posición (yo), primero establezca el ancho y el alto, y luego establezca la posición // Ancho y alto originales + ((me) * -1), Posición original + (yo) targetDiv.css({ ancho: eleSWidth + chaX * -1 + px, alto: chaY + px, izquierda: ox + chaX + px }); 7: // Si la distancia de movimiento está cerca del ancho o alto, no se realizará ningún cambio if (chaX >= eleSWidth - 10) { return } // Obtenga la diferencia de posición (yo), establezca primero el ancho y el alto; y luego establezca la posición // Ancho y alto original + ((me) *-1), posición original + (me) targetDiv.css({ ancho: eleSWidth + chaX * -1 + px, izquierda: ox + chaX + píxeles}); break; predeterminado: break; } }, // recorte clipReady() { this.btnIndex = 3; this.isMa = false; this.isMaClear = false; .btnIndex = 1; this.isMa = verdadero; this.isDrop = false; this.isMaClear = false }, // Borrador paintRectClearReady() { this.btnIndex = 2; this.isMa = false; this.isDrop = false; this.isMaClear = true }, // Dibujar mosaico paintRect(e) { var offT = this.canvas.offsetTop, / / Distancia desde arriba L = this.canvas.offsetLeft, // Distancia desde la izquierda 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; // El grado del mosaico, cuanto mayor es el número, más borroso es var num = 6; canvas var stepW = w/ num; var stepH = h/num; // Aquí están los píxeles del lienzo del bucle for(var i=0;i<stepH;i++){ for(var j=0;j<stepW; j++){ //Obtiene el color aleatorio de un cuadrado pequeño. Este es el color var obtenido de la posición aleatoria del cuadrado pequeño = this.getXY(oImg,j*num+Math.floor(Math.random()*num),i *num +Math.floor(Math.random()*num)); //Aquí están los píxeles del pequeño cuadrado circular, for(var k=0;k<num;k++){ for(var l=0; l<núm;l++){ //Establece el color del cuadrado pequeño this.setXY(oImg,j*num+l,i*num+k,color,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.altura; var d = obj.datos; var color = []; d[4*(y*w+x)]; w+x)+1]; color[2] = d[4*(y*w+x)+2]; color[3] = d[4*(y*w+x)+3]; ; }, setXY(obj,x,y,color){ var w = obj.width; var h = obj.height var d = obj.data; d[4*(y*w+x)+1] = color[1]; d[4*(y*w+x)+2] = color[2]; d[4*(y*w+x)+3] = color[3]; }, // Borrar mosaico paintRectClear(e) { var offT = this.canvasCopy.offsetTop, // Distancia desde la parte superior offL = this.canvasCopy .offsetLeft, // Distancia a la izquierda x = e.clientX, y = e.clientY, // Obtener datos de imagen en esta posición de la imagen original imageData = this.ctxCopy.getImageData( x - offL, y - offT, this.maSize, this.maSize ); this.ctx.putImageData(imageData, x - offL, y - offT }, // Cerrar el lienzo canvasClose(type); , url) { this.$emit(isShowImgChange, tipo, url } }};</script><estilo); ámbito>.canvas-clip { posición: fijo; arriba: 0; abajo: 0; izquierda: 0; índice z: 9010; fondo: #000;}.canvas-mainBox { posición: absoluto; 400px; altura: 300px; izquierda: 50%; arriba: 50%; margen izquierdo: -200px; 1px sólido #FFF; cursor: mover; índice z: 9009;}.canvas-minBox { posición: absoluto; ancho: 8px; alto: 8px; : -4px; cursor: nw-resize;}.arriba {arriba: -4px; izquierda: 50%; n-resize;}.right-up { arriba: -4px; derecha: -4px; cursor: ne-resize;}.right { arriba: 50%; margen-top: -4px; -resize;}.right-down { abajo: -4px; derecha: -4px; cursor: se-resize;}.down { abajo: -4px; margen izquierdo: -4px; cursor: s-resize;}.izquierda abajo {abajo: -4px izquierda: -4px; cursor: sw-resize;}.izquierda {arriba: 50%; ; izquierda: -4px; cursor: w-resize;}.canvas-btns { posición: fijada: 50px; índice z: 9003;}. botón canvas-btns {pantalla: inline-blovk; fondo: verde; cursor: puntero; ancho: 60px; alto de línea: 30px; tamaño de fuente: 15px;}.botón canvas-btns.activo {fondo: rgb(32, 230, 32);}.canvas-btns button.close { fondo: rgb(230, 72, 32);}.canvas-copy { posición: absoluta; arriba: 50% izquierda: 50%; margen izquierdo: -400px; índice z: 9007;}.canvas-mosatic { posición: superior: 50% izquierda; 50%; margen superior: -300px; margen izquierdo: -400px; índice z: 9009;}.área de lienzo { posición: absoluta; 50% izquierdo: 50%; margen izquierdo: -400px; índice z: 9008;}.tamaño de pintura{ margen superior: 20px; 13px; color: #FFF; altura: 30px; altura de línea: 30px; alineación de texto: derecha;}. entrada de tamaño de pintura {alineación vertical: fondo: verde;}. tamaño de pintura . mostrar: bloque en línea; ancho: 15px;}.hoverClear{ cursor: url('./paint.png'),auto;}.hoverPaint{ cursor: url('./paint.png'),auto;}</estilo>4. Las representaciones son las siguientes:
ResumirLo anterior es lo que le presenta el editor basado en el lienzo Html5 para realizar las funciones de recorte y mosaico y la función de carga en la nube. Espero que le resulte útil. Si tiene alguna pregunta, déjeme un mensaje y el editor lo hará. Responderle a tiempo. ¡También me gustaría agradecer a todos por su apoyo al sitio web de artes marciales VeVb!
Si cree que este artículo le resulta útil, puede reimprimirlo. Indique la fuente, ¡gracias!