@wot-ui/ui 1.0.0 → 2.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (386) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +134 -0
  3. package/attributes.json +1 -0
  4. package/changelog.md +63 -0
  5. package/common/AbortablePromise.ts +28 -0
  6. package/common/canvasHelper.ts +49 -0
  7. package/common/clickoutside.ts +25 -0
  8. package/common/event.ts +8 -0
  9. package/common/formatDate.ts +68 -0
  10. package/common/interceptor.ts +43 -0
  11. package/common/props.ts +53 -0
  12. package/common/util.ts +836 -0
  13. package/components/wd-action-sheet/index.scss +232 -0
  14. package/components/wd-action-sheet/types.ts +155 -0
  15. package/components/wd-action-sheet/wd-action-sheet.vue +176 -0
  16. package/components/wd-avatar/index.scss +150 -0
  17. package/components/wd-avatar/types.ts +98 -0
  18. package/components/wd-avatar/wd-avatar.vue +184 -0
  19. package/components/wd-avatar-group/index.scss +11 -0
  20. package/components/wd-avatar-group/types.ts +61 -0
  21. package/components/wd-avatar-group/wd-avatar-group.vue +115 -0
  22. package/components/wd-backtop/index.scss +67 -0
  23. package/components/wd-backtop/types.ts +66 -0
  24. package/components/wd-backtop/wd-backtop.vue +57 -0
  25. package/components/wd-badge/index.scss +116 -0
  26. package/components/wd-badge/types.ts +94 -0
  27. package/components/wd-badge/wd-badge.vue +78 -0
  28. package/components/wd-button/index.scss +436 -0
  29. package/components/wd-button/types.ts +204 -0
  30. package/components/wd-button/wd-button.vue +210 -0
  31. package/components/wd-calendar/index.scss +97 -0
  32. package/components/wd-calendar/types.ts +221 -0
  33. package/components/wd-calendar/wd-calendar.vue +339 -0
  34. package/components/wd-calendar-view/index.scss +41 -0
  35. package/components/wd-calendar-view/month/index.scss +144 -0
  36. package/components/wd-calendar-view/month/month.vue +389 -0
  37. package/components/wd-calendar-view/month/types.ts +70 -0
  38. package/components/wd-calendar-view/monthPanel/index.scss +84 -0
  39. package/components/wd-calendar-view/monthPanel/month-panel.vue +541 -0
  40. package/components/wd-calendar-view/monthPanel/types.ts +151 -0
  41. package/components/wd-calendar-view/types.ts +166 -0
  42. package/components/wd-calendar-view/utils.ts +318 -0
  43. package/components/wd-calendar-view/wd-calendar-view.vue +117 -0
  44. package/components/wd-calendar-view/year/index.scss +148 -0
  45. package/components/wd-calendar-view/year/types.ts +74 -0
  46. package/components/wd-calendar-view/year/year.vue +206 -0
  47. package/components/wd-calendar-view/yearPanel/index.scss +42 -0
  48. package/components/wd-calendar-view/yearPanel/types.ts +96 -0
  49. package/components/wd-calendar-view/yearPanel/year-panel.vue +249 -0
  50. package/components/wd-card/index.scss +104 -0
  51. package/components/wd-card/types.ts +47 -0
  52. package/components/wd-card/wd-card.vue +38 -0
  53. package/components/wd-cascader/index.scss +154 -0
  54. package/components/wd-cascader/types.ts +191 -0
  55. package/components/wd-cascader/wd-cascader.vue +589 -0
  56. package/components/wd-cell/index.scss +244 -0
  57. package/components/wd-cell/types.ts +205 -0
  58. package/components/wd-cell/wd-cell.vue +172 -0
  59. package/components/wd-cell-group/index.scss +53 -0
  60. package/components/wd-cell-group/types.ts +97 -0
  61. package/components/wd-cell-group/wd-cell-group.vue +48 -0
  62. package/components/wd-checkbox/index.scss +166 -0
  63. package/components/wd-checkbox/types.ts +118 -0
  64. package/components/wd-checkbox/wd-checkbox.vue +216 -0
  65. package/components/wd-checkbox-group/index.scss +5 -0
  66. package/components/wd-checkbox-group/types.ts +93 -0
  67. package/components/wd-checkbox-group/wd-checkbox-group.vue +148 -0
  68. package/components/wd-circle/index.scss +28 -0
  69. package/components/wd-circle/types.ts +54 -0
  70. package/components/wd-circle/wd-circle.vue +306 -0
  71. package/components/wd-col/index.scss +5 -0
  72. package/components/wd-col/types.ts +16 -0
  73. package/components/wd-col/wd-col.vue +83 -0
  74. package/components/wd-collapse/index.scss +71 -0
  75. package/components/wd-collapse/types.ts +68 -0
  76. package/components/wd-collapse/wd-collapse.vue +165 -0
  77. package/components/wd-collapse-item/index.scss +86 -0
  78. package/components/wd-collapse-item/types.ts +62 -0
  79. package/components/wd-collapse-item/wd-collapse-item.vue +184 -0
  80. package/components/wd-config-provider/index.scss +10 -0
  81. package/components/wd-config-provider/types.ts +2061 -0
  82. package/components/wd-config-provider/wd-config-provider.vue +61 -0
  83. package/components/wd-count-down/index.scss +16 -0
  84. package/components/wd-count-down/types.ts +58 -0
  85. package/components/wd-count-down/utils.ts +52 -0
  86. package/components/wd-count-down/wd-count-down.vue +62 -0
  87. package/components/wd-count-to/index.scss +25 -0
  88. package/components/wd-count-to/types.ts +121 -0
  89. package/components/wd-count-to/wd-count-to.vue +126 -0
  90. package/components/wd-curtain/index.scss +96 -0
  91. package/components/wd-curtain/types.ts +82 -0
  92. package/components/wd-curtain/wd-curtain.vue +172 -0
  93. package/components/wd-datetime-picker/index.scss +133 -0
  94. package/components/wd-datetime-picker/types.ts +198 -0
  95. package/components/wd-datetime-picker/wd-datetime-picker.vue +526 -0
  96. package/components/wd-datetime-picker-view/types.ts +171 -0
  97. package/components/wd-datetime-picker-view/util.ts +30 -0
  98. package/components/wd-datetime-picker-view/wd-datetime-picker-view.vue +402 -0
  99. package/components/wd-dialog/index.scss +281 -0
  100. package/components/wd-dialog/index.ts +168 -0
  101. package/components/wd-dialog/types.ts +452 -0
  102. package/components/wd-dialog/wd-dialog.vue +586 -0
  103. package/components/wd-divider/index.scss +122 -0
  104. package/components/wd-divider/types.ts +50 -0
  105. package/components/wd-divider/wd-divider.vue +54 -0
  106. package/components/wd-drop-menu/index.scss +90 -0
  107. package/components/wd-drop-menu/types.ts +38 -0
  108. package/components/wd-drop-menu/wd-drop-menu.vue +168 -0
  109. package/components/wd-drop-menu-item/index.scss +96 -0
  110. package/components/wd-drop-menu-item/types.ts +93 -0
  111. package/components/wd-drop-menu-item/wd-drop-menu-item.vue +205 -0
  112. package/components/wd-empty/index.scss +46 -0
  113. package/components/wd-empty/types.ts +37 -0
  114. package/components/wd-empty/wd-empty.vue +47 -0
  115. package/components/wd-fab/index.scss +124 -0
  116. package/components/wd-fab/types.ts +119 -0
  117. package/components/wd-fab/wd-fab.vue +322 -0
  118. package/components/wd-floating-panel/index.scss +73 -0
  119. package/components/wd-floating-panel/type.ts +32 -0
  120. package/components/wd-floating-panel/wd-floating-panel.vue +142 -0
  121. package/components/wd-form/adapters/zod.ts +56 -0
  122. package/components/wd-form/index.ts +2 -0
  123. package/components/wd-form/types.ts +133 -0
  124. package/components/wd-form/wd-form.vue +121 -0
  125. package/components/wd-form-item/index.scss +26 -0
  126. package/components/wd-form-item/types.ts +134 -0
  127. package/components/wd-form-item/wd-form-item.vue +182 -0
  128. package/components/wd-gap/index.scss +9 -0
  129. package/components/wd-gap/types.ts +26 -0
  130. package/components/wd-gap/wd-gap.vue +38 -0
  131. package/components/wd-grid/index.scss +11 -0
  132. package/components/wd-grid/types.ts +97 -0
  133. package/components/wd-grid/wd-grid.vue +48 -0
  134. package/components/wd-grid-item/index.scss +187 -0
  135. package/components/wd-grid-item/types.ts +98 -0
  136. package/components/wd-grid-item/wd-grid-item.vue +295 -0
  137. package/components/wd-icon/index.scss +46 -0
  138. package/components/wd-icon/types.ts +44 -0
  139. package/components/wd-icon/wd-icon.vue +66 -0
  140. package/components/wd-image-preview/index.scss +94 -0
  141. package/components/wd-image-preview/index.ts +95 -0
  142. package/components/wd-image-preview/types.ts +165 -0
  143. package/components/wd-image-preview/wd-image-preview.vue +233 -0
  144. package/components/wd-img/index.scss +82 -0
  145. package/components/wd-img/types.ts +96 -0
  146. package/components/wd-img/wd-img.vue +91 -0
  147. package/components/wd-img-cropper/index.scss +259 -0
  148. package/components/wd-img-cropper/types.ts +101 -0
  149. package/components/wd-img-cropper/wd-img-cropper.vue +653 -0
  150. package/components/wd-index-anchor/index.scss +34 -0
  151. package/components/wd-index-anchor/type.ts +9 -0
  152. package/components/wd-index-anchor/wd-index-anchor.vue +55 -0
  153. package/components/wd-index-bar/index.scss +68 -0
  154. package/components/wd-index-bar/type.ts +23 -0
  155. package/components/wd-index-bar/wd-index-bar.vue +157 -0
  156. package/components/wd-input/index.scss +129 -0
  157. package/components/wd-input/types.ts +165 -0
  158. package/components/wd-input/wd-input.vue +237 -0
  159. package/components/wd-input-number/index.scss +233 -0
  160. package/components/wd-input-number/types.ts +131 -0
  161. package/components/wd-input-number/wd-input-number.vue +473 -0
  162. package/components/wd-keyboard/constants.ts +81 -0
  163. package/components/wd-keyboard/index.scss +104 -0
  164. package/components/wd-keyboard/key/index.scss +103 -0
  165. package/components/wd-keyboard/key/index.vue +84 -0
  166. package/components/wd-keyboard/key/types.ts +44 -0
  167. package/components/wd-keyboard/types.ts +138 -0
  168. package/components/wd-keyboard/wd-keyboard.vue +236 -0
  169. package/components/wd-loading/index.scss +205 -0
  170. package/components/wd-loading/types.ts +61 -0
  171. package/components/wd-loading/wd-loading.vue +70 -0
  172. package/components/wd-loadmore/index.scss +62 -0
  173. package/components/wd-loadmore/types.ts +42 -0
  174. package/components/wd-loadmore/wd-loadmore.vue +68 -0
  175. package/components/wd-navbar/index.scss +96 -0
  176. package/components/wd-navbar/types.ts +74 -0
  177. package/components/wd-navbar/wd-navbar.vue +136 -0
  178. package/components/wd-navbar-capsule/index.scss +70 -0
  179. package/components/wd-navbar-capsule/types.ts +11 -0
  180. package/components/wd-navbar-capsule/wd-navbar-capsule.vue +48 -0
  181. package/components/wd-notice-bar/index.scss +94 -0
  182. package/components/wd-notice-bar/types.ts +97 -0
  183. package/components/wd-notice-bar/wd-notice-bar.vue +270 -0
  184. package/components/wd-notify/index.scss +114 -0
  185. package/components/wd-notify/index.ts +63 -0
  186. package/components/wd-notify/types.ts +130 -0
  187. package/components/wd-notify/wd-notify.vue +162 -0
  188. package/components/wd-overlay/index.scss +14 -0
  189. package/components/wd-overlay/types.ts +42 -0
  190. package/components/wd-overlay/wd-overlay.vue +55 -0
  191. package/components/wd-pagination/index.scss +71 -0
  192. package/components/wd-pagination/types.ts +69 -0
  193. package/components/wd-pagination/wd-pagination.vue +118 -0
  194. package/components/wd-password-input/index.scss +134 -0
  195. package/components/wd-password-input/types.ts +42 -0
  196. package/components/wd-password-input/wd-password-input.vue +51 -0
  197. package/components/wd-picker/index.scss +72 -0
  198. package/components/wd-picker/types.ts +141 -0
  199. package/components/wd-picker/wd-picker.vue +220 -0
  200. package/components/wd-picker-view/index.scss +93 -0
  201. package/components/wd-picker-view/types.ts +145 -0
  202. package/components/wd-picker-view/useSelection.ts +385 -0
  203. package/components/wd-picker-view/wd-picker-view.vue +227 -0
  204. package/components/wd-popover/index.scss +117 -0
  205. package/components/wd-popover/types.ts +106 -0
  206. package/components/wd-popover/wd-popover.vue +212 -0
  207. package/components/wd-popup/index.scss +89 -0
  208. package/components/wd-popup/types.ts +110 -0
  209. package/components/wd-popup/wd-popup.vue +174 -0
  210. package/components/wd-progress/index.scss +155 -0
  211. package/components/wd-progress/types.ts +94 -0
  212. package/components/wd-progress/wd-progress.vue +249 -0
  213. package/components/wd-radio/index.scss +189 -0
  214. package/components/wd-radio/types.ts +64 -0
  215. package/components/wd-radio/wd-radio.vue +164 -0
  216. package/components/wd-radio-group/index.scss +5 -0
  217. package/components/wd-radio-group/types.ts +70 -0
  218. package/components/wd-radio-group/wd-radio-group.vue +53 -0
  219. package/components/wd-rate/index.scss +57 -0
  220. package/components/wd-rate/types.ts +86 -0
  221. package/components/wd-rate/wd-rate.vue +168 -0
  222. package/components/wd-resize/index.scss +31 -0
  223. package/components/wd-resize/types.ts +14 -0
  224. package/components/wd-resize/wd-resize.vue +157 -0
  225. package/components/wd-root-portal/wd-root-portal.vue +77 -0
  226. package/components/wd-row/index.scss +6 -0
  227. package/components/wd-row/types.ts +36 -0
  228. package/components/wd-row/wd-row.vue +88 -0
  229. package/components/wd-search/index.scss +171 -0
  230. package/components/wd-search/types.ts +107 -0
  231. package/components/wd-search/wd-search.vue +198 -0
  232. package/components/wd-segmented/index.scss +155 -0
  233. package/components/wd-segmented/types.ts +81 -0
  234. package/components/wd-segmented/wd-segmented.vue +169 -0
  235. package/components/wd-select-picker/index.scss +72 -0
  236. package/components/wd-select-picker/types.ts +72 -0
  237. package/components/wd-select-picker/wd-select-picker.vue +371 -0
  238. package/components/wd-sidebar/index.scss +25 -0
  239. package/components/wd-sidebar/types.ts +34 -0
  240. package/components/wd-sidebar/wd-sidebar.vue +57 -0
  241. package/components/wd-sidebar-item/index.scss +91 -0
  242. package/components/wd-sidebar-item/types.ts +28 -0
  243. package/components/wd-sidebar-item/wd-sidebar-item.vue +118 -0
  244. package/components/wd-signature/index.scss +42 -0
  245. package/components/wd-signature/types.ts +295 -0
  246. package/components/wd-signature/wd-signature.vue +664 -0
  247. package/components/wd-skeleton/index.scss +112 -0
  248. package/components/wd-skeleton/types.ts +124 -0
  249. package/components/wd-skeleton/wd-skeleton.vue +110 -0
  250. package/components/wd-slide-verify/index.scss +112 -0
  251. package/components/wd-slide-verify/types.ts +98 -0
  252. package/components/wd-slide-verify/wd-slide-verify.vue +222 -0
  253. package/components/wd-slider/index.scss +485 -0
  254. package/components/wd-slider/types.ts +166 -0
  255. package/components/wd-slider/wd-slider.vue +529 -0
  256. package/components/wd-sort-button/index.scss +126 -0
  257. package/components/wd-sort-button/types.ts +68 -0
  258. package/components/wd-sort-button/wd-sort-button.vue +67 -0
  259. package/components/wd-step/index.scss +366 -0
  260. package/components/wd-step/types.ts +43 -0
  261. package/components/wd-step/wd-step.vue +181 -0
  262. package/components/wd-steps/index.scss +7 -0
  263. package/components/wd-steps/types.ts +50 -0
  264. package/components/wd-steps/wd-steps.vue +39 -0
  265. package/components/wd-sticky/index.scss +9 -0
  266. package/components/wd-sticky/types.ts +13 -0
  267. package/components/wd-sticky/wd-sticky.vue +192 -0
  268. package/components/wd-sticky-box/index.scss +6 -0
  269. package/components/wd-sticky-box/types.ts +20 -0
  270. package/components/wd-sticky-box/wd-sticky-box.vue +157 -0
  271. package/components/wd-swipe-action/index.scss +22 -0
  272. package/components/wd-swipe-action/types.ts +87 -0
  273. package/components/wd-swipe-action/wd-swipe-action.vue +320 -0
  274. package/components/wd-swiper/index.scss +69 -0
  275. package/components/wd-swiper/types.ts +275 -0
  276. package/components/wd-swiper/wd-swiper.vue +332 -0
  277. package/components/wd-swiper-nav/index.scss +179 -0
  278. package/components/wd-swiper-nav/types.ts +42 -0
  279. package/components/wd-swiper-nav/wd-swiper-nav.vue +42 -0
  280. package/components/wd-switch/index.scss +177 -0
  281. package/components/wd-switch/types.ts +93 -0
  282. package/components/wd-switch/wd-switch.vue +107 -0
  283. package/components/wd-tab/index.scss +16 -0
  284. package/components/wd-tab/types.ts +45 -0
  285. package/components/wd-tab/wd-tab.vue +99 -0
  286. package/components/wd-tabbar/index.scss +71 -0
  287. package/components/wd-tabbar/types.ts +79 -0
  288. package/components/wd-tabbar/wd-tabbar.vue +109 -0
  289. package/components/wd-tabbar-item/index.scss +50 -0
  290. package/components/wd-tabbar-item/types.ts +45 -0
  291. package/components/wd-tabbar-item/wd-tabbar-item.vue +101 -0
  292. package/components/wd-table/index.scss +128 -0
  293. package/components/wd-table/types.ts +160 -0
  294. package/components/wd-table/wd-table.vue +331 -0
  295. package/components/wd-table-column/index.scss +15 -0
  296. package/components/wd-table-column/types.ts +81 -0
  297. package/components/wd-table-column/wd-table-column.vue +198 -0
  298. package/components/wd-tabs/index.scss +332 -0
  299. package/components/wd-tabs/types.ts +155 -0
  300. package/components/wd-tabs/wd-tabs.vue +508 -0
  301. package/components/wd-tag/index.scss +325 -0
  302. package/components/wd-tag/types.ts +90 -0
  303. package/components/wd-tag/wd-tag.vue +158 -0
  304. package/components/wd-text/index.scss +52 -0
  305. package/components/wd-text/types.ts +107 -0
  306. package/components/wd-text/wd-text.vue +141 -0
  307. package/components/wd-textarea/index.scss +112 -0
  308. package/components/wd-textarea/types.ts +151 -0
  309. package/components/wd-textarea/wd-textarea.vue +212 -0
  310. package/components/wd-toast/index.scss +92 -0
  311. package/components/wd-toast/index.ts +97 -0
  312. package/components/wd-toast/types.ts +190 -0
  313. package/components/wd-toast/wd-toast.vue +158 -0
  314. package/components/wd-tooltip/index.scss +77 -0
  315. package/components/wd-tooltip/types.ts +105 -0
  316. package/components/wd-tooltip/wd-tooltip.vue +169 -0
  317. package/components/wd-tour/index.scss +106 -0
  318. package/components/wd-tour/types.ts +268 -0
  319. package/components/wd-tour/wd-tour.vue +518 -0
  320. package/components/wd-transition/index.scss +67 -0
  321. package/components/wd-transition/types.ts +106 -0
  322. package/components/wd-transition/wd-transition.vue +238 -0
  323. package/components/wd-upload/index.scss +204 -0
  324. package/components/wd-upload/types.ts +390 -0
  325. package/components/wd-upload/wd-upload.vue +565 -0
  326. package/components/wd-video-preview/index.scss +54 -0
  327. package/components/wd-video-preview/index.ts +64 -0
  328. package/components/wd-video-preview/types.ts +66 -0
  329. package/components/wd-video-preview/wd-video-preview.vue +141 -0
  330. package/components/wd-watermark/index.scss +21 -0
  331. package/components/wd-watermark/types.ts +130 -0
  332. package/components/wd-watermark/wd-watermark.vue +718 -0
  333. package/components/wot-ui/wot-ui.vue +5 -0
  334. package/composables/index.ts +16 -0
  335. package/composables/useCell.ts +34 -0
  336. package/composables/useChildren.ts +120 -0
  337. package/composables/useConfigProvider.ts +45 -0
  338. package/composables/useCountDown.ts +138 -0
  339. package/composables/useDeviceInfo.ts +136 -0
  340. package/composables/useLockScroll.ts +37 -0
  341. package/composables/useParent.ts +51 -0
  342. package/composables/usePopover.ts +212 -0
  343. package/composables/useQueue.ts +52 -0
  344. package/composables/useRaf.ts +37 -0
  345. package/composables/useTouch.ts +43 -0
  346. package/composables/useTranslate.ts +12 -0
  347. package/composables/useUpload.ts +366 -0
  348. package/global.d.ts +106 -0
  349. package/index.ts +6 -0
  350. package/locale/index.ts +32 -0
  351. package/locale/lang/ar-SA.ts +150 -0
  352. package/locale/lang/de-DE.ts +150 -0
  353. package/locale/lang/en-US.ts +150 -0
  354. package/locale/lang/es-ES.ts +150 -0
  355. package/locale/lang/fr-FR.ts +150 -0
  356. package/locale/lang/ja-JP.ts +150 -0
  357. package/locale/lang/ko-KR.ts +150 -0
  358. package/locale/lang/pt-PT.ts +150 -0
  359. package/locale/lang/ru-RU.ts +150 -0
  360. package/locale/lang/th-TH.ts +150 -0
  361. package/locale/lang/tr-TR.ts +155 -0
  362. package/locale/lang/ug-CN.ts +154 -0
  363. package/locale/lang/vi-VN.ts +89 -0
  364. package/locale/lang/zh-CN.ts +154 -0
  365. package/locale/lang/zh-HK.ts +78 -0
  366. package/locale/lang/zh-TW.ts +78 -0
  367. package/package.json +1 -11
  368. package/styles/iconfont/iconfont.scss +1243 -0
  369. package/styles/mixin/_config.scss +7 -0
  370. package/styles/mixin/_function.scss +44 -0
  371. package/styles/mixin/_mixin.scss +473 -0
  372. package/styles/theme/base/color.scss +210 -0
  373. package/styles/theme/base/font.scss +13 -0
  374. package/styles/theme/base/index.scss +8 -0
  375. package/styles/theme/base/insets.scss +32 -0
  376. package/styles/theme/base/number.scss +36 -0
  377. package/styles/theme/base/opacity.scss +9 -0
  378. package/styles/theme/base/radius.scss +13 -0
  379. package/styles/theme/base/stroke.scss +9 -0
  380. package/styles/theme/base/typography.scss +44 -0
  381. package/styles/theme/dark.scss +101 -0
  382. package/styles/theme/index.scss +16 -0
  383. package/styles/theme/light.scss +101 -0
  384. package/styles/variable.scss +472 -0
  385. package/tags.json +1 -0
  386. package/web-types.json +1 -0
