Skip to content

Commit

Permalink
feat: 新增ImageCropping
Browse files Browse the repository at this point in the history
  • Loading branch information
kailong321200875 committed Nov 15, 2023
1 parent 19ccf8b commit b0a43a7
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 59 deletions.
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -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",
Expand Down
124 changes: 69 additions & 55 deletions src/components/ImageCropping/src/ImageCropping.vue
@@ -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)
imageUrl: {
type: String,
default: '',
required: true
},
cropBoxWidth: {
type: Number,
default: 200
},
cropBoxHeight: {
type: Number,
default: 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})`
}
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 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'
watch(
() => props.imageUrl,
async (url) => {
await nextTick()
if (url) {
unref(cropperRef)?.replace(url)
}
}
)
onBeforeUnmount(() => {
unref(cropperRef)?.destroy()
})
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)'
}
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>
17 changes: 14 additions & 3 deletions src/views/Components/ImageCropping.vue
@@ -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>
3 changes: 2 additions & 1 deletion vite.config.ts
Expand Up @@ -152,7 +152,8 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
'vue-json-pretty',
'@zxcvbn-ts/core',
'dayjs',
'mockjs'
'mockjs',
'cropperjs'
]
}
}
Expand Down

0 comments on commit b0a43a7

Please sign in to comment.