ImagePreview.mjs 4.9 KB

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