ImageContain.vue 6.7 KB

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