这里把 图片裁剪组件封装起来。参数展示的东西都用父组件提供:
<template id="vueImageEditor" v-cloak>
<div class="flex space-x-5">
<div>
<div class="bg-[#ffffff] rounded-md w-390px p-6 shadow-md">
<div class="min-h-80px max-h-261px h-[calc(100vh-600px)] rounded-md"
style="box-shadow: 0 10px 8px rgba(0, 0, 0, 0.2);">
<img :src="imageUrl" class="w-full h-full object-contain rounded" />
</div>
<div class="mt-4 space-y-4 text-12px">
<div class="flex item-center">
<div class="w-100px text-left text-[#606266] pr-3 font-bold">
File name
</div>
<div class="flex-1">
<span class="text-[#999] break-all">
{{props.fileName}}
</span>
</div>
</div>
<div class="flex item-center">
<div class="w-100px text-left text-[#606266] pr-3 font-bold">
URL
</div>
<div class="flex-1">
<a class="text-[#999] break-all hover:underline" :href="previewUrl" target="_blank">
{{props.previewUrl}}
</a>
</div>
</div>
</div>
</div>
<div class="bg-[#ffffff] rounded-md w-390px p-6 mt-4 shadow-md">
<div class="flex items-center justify-between text-12px">
<span class="w-16 border-1 border-blue-300 cursor-pointer text-center rounded-md py-1.5"
:class="item.ratio===currentRatio?'isCheck':''" v-for="item in ratios" :key="item.text"
@click="handleChangeRatio(item.ratio)">{{item.text}}</span>
</div>
<div class="flex items-center my-4 space-x-2">
<el-input v-model.number="size.width" class="mr-1 widthInput"
@change="handleChangeWidth(size.width)">
<template #append><span class="">X</span></template>
</el-input>
<el-icon class="text-lg">
<Lock />
</el-icon>
<el-input v-model.number="size.height" class="mr-1 heightInput" @change="handleChangeHeight">
<template #append><span class="">Y</span></template>
</el-input>
<el-input v-model.number="angle" class="mr-1 angleInput" @change="handleChangeAngle">
<template #append><span class=" absolute -top-2.5">。</span></template>
</el-input>
</div>
<div class="text-right">
<el-button round type="primary" class="bg-[#409eff] text-[#ffffff] w-120px" size="large"
@click="handleCrop">Crop
</el-button>
</div>
</div>
</div>
<div class="rounded-md px-6 w-720px flex items-center justify-center h-auto shadow-md">
<div class="flex justify-center min-h-300px min-w-300px max-h-562px">
<cropper ref="cropper" :src="imageUrl" :stencil-props="{aspectRatio: currentRatio}"
@change="handleChangeCrop"></cropper>
</div>
</div>
</div>
</template>
<script>
const vueImageEditor = {
template: '#vueImageEditor',
components: {
cropper: VueAdvancedCropper.Cropper,
},
props: {
fileName: String,
previewUrl: String,
imageUrl:String,
},
emits: ['update:imageUrl'],
setup(props, {emit}) {
const {ref} = Vue
const size = ref({
width: 0,
height: 0,
});
const cropper = ref()
const handleCrop = () => {
console.log("cropper", cropper.value, cropper.value.getResult(), props);
if (!cropper.value) {
return;
}
const {canvas} = cropper.value.getResult();
console.log(" canvas.toDataURL()", canvas.toDataURL());
if (canvas) {
emit("update:imageUrl", canvas.toDataURL());
}
}
const handleChangeWidth = (width) => {
const transform = {
width,
}
if (currentRatio.value > 0) {
transform.height = size.value.width * currentRatio.value;
}
cropper.value.setCoordinates(transform);
}
const handleChangeHeight = () => {
if (currentRatio.value > 0) {
const width = size.value.height * currentRatio.value;
handleChangeWidth(width);
} else {
cropper.value.setCoordinates({
height: size.value.height,
});
}
}
const handleChangeCrop = () => {
setSize();
}
const ratios = [
{
text: "Free",
ratio: 0,
},
{
text: "16:9",
ratio: 16 / 9,
},
{
text: "4:3",
ratio: 4 / 5,
},
{
text: "1:1",
ratio: 1 / 1,
},
{
text: "2:3",
ratio: 2 / 3,
},
];
const angle = ref(0)
const currentRatio = ref(0)
const handleChangeRatio = (ratio) => {
cropper.value.reset();
currentRatio.value = ratio;
setSize();
}
const setSize = () => {
setTimeout(() => {
if (cropper.value) {
const {coordinates} = cropper.value.getResult();
size.value.width = coordinates.width;
size.value.height = coordinates.height;
}
}, 100);
}
let oldAngle = angle.value;
const handleChangeAngle = () => {
if (isNaN(angle.value)) {
angle.value = oldAngle;
return;
}
cropper.value.rotate(angle.value - oldAngle);
oldAngle = angle.value;
}
return {
size,
cropper,
handleCrop,
ratios,
handleChangeRatio,
currentRatio,
setSize,
angle,
handleChangeHeight,
handleChangeWidth,
handleChangeCrop,
handleChangeAngle,
oldAngle,
props
}
}
};
</script>
父组件使用:
<vue-image-Editor :file-name="media.name" :preview-url="media.url" v-model:image-url="media.imageUrl"></vue-image-editor>