ImagePreview.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import _mergeJSXProps2 from "@vue/babel-helper-vue-jsx-merge-props";
  2. import _mergeJSXProps from "@vue/babel-helper-vue-jsx-merge-props";
  3. import { use, range } from '../utils';
  4. import { preventDefault } from '../utils/event';
  5. import { PopupMixin } from '../mixins/popup';
  6. import { TouchMixin } from '../mixins/touch';
  7. import Swipe from '../swipe';
  8. import SwipeItem from '../swipe-item';
  9. var _use = use('image-preview'),
  10. sfc = _use[0],
  11. bem = _use[1];
  12. function getDistance(touches) {
  13. return Math.sqrt(Math.abs((touches[0].clientX - touches[1].clientX) * (touches[0].clientY - touches[1].clientY)));
  14. }
  15. export default sfc({
  16. mixins: [PopupMixin, TouchMixin],
  17. props: {
  18. images: Array,
  19. className: null,
  20. lazyLoad: Boolean,
  21. asyncClose: Boolean,
  22. startPosition: Number,
  23. showIndicators: Boolean,
  24. loop: {
  25. type: Boolean,
  26. "default": true
  27. },
  28. overlay: {
  29. type: Boolean,
  30. "default": true
  31. },
  32. showIndex: {
  33. type: Boolean,
  34. "default": true
  35. },
  36. minZoom: {
  37. type: Number,
  38. "default": 1 / 3
  39. },
  40. maxZoom: {
  41. type: Number,
  42. "default": 3
  43. },
  44. overlayClass: {
  45. type: String,
  46. "default": 'van-image-preview__overlay'
  47. },
  48. closeOnClickOverlay: {
  49. type: Boolean,
  50. "default": true
  51. }
  52. },
  53. data: function data() {
  54. return {
  55. scale: 1,
  56. moveX: 0,
  57. moveY: 0,
  58. moving: false,
  59. zooming: false,
  60. active: 0
  61. };
  62. },
  63. computed: {
  64. imageStyle: function imageStyle() {
  65. var scale = this.scale;
  66. var style = {
  67. transition: this.zooming || this.moving ? '' : '.3s all'
  68. };
  69. if (scale !== 1) {
  70. style.transform = "scale3d(" + scale + ", " + scale + ", 1) translate(" + this.moveX / scale + "px, " + this.moveY / scale + "px)";
  71. }
  72. return style;
  73. }
  74. },
  75. watch: {
  76. value: function value() {
  77. this.active = this.startPosition;
  78. },
  79. startPosition: function startPosition(active) {
  80. this.active = active;
  81. }
  82. },
  83. methods: {
  84. onWrapperTouchStart: function onWrapperTouchStart() {
  85. this.touchStartTime = new Date();
  86. },
  87. onWrapperTouchEnd: function onWrapperTouchEnd(event) {
  88. preventDefault(event);
  89. var deltaTime = new Date() - this.touchStartTime;
  90. var _ref = this.$refs.swipe || {},
  91. _ref$offsetX = _ref.offsetX,
  92. offsetX = _ref$offsetX === void 0 ? 0 : _ref$offsetX,
  93. _ref$offsetY = _ref.offsetY,
  94. offsetY = _ref$offsetY === void 0 ? 0 : _ref$offsetY; // prevent long tap to close component
  95. if (deltaTime < 300 && offsetX < 10 && offsetY < 10) {
  96. var index = this.active;
  97. this.resetScale();
  98. this.$emit('close', {
  99. index: index,
  100. url: this.images[index]
  101. });
  102. if (!this.asyncClose) {
  103. this.$emit('input', false);
  104. }
  105. }
  106. },
  107. startMove: function startMove(event) {
  108. var image = event.currentTarget;
  109. var rect = image.getBoundingClientRect();
  110. var winWidth = window.innerWidth;
  111. var winHeight = window.innerHeight;
  112. this.touchStart(event);
  113. this.moving = true;
  114. this.startMoveX = this.moveX;
  115. this.startMoveY = this.moveY;
  116. this.maxMoveX = Math.max(0, (rect.width - winWidth) / 2);
  117. this.maxMoveY = Math.max(0, (rect.height - winHeight) / 2);
  118. },
  119. startZoom: function startZoom(event) {
  120. this.moving = false;
  121. this.zooming = true;
  122. this.startScale = this.scale;
  123. this.startDistance = getDistance(event.touches);
  124. },
  125. onTouchStart: function onTouchStart(event) {
  126. var touches = event.touches;
  127. var _ref2 = this.$refs.swipe || {},
  128. _ref2$offsetX = _ref2.offsetX,
  129. offsetX = _ref2$offsetX === void 0 ? 0 : _ref2$offsetX;
  130. if (touches.length === 1 && this.scale !== 1) {
  131. this.startMove(event);
  132. }
  133. /* istanbul ignore else */
  134. else if (touches.length === 2 && !offsetX) {
  135. this.startZoom(event);
  136. }
  137. },
  138. onTouchMove: function onTouchMove(event) {
  139. var touches = event.touches;
  140. if (this.moving || this.zooming) {
  141. preventDefault(event, true);
  142. }
  143. if (this.moving) {
  144. this.touchMove(event);
  145. var moveX = this.deltaX + this.startMoveX;
  146. var moveY = this.deltaY + this.startMoveY;
  147. this.moveX = range(moveX, -this.maxMoveX, this.maxMoveX);
  148. this.moveY = range(moveY, -this.maxMoveY, this.maxMoveY);
  149. }
  150. if (this.zooming && touches.length === 2) {
  151. var distance = getDistance(touches);
  152. var scale = this.startScale * distance / this.startDistance;
  153. this.scale = range(scale, this.minZoom, this.maxZoom);
  154. }
  155. },
  156. onTouchEnd: function onTouchEnd(event) {
  157. /* istanbul ignore else */
  158. if (this.moving || this.zooming) {
  159. var stopPropagation = true;
  160. if (this.moving && this.startMoveX === this.moveX && this.startMoveY === this.moveY) {
  161. stopPropagation = false;
  162. }
  163. if (!event.touches.length) {
  164. this.moving = false;
  165. this.zooming = false;
  166. this.startMoveX = 0;
  167. this.startMoveY = 0;
  168. this.startScale = 1;
  169. if (this.scale < 1) {
  170. this.resetScale();
  171. }
  172. }
  173. if (stopPropagation) {
  174. preventDefault(event, true);
  175. }
  176. }
  177. },
  178. onChange: function onChange(active) {
  179. this.resetScale();
  180. this.active = active;
  181. this.$emit('change', active);
  182. },
  183. resetScale: function resetScale() {
  184. this.scale = 1;
  185. this.moveX = 0;
  186. this.moveY = 0;
  187. }
  188. },
  189. render: function render(h) {
  190. var _this = this;
  191. if (!this.value) {
  192. return;
  193. }
  194. var active = this.active,
  195. images = this.images;
  196. var Index = this.showIndex && h("div", {
  197. "class": bem('index')
  198. }, [this.slots('index') || active + 1 + "/" + images.length]);
  199. var Images = h(Swipe, {
  200. "ref": "swipe",
  201. "attrs": {
  202. "loop": this.loop,
  203. "indicatorColor": "white",
  204. "initialSwipe": this.startPosition,
  205. "showIndicators": this.showIndicators
  206. },
  207. "on": {
  208. "change": this.onChange
  209. }
  210. }, [images.map(function (image, index) {
  211. var props = {
  212. "class": bem('image'),
  213. style: index === active ? _this.imageStyle : null,
  214. on: {
  215. touchstart: _this.onTouchStart,
  216. touchmove: _this.onTouchMove,
  217. touchend: _this.onTouchEnd,
  218. touchcancel: _this.onTouchEnd
  219. }
  220. };
  221. return h(SwipeItem, [_this.lazyLoad ? h("img", _mergeJSXProps([{
  222. "directives": [{
  223. name: "lazy",
  224. value: image
  225. }]
  226. }, props])) : h("img", _mergeJSXProps2([{
  227. "attrs": {
  228. "src": image
  229. }
  230. }, props]))]);
  231. })]);
  232. return h("transition", {
  233. "attrs": {
  234. "name": "van-fade"
  235. }
  236. }, [h("div", {
  237. "class": [bem(), this.className],
  238. "on": {
  239. "touchstart": this.onWrapperTouchStart,
  240. "touchend": this.onWrapperTouchEnd,
  241. "touchcancel": this.onWrapperTouchEnd
  242. }
  243. }, [Index, Images])]);
  244. }
  245. });