ImagePreview.mjs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import { ref, watch, nextTick, reactive, onMounted, defineComponent, createVNode as _createVNode, mergeProps as _mergeProps } from "vue";
  2. import { pick, truthProp, unknownProp, windowWidth, windowHeight, makeArrayProp, makeStringProp, makeNumericProp, callInterceptor, createNamespace, HAPTICS_FEEDBACK } from "../utils/index.mjs";
  3. import { useRect } from "@vant/use";
  4. import { useExpose } from "../composables/use-expose.mjs";
  5. import { Icon } from "../icon/index.mjs";
  6. import { Swipe } from "../swipe/index.mjs";
  7. import { Popup } from "../popup/index.mjs";
  8. import ImagePreviewItem from "./ImagePreviewItem.mjs";
  9. const [name, bem] = createNamespace("image-preview");
  10. const popupProps = ["show", "teleport", "transition", "overlayStyle", "closeOnPopstate"];
  11. const imagePreviewProps = {
  12. show: Boolean,
  13. loop: truthProp,
  14. images: makeArrayProp(),
  15. minZoom: makeNumericProp(1 / 3),
  16. maxZoom: makeNumericProp(3),
  17. overlay: truthProp,
  18. vertical: Boolean,
  19. closeable: Boolean,
  20. showIndex: truthProp,
  21. className: unknownProp,
  22. closeIcon: makeStringProp("clear"),
  23. transition: String,
  24. beforeClose: Function,
  25. doubleScale: truthProp,
  26. overlayClass: unknownProp,
  27. overlayStyle: Object,
  28. swipeDuration: makeNumericProp(300),
  29. startPosition: makeNumericProp(0),
  30. showIndicators: Boolean,
  31. closeOnPopstate: truthProp,
  32. closeOnClickImage: truthProp,
  33. closeOnClickOverlay: truthProp,
  34. closeIconPosition: makeStringProp("top-right"),
  35. teleport: [String, Object]
  36. };
  37. var stdin_default = defineComponent({
  38. name,
  39. props: imagePreviewProps,
  40. emits: ["scale", "close", "closed", "change", "longPress", "update:show"],
  41. setup(props, {
  42. emit,
  43. slots
  44. }) {
  45. const swipeRef = ref();
  46. const activedPreviewItemRef = ref();
  47. const state = reactive({
  48. active: 0,
  49. rootWidth: 0,
  50. rootHeight: 0,
  51. disableZoom: false
  52. });
  53. const resize = () => {
  54. if (swipeRef.value) {
  55. const rect = useRect(swipeRef.value.$el);
  56. state.rootWidth = rect.width;
  57. state.rootHeight = rect.height;
  58. swipeRef.value.resize();
  59. }
  60. };
  61. const emitScale = (args) => emit("scale", args);
  62. const updateShow = (show) => emit("update:show", show);
  63. const emitClose = () => {
  64. callInterceptor(props.beforeClose, {
  65. args: [state.active],
  66. done: () => updateShow(false)
  67. });
  68. };
  69. const setActive = (active) => {
  70. if (active !== state.active) {
  71. state.active = active;
  72. emit("change", active);
  73. }
  74. };
  75. const renderIndex = () => {
  76. if (props.showIndex) {
  77. return _createVNode("div", {
  78. "class": bem("index")
  79. }, [slots.index ? slots.index({
  80. index: state.active
  81. }) : `${state.active + 1} / ${props.images.length}`]);
  82. }
  83. };
  84. const renderCover = () => {
  85. if (slots.cover) {
  86. return _createVNode("div", {
  87. "class": bem("cover")
  88. }, [slots.cover()]);
  89. }
  90. };
  91. const onDragStart = () => {
  92. state.disableZoom = true;
  93. };
  94. const onDragEnd = () => {
  95. state.disableZoom = false;
  96. };
  97. const renderImages = () => _createVNode(Swipe, {
  98. "ref": swipeRef,
  99. "lazyRender": true,
  100. "loop": props.loop,
  101. "class": bem("swipe"),
  102. "vertical": props.vertical,
  103. "duration": props.swipeDuration,
  104. "initialSwipe": props.startPosition,
  105. "showIndicators": props.showIndicators,
  106. "indicatorColor": "white",
  107. "onChange": setActive,
  108. "onDragEnd": onDragEnd,
  109. "onDragStart": onDragStart
  110. }, {
  111. default: () => [props.images.map((image, index) => _createVNode(ImagePreviewItem, {
  112. "ref": (item) => {
  113. if (index === state.active) {
  114. activedPreviewItemRef.value = item;
  115. }
  116. },
  117. "src": image,
  118. "show": props.show,
  119. "active": state.active,
  120. "maxZoom": props.maxZoom,
  121. "minZoom": props.minZoom,
  122. "rootWidth": state.rootWidth,
  123. "rootHeight": state.rootHeight,
  124. "disableZoom": state.disableZoom,
  125. "doubleScale": props.doubleScale,
  126. "closeOnClickImage": props.closeOnClickImage,
  127. "closeOnClickOverlay": props.closeOnClickOverlay,
  128. "vertical": props.vertical,
  129. "onScale": emitScale,
  130. "onClose": emitClose,
  131. "onLongPress": () => emit("longPress", {
  132. index
  133. })
  134. }, {
  135. image: slots.image
  136. }))]
  137. });
  138. const renderClose = () => {
  139. if (props.closeable) {
  140. return _createVNode(Icon, {
  141. "role": "button",
  142. "name": props.closeIcon,
  143. "class": [bem("close-icon", props.closeIconPosition), HAPTICS_FEEDBACK],
  144. "onClick": emitClose
  145. }, null);
  146. }
  147. };
  148. const onClosed = () => emit("closed");
  149. const swipeTo = (index, options) => {
  150. var _a;
  151. return (_a = swipeRef.value) == null ? void 0 : _a.swipeTo(index, options);
  152. };
  153. useExpose({
  154. resetScale: () => {
  155. var _a;
  156. (_a = activedPreviewItemRef.value) == null ? void 0 : _a.resetScale();
  157. },
  158. swipeTo
  159. });
  160. onMounted(resize);
  161. watch([windowWidth, windowHeight], resize);
  162. watch(() => props.startPosition, (value) => setActive(+value));
  163. watch(() => props.show, (value) => {
  164. const {
  165. images,
  166. startPosition
  167. } = props;
  168. if (value) {
  169. setActive(+startPosition);
  170. nextTick(() => {
  171. resize();
  172. swipeTo(+startPosition, {
  173. immediate: true
  174. });
  175. });
  176. } else {
  177. emit("close", {
  178. index: state.active,
  179. url: images[state.active]
  180. });
  181. }
  182. });
  183. return () => _createVNode(Popup, _mergeProps({
  184. "class": [bem(), props.className],
  185. "overlayClass": [bem("overlay"), props.overlayClass],
  186. "onClosed": onClosed,
  187. "onUpdate:show": updateShow
  188. }, pick(props, popupProps)), {
  189. default: () => [renderClose(), renderImages(), renderIndex(), renderCover()]
  190. });
  191. }
  192. });
  193. export {
  194. stdin_default as default,
  195. imagePreviewProps
  196. };