@@ -0,0 +1,653 @@
1
+ <template>
2
+ <!-- 绘制的图片canvas -->
3
+ <view v-if="modelValue" :class="`wd-img-cropper ${customClass}`" :style="customStyle" @touchmove="preventTouchMove">
4
+ <!-- 展示在用户面前的裁剪框 -->
5
+ <view class="wd-img-cropper__wrapper">
6
+ <!-- 画出裁剪框 -->
7
+ <view class="wd-img-cropper__cut">
8
+ <!-- 上方阴影块 -->
9
+ <view :class="`wd-img-cropper__overlay ${moving ? 'is-highlight' : ''}`" :style="`height: ${cutTop}px;`"></view>
10
+ <view class="wd-img-cropper__cut-middle" :style="`height: ${cutHeight}px;`">
11
+ <!-- 左侧阴影块 -->
12
+ <view :class="`wd-img-cropper__overlay ${moving ? 'is-highlight' : ''}`" :style="`width: ${cutLeft}px; height: ${cutHeight}px;`"></view>
13
+ <!-- 裁剪框 -->
14
+ <view class="wd-img-cropper__cut-body" :style="`width: ${cutWidth}px; height: ${cutHeight}px;`">
15
+ <!-- 内部网格线 -->
16
+ <view class="wd-img-cropper__gridline wd-img-cropper__gridline--x"></view>
17
+ <view class="wd-img-cropper__gridline wd-img-cropper__gridline--y"></view>
18
+ <!-- 裁剪窗体四个对角 -->
19
+ <view class="wd-img-cropper__corner wd-img-cropper__corner--left-top"></view>
20
+ <view class="wd-img-cropper__corner wd-img-cropper__corner--left-bottom"></view>
21
+ <view class="wd-img-cropper__corner wd-img-cropper__corner--right-top"></view>
22
+ <view class="wd-img-cropper__corner wd-img-cropper__corner--right-bottom"></view>
23
+ </view>
24
+ <!-- 右侧阴影块 -->
25
+ <view :class="`wd-img-cropper__overlay wd-img-cropper__overlay-right ${moving ? 'is-highlight' : ''}`"></view>
26
+ </view>
27
+
28
+ <!-- 底部阴影块 -->
29
+ <view :class="`wd-img-cropper__overlay wd-img-cropper__overlay-bottom ${moving ? 'is-highlight' : ''}`"></view>
30
+ </view>
31
+ <!-- 展示的传过来的图片: 控制图片的旋转角度(rotate)、缩放程度(imgScale)、移动位置(translate) -->
32
+ <image
33
+ class="wd-img-cropper__img"
34
+ :src="imgSrc"
35
+ :style="imageStyle"
36
+ :lazy-load="false"
37
+ @touchstart="handleImgTouchStart"
38
+ @touchmove="handleImgTouchMove"
39
+ @touchend="handleImgTouchEnd"
40
+ @error="handleImgLoadError"
41
+ @load="handleImgLoaded"
42
+ />
43
+ </view>
44
+ <!-- 绘制的图片canvas -->
45
+ <canvas
46
+ :canvas-id="canvasId"
47
+ :id="canvasId"
48
+ class="wd-img-cropper__canvas"
49
+ :disable-scroll="true"
50
+ :style="`width: ${Number(canvasWidth) * canvasScale}px; height: ${Number(canvasHeight) * canvasScale}px;`"
51
+ />
52
+ <!-- 下方按钮 -->
53
+ <view class="wd-img-cropper__footer">
54
+ <wd-icon custom-class="wd-img-cropper__rotate" v-if="!disabledRotate" name="refresh" @click="handleRotate"></wd-icon>
55
+ <view class="wd-img-cropper__footer-btn">
56
+ <wd-button custom-class="wd-img-cropper__cancel" size="large" type="info" variant="text" @click="handleCancel">
57
+ {{ cancelButtonText || translate('cancel') }}
58
+ </wd-button>
59
+
60
+ <wd-button size="large" @click="handleConfirm">{{ confirmButtonText || translate('confirm') }}</wd-button>
61
+ </view>
62
+ </view>
63
+ </view>
64
+ </template>
65
+
66
+ <script lang="ts">
67
+ export default {
68
+ name: 'wd-img-cropper',
69
+ options: {
70
+ // #ifndef MP-TOUTIAO
71
+ virtualHost: true,
72
+ // #endif
73
+ addGlobalClass: true,
74
+ styleIsolation: 'shared'
75
+ }
76
+ }
77
+ </script>
78
+
79
+ <script lang="ts" setup>
80
+ import wdIcon from '../wd-icon/wd-icon.vue'
81
+ import wdButton from '../wd-button/wd-button.vue'
82
+ import { computed, getCurrentInstance, ref, watch } from 'vue'
83
+ import { addUnit, getSystemInfo, objToStyle, uuid } from '../../common/util'
84
+ import { useTranslate } from '../../composables/useTranslate'
85
+ import { imgCropperProps, type ImgCropperExpose } from './types'
86
+ // #ifdef H5
87
+ import { useLockScroll } from '../../composables/useLockScroll'
88
+ // #endif
89
+
90
+ /** 顶部裁剪框占比 */
91
+ const TOP_PERCENT = 0.85
92
+ /** 动画过渡时长(毫秒) */
93
+ const ANIMATION_DURATION = 300
94
+ /** 节流频率(毫秒) */
95
+ const THROTTLE_INTERVAL = 1000 / 40
96
+
97
+ const canvasId = ref<string>(`cropper${uuid()}`) // canvas 组件的唯一标识符
98
+
99
+ // 延时动画设置
100
+ let animationTimer: any | null = null
101
+ // 移动节流定时器
102
+ let moveThrottleTimer: any | null = null
103
+ // 节流标志
104
+ let isThrottleActive: boolean = true
105
+ // 记录初始图片宽度
106
+ let initImgWidth: null | number | string = null
107
+ // 记录初始图片高度
108
+ let initImgHeight: null | number | string = null
109
+
110
+ const props = defineProps(imgCropperProps)
111
+ const emit = defineEmits(['imgloaded', 'imgloaderror', 'cancel', 'confirm', 'update:modelValue'])
112
+
113
+ const { translate } = useTranslate('img-cropper')
114
+
115
+ // 旋转角度
116
+ const imgAngle = ref<number>(0)
117
+ // 是否开启动画
118
+ const isAnimation = ref<boolean>(false)
119
+
120
+ // 裁剪框的宽高
121
+ const picWidth = ref<number>(0)
122
+ const picHeight = ref<number>(0)
123
+ const cutWidth = ref<number>(0)
124
+ const cutHeight = ref<number>(0)
125
+ const offset = ref<number>(20)
126
+ // 裁剪框的距顶距左
127
+ const cutLeft = ref<number>(0)
128
+ const cutTop = ref<number>(0)
129
+ // Canvas 最终成像宽高
130
+ const canvasWidth = ref<string | number>('')
131
+ const canvasHeight = ref<string | number>('')
132
+ const canvasScale = ref<number>(2)
133
+ // 图片当前缩放比例
134
+ const imgScale = ref<number>(1)
135
+ // 图片中心轴点距左的距离
136
+ const imgLeft = ref<number>(getSystemInfo().windowWidth / 2)
137
+ // 图片中心轴点距顶的距离
138
+ const imgTop = ref<number>((getSystemInfo().windowHeight / 2) * TOP_PERCENT)
139
+
140
+ // 获取的原始图片信息
141
+ const imgInfo = ref<UniApp.GetImageInfoSuccessData | null>(null)
142
+ // 系统信息缓存(窗口大小、平台等)
143
+ const info = ref(getSystemInfo())
144
+
145
+ // 是否处于移动/缩放中,用于控制裁剪框背景是否高亮
146
+ const moving = ref<boolean>(false)
147
+
148
+ /**
149
+ * 记录触摸位置信息
150
+ * [0] 存储第一根手指位置,[1] 预留给第二根手指(缩放时使用)
151
+ */
152
+ interface TouchPosition {
153
+ x: string | number
154
+ y: string | number
155
+ }
156
+ const movingPosRecord = ref<TouchPosition[]>([
157
+ { x: '', y: '' },
158
+ { x: '', y: '' }
159
+ ])
160
+ // 双指缩放时两触点间的距离(用于计算缩放比例)
161
+ const fingerDistance = ref<string | number>('')
162
+
163
+ // Canvas 绘图上下文
164
+ const ctx = ref<UniApp.CanvasContext | null>(null)
165
+
166
+ const { proxy } = getCurrentInstance() as any
167
+
168
+ watch(
169
+ () => props.modelValue,
170
+ (newValue) => {
171
+ if (newValue) {
172
+ initImgWidth = props.imgWidth
173
+ initImgHeight = props.imgHeight
174
+ info.value = getSystemInfo()
175
+
176
+ // 根据aspectRatio计算裁剪框尺寸
177
+ const [widthRatio, heightRatio] = props.aspectRatio.split(':').map(Number)
178
+ const tempCutWidth = info.value.windowWidth - offset.value * 2
179
+ const tempCutHeight = (tempCutWidth * heightRatio) / widthRatio
180
+
181
+ cutWidth.value = tempCutWidth
182
+ cutHeight.value = tempCutHeight
183
+ cutTop.value = (info.value.windowHeight * TOP_PERCENT - tempCutHeight) / 2
184
+ cutLeft.value = offset.value
185
+
186
+ canvasScale.value = props.exportScale
187
+ canvasHeight.value = tempCutHeight
188
+ canvasWidth.value = tempCutWidth
189
+ // 根据开发者设置的图片目标尺寸计算实际尺寸
190
+ initImageSize()
191
+ // 初始化canvas
192
+ initCanvas()
193
+ // 加载图片
194
+ props.imgSrc && loadImg()
195
+ } else {
196
+ resetImg()
197
+ }
198
+ },
199
+ {
200
+ deep: true,
201
+ immediate: true
202
+ }
203
+ )
204
+
205
+ watch(
206
+ () => props.imgSrc,
207
+ (newValue) => {
208
+ newValue && loadImg()
209
+ },
210
+ {
211
+ deep: true,
212
+ immediate: true
213
+ }
214
+ )
215
+
216
+ watch(
217
+ () => imgAngle.value,
218
+ (newValue) => {
219
+ if (newValue % 90) {
220
+ imgAngle.value = Math.round(newValue / 90) * 90
221
+ }
222
+ },
223
+ {
224
+ deep: true,
225
+ immediate: true
226
+ }
227
+ )
228
+ watch(
229
+ () => isAnimation.value,
230
+ (newValue) => {
231
+ // 开启过渡之后自动关闭
232
+ animationTimer && clearTimeout(animationTimer)
233
+ if (newValue) {
234
+ animationTimer = setTimeout(() => {
235
+ revertIsAnimation(false)
236
+ clearTimeout(animationTimer)
237
+ }, ANIMATION_DURATION)
238
+ }
239
+ },
240
+ {
241
+ deep: true,
242
+ immediate: true
243
+ }
244
+ )
245
+
246
+ const imageStyle = computed(() => {
247
+ const style: Record<string, string | number> = {
248
+ width: picWidth.value ? addUnit(picWidth.value) : 'auto',
249
+ height: picHeight.value ? addUnit(picHeight.value) : 'auto',
250
+ transform: `translate(${addUnit(imgLeft.value - picWidth.value / 2)}, ${addUnit(imgTop.value - picHeight.value / 2)}) scale(${
251
+ imgScale.value
252
+ }) rotate(${imgAngle.value}deg)`,
253
+ 'transition-duration': (isAnimation.value ? ANIMATION_DURATION : 0) + 'ms'
254
+ }
255
+ return objToStyle(style)
256
+ })
257
+
258
+ /**
259
+ * 切换图片过渡动画状态
260
+ * @param {boolean | { value: boolean }} animation 动画状态:true 启用,false 禁用,或包含 value 属性的对象
261
+ */
262
+ function revertIsAnimation(animation: boolean | { value: boolean }) {
263
+ if (typeof animation === 'boolean') {
264
+ isAnimation.value = animation
265
+ } else {
266
+ isAnimation.value = animation.value
267
+ }
268
+ }
269
+
270
+ /**
271
+ * 旋转图片
272
+ * @param {number} angle 旋转角度(建议使用 90 的倍数)
273
+ */
274
+ function setRotate(angle: number) {
275
+ if (!angle || props.disabledRotate) return
276
+ revertIsAnimation(true)
277
+ imgAngle.value = angle
278
+
279
+ // 重新计算缩放比例
280
+ let tempPicWidth = picWidth.value
281
+ let tempPicHeight = picHeight.value
282
+
283
+ // 旋转后宽高互换
284
+ if ((angle / 90) % 2) {
285
+ tempPicWidth = picHeight.value
286
+ tempPicHeight = picWidth.value
287
+ }
288
+
289
+ // 计算新的缩放比例
290
+ const widthRatio = cutWidth.value / tempPicWidth
291
+ const heightRatio = cutHeight.value / tempPicHeight
292
+ imgScale.value = Math.max(widthRatio, heightRatio)
293
+
294
+ // 检测边缘位置
295
+ detectImgPosIsEdge()
296
+ }
297
+
298
+ /**
299
+ * 重置图片到初始状态
300
+ * 恢复:缩放比例、旋转角度、位置信息
301
+ */
302
+ function resetImg() {
303
+ const { windowHeight, windowWidth } = getSystemInfo()
304
+ imgScale.value = 1
305
+ imgAngle.value = 0
306
+ imgLeft.value = windowWidth / 2
307
+ imgTop.value = (windowHeight / 2) * TOP_PERCENT
308
+ }
309
+
310
+ /**
311
+ * 加载图片资源文件,获取图片基本信息
312
+ */
313
+ function loadImg() {
314
+ if (!props.imgSrc) return
315
+
316
+ uni.getImageInfo({
317
+ src: props.imgSrc,
318
+ success: (res) => {
319
+ imgInfo.value = res
320
+ computeImgSize()
321
+ resetImg()
322
+ },
323
+ fail: () => {
324
+ emit('imgloaderror', { src: props.imgSrc })
325
+ }
326
+ })
327
+ }
328
+
329
+ /**
330
+ * 计算图片尺寸以填满裁剪框
331
+ */
332
+ function computeImgSize() {
333
+ let tempPicWidth: number = picWidth.value
334
+ let tempPicHeight: number = picHeight.value
335
+
336
+ if (!initImgHeight && !initImgWidth) {
337
+ // 计算图片与裁剪框的宽高比
338
+ const imgRatio = imgInfo.value!.width / imgInfo.value!.height
339
+ const cropRatio = cutWidth.value / cutHeight.value
340
+
341
+ if (imgRatio > cropRatio) {
342
+ // 图片更宽,以高度为准
343
+ tempPicHeight = cutHeight.value
344
+ tempPicWidth = tempPicHeight * imgRatio
345
+ } else {
346
+ // 图片更高,以宽度为准
347
+ tempPicWidth = cutWidth.value
348
+ tempPicHeight = tempPicWidth / imgRatio
349
+ }
350
+ } else if (initImgHeight && !initImgWidth) {
351
+ tempPicHeight = Number(initImgHeight)
352
+ tempPicWidth = (imgInfo.value!.width / imgInfo.value!.height) * tempPicHeight
353
+ } else if ((!initImgHeight && initImgWidth) || (initImgHeight && initImgWidth)) {
354
+ tempPicWidth = Number(initImgWidth)
355
+ tempPicHeight = (imgInfo.value!.height / imgInfo.value!.width) * tempPicWidth
356
+ }
357
+
358
+ // 确保计算后的尺寸至少有一边等于裁剪框尺寸
359
+ const widthRatio = cutWidth.value / tempPicWidth
360
+ const heightRatio = cutHeight.value / tempPicHeight
361
+ const scale = Math.max(widthRatio, heightRatio)
362
+
363
+ picWidth.value = tempPicWidth
364
+ picHeight.value = tempPicHeight
365
+ // 设置初始缩放以适应裁剪框
366
+ imgScale.value = scale
367
+ }
368
+
369
+ /**
370
+ * 初始化 Canvas 上下文
371
+ */
372
+ function initCanvas() {
373
+ if (!ctx.value) {
374
+ ctx.value = uni.createCanvasContext(canvasId.value, proxy)
375
+ }
376
+ }
377
+
378
+ /**
379
+ * 初始化图片尺寸,处理百分比等特殊单位
380
+ */
381
+ function initImageSize() {
382
+ // 处理宽度:支持百分比和像素值
383
+ if (initImgWidth && typeof initImgWidth === 'string' && initImgWidth.indexOf('%') !== -1) {
384
+ const width: string = initImgWidth.replace('%', '')
385
+ initImgWidth = (info.value.windowWidth / 100) * Number(width)
386
+ picWidth.value = initImgWidth as number
387
+ } else if (initImgWidth && typeof initImgWidth === 'number') {
388
+ picWidth.value = initImgWidth
389
+ }
390
+ // 处理高度:支持百分比和像素值
391
+ if (initImgHeight && typeof initImgHeight === 'string' && initImgHeight.indexOf('%') !== -1) {
392
+ const height = (props.imgHeight as string).replace('%', '')
393
+ initImgHeight = (info.value.windowHeight / 100) * Number(height)
394
+ picHeight.value = initImgHeight as number
395
+ } else if (initImgHeight && typeof initImgHeight === 'number') {
396
+ picHeight.value = Number(initImgHeight)
397
+ }
398
+ }
399
+
400
+ /**
401
+ * 检测图片位置是否已到达裁剪框边缘
402
+ * @param {number} [scale] 缩放比例,默认使用当前 imgScale.value
403
+ */
404
+ function detectImgPosIsEdge(scale?: number) {
405
+ const currentScale = scale || imgScale.value
406
+ let currentImgLeft = imgLeft.value
407
+ let currentImgTop = imgTop.value
408
+ let currentPicWidth = picWidth.value
409
+ let currentPicHeight = picHeight.value
410
+
411
+ // 翻转后宽高切换
412
+ if ((imgAngle.value / 90) % 2) {
413
+ currentPicWidth = picHeight.value
414
+ currentPicHeight = picWidth.value
415
+ }
416
+ // 左
417
+ currentImgLeft =
418
+ (currentPicWidth * currentScale) / 2 + cutLeft.value >= currentImgLeft ? currentImgLeft : (currentPicWidth * imgScale.value) / 2 + cutLeft.value
419
+ // 右
420
+ currentImgLeft =
421
+ cutLeft.value + cutWidth.value - (currentPicWidth * currentScale) / 2 <= currentImgLeft
422
+ ? currentImgLeft
423
+ : cutLeft.value + cutWidth.value - (currentPicWidth * currentScale) / 2
424
+ // 上
425
+ currentImgTop =
426
+ (currentPicHeight * currentScale) / 2 + cutTop.value >= currentImgTop ? currentImgTop : (currentPicHeight * currentScale) / 2 + cutTop.value
427
+ // 下
428
+ currentImgTop =
429
+ cutTop.value + cutHeight.value - (currentPicHeight * currentScale) / 2 <= currentImgTop
430
+ ? currentImgTop
431
+ : cutTop.value + cutHeight.value - (currentPicHeight * currentScale) / 2
432
+
433
+ imgScale.value = currentScale
434
+ imgTop.value = currentImgTop
435
+ imgLeft.value = currentImgLeft
436
+ }
437
+
438
+ /**
439
+ * 检测并校正图片缩放,防止缩放后露出背景
440
+ */
441
+ function detectImgScaleIsEdge() {
442
+ let tempPicWidth = picWidth.value
443
+ let tempPicHeight = picHeight.value
444
+ let tempImgScale = imgScale.value
445
+
446
+ // 翻转后宽高切换
447
+ if ((imgAngle.value / 90) % 2) {
448
+ tempPicWidth = picHeight.value
449
+ tempPicHeight = picWidth.value
450
+ }
451
+ if (tempPicWidth * tempImgScale < cutWidth.value) {
452
+ tempImgScale = cutWidth.value / tempPicWidth
453
+ }
454
+ if (tempPicHeight * tempImgScale < cutHeight.value) {
455
+ tempImgScale = cutHeight.value / tempPicHeight
456
+ }
457
+ detectImgPosIsEdge(tempImgScale)
458
+ }
459
+
460
+ /**
461
+ * 节流处理,用于优化移动事件的处理频率
462
+ */
463
+ function throttle() {
464
+ if (info.value.platform === 'android') {
465
+ moveThrottleTimer && clearTimeout(moveThrottleTimer)
466
+ moveThrottleTimer = setTimeout(() => {
467
+ isThrottleActive = true
468
+ }, THROTTLE_INTERVAL)
469
+ } else {
470
+ !isThrottleActive && (isThrottleActive = true)
471
+ }
472
+ }
473
+
474
+ /**
475
+ * 处理图片触摸开始事件,支持单指拖动和双指缩放
476
+ * @param {TouchEvent} event 触摸事件
477
+ */
478
+ function handleImgTouchStart(event: any) {
479
+ // 如果处于在拖动中,背景为淡色展示全部,拖动结束则为 0.85 透明度
480
+ moving.value = true
481
+ if (event.touches.length === 1) {
482
+ // 单指拖动
483
+ movingPosRecord.value[0] = {
484
+ x: event.touches[0].clientX - imgLeft.value,
485
+ y: event.touches[0].clientY - imgTop.value
486
+ }
487
+ } else {
488
+ // 以两指为坐标点 做直角三角形 a2 + b2 = c2
489
+ const width = Math.abs(event.touches[1].clientX - event.touches[0].clientX)
490
+ const height = Math.abs(event.touches[1].clientY - event.touches[0].clientY)
491
+ fingerDistance.value = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2))
492
+ }
493
+ }
494
+
495
+ /**
496
+ * 处理图片触摸移动事件,实现单指拖动和双指缩放功能
497
+ * @param {TouchEvent} event 触摸事件
498
+ */
499
+ function handleImgTouchMove(event: any) {
500
+ if (event.touches.length === 1) {
501
+ // 单指拖动
502
+ const { x, y } = movingPosRecord.value[0]
503
+ const left = event.touches[0].clientX - Number(x)
504
+ const top = event.touches[0].clientY - Number(y)
505
+ imgLeft.value = left
506
+ imgTop.value = top
507
+ detectImgPosIsEdge()
508
+ } else {
509
+ // 以两指为坐标点 做直角三角形 a2 + b2 = c2
510
+ const width = Math.abs(event.touches[1].clientX - event.touches[0].clientX)
511
+ const height = Math.abs(event.touches[1].clientY - event.touches[0].clientY)
512
+ const hypotenuse = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2))
513
+ const scale = imgScale.value * (hypotenuse / Number(fingerDistance.value))
514
+ imgScale.value = Math.min(scale, props.maxScale)
515
+ detectImgScaleIsEdge()
516
+ fingerDistance.value = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2))
517
+ }
518
+ }
519
+
520
+ /**
521
+ * 处理图片触摸结束事件
522
+ */
523
+ function handleImgTouchEnd() {
524
+ moving.value = false
525
+ }
526
+
527
+ /**
528
+ * 处理图片加载完成事件
529
+ * @param {any} res 图片加载结果
530
+ */
531
+ function handleImgLoaded(res: any) {
532
+ emit('imgloaded', res)
533
+ }
534
+
535
+ /**
536
+ * 处理图片加载失败事件
537
+ * @param {any} err 错误信息
538
+ */
539
+ function handleImgLoadError(err: any) {
540
+ emit('imgloaderror', err)
541
+ }
542
+
543
+ /**
544
+ * 处理旋转按钮点击,逆时针旋转 90 度
545
+ */
546
+ function handleRotate() {
547
+ setRotate(imgAngle.value - 90)
548
+ }
549
+
550
+ /**
551
+ * 处理取消按钮点击
552
+ */
553
+ function handleCancel() {
554
+ emit('cancel')
555
+ emit('update:modelValue', false)
556
+ }
557
+
558
+ /**
559
+ * 处理确认按钮点击,触发图片裁剪和导出流程
560
+ */
561
+ function handleConfirm() {
562
+ draw()
563
+ }
564
+
565
+ /**
566
+ * 将 Canvas 画布转换为图片文件
567
+ */
568
+ function canvasToImage() {
569
+ const { fileType, quality, exportScale } = props
570
+ uni.canvasToTempFilePath(
571
+ {
572
+ width: cutWidth.value * exportScale,
573
+ height: Math.round(cutHeight.value * exportScale),
574
+ destWidth: cutWidth.value * exportScale,
575
+ destHeight: Math.round(cutHeight.value * exportScale),
576
+ fileType,
577
+ quality,
578
+ canvasId: canvasId.value,
579
+ success: (res: any) => {
580
+ const result = { tempFilePath: res.tempFilePath, width: cutWidth.value * exportScale, height: cutHeight.value * exportScale }
581
+ // #ifdef MP-DINGTALK
582
+ result.tempFilePath = res.filePath
583
+ // #endif
584
+ emit('confirm', result)
585
+ },
586
+ complete: () => {
587
+ emit('update:modelValue', false)
588
+ }
589
+ },
590
+ proxy
591
+ )
592
+ }
593
+
594
+ /**
595
+ * 使用 Canvas 绘制裁剪后的图片
596
+ *
597
+ * 流程:
598
+ * 1. 计算导出图片的实际像素尺寸(考虑缩放倍数)
599
+ * 2. 计算图片在 Canvas 中的位置(相对于裁剪框)
600
+ * 3. 应用几何变换:位移 + 旋转
601
+ * 4. 绘制图片,自动裁剪超出部分
602
+ */
603
+ function draw() {
604
+ if (!props.imgSrc) return
605
+ const draw = () => {
606
+ // 计算导出时的图片实际像素尺寸(考虑 exportScale 倍数)
607
+ const width = picWidth.value * imgScale.value * props.exportScale
608
+ const height = picHeight.value * imgScale.value * props.exportScale
609
+ // 计算图片左上角在 Canvas 中的位置(裁剪框坐标系内)
610
+ const x = imgLeft.value - cutLeft.value
611
+ const y = imgTop.value - cutTop.value
612
+ // Canvas 绘图:位移变换(设置绘制原点到裁剪框中心)
613
+ ctx.value!.translate(x * props.exportScale, y * props.exportScale)
614
+ // Canvas 绘图:旋转变换(如未禁用旋转功能)
615
+ if (!props.disabledRotate) {
616
+ ctx.value!.rotate((imgAngle.value * Math.PI) / 180)
617
+ }
618
+ // drawImage 的 旋转是根据以当前图片的图片水平垂直方向为x、y轴,并在x y轴上移动
619
+ ctx.value!.drawImage(props.imgSrc, -width / 2, -height / 2, width, height)
620
+
621
+ ctx.value!.restore()
622
+
623
+ // 绘制图片
624
+ ctx.value!.draw(false, () => {
625
+ canvasToImage()
626
+ })
627
+ }
628
+
629
+ canvasHeight.value = cutHeight.value
630
+ canvasWidth.value = cutWidth.value
631
+ draw()
632
+ }
633
+
634
+ /**
635
+ * 阻止裁剪区域外的页面滚动
636
+ * 仅在裁剪框内触摸移动时禁用页面滚动
637
+ */
638
+ function preventTouchMove() {}
639
+
640
+ // #ifdef H5
641
+ useLockScroll(() => props.modelValue)
642
+ // #endif
643
+
644
+ defineExpose<ImgCropperExpose>({
645
+ revertIsAnimation,
646
+ setRotate,
647
+ resetImg
648
+ })
649
+ </script>
650
+
651
+ <style lang="scss" scoped>
652
+ @use './index.scss';
653
+ </style>
@@ -0,0 +1,34 @@
1
+ @use '../../styles/mixin/mixin.scss' as *;
2
+ @use '../../styles/variable.scss' as *;
3
+
4
+ $index-anchor-bg: var(--wot-index-anchor-bg, $filled-bottom) !default;
5
+ $index-anchor-padding: var(--wot-index-anchor-padding, $padding-main) !default;
6
+ $index-anchor-font-size: var(--wot-index-anchor-font-size, $typography-body-size-main) !default;
7
+ $index-anchor-color: var(--wot-index-anchor-color, $text-main) !default;
8
+ $index-anchor-z-index: var(--wot-index-anchor-z-index, 1) !default;
9
+
10
+ // #ifdef MP-DINGTALK
11
+ @include b(index-anchor-ding) {
12
+
13
+ @include when(sticky){
14
+ position: sticky;
15
+ top: 0;
16
+ left: 0;
17
+ z-index: $index-anchor-z-index;
18
+ }
19
+ }
20
+ // #endif
21
+
22
+ @include b(index-anchor) {
23
+ background: $index-anchor-bg;
24
+ padding: $index-anchor-padding;
25
+ font-size: $index-anchor-font-size;
26
+ color: $index-anchor-color;
27
+
28
+ @include when(sticky){
29
+ position: sticky;
30
+ top: 0;
31
+ left: 0;
32
+ z-index: $index-anchor-z-index;
33
+ }
34
+ }
@@ -0,0 +1,9 @@
1
+ import type { ExtractPropTypes } from 'vue'
2
+ import { baseProps, makeRequiredProp } from '../../common/props'
3
+
4
+ export const indexAnchorProps = {
5
+ ...baseProps,
6
+ index: makeRequiredProp([String, Number])
7
+ }
8
+
9
+ export type IndexAnchorProps = ExtractPropTypes<typeof indexAnchorProps>