cropper下載? ?https://download.csdn.net/download/dongyan3595/90970115
前端代碼
<!doctype html>
<html lang="en">
<head><base href="/aishop/"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta charset="UTF-8"/><link rel="stylesheet" type="text/css" href="assets/fonts/font-awesome/css/font-awesome.css"/><link rel="stylesheet" type="text/css" href="assets/js/vendor/bootstrap/css/bootstrap.min.css"/><link rel="stylesheet" type="text/css" href="assets/js/vendor/cropper/cropper.min.css"/><style>.upload-image {height: 100%; opacity: 0;}.upload-image-preview {width: 200px;height: 200px;overflow: hidden;margin: 0 44px;display: inline-block;}#imageBox {height: 451px;overflow: auto;background-color: #F1F1F1;}#imagePreviewBox .upload-image-preview {background-color: #F1F1F1;}.cropper-container {height:432px!important;width: 529px!important;}.cropper-crop-box{max-height:430px!important;max-width:527px!important;}.mosaic-canvas {position: absolute;top: 0;left: 0;pointer-events: auto; /* 改為auto,允許鼠標事件 */z-index: 1000;}.mosaic-active {cursor: crosshair !important;}</style>
</head>
<div><div id="content" class="edit-content" style="padding: 5px; opacity: 0;"><div id="imageBox" class="col-xs-8" style="position: relative;"><img id="image" class="upload-image" src="assets/images/ruanzhu.jpg"><canvas id="mosaicCanvas" class="mosaic-canvas" style="display:none;"></canvas></div><div id="imagePreviewBox" class="col-xs-4"><div class="upload-image-preview"></div></div><div class="col-xs-4" style="text-align: center;"><button id="dragBtn" type="button" class="btn btn-danger fa fa-arrows" onclick="dragImage()" title="移動"></button><button type="button" class="btn btn-danger fa fa-search-plus" onclick="zoomPlus()" title="放大圖片"></button><button type="button" class="btn btn-danger fa fa-search-minus" onclick="zoomMinus()" title="縮小圖片"></button><button type="button" class="btn btn-danger fa fa-refresh" onclick="reset()" title="重置圖片"></button><button id="mosaicBtn" type="button" class="btn btn-warning fa fa-th" onclick="toggleMosaic()" title="馬賽克"></button><button type="button" class="btn btn-info fa fa-reply" onclick="undoEdit()" title="撤銷" id="undoBtn" disabled></button></div><div class="col-xs-4" style="margin-top: 5px; text-align: center;"><div class="btn-group"><button class="btn btn-danger fa fa-undo" onclick="rateLeft()" type="button" title="向左旋轉90°"> 向左旋轉</button></div><div class="btn-group"><button class="btn btn-danger fa fa-repeat" onclick="rateRight()" type="button" title="向右旋轉90°"> 向右旋轉</button></div></div><div class="col-xs-4" style="margin-top: 5px; text-align: center;"><div class="btn-group"><form id="uploadFileForm" action="#" style="display: none;"><input type="file" id="uploadImage" name="image" accept="image/jpeg,image/gif,image/png,image/jpg" onchange="doUploadFile()"/></form><button class="btn btn-primary btn-block fa fa-upload" type="button" onclick="openUploadImage()"> 上傳圖片</button></div><div class="btn-group"><button class="btn btn-primary btn-block fa fa-save" type="button" onclick="doCrop()"> 裁剪上傳</button></div></div><div class="col-xs-12" style="margin-top: 5px; text-align: center;"><div class="form-group"><label for="mosaicSize">馬賽克大小:</label><input type="range" id="mosaicSize" min="5" max="30" value="10" class="form-control"><span id="mosaicSizeValue">10px</span></div></div><div class="col-xs-12" style="margin-top: 5px; text-align: center;"><div class="form-group"><label for="imageSize">圖片壓縮:</label><input type="range" id="imageSize" min="10" max="100" value="100" class="form-control"><span id="imageSizeValue">100%</span></div></div></div>
</div>
<script type="text/javascript" src="assets/js/jquery-3.5.1.min.js"></script>
<script type="text/javascript" src="assets/js/vendor/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="assets/js/vendor/cropper/cropper.min.js"></script>
<script type="text/javascript" src="assets/js/common.js"></script>
<script src="assets/layuiadmin/layui/layui.js"></script>
<script type="text/javascript">var toggleMosaic = function() {}var undoEdit = function() {}var doCrop = function() {}var zoomPlus= function() {}var zoomMinus= function() {}var reset= function() {}var rateLeft= function() {}var rateRight= function() {}var dragImage= function() {}var openUploadImage= function() {}var doUploadFile= function() {}layui.config({base: 'assets/layuiadmin/'}).extend({index: 'lib/index'}).use(['index', 'restajax', 'datamessage', 'dialog'], function() {var restAjax = layui.restajax;var dialog = layui.dialog;var dataMessage = layui.datamessage;function doSubmitForm() {return false;}var cropper;var isMosaicMode = false;var mosaicCanvas = document.getElementById('mosaicCanvas');var mosaicCtx = mosaicCanvas.getContext('2d');var isDrawing = false;var lastX, lastY;var editHistory = [];var currentHistoryIndex = -1;var maxHistorySteps = 10;// 放大zoomPlus = function () {cropper.zoom(0.1);}// 縮小zoomMinus = function () {cropper.zoom(-0.1);}// 逆時針90rateLeft = function () {cropper.rotate(-90);}// 順時針90rateRight = function () {cropper.rotate(90);}// 重置reset = function () {cropper.reset();}// 圖片移動var isDrag = false;dragImage = function () {if (!isDrag) {cropper.setDragMode('move');isDrag = true;$('#dragBtn').addClass('btn-default');$('#dragBtn').removeClass('btn-danger');} else {cropper.setDragMode('crop');isDrag = false;$('#dragBtn').addClass('btn-danger');$('#dragBtn').removeClass('btn-default');}}// 切換馬賽克模式toggleMosaic = function() {isMosaicMode = !isMosaicMode;if (isMosaicMode) {// 進入馬賽克模式$('#mosaicBtn').addClass('btn-success');$('#mosaicBtn').removeClass('btn-warning');// 保存當前狀態到歷史記錄saveToHistory();// 顯示馬賽克畫布setupMosaicCanvas();$('#mosaicCanvas').show();// 重要:將畫布設置為可接收鼠標事件mosaicCanvas.style.pointerEvents = 'auto';// 添加鼠標事件$(mosaicCanvas).on('mousedown', startMosaic);$(document).on('mousemove', drawMosaic);$(document).on('mouseup mouseleave', stopMosaic);// 添加觸摸事件$(mosaicCanvas).on('touchstart', handleTouchStart);$(document).on('touchmove', handleTouchMove);$(document).on('touchend touchcancel', handleTouchEnd);// 添加馬賽克模式樣式$('#imageBox').addClass('mosaic-active');} else {// 退出馬賽克模式$('#mosaicBtn').addClass('btn-warning');$('#mosaicBtn').removeClass('btn-success');// 重要:將畫布設置為不可接收鼠標事件mosaicCanvas.style.pointerEvents = 'none';// 移除事件監聽$(mosaicCanvas).off('mousedown');$(document).off('mousemove', drawMosaic);$(document).off('mouseup mouseleave', stopMosaic);$(mosaicCanvas).off('touchstart');$(document).off('touchmove', handleTouchMove);$(document).off('touchend touchcancel', handleTouchEnd);// 移除馬賽克模式樣式$('#imageBox').removeClass('mosaic-active');}}// 設置馬賽克畫布function setupMosaicCanvas() {var cropperCanvas = $('.cropper-canvas')[0];var cropperImage = $('.cropper-canvas img')[0];if (cropperCanvas && cropperImage) {var containerRect = cropperCanvas.getBoundingClientRect();var imageBoxRect = $('#imageBox')[0].getBoundingClientRect();// 設置畫布尺寸mosaicCanvas.width = containerRect.width;mosaicCanvas.height = containerRect.height;mosaicCanvas.style.width = containerRect.width + 'px';mosaicCanvas.style.height = containerRect.height + 'px';// 設置畫布位置 - 相對于imageBox計算偏移mosaicCanvas.style.position = 'absolute';mosaicCanvas.style.top = (containerRect.top - imageBoxRect.top) + 'px';mosaicCanvas.style.left = (containerRect.left - imageBoxRect.left) + 'px';// 清除畫布mosaicCtx.clearRect(0, 0, mosaicCanvas.width, mosaicCanvas.height);}}// 開始繪制馬賽克function startMosaic(e) {e.preventDefault(); // 阻止默認行為isDrawing = true;var rect = mosaicCanvas.getBoundingClientRect();lastX = e.clientX - rect.left;lastY = e.clientY - rect.top;// 立即開始繪制一個點drawMosaicEffect(lastX, lastY, lastX, lastY);}// 處理觸摸開始事件function handleTouchStart(e) {e.preventDefault();var touch = e.originalEvent.touches[0];var rect = mosaicCanvas.getBoundingClientRect();lastX = touch.clientX - rect.left;lastY = touch.clientY - rect.top;isDrawing = true;}// 處理觸摸移動事件function handleTouchMove(e) {if (!isDrawing || !isMosaicMode) return;e.preventDefault();var touch = e.originalEvent.touches[0];var rect = mosaicCanvas.getBoundingClientRect();var x = touch.clientX - rect.left;var y = touch.clientY - rect.top;drawMosaicEffect(lastX, lastY, x, y);lastX = x;lastY = y;}// 處理觸摸結束事件function handleTouchEnd(e) {isDrawing = false;}// 繪制馬賽克function drawMosaic(e) {if (!isDrawing || !isMosaicMode) return;var rect = mosaicCanvas.getBoundingClientRect();var x = e.clientX - rect.left;var y = e.clientY - rect.top;drawMosaicEffect(lastX, lastY, x, y);lastX = x;lastY = y;}// 繪制馬賽克效果function drawMosaicEffect(startX, startY, endX, endY) {var cropperImage = $('.cropper-canvas img')[0];if (!cropperImage) return;var mosaicSize = parseInt($('#mosaicSize').val());var dx = endX - startX;var dy = endY - startY;var distance = Math.sqrt(dx * dx + dy * dy);var steps = Math.max(Math.floor(distance), 1);for (var i = 0; i <= steps; i++) {var x = startX + (dx * i / steps);var y = startY + (dy * i / steps);// 計算馬賽克塊的左上角坐標var mosaicX = Math.floor(x / mosaicSize) * mosaicSize;var mosaicY = Math.floor(y / mosaicSize) * mosaicSize;// 從原始圖像獲取像素數據var cropperRect = cropperImage.getBoundingClientRect();var cropperCanvas = document.createElement('canvas');cropperCanvas.width = mosaicSize;cropperCanvas.height = mosaicSize;var cropperCtx = cropperCanvas.getContext('2d');try {// 獲取馬賽克區域的圖像數據cropperCtx.drawImage(cropperImage,mosaicX, mosaicY, mosaicSize, mosaicSize,0, 0, mosaicSize, mosaicSize);// 計算平均顏色var imageData = cropperCtx.getImageData(0, 0, mosaicSize, mosaicSize);var data = imageData.data;var r = 0, g = 0, b = 0, a = 0, count = 0;for (var j = 0; j < data.length; j += 4) {r += data[j];g += data[j + 1];b += data[j + 2];a += data[j + 3];count++;}// 計算平均值r = Math.round(r / count);g = Math.round(g / count);b = Math.round(b / count);a = Math.round(a / count);// 繪制馬賽克塊mosaicCtx.fillStyle = `rgba(${r}, ${g}, ${b}, ${a / 255})`;mosaicCtx.fillRect(mosaicX, mosaicY, mosaicSize, mosaicSize);} catch (error) {console.error('馬賽克繪制錯誤:', error);}}}// 停止繪制馬賽克function stopMosaic() {if (isDrawing) {isDrawing = false;saveToHistory();}}// 保存到歷史記錄function saveToHistory() {// 如果當前不是最新狀態,刪除后面的歷史if (currentHistoryIndex >= 0 && currentHistoryIndex < editHistory.length - 1) {editHistory = editHistory.slice(0, currentHistoryIndex + 1);}// 獲取當前畫布狀態var imageData = null;if (mosaicCanvas.width > 0 && mosaicCanvas.height > 0) {try {imageData = mosaicCtx.getImageData(0, 0, mosaicCanvas.width, mosaicCanvas.height);} catch (e) {console.error('獲取畫布數據失敗:', e);}}// 添加到歷史記錄if (imageData) {editHistory.push(imageData);// 限制歷史記錄數量if (editHistory.length > maxHistorySteps) {editHistory.shift();}currentHistoryIndex = editHistory.length - 1;// 啟用撤銷按鈕$('#undoBtn').prop('disabled', false);}}// 撤銷編輯undoEdit = function () {if (currentHistoryIndex > 0) {currentHistoryIndex--;var imageData = editHistory[currentHistoryIndex];// 恢復到之前的狀態mosaicCtx.putImageData(imageData, 0, 0);} else if (currentHistoryIndex === 0) {// 恢復到初始狀態(清空畫布)mosaicCtx.clearRect(0, 0, mosaicCanvas.width, mosaicCanvas.height);currentHistoryIndex = -1;$('#undoBtn').prop('disabled', true);}}// 重置馬賽克畫布和歷史記錄function resetMosaicCanvas() {if (mosaicCanvas) {mosaicCtx.clearRect(0, 0, mosaicCanvas.width, mosaicCanvas.height);}editHistory = [];currentHistoryIndex = -1;$('#undoBtn').prop('disabled', true);}// 打開上傳圖片openUploadImage = function () {$('#uploadImage').click();}// 添加鍵盤快捷鍵支持$(document).on('keydown', function(e) {// 檢測 CTRL + Zif (e.ctrlKey && e.keyCode === 90) {// 阻止瀏覽器默認的撤銷行為e.preventDefault();// 調用撤銷函數undoEdit();}});// 上傳文件doUploadFile = function () {var loadLayerIndex;var formData = new FormData($('#uploadFileForm')[0]);restAjax.postFile('api/goods/upload-image', formData, {}, function (code, data) {dialog.msg('上傳成功');cropper.replace('route/file/download/false/' + data.data, false);localStorage.setItem('uploadImage', data.data);// 重置馬賽克畫布和歷史記錄resetMosaicCanvas();}, function(code, data) {dialog.msg(data.msg);}, function() {loadLayerIndex = dialog.msg(dataMessage.uploading, {icon: 16, time: 0, shade: 0.3});}, function() {dialog.close(loadLayerIndex);});}// 裁剪doCrop = function () {// 獲取裁剪后的畫布var croppedCanvas = cropper.getCroppedCanvas();// 如果有馬賽克效果,將馬賽克合并到裁剪后的圖像上if (mosaicCanvas.width > 0 && mosaicCanvas.height > 0) {var cropperCanvas = $('.cropper-canvas')[0];var cropBox = $('.cropper-crop-box')[0];if (cropperCanvas && cropBox) {var cropBoxRect = cropBox.getBoundingClientRect();var cropperRect = cropperCanvas.getBoundingClientRect();// 計算裁剪框相對于畫布的位置var left = cropBoxRect.left - cropperRect.left;var top = cropBoxRect.top - cropperRect.top;// 獲取裁剪框的尺寸var width = cropBoxRect.width;var height = cropBoxRect.height;// 在裁剪后的畫布上繪制馬賽克var ctx = croppedCanvas.getContext('2d');ctx.drawImage(mosaicCanvas,left, top, width, height,0, 0, croppedCanvas.width, croppedCanvas.height);}}croppedCanvas.toBlob(function (cropBlob) {var formData = new FormData();formData.append("file", cropBlob);formData.append("picturesThumbnails", $('#imageSize').val());var loadLayerIndex;restAjax.postFile('api/goods/upload-image', formData, {}, function (code, data) {dialog.msg('裁剪成功');// 完全重置馬賽克畫布和歷史記錄resetMosaicCanvas();// 替換圖像cropper.replace('route/file/download/false/' + data.data, false);localStorage.setItem('uploadImage', data.data);// 在圖像加載完成后重新設置馬賽克畫布setTimeout(function() {setupMosaicCanvas();}, 500);}, function(code, data) {dialog.msg(data.msg);}, function() {loadLayerIndex = dialog.msg(dataMessage.uploading, {icon: 16, time: 0, shade: 0.3});}, function() {dialog.close(loadLayerIndex);});});}$(function () {var image = document.getElementById('image');cropper = new Cropper(image, {aspectRatio: 1.5 / 2,viewMode: 1,minContainerHeight: 430,maxContainerHeight: 430,preview: '.upload-image-preview',crop(event) {// 如果在馬賽克模式下,實時更新馬賽克畫布位置if (isMosaicMode) {setupMosaicCanvas();}},ready() {// 初始化馬賽克畫布setTimeout(function() {setupMosaicCanvas();}, 500);}});$('#content').fadeTo(1000, 1);var fileId = restAjax.params(window.location.href).fileId;if (fileId != 'undefined' && fileId != undefined && fileId.length > 1) {localStorage.setItem('uploadImage', fileId);cropper.replace('route/file/download/false/' + fileId, false);}// 馬賽克大小滑塊事件$('#mosaicSize').on('input', function() {var size = $(this).val();$('#mosaicSizeValue').text(size + 'px');});$('#imageSize').on('input', function() {var size = $(this).val();$('#imageSizeValue').text(size + '%');});// 確保馬賽克畫布初始化setTimeout(function() {setupMosaicCanvas();}, 1000); // 延遲1秒,確保cropper已完全初始化})});
</script>
</body>
</html>
后臺代碼
@PostMapping({"upload-image"})public SuccessResultData<String> uploadFile(@RequestParam("file") MultipartFile file, Double picturesThumbnails) throws IOException {// 參數校驗if (file.isEmpty()) {throw new SaveException("上傳文件不能為空");}InputStream inputStream = file.getInputStream();BufferedImage bufferedImage = ImageIO.read(inputStream);if (bufferedImage == null) {throw new SaveException("無效的圖片文件");}BufferedImage image = bufferedImage;if (picturesThumbnails != null) {try {image = Thumbnails.of(new BufferedImage[]{bufferedImage}).scale(1).outputQuality(picturesThumbnails / 100).outputFormat("jpg").asBufferedImage();} catch (IOException e) {throw new SaveException("圖片壓縮出現異常");}}MultipartFile file1 = new MockMultipartFile("file", // 參數名(表單中的name)UUIDUtil.get32UUID() + ".jpg", // 原始文件名"image/jpeg",convertToInputStream(image, "jpg") // 文件輸入流);Map<String, Object> params1 = new HashMap<>();String fileId1 = iFileService.uploadSingleByUserId(securityComponent.getCurrentUser().getUserId(), file1, UploadTypeEnum.IMAGE, params1);return new SuccessResultData(fileId1);}