magnifie.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. <template>
  2. <div class="pic-img">
  3. <div class="img-container" @mousemove="!moveEvent && mouseMove($event)" @mouseleave="!leaveEvent && mouseLeave($event)">
  4. <img ref="img" :src="!lazyload ? url : imgLoadedFlag && url" style="width:100%" @load="!lazyload && imgLoaded($event)" />
  5. <div
  6. v-if="!hideZoom && imgLoadedFlag && !hideSelector"
  7. :class="['img-selector', { circle: type === 'circle' }]"
  8. :style="[imgSelectorStyle, imgSelectorSize, imgSelectorPosition, !outShow && imgBg, !outShow && imgBgSize, !outShow && imgBgPosition]"
  9. >
  10. <slot></slot>
  11. </div>
  12. <div
  13. v-if="outShow"
  14. v-show="!hideOutShow"
  15. :class="['img-out-show', { 'base-line': baseline }]"
  16. :style="[imgOutShowSize, imgOutShowPosition, imgBg, imgBgSize, imgBgPosition]"
  17. >
  18. <div v-if="pointer" class="img-selector-point"></div>
  19. </div>
  20. </div>
  21. </div>
  22. </template>
  23. <script>
  24. export default {
  25. props: {
  26. url: {
  27. type: String,
  28. default: ''
  29. },
  30. highUrl: {
  31. type: String,
  32. default: ''
  33. },
  34. width: {
  35. type: Number,
  36. default: 168
  37. },
  38. type: {
  39. type: String,
  40. default: 'circle',
  41. validator: function(value) {
  42. return ['circle', 'square'].indexOf(value) !== -1;
  43. }
  44. },
  45. selectorStyle: {
  46. type: Object,
  47. default() {
  48. return {};
  49. }
  50. },
  51. outShowStyle: {},
  52. scale: {
  53. type: Number,
  54. default: 3
  55. },
  56. lazyload: {
  57. type: Boolean,
  58. default: false
  59. },
  60. moveEvent: {
  61. type: [Object, MouseEvent],
  62. default: null
  63. },
  64. leaveEvent: {
  65. type: [Object, MouseEvent],
  66. default: null
  67. },
  68. hideZoom: {
  69. type: Boolean,
  70. default: false
  71. },
  72. outShow: {
  73. type: Boolean,
  74. default: false
  75. },
  76. pointer: {
  77. type: Boolean,
  78. default: false
  79. },
  80. baseline: {
  81. type: Boolean,
  82. default: false
  83. }
  84. },
  85. data() {
  86. return {
  87. selector: {
  88. width: this.width,
  89. top: 0,
  90. left: 0,
  91. bgTop: 0,
  92. bgLeft: 0,
  93. rightBound: 0,
  94. bottomBound: 0,
  95. absoluteLeft: 0,
  96. absoluteTop: 0
  97. },
  98. imgInfo: {},
  99. $img: null,
  100. screenWidth: document.body.clientWidth,
  101. outShowInitTop: 0,
  102. outShowTop: 0,
  103. hideOutShow: true,
  104. imgLoadedFlag: false,
  105. highImgLoadedFlag: false,
  106. hideSelector: true,
  107. timer: null
  108. };
  109. },
  110. computed: {
  111. addWidth() {
  112. return !this.outShow ? (this.width / 2) * (1 - this.scale) : 0;
  113. },
  114. imgSelectorPosition() {
  115. const { top, left } = this.selector;
  116. return {
  117. top: `${top}px`,
  118. left: `${left}px`
  119. };
  120. },
  121. imgSelectorSize() {
  122. const width = this.selector.width;
  123. return {
  124. width: `${width}px`,
  125. height: `${width}px`
  126. };
  127. },
  128. imgSelectorStyle() {
  129. return this.selectorStyle;
  130. },
  131. imgOutShowSize() {
  132. const {
  133. scale,
  134. selector: { width }
  135. } = this;
  136. return {
  137. width: `${width * scale}px`,
  138. height: `${width * scale}px`
  139. };
  140. },
  141. imgOutShowPosition() {
  142. return {
  143. top: `${this.outShowTop}px`,
  144. right: `${-8}px`
  145. };
  146. },
  147. imgBg() {
  148. return {
  149. backgroundImage: `url(${this.highUrl || this.url})`
  150. };
  151. },
  152. imgBgSize() {
  153. const {
  154. scale,
  155. imgInfo: { height, width }
  156. } = this;
  157. return {
  158. backgroundSize: `${width * scale}px ${height * scale}px`
  159. };
  160. },
  161. imgBgPosition() {
  162. const { bgLeft, bgTop } = this.selector;
  163. return {
  164. backgroundPosition: `${bgLeft}px ${bgTop}px`
  165. };
  166. }
  167. },
  168. watch: {
  169. moveEvent(e) {
  170. this.mouseMove(e);
  171. },
  172. leaveEvent(e) {
  173. this.mouseLeave(e);
  174. },
  175. url() {
  176. this.handlerUrlChange();
  177. },
  178. width(n) {
  179. this.initSelectorProperty(n);
  180. },
  181. screenWidth(val) {
  182. if (!this.timer) {
  183. this.screenWidth = val;
  184. this.timer = setTimeout(() => {
  185. this.imgLoaded();
  186. clearTimeout(this.timer);
  187. this.timer = null;
  188. }, 400);
  189. }
  190. }
  191. },
  192. created() {
  193. this.url && this.lazyload && this.handlerUrlChange();
  194. },
  195. mounted() {
  196. this.$img = this.$refs['img'];
  197. },
  198. methods: {
  199. handlerUrlChange() {
  200. this.imgLoadedFlag = false;
  201. this.lazyload && this.loadImg(this.url).then(this.imgLoaded, err => console.error(err));
  202. },
  203. loadImg(url) {
  204. return new Promise((resolve, reject) => {
  205. const img = document.createElement('img');
  206. img.addEventListener('load', resolve);
  207. img.addEventListener('error', reject);
  208. img.src = url;
  209. });
  210. },
  211. imgLoaded() {
  212. const imgInfo = this.$img.getBoundingClientRect();
  213. if (JSON.stringify(this.imgInfo) != JSON.stringify(imgInfo)) {
  214. this.imgInfo = imgInfo;
  215. this.initSelectorProperty(this.width);
  216. this.resetOutShowInitPosition();
  217. }
  218. if (!this.imgLoadedFlag) {
  219. this.imgLoadedFlag = true;
  220. this.$emit('created', imgInfo);
  221. }
  222. },
  223. mouseMove(e) {
  224. if (!this.hideZoom && this.imgLoadedFlag) {
  225. this.imgLoaded();
  226. const { pageX, pageY, clientY } = e;
  227. const { scale, selector, outShow, addWidth, outShowAutoScroll } = this;
  228. let { outShowInitTop } = this;
  229. const scrollTop = pageY - clientY;
  230. const { absoluteLeft, absoluteTop, rightBound, bottomBound } = selector;
  231. const x = pageX - absoluteLeft; // 选择器的x坐标 相对于图片
  232. const y = pageY - absoluteTop; // 选择器的y坐标
  233. if (outShow) {
  234. if (!outShowInitTop) {
  235. outShowInitTop = this.outShowInitTop = scrollTop + this.imgInfo.top;
  236. }
  237. this.hideOutShow && (this.hideOutShow = false);
  238. this.outShowTop = scrollTop > outShowInitTop ? scrollTop - outShowInitTop : 0;
  239. }
  240. this.hideSelector && (this.hideSelector = false);
  241. selector.top = y > 0 ? (y < bottomBound ? y : bottomBound) : 0;
  242. selector.left = x > 0 ? (x < rightBound ? x : rightBound) : 0;
  243. selector.bgLeft = addWidth - x * scale; // 选择器图片的坐标位置
  244. selector.bgTop = addWidth - y * scale;
  245. }
  246. },
  247. initSelectorProperty(selectorWidth) {
  248. const selectorHalfWidth = selectorWidth / 2;
  249. const selector = this.selector;
  250. const { width, height, left, top } = this.imgInfo;
  251. const scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
  252. const scrollLeft = document.documentElement.scrollLeft || window.pageXOffset || document.body.scrollLeft;
  253. selector.width = selectorWidth;
  254. selector.rightBound = width - selectorWidth;
  255. selector.bottomBound = height - selectorWidth;
  256. selector.absoluteLeft = left + selectorHalfWidth + scrollLeft;
  257. selector.absoluteTop = top + selectorHalfWidth + scrollTop;
  258. },
  259. mouseLeave() {
  260. this.hideSelector = true;
  261. if (this.outShow) {
  262. this.hideOutShow = true;
  263. }
  264. },
  265. reset() {
  266. Object.assign(this.selector, {
  267. top: 0,
  268. left: 0,
  269. bgLeft: 0,
  270. bgTop: 0
  271. });
  272. this.resetOutShowInitPosition();
  273. },
  274. resetOutShowInitPosition() {
  275. this.outShowInitTop = 0;
  276. }
  277. }
  278. };
  279. </script>
  280. <style scoped>
  281. .img-container {
  282. position: relative;
  283. }
  284. .img-selector {
  285. position: absolute;
  286. cursor: crosshair;
  287. border: 1px solid rgba(0, 0, 0, 0.1);
  288. background-repeat: no-repeat;
  289. background-color: rgba(0, 0, 0, 0.6);
  290. }
  291. .img-selector.circle {
  292. border-radius: 50%;
  293. }
  294. .img-out-show {
  295. position: absolute;
  296. background-repeat: no-repeat;
  297. transform: translate(100%, 0);
  298. border: 1px solid rgba(0, 0, 0, 0.1);
  299. }
  300. .img-selector-point {
  301. position: absolute;
  302. width: 4px;
  303. height: 4px;
  304. top: 50%;
  305. left: 50%;
  306. transform: translate(-50%, -50%);
  307. background-color: black;
  308. }
  309. .img-out-show.base-line::after {
  310. position: absolute;
  311. box-sizing: border-box;
  312. content: '';
  313. width: 1px;
  314. border: 1px dashed rgba(0, 0, 0, 0.36);
  315. top: 0;
  316. bottom: 0;
  317. left: 50%;
  318. transform: translateX(-50%);
  319. }
  320. .img-out-show.base-line::before {
  321. position: absolute;
  322. box-sizing: border-box;
  323. content: '';
  324. height: 1px;
  325. border: 1px dashed rgba(0, 0, 0, 0.36);
  326. left: 0;
  327. right: 0;
  328. top: 50%;
  329. transform: translateY(-50%);
  330. }
  331. </style>