沙钢智慧能源系统前端代码
houzhongjian
2024-10-09 314507f8ddadd9c66e98d260c3b2a5dad1a04015
提交 | 用户 | 时间
314507 1 <template>
H 2   <div :class="getClass" :style="getWrapperStyle">
3     <img
4       v-show="isReady"
5       ref="imgElRef"
6       :alt="alt"
7       :crossorigin="crossorigin"
8       :src="src"
9       :style="getImageStyle"
10     />
11   </div>
12 </template>
13 <script lang="ts" setup>
14 import { CSSProperties, PropType } from 'vue'
15 import Cropper from 'cropperjs'
16 import 'cropperjs/dist/cropper.css'
17 import { useDesign } from '@/hooks/web/useDesign'
18 import { propTypes } from '@/utils/propTypes'
19 import { useDebounceFn } from '@vueuse/core'
20
21 defineOptions({ name: 'Cropper' })
22
23 type Options = Cropper.Options
24
25 const defaultOptions: Options = {
26   aspectRatio: 1,
27   zoomable: true,
28   zoomOnTouch: true,
29   zoomOnWheel: true,
30   cropBoxMovable: true,
31   cropBoxResizable: true,
32   toggleDragModeOnDblclick: true,
33   autoCrop: true,
34   background: true,
35   highlight: true,
36   center: true,
37   responsive: true,
38   restore: true,
39   checkCrossOrigin: true,
40   checkOrientation: true,
41   scalable: true,
42   modal: true,
43   guides: true,
44   movable: true,
45   rotatable: true
46 }
47
48 const props = defineProps({
49   src: propTypes.string.def(''),
50   alt: propTypes.string.def(''),
51   circled: propTypes.bool.def(false),
52   realTimePreview: propTypes.bool.def(true),
53   height: propTypes.string.def('360px'),
54   crossorigin: {
55     type: String as PropType<'' | 'anonymous' | 'use-credentials' | undefined>,
56     default: undefined
57   },
58   imageStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
59   options: { type: Object as PropType<Options>, default: () => ({}) }
60 })
61
62 const emit = defineEmits(['cropend', 'ready', 'cropendError'])
63 const attrs = useAttrs()
64 const imgElRef = ref<ElRef<HTMLImageElement>>()
65 const cropper = ref<Nullable<Cropper>>()
66 const isReady = ref(false)
67
68 const { getPrefixCls } = useDesign()
69 const prefixCls = getPrefixCls('cropper-image')
70 const debounceRealTimeCroppered = useDebounceFn(realTimeCroppered, 80)
71
72 const getImageStyle = computed((): CSSProperties => {
73   return {
74     height: props.height,
75     maxWidth: '100%',
76     ...props.imageStyle
77   }
78 })
79
80 const getClass = computed(() => {
81   return [
82     prefixCls,
83     attrs.class,
84     {
85       [`${prefixCls}--circled`]: props.circled
86     }
87   ]
88 })
89 const getWrapperStyle = computed((): CSSProperties => {
90   return { height: `${props.height}`.replace(/px/, '') + 'px' }
91 })
92
93 onMounted(init)
94
95 onUnmounted(() => {
96   cropper.value?.destroy()
97 })
98
99 async function init() {
100   const imgEl = unref(imgElRef)
101   if (!imgEl) {
102     return
103   }
104   cropper.value = new Cropper(imgEl, {
105     ...defaultOptions,
106     ready: () => {
107       isReady.value = true
108       realTimeCroppered()
109       emit('ready', cropper.value)
110     },
111     crop() {
112       debounceRealTimeCroppered()
113     },
114     zoom() {
115       debounceRealTimeCroppered()
116     },
117     cropmove() {
118       debounceRealTimeCroppered()
119     },
120     ...props.options
121   })
122 }
123
124 // Real-time display preview
125 function realTimeCroppered() {
126   props.realTimePreview && croppered()
127 }
128
129 // event: return base64 and width and height information after cropping
130 function croppered() {
131   if (!cropper.value) {
132     return
133   }
134   let imgInfo = cropper.value.getData()
135   const canvas = props.circled ? getRoundedCanvas() : cropper.value.getCroppedCanvas()
136   canvas.toBlob((blob) => {
137     if (!blob) {
138       return
139     }
140     let fileReader: FileReader = new FileReader()
141     fileReader.readAsDataURL(blob)
142     fileReader.onloadend = (e) => {
143       emit('cropend', {
144         imgBase64: e.target?.result ?? '',
145         imgInfo
146       })
147     }
148     fileReader.onerror = () => {
149       emit('cropendError')
150     }
151   }, 'image/png')
152 }
153
154 // Get a circular picture canvas
155 function getRoundedCanvas() {
156   const sourceCanvas = cropper.value!.getCroppedCanvas()
157   const canvas = document.createElement('canvas')
158   const context = canvas.getContext('2d')!
159   const width = sourceCanvas.width
160   const height = sourceCanvas.height
161   canvas.width = width
162   canvas.height = height
163   context.imageSmoothingEnabled = true
164   context.drawImage(sourceCanvas, 0, 0, width, height)
165   context.globalCompositeOperation = 'destination-in'
166   context.beginPath()
167   context.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, 2 * Math.PI, true)
168   context.fill()
169   return canvas
170 }
171 </script>
172 <style lang="scss">
173 $prefix-cls: #{$namespace}-cropper-image;
174
175 .#{$prefix-cls} {
176   &--circled {
177     .cropper-view-box,
178     .cropper-face {
179       border-radius: 50%;
180     }
181   }
182 }
183 </style>