ImagePreview.vue 7.6 KB

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