Skip to content

Commit b0a43a7

Browse files
feat: 新增ImageCropping
1 parent 19ccf8b commit b0a43a7

File tree

4 files changed

+86
-59
lines changed

4 files changed

+86
-59
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"@zxcvbn-ts/core": "^3.0.4",
3636
"animate.css": "^4.1.1",
3737
"axios": "^1.6.0",
38+
"cropperjs": "^1.6.1",
3839
"dayjs": "^1.11.10",
3940
"driver.js": "^1.3.0",
4041
"echarts": "^5.4.3",
Lines changed: 69 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,90 @@
11
<script setup lang="ts">
22
import { useDesign } from '@/hooks/web/useDesign'
3-
import { propTypes } from '@/utils/propTypes'
4-
import { CSSProperties, computed } from 'vue'
3+
import { nextTick, unref, ref, watch, onBeforeUnmount, onMounted } from 'vue'
4+
import Cropper from 'cropperjs'
5+
import 'cropperjs/dist/cropper.min.css'
6+
7+
// interface CropperOptions extends /* @vue-ignore */ Cropper.Options {
8+
// imageUrl: string
9+
// }
510
611
const { getPrefixCls } = useDesign()
712
813
const prefixCls = getPrefixCls('image-cropping')
914
10-
const bgIcon =
11-
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC'
12-
1315
const props = defineProps({
14-
imageUrl: propTypes.string,
15-
boxWidth: propTypes.oneOfType([propTypes.number, propTypes.string]).def('100%'),
16-
boxHeight: propTypes.oneOfType([propTypes.number, propTypes.string]).def('100%'),
17-
dragWidth: propTypes.oneOfType([propTypes.number, propTypes.string]).def(200),
18-
dragHeight: propTypes.oneOfType([propTypes.number, propTypes.string]).def(200),
19-
cropWidth: propTypes.oneOfType([propTypes.number, propTypes.string]).def(200),
20-
cropHeight: propTypes.oneOfType([propTypes.number, propTypes.string]).def(200)
16+
imageUrl: {
17+
type: String,
18+
default: '',
19+
required: true
20+
},
21+
cropBoxWidth: {
22+
type: Number,
23+
default: 200
24+
},
25+
cropBoxHeight: {
26+
type: Number,
27+
default: 200
28+
}
2129
})
2230
23-
const boxStyles = computed((): CSSProperties => {
24-
return {
25-
width: (typeof props.boxWidth === 'number' ? `${props.boxWidth}px` : props.boxWidth) ?? '100%',
26-
height:
27-
(typeof props.boxHeight === 'number' ? `${props.boxHeight}px` : props.boxHeight) ?? '100%',
28-
position: 'relative',
29-
backgroundImage: `url(${bgIcon})`
30-
}
31+
const imgRef = ref<HTMLImageElement>()
32+
const cropperRef = ref<Cropper>()
33+
const intiCropper = () => {
34+
if (!unref(imgRef)) return
35+
const imgEl = unref(imgRef)!
36+
cropperRef.value = new Cropper(imgEl, {
37+
aspectRatio: 1,
38+
viewMode: 1,
39+
dragMode: 'move',
40+
cropBoxResizable: false,
41+
cropBoxMovable: false,
42+
toggleDragModeOnDblclick: false,
43+
checkCrossOrigin: false,
44+
ready() {
45+
const containerData = unref(cropperRef)?.getContainerData()
46+
unref(cropperRef)?.setCropBoxData({
47+
width: props.cropBoxWidth,
48+
height: props.cropBoxHeight,
49+
left: (containerData?.width || 0) / 2 - 100,
50+
top: (containerData?.height || 0) / 2 - 100
51+
})
52+
}
53+
})
54+
}
55+
56+
onMounted(() => {
57+
intiCropper()
3158
})
3259
33-
const dragStyles = computed((): CSSProperties => {
34-
return {
35-
width: (typeof props.dragWidth === 'number' ? `${props.dragWidth}px` : props.dragWidth) ?? 200,
36-
height:
37-
(typeof props.dragHeight === 'number' ? `${props.dragHeight}px` : props.dragHeight) ?? 200,
38-
position: 'absolute',
39-
top: '50%',
40-
left: '50%',
41-
transform: 'translate(-50%, -50%)',
42-
zIndex: 1,
43-
boxShadow: '0 0 0 1px var(--el-color-primary),0 0 0 10000px rgba(0,0,0,.5)',
44-
cursor: 'move'
60+
watch(
61+
() => props.imageUrl,
62+
async (url) => {
63+
await nextTick()
64+
if (url) {
65+
unref(cropperRef)?.replace(url)
66+
}
4567
}
68+
)
69+
70+
onBeforeUnmount(() => {
71+
unref(cropperRef)?.destroy()
4672
})
4773
48-
const cropStyles = computed((): CSSProperties => {
49-
return {
50-
width: (typeof props.cropWidth === 'number' ? `${props.cropWidth}px` : props.cropWidth) ?? 300,
51-
height:
52-
(typeof props.cropHeight === 'number' ? `${props.cropHeight}px` : props.cropHeight) ?? 300,
53-
position: 'absolute',
54-
top: '50%',
55-
left: '80px',
56-
transform: 'translate(0, -50%)',
57-
overflow: 'hidden',
58-
borderRadius: '50%',
59-
border: '1px solid var(--el-border-color)'
60-
}
74+
defineExpose({
75+
cropperExpose: () => unref(cropperRef)
6176
})
6277
</script>
6378

6479
<template>
65-
<div :class="prefixCls" class="flex">
66-
<div class="flex-1">
67-
<div :style="boxStyles">
68-
<img :src="imageUrl" class="w-full absolute top-[50%] left-[50%]" alt="" srcset="" />
69-
<div :style="dragStyles"> </div>
70-
</div>
71-
</div>
72-
<div class="relative w-full">
73-
<div :style="cropStyles"></div>
74-
</div>
80+
<div :class="prefixCls" class="flex justify-center items-center">
81+
<img
82+
ref="imgRef"
83+
:src="imageUrl"
84+
class="block max-w-full"
85+
crossorigin="anonymous"
86+
alt=""
87+
srcset=""
88+
/>
7589
</div>
7690
</template>
Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
<script setup lang="ts">
22
import { ContentWrap } from '@/components/ContentWrap'
33
import { ImageCropping } from '@/components/ImageCropping'
4+
import { ref, unref } from 'vue'
5+
import { ElButton, ElInput } from 'element-plus'
6+
7+
const cropperExpose = ref<InstanceType<typeof ImageCropping>>()
8+
9+
const base64 = ref('')
10+
11+
const getBase64 = () => {
12+
base64.value = unref(cropperExpose)?.cropperExpose()?.getCroppedCanvas()?.toDataURL() ?? ''
13+
}
414
</script>
515

616
<template>
717
<ContentWrap title="图片裁剪">
18+
<ElButton type="primary" class="mb-20px" @click="getBase64">裁剪</ElButton>
19+
<ElInput v-model="base64" class="mb-20px" type="textarea" />
820
<ImageCropping
9-
:box-width="350"
10-
:box-height="380"
11-
image-url="https://images6.alphacoders.com/657/thumbbig-657194.webp"
21+
ref="cropperExpose"
22+
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:*"
1223
/>
1324
</ContentWrap>
1425
</template>

vite.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
152152
'vue-json-pretty',
153153
'@zxcvbn-ts/core',
154154
'dayjs',
155-
'mockjs'
155+
'mockjs',
156+
'cropperjs'
156157
]
157158
}
158159
}

0 commit comments

Comments
 (0)