Os recursos deste componente incluem:
Recorte de imagem (arraste a caixa de corte e altere o tamanho da caixa de corte);
Mosaico de imagens (desenhar mosaico, mosaico claro);
Visualização de imagem, restauração de imagem (retorno à imagem original, retorno à imagem processada);
Upload de imagem (obter assinatura, fazer upload de imagem).
2. Lógica central2.1 Recorte de imagem
Obtenha a posição da caixa de corte (retângulo) em relação à tela (canto superior esquerdo) e a altura e largura da caixa de corte. Obtenha (getImageData) o objeto de imagem (ImageData) na posição correspondente da tela. Limpe a tela da tela. Desenhe o objeto de imagem (ImageData) obtido por (putImageData) na posição correspondente da tela. Gere imagem de visualização.
2.2 Mosaico de imagens
O desenho em mosaico consiste em redesenhar a área centralizada no caminho do traço do mouse (largura do pincel) em outras cores. O resultado geral é que as cores circundantes serão semelhantes.
Método de seleção de cores:
1) Por exemplo, se você tiver as coordenadas (x, y) de um ponto atravessado pelo mouse, defina um retângulo com coordenadas do canto superior esquerdo (x, y), 30px de largura e 30px de altura. Dividimos a largura e a altura do retângulo por 5 (dividido em 5 partes, que podem ser personalizadas em n partes), então agora existem 25 pequenas grades de 6px. Cada pequena grade tem largura e altura de 6px.
2) Em seguida, obtemos aleatoriamente uma pequena grade e obtemos (getImageData) o objeto de imagem (ImageData) dessa pequena grade e, em seguida, obtemos aleatoriamente a cor da cor (rgba: ImageD) de um determinado pixel (largura 1px, altura 1px) neste; objeto de imagem ata.data[0], ImageData.data[1], ImageData.data[2], ImageData.data[3]); Finalmente, definimos a cor de cada pixel da primeira grade pequena de 6x6px como color .
3) Para as cores das outras 24 grades pequenas, basta seguir 2 passos.
2.3 Mosaico claro
Precisamos entender um problema, seja desenhar um mosaico ou limpar um mosaico, a essência é fazer um desenho. Desenhamos o mosaico em um determinado local. Quando o limpamos, desenhamos novamente o objeto da imagem original no local atual. O efeito da limpeza é alcançado. Portanto, precisamos fazer backup de uma tela que seja exatamente igual à imagem original. Ao limpar, precisamos obter a imagem na posição correspondente na tela de backup e desenhá-la na posição do mosaico.
2.4 Visualização da imagem
A visualização da imagem serve para obter a área do quadro de recorte e obter os objetos da imagem na área. Depois desenhe na tela.
2.5 Restaurar a imagem para a imagem original
Limpe a tela e desenhe a imagem original novamente
2.6 Restaurar a imagem manipulada
A visualização serve para salvar o objeto de imagem da tela (ImageData), limpar a tela e desenhar o objeto de imagem salvo na tela.
2.7 Carregamento de imagem
Obtenha o caminho da imagem da tela (toDataURL) e converta a imagem base64 obtida em um objeto File. para carregar.
3. O código completo é o seguinte:
<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 à direita @mousedown.stop=startResize($event,2)></div> <div class=canvas- minBox à direita @mousedown.stop=startResize($event,3)></div> <div class=canvas-minBox à direita @mousedown.stop=startResize($event,4)></div> <div class=canvas-minBox para baixo @mousedown.stop=startResize($event,5)></div> <div class=canvas-minBox para baixo @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> <!-- Tela de backup--> <canvas class=canvas-copy ref=canvasCopy :width=canvasWidth :height=canvasHeight></canvas> <div class=canvas-btns> <button v-if=backBtn @click=clipBack>Voltar</button> <button :class={active:btnIndex==0} @click= sourceImg>Imagem original</button> <button :class={active:btnIndex==1} @click=paintRectReady :disabled=isDisabled>Mosaico</button> <button :class={active:btnIndex==2} @click=paintRectClearReady :disabled=isDisabled>Borracha</button> <button :class={active:btnIndex==3 } @click=clipReady :disabled=isDisabled>Cortar</button> <button :class={active:btnIndex==4} @click=clipPosition>Visualizar</button> <button @click=getSignature>Fazer upload</button> <button class=close @click=canvasClose()>x</button> <!-- <div class=paint-size v-if=isMaClear || isMa> <span>Tamanho do pincel</span> <input :defaultValue=maSize v-model=maSize max=100 min=1 type=range> <span class=size-num>{{maSize}}</span> </div> --> </div> </div></template><script>importar axios de axios;importar md5 de js-md5 ;importar req de ../../axios/config;exportar padrão { adereços: [imgUrl], data() { return { resizeFX: , movePrev: , canvasWidth: 800, // Largura da tela canvasHeight: 600, // Carregamento da altura da tela: false, isDrop: false, // Corte isMa: false, // Mosaico maSize: 30, // Tamanho do mosaico isMaClear: false, // Limpar mosaico backBtn: false, // Botão retornar isDisabled: false, // Desativar botão btnIndex: 0, //Botão atual mouseX:'', // Posição do mouse mouseY:'', clipEle: , // Elemento da caixa de corte canvasDataSession: , // Informações do canvas antes da visualização canvas: , // Canvas ctx: , // Contexto do Canvas canvasCopy: , // copia a tela ctxCopy: , // copia o contexto da tela uploadOption: { // Parâmetros de upload de imagem path: , política: , assinatura: , nome de usuário: } }; }, montado() { this.clipEle = this.$refs[canvas-mainBox]; this.canvas = this.$refs[canvas]; = this.$refs[canvasCopy]; this.ctxCopy = this.canvasCopy.getContext(2d); this.draw() }, métodos: { // Crie uma imagem draw() { var img = new Image(); , 600); this.ctxCopy.drawImage(img, 0, 0, 800, 600 }); img.src = this.imgUrl + '?time=' + new Date().valueOf() }, //Preview calcula a posição da caixa de corte (coordenada superior esquerda) clipPosition() { this.isDisabled = true; this.backBtn = true; this.isMa = false; this.isMaClear = false; this.btnIndex = 4; = this.canvas.offsetTop; if (this.isDrop) { // Posição da caixa de corte var clipPx = this.clipEle.offsetLeft, clipPy = this.clipEle.offsetTop, x = clipPx - canvasPx, y = clipPy - canvasPy, w = this.clipEle.offsetWidth, h = this.clipEle.offsetHeight, // Centraliza a imagem de visualização positionX = 400 - this.clipEle.offsetWidth / 2, positionY = 300 - this.clipEle.offsetHeight / 2; } else { // Sem caixa de corte, salve a imagem completa var x = 0, y = 0, w = this.canvas.offsetWidth , h = this.canvas.offsetHeight, // Centraliza a imagem de visualização 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, posiçãoX, posiçãoY); this.clipEle.style.display = none; this.canvasCopy.style.display = none }, // Retornar ao estado de pré-visualização clipBack() { this.btnIndex = -1; = falso; this.isDrop = false; this.ctx.putImageData (this.canvasDataSession, 0, 0); block; }, // Imagem original sourceImg() { this.isDisabled = false; this.isMaClear = false; = nova imagem(); this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); img.setAttribute('crossOrigin', 'anonymous'); img.onload = () => { img.src = this.canvasWidth, this.canvasHeight }; .imgUrl + '?time=' + new Date().valueOf(); this.canvasCopy.style.display = block }, // Obtenha a assinatura getSignature() { // Imagem da tela base64 para o objeto Arquivo 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); formData.get(file).type.split(/)[1]; req .get(/carsource-api/upyun/sign, { sufixo: sufixo }) .then(response => { if (response.data.code === 0) { this.uploadOption.path = response.data.data.path; formData.append(policy, response.data.data.policy); formData.append(autorização,response.data.data.signature); this.updateImg(formData) { axios({ url) : http://v0.api.upyun.com/tmp-img, método: POST, dados: formData }).then(response => { if (response.data.code == 200) { this.$message.success(imagem modificada com sucesso); this.canvasClose(upload, response.data.url.slice(4)); Movimento de zoom da caixa startResize(e, n) { this.resizeFX = n; document.addEventListener(mouseup, this.stopResize }, stopResize(e) { $(document).off(mousemove, this.resizeDiv); 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 if (this.isMa) { this.paintRect(e); // Limpar mosaico if (this.isMaClear) { this.paintRectClear(e }); // Cortar 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)); px }); this.movePrev = [e.pageX, e.pageY] } }, resizeDiv(e) { e.preventDefault(); e.stopPropagation(); // Obtém a distância do elemento cujo tamanho precisa ser alterado até a página var targetDiv = $(#canvas-mainBox), offsetArr = targetDiv.offset(); = targetDiv.width() , eleSHeight = targetDiv.height(), ox = parseFloat(targetDiv.css(esquerda)), oy = parseFloat(targetDiv.css(top)); // Obtenha a posição do mouse e compare-a com o deslocamento inicial do elemento, var chaX = e.pageX - offsetArr.left, chaY = e.pageY - offsetArr.top switch (; this.resizeFX) { case 0: //Se a distância do movimento estiver próxima da largura ou altura, nenhuma alteração será feita if (chaX >= eleSWidth - 10 || chaY >= eleSHeight - 10) { return; } // Obtenha a diferença de posição (me), defina a largura e a altura primeiro e depois defina a posição // Largura e altura originais + ((me) *-1), posição original + (me) targetDiv.css({ largura: eleSWidth + chaX * -1 + px, altura: eleSHeight + chaY * -1 + px, esquerda: ox + chaX + px, topo: oy + chaY + px }); break; case 1: //Se a distância do movimento estiver próxima da largura ou altura, nenhuma alteração será feita if (chaY >= eleSHeight - 10) { return } // Obtém a diferença de posição (me), define a largura e altura primeiro e, em seguida, defina a posição // Largura e altura originais + ((me) *-1), posição original + (me) targetDiv.css({ height: eleSHeight + chaY * -1 + px, top: oy + chaY + px }); break; case 2: //Se a distância do movimento estiver próxima da largura ou altura, nenhuma alteração será feita if (chaX <= 10 || chaY >= eleSHeight - 10) { return } // Obtém a posição diferença (me), primeiro defina a largura e a altura, defina a posição // altura original + ((me) *-1), largura original + ((me)), posição original + (me) targetDiv.css({ largura : chaX + px, height: eleSHeight + chaY * -1 + px, top: oy + chaY + px }); break; //Se a distância do movimento estiver próxima da largura ou altura, nenhuma alteração será feita if (chaX <= 10 ) { return } // Obtenha a diferença de posição (me), primeiro defina a largura e a altura e depois defina a posição // Largura e altura originais + ((me) *-1), posição original + (me) targetDiv .css({ largura: chaX + px }); break; case 4: //Se a distância do movimento estiver próxima da largura ou altura, nenhuma alteração será feita if (chaX <= 10 || chaY <= 10) { return } // Obtém a posição diferença (me), defina a largura e a altura primeiro e, em seguida, defina a posição // Largura e altura originais + ((me) *-1), posição original + (me) targetDiv.css({ largura: chaX + px, altura : chaY + px }); break; case 5: //Se a distância do movimento estiver próxima da largura ou altura, nenhuma alteração será feita if (chaY <= 10) { return } // Obtém a diferença de posição (me), define a largura e a altura; primeiro e depois defina a posição // Largura e altura originais + ((me) *-1), posição original + (me) targetDiv.css({ height: chaY + px }); a distância do movimento estiver próxima da largura ou altura, nenhuma ação será tomada se (chaX. >= eleSWidth - 10 || chaY <= 10) { return; } // Obtenha a diferença de posição (me), primeiro defina a largura e a altura e depois defina a posição // Largura e altura originais + ((me) * -1), Posição original + (me) targetDiv.css({ largura: eleSWidth + chaX * -1 + px, altura: chaY + px, esquerda: ox + chaX + px case break }); 7: //Se a distância do movimento estiver próxima da largura ou altura, nenhuma alteração será feita if (chaX >= eleSWidth - 10) { return } // Obtenha a diferença de posição (me), defina a largura e a altura primeiro; e, em seguida, defina a posição // Largura e altura originais + ((me) *-1), posição original + (me) targetDiv.css({ largura: eleSWidth + chaX * -1 + px, esquerda: ox + chaX + px }); break; padrão: break; } }, // recorte clipReady() { this.btnIndex = 3; .btnIndex = 1; this.isMa = true; this.isDrop = false; this.isMaClear = false }, // Borracha paintRectClearReady() { this.btnIndex = 2; this.isMa = false; this.isDrop = false; this.isMaClear = true }, // Desenhar mosaico paintRect(e) { var offT = this.canvas.offsetTop, / / Distância do topo L = this.canvas.offsetLeft, // Distância da esquerda 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; //O grau do mosaico, quanto maior o número, mais desfocado é var num = 6; canvas var stepW = w/ num; var stepH = h/num; //Aqui estão os pixels do loop canvas for(var i=0;i<stepH;i++){ for(var j=0;j<stepW; j++){ //Obtém a cor aleatória de um quadrado pequeno. Esta é a cor var obtida da posição aleatória do quadrado pequeno = this.getXY(oImg,j*num+Math.floor(Math.random()*num),i *num +Math.floor(Math.random()*num)); //Aqui estão os pixels do pequeno quadrado circular, for(var k=0;k<num;k++){ for(var l=0; l< num;l++){ //Defina a cor do pequeno quadrado 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.altura; w+x)+1]; cor[2] = d[4*(y*w+x)+2]; ;}, setXY(obj,x,y,cor){ var w = obj.largura; var h = obj.altura; d[4*(y*w+x)+1] = cor[1]; d[4*(y*w+x)+3] = color[3] }, // Limpar mosaico paintRectClear(e) { var offT = this.canvasCopy.offsetTop, // Distância do topo offL = this.canvasCopy .offsetLeft, // Distância à esquerda x = e.clientX, y = e.clientY, // Obtenha os dados da imagem nesta posição da imagem original imageData = this.ctxCopy.getImageData( x - offL, y - offT, this.maSize, this.maSize ); this.ctx.putImageData(imageData, x - offL, y - offT }, // Fecha a tela canvasClose(type); , url) { this.$emit(isShowImgChange, type, url } }};</script><style); escopo> .canvas-clip { posição: fixo; 0; inferior: 0; esquerda: 0; índice z: 9010; 400px; altura: 300px; margem esquerda: -150px; 1px sólido #FFF; cursor: mover; índice z: 9009;}.canvas-minBox { posição: largura absoluta: 8px; : -4px; cursor: nw-resize;}.up { superior: -4px; margem esquerda: -4px; n-redimensionar;}.direita para cima { superior: -4px; direita: -4px; cursor: ne-redimensionar;}.direita { superior: 50%; margem superior: -4px; -redimensionar;}.direita para baixo { inferior: -4px; direita: -4px; cursor: se-redimensionar;}.para baixo { inferior: -4px esquerda: 50%; margem esquerda: -4px; cursor: s-resize;}.esquerda para baixo {margem superior: -4px; ;esquerda: -4px; cursor: w-resize;}.canvas-btns { posição: fixa direita: 50px superior: 30px; índice z: 9003;} botão .canvas-btns { display: altura da linha: 30px; tamanho da fonte: 15px;}.canvas-btns button.active { background: rgb(32, 230, 32);}.canvas-btns button.close { background: rgb(230, 72, 32);}.canvas-copy {posição: topo absoluto: 50% esquerda: 50%; margem esquerda: -400px; índice z: 9007;}.canvas-mosatic {posição: topo absoluto: 50% esquerda: 50%;margem superior: -300px;margem esquerda: -400px;margem superior: -300px; margem esquerda: -400px; índice z: 9008;}.paint-size{margem superior: 20px; 13px; cor: #FFF; altura: 30px; altura da linha: 30px; alinhamento de texto: direita;}.paint-size input{ alinhamento vertical: meio; display: bloco embutido; largura: 15px;}.hoverClear{ cursor: url('./paint.png'),auto;}.hoverPaint{ cursor: url('./paint.png'),auto;}</style>
4. As renderizações são as seguintes:
ResumirO texto acima é o que o editor apresenta a você com base na tela HTML5 para realizar as funções de corte e mosaico e a função de upload na nuvem. Espero que seja útil para você. Se você tiver alguma dúvida, deixe-me uma mensagem e o editor irá. responder a você a tempo. Gostaria também de agradecer a todos pelo apoio ao site de artes marciais VeVb!
Se você acha que este artigo é útil para você, fique à vontade para reimprimi-lo, indique a fonte, obrigado!