feat: 新增ImageCropping
This commit is contained in:
parent
19ccf8b518
commit
b0a43a70e6
|
@ -35,6 +35,7 @@
|
|||
"@zxcvbn-ts/core": "^3.0.4",
|
||||
"animate.css": "^4.1.1",
|
||||
"axios": "^1.6.0",
|
||||
"cropperjs": "^1.6.1",
|
||||
"dayjs": "^1.11.10",
|
||||
"driver.js": "^1.3.0",
|
||||
"echarts": "^5.4.3",
|
||||
|
|
|
@ -1,76 +1,90 @@
|
|||
<script setup lang="ts">
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { CSSProperties, computed } from 'vue'
|
||||
import { nextTick, unref, ref, watch, onBeforeUnmount, onMounted } from 'vue'
|
||||
import Cropper from 'cropperjs'
|
||||
import 'cropperjs/dist/cropper.min.css'
|
||||
|
||||
// interface CropperOptions extends /* @vue-ignore */ Cropper.Options {
|
||||
// imageUrl: string
|
||||
// }
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('image-cropping')
|
||||
|
||||
const bgIcon =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC'
|
||||
|
||||
const props = defineProps({
|
||||
imageUrl: propTypes.string,
|
||||
boxWidth: propTypes.oneOfType([propTypes.number, propTypes.string]).def('100%'),
|
||||
boxHeight: propTypes.oneOfType([propTypes.number, propTypes.string]).def('100%'),
|
||||
dragWidth: propTypes.oneOfType([propTypes.number, propTypes.string]).def(200),
|
||||
dragHeight: propTypes.oneOfType([propTypes.number, propTypes.string]).def(200),
|
||||
cropWidth: propTypes.oneOfType([propTypes.number, propTypes.string]).def(200),
|
||||
cropHeight: propTypes.oneOfType([propTypes.number, propTypes.string]).def(200)
|
||||
})
|
||||
|
||||
const boxStyles = computed((): CSSProperties => {
|
||||
return {
|
||||
width: (typeof props.boxWidth === 'number' ? `${props.boxWidth}px` : props.boxWidth) ?? '100%',
|
||||
height:
|
||||
(typeof props.boxHeight === 'number' ? `${props.boxHeight}px` : props.boxHeight) ?? '100%',
|
||||
position: 'relative',
|
||||
backgroundImage: `url(${bgIcon})`
|
||||
imageUrl: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
},
|
||||
cropBoxWidth: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
cropBoxHeight: {
|
||||
type: Number,
|
||||
default: 200
|
||||
}
|
||||
})
|
||||
|
||||
const dragStyles = computed((): CSSProperties => {
|
||||
return {
|
||||
width: (typeof props.dragWidth === 'number' ? `${props.dragWidth}px` : props.dragWidth) ?? 200,
|
||||
height:
|
||||
(typeof props.dragHeight === 'number' ? `${props.dragHeight}px` : props.dragHeight) ?? 200,
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
zIndex: 1,
|
||||
boxShadow: '0 0 0 1px var(--el-color-primary),0 0 0 10000px rgba(0,0,0,.5)',
|
||||
cursor: 'move'
|
||||
}
|
||||
const imgRef = ref<HTMLImageElement>()
|
||||
const cropperRef = ref<Cropper>()
|
||||
const intiCropper = () => {
|
||||
if (!unref(imgRef)) return
|
||||
const imgEl = unref(imgRef)!
|
||||
cropperRef.value = new Cropper(imgEl, {
|
||||
aspectRatio: 1,
|
||||
viewMode: 1,
|
||||
dragMode: 'move',
|
||||
cropBoxResizable: false,
|
||||
cropBoxMovable: false,
|
||||
toggleDragModeOnDblclick: false,
|
||||
checkCrossOrigin: false,
|
||||
ready() {
|
||||
const containerData = unref(cropperRef)?.getContainerData()
|
||||
unref(cropperRef)?.setCropBoxData({
|
||||
width: props.cropBoxWidth,
|
||||
height: props.cropBoxHeight,
|
||||
left: (containerData?.width || 0) / 2 - 100,
|
||||
top: (containerData?.height || 0) / 2 - 100
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
intiCropper()
|
||||
})
|
||||
|
||||
const cropStyles = computed((): CSSProperties => {
|
||||
return {
|
||||
width: (typeof props.cropWidth === 'number' ? `${props.cropWidth}px` : props.cropWidth) ?? 300,
|
||||
height:
|
||||
(typeof props.cropHeight === 'number' ? `${props.cropHeight}px` : props.cropHeight) ?? 300,
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '80px',
|
||||
transform: 'translate(0, -50%)',
|
||||
overflow: 'hidden',
|
||||
borderRadius: '50%',
|
||||
border: '1px solid var(--el-border-color)'
|
||||
watch(
|
||||
() => props.imageUrl,
|
||||
async (url) => {
|
||||
await nextTick()
|
||||
if (url) {
|
||||
unref(cropperRef)?.replace(url)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
unref(cropperRef)?.destroy()
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
cropperExpose: () => unref(cropperRef)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="prefixCls" class="flex">
|
||||
<div class="flex-1">
|
||||
<div :style="boxStyles">
|
||||
<img :src="imageUrl" class="w-full absolute top-[50%] left-[50%]" alt="" srcset="" />
|
||||
<div :style="dragStyles"> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative w-full">
|
||||
<div :style="cropStyles"></div>
|
||||
</div>
|
||||
<div :class="prefixCls" class="flex justify-center items-center">
|
||||
<img
|
||||
ref="imgRef"
|
||||
:src="imageUrl"
|
||||
class="block max-w-full"
|
||||
crossorigin="anonymous"
|
||||
alt=""
|
||||
srcset=""
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,14 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { ImageCropping } from '@/components/ImageCropping'
|
||||
import { ref, unref } from 'vue'
|
||||
import { ElButton, ElInput } from 'element-plus'
|
||||
|
||||
const cropperExpose = ref<InstanceType<typeof ImageCropping>>()
|
||||
|
||||
const base64 = ref('')
|
||||
|
||||
const getBase64 = () => {
|
||||
base64.value = unref(cropperExpose)?.cropperExpose()?.getCroppedCanvas()?.toDataURL() ?? ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap title="图片裁剪">
|
||||
<ElButton type="primary" class="mb-20px" @click="getBase64">裁剪</ElButton>
|
||||
<ElInput v-model="base64" class="mb-20px" type="textarea" />
|
||||
<ImageCropping
|
||||
:box-width="350"
|
||||
:box-height="380"
|
||||
image-url="https://images6.alphacoders.com/657/thumbbig-657194.webp"
|
||||
ref="cropperExpose"
|
||||
image-url="https://hips.hearstapps.com/hmg-prod/images/%E5%AE%8B%E6%99%BA%E5%AD%9D-1597774015.jpg?crop=0.500xw:1.00xh;0.500xw,0&resize=640:*"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
|
|
@ -152,7 +152,8 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
|||
'vue-json-pretty',
|
||||
'@zxcvbn-ts/core',
|
||||
'dayjs',
|
||||
'mockjs'
|
||||
'mockjs',
|
||||
'cropperjs'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue