ImagePreview.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <template>
  2. <Modal
  3. :class="prefixCls"
  4. v-model="modalIsShow"
  5. title="图片预览"
  6. :mask-closable="false"
  7. fullscreen
  8. footer-hide
  9. @on-visible-change="visibleChange"
  10. >
  11. <div slot="header"></div>
  12. <div :class="[`${prefixCls}-close`]" @click="modalIsShow = false">
  13. <i class="el-icon-circle-close"></i>
  14. <Icon type="ios-close" />
  15. </div>
  16. <div :class="[`${prefixCls}-header`]" v-if="!headerHide">
  17. <h3>{{ curFile.name }}</h3>
  18. <div :class="[`${prefixCls}-index`]">
  19. {{ this.curIndex + 1 }} / {{ this.lastIndex }}
  20. </div>
  21. </div>
  22. <div :class="[`${prefixCls}-body`]" ref="ReviewBody">
  23. <div
  24. :class="[`${prefixCls}-guide`, `${prefixCls}-guide-prev`]"
  25. @click="showPrev"
  26. >
  27. <Icon type="ios-arrow-back" />
  28. </div>
  29. <div
  30. :class="[`${prefixCls}-guide`, `${prefixCls}-guide-next`]"
  31. @click="showNext"
  32. >
  33. <Icon type="ios-arrow-forward" />
  34. </div>
  35. <div
  36. :class="[
  37. `${prefixCls}-imgs`,
  38. { [`${prefixCls}-imgs-nosition`]: nosition }
  39. ]"
  40. :style="styles"
  41. v-move-ele.prevent="{ mouseMove }"
  42. >
  43. <img :src="curFile.url" :alt="curFile.name" ref="PreviewImgDetail" />
  44. </div>
  45. </div>
  46. <div :class="[`${prefixCls}-footer`]">
  47. <ul>
  48. <li title="合适大小" @click="toOrigin">
  49. <Icon type="md-expand" />
  50. </li>
  51. <li
  52. title="放大"
  53. @click="toMagnify"
  54. :class="{
  55. 'li-disabled': transform.scale === maxScale
  56. }"
  57. >
  58. <Icon type="md-add-circle" />
  59. </li>
  60. <li
  61. title="缩小"
  62. @click="toShrink"
  63. :class="{
  64. 'li-disabled': transform.scale === minScale
  65. }"
  66. >
  67. <Icon type="md-remove-circle" />
  68. </li>
  69. <li title="旋转" @click="toRotate">
  70. <Icon type="ios-refresh-circle" />
  71. </li>
  72. </ul>
  73. </div>
  74. </Modal>
  75. </template>
  76. <script>
  77. import MoveEle from "./move-ele";
  78. const prefixCls = "cc-image-preview";
  79. export default {
  80. name: "image-preview",
  81. props: {
  82. imageList: {
  83. type: Array,
  84. default() {
  85. return [];
  86. }
  87. },
  88. initIndex: {
  89. type: Number,
  90. default: 0
  91. },
  92. headerHide: {
  93. type: Boolean,
  94. default: false
  95. },
  96. loop: {
  97. type: Boolean,
  98. default: false
  99. }
  100. },
  101. directives: {
  102. MoveEle
  103. },
  104. data() {
  105. return {
  106. prefixCls,
  107. modalIsShow: false,
  108. curFile: { name: "", url: null },
  109. curIndex: 0,
  110. minScale: 0.2,
  111. maxScale: 3,
  112. styles: { width: "", height: "", top: "", left: "", transform: "" },
  113. initWidth: 500,
  114. transform: {
  115. scale: 1,
  116. rotate: 0
  117. },
  118. nosition: false
  119. };
  120. },
  121. computed: {
  122. isFirst() {
  123. return this.curIndex === 0;
  124. },
  125. isLast() {
  126. return this.lastIndex - 1 === this.curIndex;
  127. },
  128. lastIndex() {
  129. return this.imageList.length;
  130. }
  131. },
  132. // watch: {
  133. // curIndex: {
  134. // immediate: true,
  135. // handler(val, oldVal) {
  136. // this.curFileChange(val);
  137. // }
  138. // }
  139. // },
  140. mounted() {
  141. this.registfileLoad();
  142. },
  143. methods: {
  144. initData() {
  145. if (!this.imageList.length) return;
  146. this.curIndex = this.initIndex;
  147. this.curFileChange(this.curIndex);
  148. },
  149. visibleChange(visible) {
  150. if (!visible) return;
  151. this.initData();
  152. },
  153. curFileChange(val) {
  154. const index = val || 0;
  155. this.nosition = true;
  156. this.curFile = this.imageList[index];
  157. },
  158. registfileLoad() {
  159. const imgDom = this.$refs.PreviewImgDetail;
  160. imgDom.onload = () => {
  161. const { naturalWidth, naturalHeight } = imgDom;
  162. const imageSize = this.getImageSizePos({
  163. win: {
  164. width: this.$refs.ReviewBody.clientWidth,
  165. height: this.$refs.ReviewBody.clientHeight
  166. },
  167. img: {
  168. width: naturalWidth,
  169. height: naturalHeight
  170. },
  171. rotate: 0
  172. });
  173. this.styles = Object.assign(this.styles, {
  174. width: imageSize.width + "px",
  175. height: imageSize.height + "px",
  176. top: imageSize.top + "px",
  177. left: imageSize.left + "px",
  178. transform: ""
  179. });
  180. this.transform = {
  181. scale: 1,
  182. rotate: 0
  183. };
  184. setTimeout(() => {
  185. this.nosition = false;
  186. }, 100);
  187. };
  188. },
  189. getImageSizePos({ win, img, rotate }) {
  190. const imageSize = {
  191. width: 0,
  192. height: 0,
  193. top: 0,
  194. left: 0
  195. };
  196. const isHorizontal = !!(rotate % 180);
  197. const rateWin = isHorizontal
  198. ? win.height / win.width
  199. : win.width / win.height;
  200. const hwin = isHorizontal
  201. ? {
  202. width: win.height,
  203. height: win.width
  204. }
  205. : win;
  206. const rateImg = img.width / img.height;
  207. if (rateImg <= rateWin) {
  208. imageSize.height = Math.min(hwin.height, img.height);
  209. imageSize.width = Math.floor(
  210. (imageSize.height * img.width) / img.height
  211. );
  212. } else {
  213. imageSize.width = Math.min(hwin.width, img.width);
  214. imageSize.height = Math.floor(
  215. (imageSize.width * img.height) / img.width
  216. );
  217. }
  218. imageSize.left = (win.width - imageSize.width) / 2;
  219. imageSize.top = (win.height - imageSize.height) / 2;
  220. return imageSize;
  221. },
  222. cancel() {
  223. this.modalIsShow = false;
  224. },
  225. open() {
  226. this.modalIsShow = true;
  227. },
  228. showPrev() {
  229. if (this.isFirst) {
  230. if (this.loop) {
  231. this.curIndex = this.lastIndex - 1;
  232. } else {
  233. this.$emit("on-page-prev");
  234. return;
  235. }
  236. } else {
  237. this.curIndex -= 1;
  238. }
  239. this.$emit("on-prev", this.curIndex);
  240. this.curFileChange(this.curIndex);
  241. },
  242. showNext() {
  243. if (this.isLast) {
  244. if (this.loop) {
  245. this.curIndex = 0;
  246. } else {
  247. this.$emit("on-page-next");
  248. return;
  249. }
  250. } else {
  251. this.curIndex += 1;
  252. }
  253. this.$emit("on-next", this.curIndex);
  254. this.curFileChange(this.curIndex);
  255. },
  256. // dome-move
  257. mouseMove({ left, top }) {
  258. this.styles.left = left + "px";
  259. this.styles.top = top + "px";
  260. },
  261. setStyleTransform() {
  262. const { scale, rotate } = this.transform;
  263. this.styles.transform = `scale(${scale}, ${scale}) rotate(${rotate}deg)`;
  264. },
  265. toOrigin() {
  266. this.transform.scale = 1;
  267. this.setStyleTransform();
  268. },
  269. toMagnify() {
  270. const scale = (this.transform.scale * 1.2).toFixed(2);
  271. this.transform.scale = scale >= this.maxScale ? this.maxScale : scale;
  272. this.setStyleTransform();
  273. },
  274. toShrink() {
  275. const scale = (this.transform.scale * 0.75).toFixed(2);
  276. this.transform.scale = scale <= this.minScale ? this.minScale : scale;
  277. this.setStyleTransform();
  278. },
  279. toRotate() {
  280. this.transform.rotate = this.transform.rotate + 90;
  281. this.setStyleTransform();
  282. // 调整图片尺寸
  283. const { naturalWidth, naturalHeight } = this.$refs.PreviewImgDetail;
  284. const imageSize = this.getImageSizePos({
  285. win: {
  286. width: this.$refs.ReviewBody.clientWidth,
  287. height: this.$refs.ReviewBody.clientHeight
  288. },
  289. img: {
  290. width: naturalWidth,
  291. height: naturalHeight
  292. },
  293. rotate: this.transform.rotate
  294. });
  295. this.styles = Object.assign(this.styles, {
  296. width: imageSize.width + "px",
  297. height: imageSize.height + "px",
  298. top: imageSize.top + "px",
  299. left: imageSize.left + "px"
  300. });
  301. // 360度无缝切换到0度
  302. setTimeout(() => {
  303. if (this.transform.rotate >= 360) {
  304. this.nosition = true;
  305. this.transform.rotate = 0;
  306. this.setStyleTransform();
  307. setTimeout(() => {
  308. this.nosition = false;
  309. }, 100);
  310. }
  311. }, 200);
  312. }
  313. }
  314. };
  315. </script>