借助 vue-img-cutter 可以在网页端实现图片裁剪功能,最终功能效果如下:
组件 npm 安装
vue
npm install vue-img-cutter@2 --save-dev # for vue2
npm install vue-img-cutter@3 --save-dev # for vue3
vue-img-cutter使用
template
模板标签模块,定义了两个 div 标签,用 flex 做了水平布局,左侧区域为裁剪主区域,右侧为显示裁剪后的效果
vue
<template>
<div>
<div style="display: flex;flex-direction: row;">
<ImgCutter v-on:cutDown="cutDownImg" :WatermarkText="''" :is-modal="false" :tool="false"
@onPrintImg="cutterPrintImg" style="padding: 10px;">
<template #cancel>
<a-button @click="() => {
isShowable = false
}">
取消
</a-button>
</template>
</ImgCutter>
<div style="margin-left: 10px ; padding: 10px;">
<div style="margin-left: 10px; padding-bottom: 20px; font-size: 18px;font-weight: bold;">
裁剪后图像
</div>
<div style="padding: 2px; background-color: rgba(0,0,0,0.1);">
<img :src="temImgPath" style="width: 100%;" />
</div>
</div>
</div>
</template>
在 demo 中,主要用到了 vue-img-cutter 组件中两个回调函数
onPrintImg
事件函数 接受实时裁剪图片;cutDown
裁剪成功后会触发的回调函数,用来处理裁剪后的图片,例如,本案例中将裁剪后图片上传至 文件服务器上
组件除了提供以上函数外,还提拱了一些其他属性以及信号槽,使用时可以根据功能需求对其进行重写
详情请参考:https://github.com/acccccccb/vue-img-cutter
javascript
<script setup>
import { Client } from '@/utils/Client'
import { message } from 'ant-design-vue'
import ImgCutter from 'vue-img-cutter'
import { ref, defineProps, watch, onMounted, defineEmits } from 'vue'
const emit = defineEmits(['update:value'])
const Props = defineProps({
value: String,
type: {
type: String,
default: 'IMAGE'
}
})
const loading = ref(false)
const isShowable = ref(false)
const currentImgPath = ref('')
const temImgPath = ref('')
const imgForm = ref(null)
watch(
() => Props.value,
(val, o) => {
currentImgPath.value = val
}
)
onMounted(() => {
currentImgPath.value = Props.value
temImgPath.value = currentImgPath.value
})
// 图片裁剪确认后触发
function cutDownImg(option) {
// console.log(fileName)
handleHttpRequest(option)
}
function cutterPrintImg(obj) {
console.log('obj is ', obj)
temImgPath.value = obj.dataURL
}
function onCancel() {
isShowable.value = false
}
// 触发裁剪功能
function touchCutDown() {
isShowable.value = true
}
function randomString(len) {
const len1 = len || 32
const chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz12345678'
const maxPos = chars.length
let pwd = ''
for (let i = 0; i < len1; i++) {
pwd += chars.charAt(Math.floor(Math.random() * maxPos))
}
return pwd
}
async function handleHttpRequest(option) {
// 上传OSS
const client = Client()
// 随机命名
const randomName = `${randomString(6)}_${new Date().getTime()}.${option.file.name
.split('.')
.pop()}`
try {
// 分片上传文件
const result = await client.put(randomName, option.file)
if (result) {
if (result.url.startsWith('http://')) {
result.url = result.url.replace('http://', 'https://')
}
currentImgPath.value = result.url
emit('update:value', currentImgPath.value)
isShowable.value = false
}
} catch (error) {
message.error('上传失败')
}
}
</script>
最终style 样式模块如下
less
<style lang="less" scoped>
.remove-btn-wrap {
position: absolute;
left: 0;
bottom: 0;
right: 0;
top: 0;
text-align: center;
display: none;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.5);
}
.ant-upload:hover .remove-btn-wrap {
display: flex;
}
/deep/ .ant-upload {
img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 2px;
}
}
</style>