应该是CropperJS的版本问题,2和1的操作方式不一样,使用2版本的方式 功能ok,可以测试

This commit is contained in:
2025-11-19 18:53:05 +08:00
parent 725cd07a45
commit c0525ed476
@@ -1,131 +1,217 @@
<template> <template>
<div class="image-cropper"> <div class="basic_container">
<div class="cropper-actions"> <div class="tool_wrap">
<input <button :class="{ active: isCropperMove }" @click="handleMove">
type="file" 移动
ref="fileInput" </button>
@change="handleFileSelect" <button @click="handleRotate">旋转</button>
accept="image/*" <button :class="{ active: isCropperSelection }" @click="handleCropper">
style="display: none" {{ isCropperSelection ? '重置选区' : '裁剪' }}
/> </button>
<button @click="$refs.fileInput.click()">选择图片</button>
<div v-if="haveImageSrc" class="action-buttons">
<button @click="setAspectRatio(1)">1:1</button>
<button @click="setAspectRatio(16 / 9)">16:9</button>
<button @click="setAspectRatio(4 / 3)">4:3</button>
<button @click="rotate(-90)"></button>
<button @click="rotate(90)"></button>
<button @click="crop">裁剪</button>
<button @click="reset">重置</button>
</div>
</div> </div>
<div class="dialog_wrap">
<div v-if="haveImageSrc" class="cropper-container"> <div class="image_wrap" ref="imageWrap">
<img ref="imageEl" alt="裁剪图片" /> <cropper-canvas ref="croppercanvas" background>
<cropper-image
:src="fileObj.fileShow"
alt="Picture"
ref="cropperimage"
rotatable
scalable
skewable
translatable
></cropper-image>
<cropper-shade hidden ref="cropperShade"></cropper-shade>
<cropper-handle :action="currentType" plain></cropper-handle>
<cropper-selection
id="cropperSelection"
ref="cropperselection"
movable
resizable
hidden
outlined
@change="onCropperSelectionChange"
>
<cropper-crosshair centered />
<cropper-handle
action="move"
theme-color="rgba(255, 255, 255, 0.35)"
/>
<cropper-handle action="n-resize" />
<cropper-handle action="e-resize" />
<cropper-handle action="s-resize" />
<cropper-handle action="w-resize" />
<cropper-handle action="ne-resize" />
<cropper-handle action="nw-resize" />
<cropper-handle action="se-resize" />
<cropper-handle action="sw-resize" />
</cropper-selection>
</cropper-canvas>
</div>
<div class="info_wrap">
<div class="cropper_preview">
<cropper-viewer
selection="#cropperSelection"
style="width: 200px"
></cropper-viewer>
</div>
<div class="btn_wrap">
<input type="file" ref="input_form" @change="handleUploadSuccess" />
<button type="primary" @click="handleConfirm"> </button>
</div>
点击确认后看控制台有信息
</div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, nextTick } from "vue"; import 'cropperjs';
import Cropper from "cropperjs"; import { computed, ref } from 'vue';
const emit = defineEmits(["cropped", "error"]); const fileObj = ref({});
const imageEl = ref(null); const croppercanvas = ref();
const fileInput = ref(null); const cropperimage = ref();
const haveImageSrc = ref(false); const cropperselection = ref();
const resultImage = ref("");
let cropperInstance = null;
// 读取文件为DataURL /**
const readFileAsDataURL = (file) => { * 选区逻辑
return new Promise((resolve, reject) => { */
const reader = new FileReader(); // 是否正在开始选区
reader.onload = (e) => resolve(e.target.result); const isCropperSelection = ref(false);
reader.onerror = reject; const isCropperMove = ref(true);
reader.readAsDataURL(file);
});
};
// 选择文件 // 判断当前是移动还是选区
const handleFileSelect = async (event) => { const currentType = computed(() => (isCropperMove.value ? 'move' : 'select'));
const file = event.target.files[0];
if (!file) return;
if (!file.type.startsWith("image/")) { /**
emit("error", "请选择图片文件"); * 按钮方法
return; */
// 旋转
function handleRotate() {
cropperimage.value.$rotate('90deg');
cropperimage.value.$center('contain');
}
// 裁剪
function handleCropper() {
isCropperMove.value = false;
if (isCropperMove.value) {
cropperselection.value.$clear();
} else {
const cropperCanvas = croppercanvas.value;
const cropperCanvasRect = cropperCanvas.getBoundingClientRect();
const cropperImage = cropperimage.value;
const cropperImageRect = cropperImage.getBoundingClientRect();
const maxSelection = {
x: cropperImageRect.left - cropperCanvasRect.left,
y: cropperImageRect.top - cropperCanvasRect.top,
width: cropperImageRect.width,
height: cropperImageRect.height,
};
cropperselection.value.$change(
maxSelection.x,
maxSelection.y,
maxSelection.width,
maxSelection.height,
);
} }
console.log("选择了文件:", file.name); }
try { // 移动
const dataUrl = await readFileAsDataURL(file); function handleMove() {
haveImageSrc.value = true; if (!isCropperMove.value) {
console.log("图片加载完成,初始化裁剪器"); isCropperMove.value = true;
await nextTick(); // 如果想要点击移动,清除选区,可以打开下面的代码注释
if (cropperInstance) { // cropperselection.value.$clear();
cropperInstance.destroy(); }
console.log("销毁旧的Cropper实例"); }
}
console.log("加载图片进行裁剪"); /**
imageEl.value.src = dataUrl; * 监听选择区变化
* @param event
cropperInstance = new Cropper(imageEl.value, { */
aspectRatio: 1, function onCropperSelectionChange(event) {
viewMode: 2, if (event.detail.width && event.detail.height) {
autoCropArea: 0.8, isCropperSelection.value = true;
zoomable: true, } else {
zoomOnWheel: true, isCropperSelection.value = false;
zoomOnTouch: true, }
wheelZoomRatio: 0.1, }
ready: function () {
console.log("Cropper 初始化成功"); /**
}, * 确认裁剪
crop: function (event) { */
// 可以在这里获取裁剪区域的信息 const emit = defineEmits(['success']);
console.log(event.detail); async function handleConfirm() {
}, if (isCropperSelection.value) {
const res = await cropperselection.value.$toCanvas();
const dataImage = res.toDataURL('image/png');
const file = dataURLtoFile(dataImage, fileObj.value.name);
emit('success', {
...fileObj.value,
file: file,
fileShow: dataImage,
}); });
} catch (error) {
emit("error", "图片加载失败");
} }
}; }
// 将data:image转成新的file
function dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
const blob = new Blob([u8arr], { type: mime });
const file = new File([blob], filename, { type: mime });
return file;
}
/**
* 文件上传
*/
const input_form = ref();
function handleUploadSuccess() {
const files = input_form.value.files;
if (files.length) {
fileObj.value = {
name: files[0].name,
file: files[0],
fileShow: URL.createObjectURL(files[0]),
};
}
}
</script> </script>
<style scoped> <style scoped>
.image-cropper { .dialog_wrap {
max-width: 600px; display: flex;
margin: 0 auto; .image_wrap {
} width: 400px;
height: 300px;
flex-shrink: 0;
.cropper-actions { cropper-canvas {
margin-bottom: 20px; width: 100%;
height: 100%;
}
}
.info_wrap {
margin-left: 20px;
}
} }
button {
.action-buttons { & + button {
margin-top: 10px; margin-left: 20px;
}
} }
button.active {
.action-buttons button { background-color: #c6dff8;
margin: 0 5px 5px 0; border-color: #409eff;
padding: 5px 10px;
}
.cropper-container {
max-height: 400px;
overflow: hidden;
}
.result-container {
margin-top: 20px;
text-align: center;
}
.result-container img {
max-width: 300px;
max-height: 300px;
border: 1px solid #ddd;
margin: 10px 0;
} }
</style> </style>