SimpleImagePreview.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <template>
  2. <el-dialog
  3. :class="[prefixCls, 'opacity-dialog']"
  4. :visible.sync="modalIsShow"
  5. title="图片预览"
  6. fullscreen
  7. append-to-body
  8. :close-on-click-modal="false"
  9. :close-on-press-escape="false"
  10. >
  11. <div slot="title"></div>
  12. <div slot="footer"></div>
  13. <div :class="[`${prefixCls}-close`]" @click="cancel">
  14. <i class="el-icon-circle-close"></i>
  15. </div>
  16. <div :class="[`${prefixCls}-body`]" ref="ReviewBody">
  17. <div
  18. v-if="!simple"
  19. :class="[`${prefixCls}-guide`, `${prefixCls}-guide-prev`]"
  20. @click.stop="showPrev"
  21. >
  22. <i class="el-icon-arrow-left"></i>
  23. </div>
  24. <div
  25. v-if="!simple"
  26. :class="[`${prefixCls}-guide`, `${prefixCls}-guide-next`]"
  27. @click.stop="showNext"
  28. >
  29. <i class="el-icon-arrow-right"></i>
  30. </div>
  31. <div
  32. :class="[
  33. `${prefixCls}-imgs`,
  34. { [`${prefixCls}-imgs-nosition`]: nosition }
  35. ]"
  36. :style="styles"
  37. v-if="modalIsShow"
  38. >
  39. <img
  40. :key="curImage.url"
  41. :src="curImage.url"
  42. :alt="curImage.filename"
  43. ref="PreviewImgDetail"
  44. @load="reizeImage"
  45. />
  46. </div>
  47. <div :class="[`${prefixCls}-none`]" v-if="!curImage.url">
  48. <i class="el-icon-picture"></i>
  49. <p>暂无数据</p>
  50. </div>
  51. <div :class="[`${prefixCls}-loading`]" v-show="loading">
  52. <i class="el-icon-loading"></i>
  53. </div>
  54. </div>
  55. <div v-if="!simple" :class="[`${prefixCls}-footer`]">
  56. <ul>
  57. <li title="旋转" @click.stop="toRotate">
  58. <i class="el-icon-refresh-right"></i>
  59. </li>
  60. </ul>
  61. </div>
  62. </el-dialog>
  63. </template>
  64. <script>
  65. const prefixCls = "cc-image-preview";
  66. export default {
  67. name: "simple-image-preview",
  68. props: {
  69. curImage: {
  70. type: Object,
  71. default() {
  72. return {};
  73. }
  74. },
  75. simple: {
  76. type: Boolean,
  77. default: false
  78. }
  79. },
  80. data() {
  81. return {
  82. prefixCls,
  83. modalIsShow: false,
  84. styles: { width: "", height: "", top: "", left: "", transform: "" },
  85. initWidth: 500,
  86. transform: {
  87. scale: 1,
  88. rotate: 0
  89. },
  90. loading: false,
  91. loadingSetT: null,
  92. nosition: false
  93. };
  94. },
  95. watch: {
  96. "curImage.url": {
  97. handler(val) {
  98. if (val) {
  99. this.loadingSetT = setTimeout(() => {
  100. this.loading = true;
  101. }, 300);
  102. this.styles = {
  103. width: "",
  104. height: "",
  105. top: "",
  106. left: "",
  107. transform: ""
  108. };
  109. }
  110. }
  111. }
  112. },
  113. methods: {
  114. reizeImage() {
  115. if (this.loadingSetT) clearTimeout(this.loadingSetT);
  116. const imgDom = this.$refs.PreviewImgDetail;
  117. const { naturalWidth, naturalHeight } = imgDom;
  118. const imageSize = this.getImageSizePos({
  119. win: {
  120. width: this.$refs.ReviewBody.clientWidth,
  121. height: this.$refs.ReviewBody.clientHeight
  122. },
  123. img: {
  124. width: naturalWidth,
  125. height: naturalHeight
  126. },
  127. rotate: 0
  128. });
  129. this.styles = Object.assign(this.styles, {
  130. width: imageSize.width + "px",
  131. height: imageSize.height + "px",
  132. top: imageSize.top + "px",
  133. left: imageSize.left + "px",
  134. marginLeft: "auto",
  135. transform: "none"
  136. });
  137. this.transform = {
  138. scale: 1,
  139. rotate: 0
  140. };
  141. this.loading = false;
  142. setTimeout(() => {
  143. this.nosition = false;
  144. }, 100);
  145. },
  146. getImageSizePos({ win, img, rotate }) {
  147. const imageSize = {
  148. width: 0,
  149. height: 0,
  150. top: 0,
  151. left: 0
  152. };
  153. const isHorizontal = !!(rotate % 180);
  154. const rateWin = isHorizontal
  155. ? win.height / win.width
  156. : win.width / win.height;
  157. const hwin = isHorizontal
  158. ? {
  159. width: win.height,
  160. height: win.width
  161. }
  162. : win;
  163. const rateImg = img.width / img.height;
  164. if (rateImg <= rateWin) {
  165. imageSize.height = Math.min(hwin.height, img.height);
  166. imageSize.width = Math.floor(
  167. (imageSize.height * img.width) / img.height
  168. );
  169. } else {
  170. imageSize.width = Math.min(hwin.width, img.width);
  171. imageSize.height = Math.floor(
  172. (imageSize.width * img.height) / img.width
  173. );
  174. }
  175. imageSize.left = (win.width - imageSize.width) / 2;
  176. imageSize.top = (win.height - imageSize.height) / 2;
  177. return imageSize;
  178. },
  179. cancel() {
  180. this.modalIsShow = false;
  181. this.$emit("on-close");
  182. },
  183. open() {
  184. this.modalIsShow = true;
  185. },
  186. showPrev() {
  187. this.$emit("on-prev");
  188. // this.initData();
  189. },
  190. showNext() {
  191. this.$emit("on-next");
  192. // this.initData();
  193. },
  194. // dome-move
  195. setStyleTransform() {
  196. const { scale, rotate } = this.transform;
  197. this.styles.transform = `scale(${scale}, ${scale}) rotate(${rotate}deg)`;
  198. },
  199. toRotate() {
  200. this.transform.rotate = this.transform.rotate + 90;
  201. this.setStyleTransform();
  202. // 调整图片尺寸
  203. const { naturalWidth, naturalHeight } = this.$refs.PreviewImgDetail;
  204. const imageSize = this.getImageSizePos({
  205. win: {
  206. width: this.$refs.ReviewBody.clientWidth,
  207. height: this.$refs.ReviewBody.clientHeight
  208. },
  209. img: {
  210. width: naturalWidth,
  211. height: naturalHeight
  212. },
  213. rotate: this.transform.rotate
  214. });
  215. this.styles = Object.assign(this.styles, {
  216. width: imageSize.width + "px",
  217. height: imageSize.height + "px",
  218. top: imageSize.top + "px",
  219. left: imageSize.left + "px"
  220. });
  221. // 360度无缝切换到0度
  222. if (this.transform.rotate >= 360) {
  223. setTimeout(() => {
  224. this.nosition = true;
  225. this.transform.rotate = 0;
  226. this.setStyleTransform();
  227. setTimeout(() => {
  228. this.nosition = false;
  229. }, 100);
  230. }, 200);
  231. // 200ms当次旋转动画持续时间
  232. }
  233. }
  234. }
  235. };
  236. </script>