Возможности этого компонента включают в себя:
Обрезка изображения (перетащите рамку обрезки и измените размер рамки обрезки);
Картинная мозаика (нарисовать мозаику, четкую мозаику);
Предварительный просмотр изображения, восстановление изображения (возврат к исходному изображению, возврат к обработанному изображению);
Загрузка изображения (получить подпись, загрузить изображение).
2. Основная логика2.1 Обрезка изображения
Получите положение рамки обрезки (прямоугольник) относительно холста (вверху слева), а также высоту и ширину рамки обрезки. Получите (getImageData) объект изображения (ImageData) в соответствующей позиции холста. Очистите холст холста. Нарисуйте объект изображения (ImageData), полученный с помощью (putImageData), в соответствующей позиции холста холста. Создать изображение предварительного просмотра.
2.2 Мозаика из изображений
Рисование мозаики заключается в перерисовке области, расположенной по центру пути движения мыши (ширины кисти), в другие цвета. Общий результат состоит в том, что окружающие цвета будут одинаковыми.
Метод подбора цвета:
1) Например, если у вас есть координаты (x, y) точки, которую пересекает мышь, определите прямоугольник с координатами верхнего левого угла (x, y), шириной 30 пикселей и высотой 30 пикселей. Мы делим ширину и высоту прямоугольника на 5 (разделим на 5 частей, которые можно настроить на n частей), так что теперь есть 25 маленьких сеток по 6 пикселей. Каждая маленькая сетка имеет ширину и высоту 6 пикселей.
2) Затем мы случайным образом получаем небольшую сетку и получаем (getImageData) объект изображения (ImageData) этой маленькой сетки, затем случайным образом получаем цвет цвета (rgba: ImageD) определенного пикселя (ширина 1 пиксель, высота 1 пиксель); объект изображения ata.data[0], ImageData.data[1], ImageData.data[2], ImageData.data[3]); Наконец, мы устанавливаем цвет каждого пикселя первой маленькой сетки размером 6x6 пикселей в color .
3) Для цветов остальных 24 маленьких сеток выполните всего 2 шага.
2.3 Четкая мозаика
Нам нужно понять задачу, будь то рисование мозаики или расчистка мозаики, суть в том, чтобы нарисовать картинку. Мы нарисовали мозаику в определенном месте. Когда мы ее очищаем, мы снова рисуем исходный объект изображения в текущем месте. Эффект очистки достигнут. Поэтому нам необходимо создать резервную копию холста, точно такого же, как исходное изображение. При очистке нам необходимо получить изображение в соответствующей позиции на резервном холсте и отрисовать его в позиции мозаики.
2.4 Предварительный просмотр изображения
Предварительный просмотр изображения заключается в получении области рамки обрезки и получении объектов изображения в этой области. Затем нарисуйте его на холсте.
2.5 Восстановить картинку до исходной картинки
Очистите холст и снова нарисуйте исходное изображение.
2.6 Восстановление измененного изображения
Предварительный просмотр предназначен для сохранения объекта изображения холста (ImageData), очистки холста и рисования сохраненного объекта изображения на холсте.
2.7 Загрузка изображения
Получите путь к изображению холста (toDataURL) и преобразуйте полученное изображение в формате Base64 в объект File. загрузить.
3. Полный код выглядит следующим образом:
<шаблон> <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 слева вверх @mousedown.stop=startResize($event,0)></div> <div class=canvas-minBox вверх @mousedown.stop=startResize($event,1)></div> <div class=canvas-minBox right-up @mousedown.stop=startResize($event,2)></div> <div class=canvas- minBox вправо @mousedown.stop=startResize($event,3)></div> <div class=canvas-minBox вправо вниз @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> <!-- Резервное копирование холста--> <canvas class=canvas-copy ref=canvasCopy:width=canvasWidth :height=canvasHeight></canvas> <div class=canvas-btns> <button v-if=backBtn @click=clipBack>Назад</button> <button :class={active:btnIndex==0} @click= sourceImg>Исходное изображение</button> <button :class={active:btnIndex==1} @click=paintRectReady :disabled=isDisabled>Мозаика</button> <button :class={active:btnIndex==2} @click=paintRectClearReady :disabled=isDisabled>Ластик</button> <button :class={active:btnIndex==3 } @click=clipReady :disabled=isDisabled>Обрезать</button> <button :class={active:btnIndex==4} @click=clipPosition>Предварительный просмотр</button> <button @click=getSignature>Загрузить</button> <button class=close @click=canvasClose()>x</button> <!-- <div class=paint-size v-if=isMaClear || isMa> <span>Размер кисти</span> <input :defaultValue=maSize v-model=maSize max=100 min=1 type=range> <span class=size-num>{{maSize}}</span> </div> --> </div> </div></template><script>импортировать аксиомы из axios;импортировать md5 из js-md5 ;import req из ../../axios/config;export default { props: [imgUrl], data() { return { resizeFX: , movePrev: , CanvasWidth: 800, // Ширина холста CanvasHeight: 600, // Загрузка высоты холста: false, isDrop: false, // Обрезка isMa: false, // Мозаика maSize: 30, // Размер мозаики isMaClear: false, // Очистить мозаику backBtn: false, // Кнопка возврата isDisabled: false, // Отключить кнопку btnIndex: 0, //Текущая кнопка mouseX:'', // Положение мыши mouseY:'', clipEle: , // Элемент рамки обрезки CanvasDataSession: , // Информация о холсте перед предварительным просмотром Canvas: , // Canvas ctx: , // Контекст холста CanvasCopy: , // копируем холст ctxCopy: , // копируем контекст холста uploadOption: { // Параметры загрузки изображения path: , policy: , подпись: , имя пользователя: } }; }, mount() { this.clipEle = this.$refs[canvas-mainBox]; this.canvas = this.$refs[canvas]; this.ctx = this.canvas.getContext(2d); = this.$refs[canvasCopy]; this.ctxCopy = this.canvasCopy.getContext(2d); this.draw(); Создайте изображение draw() { var img = new Image(); 'crossOrigin', 'anonymous'); , 600); this.ctxCopy.drawImage(img, 0, 0, 800, 600 }); img.src = this.imgUrl + '?time=' + new Date().valueOf() }, //Предварительный просмотр вычисляет положение рамки обрезки (верхняя левая координата) clipPosition() { this.isDisabled = true; this.backBtn = true; this.isMa = false; this.isMaClear = false; this.btnIndex = 4; //Положение холста var CanvasPx = this.canvas.offsetLeft, CanvasPy = this.canvas.offsetTop; if (this.isDrop) { // Положение рамки обрезки var clipPx = this.clipEle.offsetLeft, clipPy = this.clipEle.offsetTop, x = clipPx - CanvasPx, y = clipPy - CanvasPy, w = this.clipEle.offsetWidth, h = this.clipEle.offsetHeight, // Центрируем положение изображения предварительного просмотра X = 400 - this.clipEle.offsetWidth / 2, PositionY = 300 - this.clipEle.offsetHeight / 2 } else { // Без рамки обрезки, сохраните полное изображение var x = 0, y = 0, w = this.canvas.offsetWidth; , h = this.canvas.offsetHeight, // Центрируем изображение предварительного просмотра 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 }, // Возврат в состояние предварительного просмотра clipBack() { this.btnIndex = -1; this.backBtn = false; = false; this.isDrop = false; this.ctx.putImageData(this.canvasDataSession, 0, 0); блок }, // Исходное изображение sourceImg() { this.isDisabled = false; this.backBtn = false; this.isDrop = false; this.isMaClear = false; = новое изображение(); 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 = блок }, // Получить подпись getSignature() { // Изображение Canvas в base64 для объекта 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); // Получаем суффикс файла var suffix = formData.get(file).type.split(/)[1]; req .get(/carsource-api/upyun/sign, { суффикс: суффикс }) .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) {} }, // Загрузить updateImg(formData) { axios({ url); : http://v0.api.upyun.com/tmp-img, метод: POST, данные: formData }).then(response => { if (response.data.code == 200) { this.$message.success(изображение успешно изменено); this.canvasClose(upload, response.data.url.slice(4) }, // Обрезка Масштабирование рамки 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) { // Мозаика if (this.isMa) { this.paintRect(e); } // Очистить мозаику if (this.isMaClear) { this.paintRectClear(e } // Обрезать 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({ слева: ox + chaX + px, вверху: oy + chaY + px }); this.movePrev = [e.pageX, e.pageY] } }, resizeDiv(e) {; e.preventDefault(); e.stopPropagation(); // Получаем расстояние от элемента, размер которого необходимо изменить, до страницы var targetDiv = $(#canvas-mainBox), offsetArr = targetDiv.offset(); = targetDiv.width(), eleSHeight = targetDiv.height(), ox = parseFloat(targetDiv.css(left)), oy = parseFloat(targetDiv.css(top)); // Получаем положение мыши и сравниваем его с начальным смещением элемента, var chaX = e.pageX - offsetArr.left, chaY = e.pageY - offsetArr.top; this.resizeFX) { case 0: //Если расстояние перемещения близко к ширине или высоте, никаких изменений не происходит if (chaX >= eleSWidth - 10 || chaY >= eleSHeight - 10) { return; } // Получаем разницу позиции (me), сначала устанавливаем ширину и высоту, а затем устанавливаем позицию // Исходная ширина и высота + ((me) *-1), исходная позиция + (me) targetDiv.css({ ширина: eleSWidth + chaX * -1 + px, высота: eleSHeight + chaY * -1 + px, слева: ox + chaX + px, сверху: oy + chaY + px }); break; case 1: //Если расстояние перемещения близко к ширине или высоте, никаких изменений не происходит if (chaY >= eleSHeight - 10) { return; // Получаем разницу позиций (me), устанавливаем ширину и сначала высоту, а затем устанавливаем позицию // Исходная ширина и высота + ((me) *-1), исходная позиция + (me) targetDiv.css({ height: eleSHeight + chaY * -1 + px, top: oy + чай + пикс }); перерыв; случай 2: // Если расстояние перемещения близко к ширине или высоте, никаких изменений не происходит if (chaX <= 10 || chaY >= eleSHeight - 10) { return }); разница (я), сначала установите ширину и высоту, установите позицию // исходную высоту + ((me) *-1), исходную ширину + ((me)), исходную позицию + (me) targetDiv.css({ width : чаХ + пикс, height: eleSHeight + chaY * -1 + px, top: oy + chaY + px }; case 3: //Если расстояние перемещения близко к ширине или высоте, изменения не производятся if (chaX <= 10); ) { return } // Получаем разницу позиции (me), сначала устанавливаем ширину и высоту, а затем устанавливаем позицию // Исходная ширина и высота + ((me) *-1), исходная позиция + (me) targetDiv .css({ ширина: chaX + px });break; case 4: //Если расстояние перемещения близко к ширине или высоте, никаких изменений не происходит if (chaX <= 10 || chaY <= 10) { return } // Получаем позицию; разница (я), сначала установите ширину и высоту, затем установите позицию // Исходная ширина и высота + ((me) *-1), исходное положение + (me) targetDiv.css({ width: chaX + px, height : chaY + px }); сломать; случай 5: // Если расстояние перемещения близко к ширине или высоте, никаких изменений не произойдет if (chaY <= 10) { return } // Получаем разницу позиции (me), устанавливаем ширину и высоту; сначала, а затем установите позицию // Исходная ширина и высота + ((me) *-1), исходная позиция + (me) targetDiv.css({ height: chaY + px }); //If Break; расстояние перемещения близко к ширине или высоте, никаких действий не будет, если (chaX. >= eleSWidth - 10 || chaY <= 10) { return } // Получаем разницу позиции (me), сначала устанавливаем ширину и высоту, а затем устанавливаем позицию // Исходная ширина и высота + ((me) * -1), исходная позиция + (me) targetDiv.css({ width: eleSWidth + chaX * -1 + px, height: chaY + px, left: ox + chaX + px }); 7: //Если расстояние перемещения близко к ширине или высоте, никаких изменений не произойдет if (chaX >= eleSWidth - 10) { return } // Получаем разницу положения (me), сначала устанавливаем ширину и высоту , а затем установите позицию // Исходная ширина и высота + ((me) *-1), исходная позиция + (me) targetDiv.css({ width: eleSWidth + chaX * -1 + px, left: ox + chaX + пикс }); перерыв; по умолчанию: перерыв; } }, // обрезка clipReady() { this.isMa = false; this.isDrop = true; this.isMaClear = false }, // мозаика PaintRectReady() { this. .btnIndex = 1; this.isMa = true; this.isDrop = false; this.isMaClear = false }, // Ластик PaintRectClearReady() { this.btnIndex = 2; this.isMa = false; this.isMaClear = true }, // Рисуем мозаику PaintRect(e) { var offT = this.canvas.offsetTop, / / Расстояние сверху offL = this.canvas.offsetLeft, // Расстояние слева 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; // Чем больше число, тем размытее оно var num = 6; Canvas var StepW = w/ num; var StepH = h/num; // Вот пиксели холста цикла for(var i=0;i<stepH;i++){ for(var j=0;j<stepW; j++){ //Получаем случайный цвет маленького квадрата. Это цвет переменной, полученный из случайной позиции маленького квадрата = this.getXY(oImg,j*num+Math.floor(Math.random()*num),i *num +Math.floor(Math.random()*num)); //Вот пиксели круглого квадратика, for(var k=0;k<num;k++){ for(var l=0; l< число;l++){ //Установим цвет маленького квадрата 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)]; w+x)+1]; цвет[2] = d[4*(y*w+x)+2]; цвет[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)] = color[0]; d[4*(y*w+x)+1] = цвет[1]; d[4*(y*w+x)+2] = цвет[2]; d[4*(y*w+x)+3] = color[3] }, // Очистить мозаику PaintRectClear(e) { var offT = this.canvasCopy.offsetTop, // Расстояние от верха offL = this.canvasCopy .offsetLeft, // Расстояние влево x = e.clientX, y = e.clientY, // Получаем данные изображения в этой позиции исходного изображения imageData = this.ctxCopy.getImageData( x - offL, y - offT, this.maSize, this.maSize ); this.ctx.putImageData(imageData, x - offL, y - offT }, // Закрываем холст CanvasClose(type); , url) { this.$emit(isShowImgChange, type, url } }};</script><style); область действия>.canvas-clip {позиция: фиксированная; верхняя: 0; нижняя: 0; правая: 0; z-индекс: #000;}.canvas-mainBox {позиция: абсолютная; 400 пикселей; высота: 300 пикселей; слева: 50%; поле слева: -200 пикселей; граница: -150 пикселей; 1 пиксель сплошной #FFF; курсор: перемещение; z-index: 9009;}.canvas-minBox { позиция: абсолютная ширина: 8 пикселей; высота: 8 пикселей; фон: #FFF;}.left-up { верх: -4 пикселя; : -4px; курсор: nw-resize;}.up { верх: -4px; поле слева: -4px; n-resize;}.right-up { сверху: -4px; вправо: -4px; курсор: ne-resize;}.right { сверху: 50%; -4px вправо: -4px курсор: e; -resize;}.right-down { внизу: -4px; вправо: -4px; курсор: se-resize;}.down { снизу: -4px влево: 50%; маржа влево: -4 пикселей; курсор: s-resize;}.left-down { внизу: -4px; влево: -4px; курсор: sw-resize;}.left { сверху: 50%; ; слева: -4 пикселей; курсор: w-resize;}.canvas-btns { позиция: фиксированная; справа: 50 пикселей; сверху: 30 пикселей; z-index: 9003;}.canvas-btns button { display: inline-blovk; курсор: указатель; граница: нет; высота: 30 пикселей; цвет: #fff; размер шрифта: 15 пикселей;}.canvas-btns button.active {background: rgb(32, 230, 32);}.canvas-btns button.close { фон: rgb(230, 72, 32);}.canvas-copy { позиция: абсолютная; верхнее: 50%; левое: маржа-верхнее: -300 пикселей; поле слева: -400 пикселей; z-индекс: 9007;}.canvas-mosatic { позиция: абсолютная; сверху: 50%; 50%; поле сверху: -300 пикселей; поле слева: -400 пикселей; z-index: 9009;}.canvas-area { позиция: абсолютная; сверху: 50%; поле слева: -400 пикселей; z-индекс: 9008;}.paint-size { размер шрифта: 20 пикселей; 13 пикселей; цвет: #FFF; высота: 30 пикселей; выравнивание текста: по правому краю;}.paint-size input {вертикальное выравнивание: средний;}.paint-size .size-num{ дисплей: встроенный блок; ширина: 15 пикселей;}.hoverClear{курсор: URL('./paint.png'),auto;}.hoverPaint{ курсор: url('./paint.png'),auto;</style>
4. Визуализации следующие:
Подвести итогВышеупомянутое представляет вам редактор на основе холста Html5 для реализации функций обрезки и мозаики, а также функции загрузки в облако. Если у вас есть какие-либо вопросы, оставьте мне сообщение, и редактор свяжется с вами. ответить вам вовремя. Я также хотел бы поблагодарить всех за поддержку сайта боевых искусств VeVb!
Если вы считаете, что эта статья вам полезна, вы можете ее перепечатать, укажите источник, спасибо!