Commit b7c7c46853d332641d116d818e657447884784f3
Committed by
GitHub
1 parent
33cd8fe6
perf: add createImgPreview func (#713)
Showing
12 changed files
with
502 additions
and
2 deletions
src/assets/svg/preview/p-rotate.svg
0 → 100644
1 | +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595306944988" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1820" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><defs><style type="text/css"></style></defs><path d="M1464.3 279.7" p-id="1821" fill="#ffffff"></path><path d="M512 960c-60.5 0-119.1-11.9-174.4-35.2-53.4-22.6-101.3-54.9-142.4-96s-73.4-89-96-142.4C75.9 631.1 64 572.5 64 512s11.9-119.1 35.2-174.4c22.6-53.4 54.9-101.3 96-142.4s89-73.4 142.4-96C392.9 75.9 451.5 64 512 64s119.1 11.9 174.4 35.2c53.4 22.6 101.3 54.9 142.4 96s73.4 89 96 142.4C948.1 392.9 960 451.5 960 512c0 19.1-15.5 34.6-34.6 34.6s-34.6-15.5-34.6-34.6c0-51.2-10-100.8-29.8-147.4-19.1-45.1-46.4-85.6-81.2-120.4C745 209.4 704.5 182 659.4 163c-46.7-19.7-96.3-29.8-147.4-29.8-51.2 0-100.8 10-147.4 29.8-45.1 19.1-85.6 46.4-120.4 81.2S182 319.5 163 364.6c-19.7 46.7-29.8 96.3-29.8 147.4 0 51.2 10 100.8 29.8 147.4 19.1 45.1 46.4 85.6 81.2 120.4C279 814.6 319.5 842 364.6 861c46.7 19.7 96.3 29.8 147.4 29.8 64.6 0 128.4-16.5 184.4-47.8 54.4-30.4 100.9-74.1 134.6-126.6 10.3-16.1 31.7-20.8 47.8-10.4 16.1 10.3 20.8 31.7 10.4 47.8-39.8 62-94.8 113.7-159.1 149.6-66.2 37-141.7 56.6-218.1 56.6z" p-id="1822" fill="#ffffff"></path><path d="M924 552c-19.8 0-36-16.2-36-36V228c0-19.8 16.2-36 36-36s36 16.2 36 36v288c0 19.8-16.2 36-36 36zM275.4 575.5c9.5-2.5 19.1 2.9 22.3 12.2 3.5 10.2 9.9 17.7 19.1 22.6 7.1 3.9 15.1 5.8 24 5.8 16.6 0 30.8-6.9 42.5-20.8 11.7-13.8 20-32.7 24.9-75.1-7.7 12.2-17.3 20.8-28.7 25.8-11.4 5-23.7 7.4-36.8 7.4-26.7 0-47.7-8.3-63.3-24.9-15.5-16.6-23.3-37.9-23.3-64.1 0-25.1 7.7-47.1 23-66.2 15.3-19 37.9-28.6 67.8-28.6 40.3 0 68.1 18.1 83.4 54.4 8.5 19.9 12.7 44.9 12.7 74.9 0 33.8-5.1 63.8-15.3 89.9-16.9 43.5-45.5 65.2-85.8 65.2-27 0-47.6-7.1-61.6-21.2-10-10.1-16.4-22-19.3-35.8-2-9.6 4-19.1 13.5-21.6l0.9 0.1z m103-74.4c9.4-7.5 14.1-20.6 14.1-39.3 0-16.8-4.2-29.3-12.7-37.5S360.6 412 347.5 412c-14 0-25.2 4.7-33.4 14.1-8.2 9.4-12.4 22-12.4 37.7 0 14.9 3.6 26.7 10.9 35.5 7.2 8.8 18.8 13.1 34.6 13.1 11.4 0 21.8-3.8 31.2-11.3zM646.6 414.4c12.4 22.8 18.5 54 18.5 93.7 0 37.6-5.6 68.7-16.8 93.3-16.2 35.3-42.8 52.9-79.6 52.9-33.2 0-57.9-14.4-74.2-43.3-13.5-24.1-20.3-56.4-20.3-97 0-31.4 4.1-58.4 12.2-80.9 15.2-42 42.7-63 82.5-63 35.9 0 61.8 14.8 77.7 44.3z m-40.2 173.3c9.4-13.9 14-39.9 14-78 0-27.4-3.4-50-10.1-67.7-6.8-17.7-19.9-26.6-39.4-26.6-17.9 0-31 8.4-39.3 25.2-8.3 16.8-12.4 41.6-12.4 74.3 0 24.6 2.6 44.4 7.9 59.4 8.1 22.8 22 34.3 41.6 34.3 15.7 0 28.3-7 37.7-20.9zM803.3 387.2c11.2 11.3 16.8 25 16.8 41.2 0 16.7-5.8 30.7-17.5 41.8C791 481.4 777.4 487 762 487c-17.1 0-31.2-5.8-42.1-17.4-10.9-11.6-16.4-25.1-16.4-40.6 0-16.5 5.8-30.4 17.3-41.7 11.5-11.3 25.3-17 41.2-17 16.3 0 30.1 5.7 41.3 16.9zM739.5 451c6.2 6.2 13.7 9.3 22.5 9.3 8.4 0 15.8-3.1 22.1-9.3 6.3-6.2 9.4-13.7 9.4-22.6 0-8.5-3.1-15.9-9.3-22.1-6.2-6.2-13.6-9.3-22.2-9.3s-16.1 3.1-22.4 9.3c-6.3 6.2-9.4 13.7-9.4 22.6-0.1 8.4 3 15.8 9.3 22.1z" p-id="1823" fill="#ffffff"></path></svg> |
src/assets/svg/preview/resume.svg
0 → 100644
1 | +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595307154239" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7317" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><defs><style type="text/css"></style></defs><path d="M316 672h60c4.4 0 8-3.6 8-8V360c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v304c0 4.4 3.6 8 8 8zM512 622c22.1 0 40-17.9 40-39 0-23.1-17.9-41-40-41s-40 17.9-40 41c0 21.1 17.9 39 40 39zM512 482c22.1 0 40-17.9 40-39 0-23.1-17.9-41-40-41s-40 17.9-40 41c0 21.1 17.9 39 40 39z" p-id="7318" fill="#ffffff"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32z m-40 728H184V184h656v656z" p-id="7319" fill="#ffffff"></path><path d="M648 672h60c4.4 0 8-3.6 8-8V360c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v304c0 4.4 3.6 8 8 8z" p-id="7320" fill="#ffffff"></path></svg> |
src/assets/svg/preview/scale.svg
0 → 100644
1 | +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595307195033" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8116" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><defs><style type="text/css"></style></defs><path d="M887.081 904.791a25.8 25.8 0 0 1-18.376-7.619L705.618 734.075l-4.163 3.369c-58.255 47.18-131.522 73.16-206.32 73.16-181.07 0-328.377-147.308-328.377-328.367 0-181.068 147.308-328.376 328.377-328.376 181.063 0 328.376 147.308 328.376 328.376 0 77.072-27.412 152.07-77.169 211.17l-3.522 4.173 162.719 162.744a25.846 25.846 0 0 1 7.639 18.432 26.081 26.081 0 0 1-26.051 26.045l-0.046-0.01zM495.13 205.957c-152.336 0-276.27 123.935-276.27 276.27 0 152.33 123.934 276.27 276.27 276.27 152.34 0 276.275-123.94 276.275-276.27 0-152.335-123.935-276.27-276.275-276.27z" fill="#ffffff" p-id="8117"></path><path d="M626.545 508.355h-262.83a26.127 26.127 0 0 1 0-52.255h262.83a26.127 26.127 0 0 1 0 52.255z" fill="#ffffff" p-id="8118"></path><path d="M495.13 639.77a26.127 26.127 0 0 1-26.128-26.128v-262.83a26.127 26.127 0 0 1 52.255 0v262.835a26.127 26.127 0 0 1-26.127 26.123z" fill="#ffffff" p-id="8119"></path></svg> |
src/assets/svg/preview/unrotate.svg
0 → 100644
1 | +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595306911635" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1352" width="48" height="48" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M924.8 337.6c-22.6-53.4-54.9-101.3-96-142.4s-89-73.4-142.4-96C631.1 75.9 572.5 64 512 64S392.9 75.9 337.6 99.2c-53.4 22.6-101.3 54.9-142.4 96-22.4 22.4-42.2 46.8-59.2 73.1V228c0-19.8-16.2-36-36-36s-36 16.2-36 36v288c0 19.8 16.2 36 36 36s36-16.2 36-36v-50.2c4.2-34.8 13.2-68.7 27-101.2 19.1-45.1 46.4-85.6 81.2-120.4C279 209.4 319.5 182 364.6 163c46.7-19.7 96.3-29.8 147.4-29.8 51.2 0 100.8 10 147.4 29.8 45.1 19.1 85.6 46.4 120.4 81.2C814.6 279 842 319.5 861 364.6c19.7 46.7 29.8 96.3 29.8 147.4 0 51.2-10 100.8-29.8 147.4-19.1 45.1-46.4 85.6-81.2 120.4C745 814.6 704.5 842 659.4 861c-46.7 19.7-96.3 29.8-147.4 29.8-64.6 0-128.4-16.5-184.4-47.8-54.4-30.4-100.9-74.1-134.6-126.6-10.3-16.1-31.7-20.8-47.8-10.4-16.1 10.3-20.8 31.7-10.4 47.8 39.8 62 94.8 113.7 159.1 149.6 66.2 37 141.7 56.6 218.1 56.6 60.5 0 119.1-11.9 174.4-35.2 53.4-22.6 101.3-54.9 142.4-96 41.1-41.1 73.4-89 96-142.4C948.1 631.1 960 572.5 960 512s-11.9-119.1-35.2-174.4z" p-id="1353" fill="#ffffff"></path><path d="M275.4 575.5c9.5-2.5 19.1 2.9 22.3 12.2 3.5 10.2 9.9 17.7 19.1 22.6 7.1 3.9 15.1 5.8 24 5.8 16.6 0 30.8-6.9 42.5-20.8 11.7-13.8 20-32.7 24.9-75.1-7.7 12.2-17.3 20.8-28.7 25.8-11.4 5-23.7 7.4-36.8 7.4-26.7 0-47.7-8.3-63.3-24.9-15.5-16.6-23.3-37.9-23.3-64.1 0-25.1 7.7-47.1 23-66.2 15.3-19 37.9-28.6 67.8-28.6 40.3 0 68.1 18.1 83.4 54.4 8.5 19.9 12.7 44.9 12.7 74.9 0 33.8-5.1 63.8-15.3 89.9-16.9 43.5-45.5 65.2-85.8 65.2-27 0-47.6-7.1-61.6-21.2-10-10.1-16.4-22-19.3-35.8-2-9.6 4-19.1 13.5-21.6l0.9 0.1z m103-74.4c9.4-7.5 14.1-20.6 14.1-39.3 0-16.8-4.2-29.3-12.7-37.5S360.6 412 347.5 412c-14 0-25.2 4.7-33.4 14.1-8.2 9.4-12.4 22-12.4 37.7 0 14.9 3.6 26.7 10.9 35.5 7.2 8.8 18.8 13.1 34.6 13.1 11.4 0 21.8-3.8 31.2-11.3zM646.6 414.4c12.4 22.8 18.5 54 18.5 93.7 0 37.6-5.6 68.7-16.8 93.3-16.2 35.3-42.8 52.9-79.6 52.9-33.2 0-57.9-14.4-74.2-43.3-13.5-24.1-20.3-56.4-20.3-97 0-31.4 4.1-58.4 12.2-80.9 15.2-42 42.7-63 82.5-63 35.9 0 61.8 14.8 77.7 44.3z m-40.2 173.3c9.4-13.9 14-39.9 14-78 0-27.4-3.4-50-10.1-67.7-6.8-17.7-19.9-26.6-39.4-26.6-17.9 0-31 8.4-39.3 25.2-8.3 16.8-12.4 41.6-12.4 74.3 0 24.6 2.6 44.4 7.9 59.4 8.1 22.8 22 34.3 41.6 34.3 15.7 0 28.3-7 37.7-20.9zM803.3 387.2c11.2 11.3 16.8 25 16.8 41.2 0 16.7-5.8 30.7-17.5 41.8C791 481.4 777.4 487 762 487c-17.1 0-31.2-5.8-42.1-17.4-10.9-11.6-16.4-25.1-16.4-40.6 0-16.5 5.8-30.4 17.3-41.7 11.5-11.3 25.3-17 41.2-17 16.3 0 30.1 5.7 41.3 16.9zM739.5 451c6.2 6.2 13.7 9.3 22.5 9.3 8.4 0 15.8-3.1 22.1-9.3 6.3-6.2 9.4-13.7 9.4-22.6 0-8.5-3.1-15.9-9.3-22.1-6.2-6.2-13.6-9.3-22.2-9.3s-16.1 3.1-22.4 9.3c-6.3 6.2-9.4 13.7-9.4 22.6-0.1 8.4 3 15.8 9.3 22.1z" p-id="1354" fill="#ffffff"></path></svg> |
src/assets/svg/preview/unscale.svg
0 → 100644
1 | +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595308005241" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9878" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><defs><style type="text/css"></style></defs><path d="M750.3 198.7C598 46.4 351.1 46.4 198.7 198.7s-152.3 399.2 0 551.5C345.1 896.6 578.8 902.3 732 767.3l172.1 172.1 35.4-35.4-172.1-171.9c135-153.2 129.3-387-17.1-533.4z m39.3 403.8c-17.1 42.1-42.2 80-74.7 112.4-32.5 32.5-70.3 57.6-112.4 74.7-40.7 16.5-83.8 24.9-128 24.9s-87.2-8.4-128-24.9c-42.1-17.1-80-42.2-112.4-74.7s-57.6-70.3-74.7-112.4c-16.5-40.7-24.9-83.8-24.9-128s8.4-87.2 24.9-128c17.1-42.1 42.2-80 74.7-112.4s70.3-57.6 112.4-74.7c40.7-16.5 83.8-24.9 128-24.9s87.2 8.4 128 24.9c42.1 17.1 80 42.2 112.4 74.7 32.5 32.5 57.6 70.3 74.7 112.4 16.5 40.7 24.9 83.8 24.9 128s-8.4 87.3-24.9 128zM671 502H271v-50h400v50z" fill="#ffffff" p-id="9879"></path></svg> |
src/components/Preview/index.ts
src/components/Preview/src/functional.ts
0 → 100644
1 | +import ImgPreview from './index'; | ||
2 | +import { isClient } from '/@/utils/is'; | ||
3 | + | ||
4 | +import type { Options, Props } from './types'; | ||
5 | + | ||
6 | +import { createVNode, render } from 'vue'; | ||
7 | + | ||
8 | +let instance: any = null; | ||
9 | +export function createImgPreview(options: Options) { | ||
10 | + if (!isClient) return; | ||
11 | + const { imageList, show = true, index = 0 } = options; | ||
12 | + | ||
13 | + const propsData: Partial<Props> = {}; | ||
14 | + const container = document.createElement('div'); | ||
15 | + propsData.imageList = imageList; | ||
16 | + propsData.show = show; | ||
17 | + propsData.index = index; | ||
18 | + | ||
19 | + instance = createVNode(ImgPreview, propsData); | ||
20 | + render(instance, container); | ||
21 | + document.body.appendChild(container); | ||
22 | +} |
src/components/Preview/src/index.less
0 → 100644
1 | +.img-preview { | ||
2 | + position: fixed; | ||
3 | + top: 0; | ||
4 | + right: 0; | ||
5 | + bottom: 0; | ||
6 | + left: 0; | ||
7 | + z-index: @preview-comp-z-index; | ||
8 | + background: rgba(0, 0, 0, 0.5); | ||
9 | + user-select: none; | ||
10 | + | ||
11 | + &-content { | ||
12 | + display: flex; | ||
13 | + width: 100%; | ||
14 | + height: 100%; | ||
15 | + color: @white; | ||
16 | + justify-content: center; | ||
17 | + align-items: center; | ||
18 | + } | ||
19 | + | ||
20 | + &-image { | ||
21 | + cursor: pointer; | ||
22 | + transition: transform 0.3s; | ||
23 | + } | ||
24 | + | ||
25 | + &__close { | ||
26 | + position: absolute; | ||
27 | + top: -40px; | ||
28 | + right: -40px; | ||
29 | + width: 80px; | ||
30 | + height: 80px; | ||
31 | + overflow: hidden; | ||
32 | + color: @white; | ||
33 | + cursor: pointer; | ||
34 | + background-color: rgba(0, 0, 0, 0.5); | ||
35 | + border-radius: 50%; | ||
36 | + transition: all 0.2s; | ||
37 | + | ||
38 | + &-icon { | ||
39 | + position: absolute; | ||
40 | + top: 46px; | ||
41 | + left: 16px; | ||
42 | + font-size: 16px; | ||
43 | + } | ||
44 | + | ||
45 | + &:hover { | ||
46 | + background-color: rgba(0, 0, 0, 0.8); | ||
47 | + } | ||
48 | + } | ||
49 | + | ||
50 | + &__index { | ||
51 | + position: absolute; | ||
52 | + bottom: 5%; | ||
53 | + left: 50%; | ||
54 | + padding: 0 22px; | ||
55 | + font-size: 16px; | ||
56 | + background: rgba(109, 109, 109, 0.6); | ||
57 | + border-radius: 15px; | ||
58 | + transform: translateX(-50%); | ||
59 | + } | ||
60 | + | ||
61 | + &__controller { | ||
62 | + position: absolute; | ||
63 | + bottom: 10%; | ||
64 | + left: 50%; | ||
65 | + display: flex; | ||
66 | + width: 260px; | ||
67 | + height: 44px; | ||
68 | + padding: 0 22px; | ||
69 | + margin-left: -139px; | ||
70 | + background: rgba(109, 109, 109, 0.6); | ||
71 | + border-radius: 22px; | ||
72 | + justify-content: center; | ||
73 | + | ||
74 | + &-item { | ||
75 | + display: flex; | ||
76 | + height: 100%; | ||
77 | + padding: 0 9px; | ||
78 | + font-size: 24px; | ||
79 | + cursor: pointer; | ||
80 | + transition: all 0.2s; | ||
81 | + | ||
82 | + &:hover { | ||
83 | + transform: scale(1.2); | ||
84 | + } | ||
85 | + | ||
86 | + img { | ||
87 | + width: 1em; | ||
88 | + } | ||
89 | + } | ||
90 | + } | ||
91 | + | ||
92 | + &__arrow { | ||
93 | + position: absolute; | ||
94 | + top: 50%; | ||
95 | + display: flex; | ||
96 | + align-items: center; | ||
97 | + justify-content: center; | ||
98 | + width: 50px; | ||
99 | + height: 50px; | ||
100 | + font-size: 28px; | ||
101 | + cursor: pointer; | ||
102 | + background-color: rgba(0, 0, 0, 0.5); | ||
103 | + border-radius: 50%; | ||
104 | + transition: all 0.2s; | ||
105 | + | ||
106 | + &:hover { | ||
107 | + background-color: rgba(0, 0, 0, 0.8); | ||
108 | + } | ||
109 | + | ||
110 | + &.left { | ||
111 | + left: 50px; | ||
112 | + } | ||
113 | + | ||
114 | + &.right { | ||
115 | + right: 50px; | ||
116 | + } | ||
117 | + } | ||
118 | +} |
src/components/Preview/src/index.tsx
0 → 100644
1 | +import './index.less'; | ||
2 | + | ||
3 | +import { defineComponent, ref, unref, computed, reactive, watchEffect } from 'vue'; | ||
4 | + | ||
5 | +// @ts-ignore | ||
6 | +import { basicProps } from './props'; | ||
7 | +// @ts-ignore | ||
8 | +import { Props } from './types'; | ||
9 | + | ||
10 | +import { CloseOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; | ||
11 | +// import { Spin } from 'ant-design-vue'; | ||
12 | + | ||
13 | +import resumeSvg from '/@/assets/svg/preview/resume.svg'; | ||
14 | +import rotateSvg from '/@/assets/svg/preview/p-rotate.svg'; | ||
15 | +import scaleSvg from '/@/assets/svg/preview/scale.svg'; | ||
16 | +import unScaleSvg from '/@/assets/svg/preview/unscale.svg'; | ||
17 | +import unRotateSvg from '/@/assets/svg/preview/unrotate.svg'; | ||
18 | +enum StatueEnum { | ||
19 | + LOADING, | ||
20 | + DONE, | ||
21 | + FAIL, | ||
22 | +} | ||
23 | +interface ImgState { | ||
24 | + currentUrl: string; | ||
25 | + imgScale: number; | ||
26 | + imgRotate: number; | ||
27 | + imgTop: number; | ||
28 | + imgLeft: number; | ||
29 | + currentIndex: number; | ||
30 | + status: StatueEnum; | ||
31 | + moveX: number; | ||
32 | + moveY: number; | ||
33 | + show: boolean; | ||
34 | +} | ||
35 | + | ||
36 | +const prefixCls = 'img-preview'; | ||
37 | +export default defineComponent({ | ||
38 | + name: 'ImagePreview', | ||
39 | + props: basicProps, | ||
40 | + setup(props: Props) { | ||
41 | + const imgState = reactive<ImgState>({ | ||
42 | + currentUrl: '', | ||
43 | + imgScale: 1, | ||
44 | + imgRotate: 0, | ||
45 | + imgTop: 0, | ||
46 | + imgLeft: 0, | ||
47 | + status: StatueEnum.LOADING, | ||
48 | + currentIndex: 0, | ||
49 | + moveX: 0, | ||
50 | + moveY: 0, | ||
51 | + show: props.show, | ||
52 | + }); | ||
53 | + | ||
54 | + const wrapElRef = ref<HTMLDivElement | null>(null); | ||
55 | + const imgElRef = ref<HTMLImageElement | null>(null); | ||
56 | + | ||
57 | + // 初始化 | ||
58 | + function init() { | ||
59 | + initMouseWheel(); | ||
60 | + const { index, imageList } = props; | ||
61 | + | ||
62 | + if (!imageList || !imageList.length) { | ||
63 | + throw new Error('imageList is undefined'); | ||
64 | + } | ||
65 | + imgState.currentIndex = index; | ||
66 | + handleIChangeImage(imageList[index]); | ||
67 | + } | ||
68 | + | ||
69 | + // 重置 | ||
70 | + function initState() { | ||
71 | + imgState.imgScale = 1; | ||
72 | + imgState.imgRotate = 0; | ||
73 | + imgState.imgTop = 0; | ||
74 | + imgState.imgLeft = 0; | ||
75 | + } | ||
76 | + | ||
77 | + // 初始化鼠标滚轮事件 | ||
78 | + function initMouseWheel() { | ||
79 | + const wrapEl = unref(wrapElRef); | ||
80 | + if (!wrapEl) { | ||
81 | + return; | ||
82 | + } | ||
83 | + (wrapEl as any).onmousewheel = scrollFunc; | ||
84 | + // 火狐浏览器没有onmousewheel事件,用DOMMouseScroll代替 | ||
85 | + document.body.addEventListener('DOMMouseScroll', scrollFunc); | ||
86 | + // 禁止火狐浏览器下拖拽图片的默认事件 | ||
87 | + document.ondragstart = function () { | ||
88 | + return false; | ||
89 | + }; | ||
90 | + } | ||
91 | + | ||
92 | + // 监听鼠标滚轮 | ||
93 | + function scrollFunc(e: any) { | ||
94 | + e = e || window.event; | ||
95 | + e.delta = e.wheelDelta || -e.detail; | ||
96 | + | ||
97 | + e.preventDefault(); | ||
98 | + if (e.delta > 0) { | ||
99 | + // 滑轮向上滚动 | ||
100 | + scaleFunc(0.015); | ||
101 | + } | ||
102 | + if (e.delta < 0) { | ||
103 | + // 滑轮向下滚动 | ||
104 | + scaleFunc(-0.015); | ||
105 | + } | ||
106 | + } | ||
107 | + // 缩放函数 | ||
108 | + function scaleFunc(num: number) { | ||
109 | + if (imgState.imgScale <= 0.2 && num < 0) return; | ||
110 | + imgState.imgScale += num; | ||
111 | + } | ||
112 | + | ||
113 | + // 旋转图片 | ||
114 | + function rotateFunc(deg: number) { | ||
115 | + imgState.imgRotate += deg; | ||
116 | + } | ||
117 | + | ||
118 | + // 鼠标事件 | ||
119 | + function handleMouseUp() { | ||
120 | + const imgEl = unref(imgElRef); | ||
121 | + if (!imgEl) return; | ||
122 | + imgEl.onmousemove = null; | ||
123 | + } | ||
124 | + | ||
125 | + // 更换图片 | ||
126 | + function handleIChangeImage(url: string) { | ||
127 | + imgState.status = StatueEnum.LOADING; | ||
128 | + const img = new Image(); | ||
129 | + img.src = url; | ||
130 | + img.onload = () => { | ||
131 | + imgState.currentUrl = url; | ||
132 | + imgState.status = StatueEnum.DONE; | ||
133 | + }; | ||
134 | + img.onerror = () => { | ||
135 | + imgState.status = StatueEnum.FAIL; | ||
136 | + }; | ||
137 | + } | ||
138 | + | ||
139 | + // 关闭 | ||
140 | + function handleClose(e: MouseEvent) { | ||
141 | + e && e.stopPropagation(); | ||
142 | + imgState.show = false; | ||
143 | + // 移除火狐浏览器下的鼠标滚动事件 | ||
144 | + document.body.removeEventListener('DOMMouseScroll', scrollFunc); | ||
145 | + // 恢复火狐及Safari浏览器下的图片拖拽 | ||
146 | + document.ondragstart = null; | ||
147 | + } | ||
148 | + | ||
149 | + // 图片复原 | ||
150 | + function resume() { | ||
151 | + initState(); | ||
152 | + } | ||
153 | + | ||
154 | + // 上一页下一页 | ||
155 | + function handleChange(direction: 'left' | 'right') { | ||
156 | + const { currentIndex } = imgState; | ||
157 | + const { imageList } = props; | ||
158 | + if (direction === 'left') { | ||
159 | + imgState.currentIndex--; | ||
160 | + if (currentIndex <= 0) { | ||
161 | + imgState.currentIndex = imageList.length - 1; | ||
162 | + } | ||
163 | + } | ||
164 | + if (direction === 'right') { | ||
165 | + imgState.currentIndex++; | ||
166 | + if (currentIndex >= imageList.length - 1) { | ||
167 | + imgState.currentIndex = 0; | ||
168 | + } | ||
169 | + } | ||
170 | + handleIChangeImage(imageList[imgState.currentIndex]); | ||
171 | + } | ||
172 | + | ||
173 | + function handleAddMoveListener(e: MouseEvent) { | ||
174 | + e = e || window.event; | ||
175 | + imgState.moveX = e.clientX; | ||
176 | + imgState.moveY = e.clientY; | ||
177 | + const imgEl = unref(imgElRef); | ||
178 | + if (imgEl) { | ||
179 | + imgEl.onmousemove = moveFunc; | ||
180 | + } | ||
181 | + } | ||
182 | + | ||
183 | + function moveFunc(e: MouseEvent) { | ||
184 | + e = e || window.event; | ||
185 | + e.preventDefault(); | ||
186 | + const movementX = e.clientX - imgState.moveX; | ||
187 | + const movementY = e.clientY - imgState.moveY; | ||
188 | + imgState.imgLeft += movementX; | ||
189 | + imgState.imgTop += movementY; | ||
190 | + imgState.moveX = e.clientX; | ||
191 | + imgState.moveY = e.clientY; | ||
192 | + } | ||
193 | + | ||
194 | + // 获取图片样式 | ||
195 | + const getImageStyle = computed(() => { | ||
196 | + const { imgScale, imgRotate, imgTop, imgLeft } = imgState; | ||
197 | + return { | ||
198 | + transform: `scale(${imgScale}) rotate(${imgRotate}deg)`, | ||
199 | + marginTop: `${imgTop}px`, | ||
200 | + marginLeft: `${imgLeft}px`, | ||
201 | + }; | ||
202 | + }); | ||
203 | + | ||
204 | + const getIsMultipleImage = computed(() => { | ||
205 | + const { imageList } = props; | ||
206 | + return imageList.length > 1; | ||
207 | + }); | ||
208 | + | ||
209 | + watchEffect(() => { | ||
210 | + if (props.show) { | ||
211 | + init(); | ||
212 | + } | ||
213 | + if (props.imageList) { | ||
214 | + initState(); | ||
215 | + } | ||
216 | + }); | ||
217 | + | ||
218 | + const renderClose = () => { | ||
219 | + return ( | ||
220 | + <div class={`${prefixCls}__close`} onClick={handleClose}> | ||
221 | + <CloseOutlined class={`${prefixCls}__close-icon`} /> | ||
222 | + </div> | ||
223 | + ); | ||
224 | + }; | ||
225 | + | ||
226 | + const renderIndex = () => { | ||
227 | + if (!unref(getIsMultipleImage)) { | ||
228 | + return null; | ||
229 | + } | ||
230 | + const { currentIndex } = imgState; | ||
231 | + const { imageList } = props; | ||
232 | + return ( | ||
233 | + <div class={`${prefixCls}__index`}> | ||
234 | + {currentIndex + 1} / {imageList.length} | ||
235 | + </div> | ||
236 | + ); | ||
237 | + }; | ||
238 | + | ||
239 | + const renderController = () => { | ||
240 | + return ( | ||
241 | + <div class={`${prefixCls}__controller`}> | ||
242 | + <div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(-0.15)}> | ||
243 | + <img src={unScaleSvg} /> | ||
244 | + </div> | ||
245 | + <div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(0.15)}> | ||
246 | + <img src={scaleSvg} /> | ||
247 | + </div> | ||
248 | + <div class={`${prefixCls}__controller-item`} onClick={resume}> | ||
249 | + <img src={resumeSvg} /> | ||
250 | + </div> | ||
251 | + <div class={`${prefixCls}__controller-item`} onClick={() => rotateFunc(-90)}> | ||
252 | + <img src={unRotateSvg} /> | ||
253 | + </div> | ||
254 | + <div class={`${prefixCls}__controller-item`} onClick={() => rotateFunc(90)}> | ||
255 | + <img src={rotateSvg} /> | ||
256 | + </div> | ||
257 | + </div> | ||
258 | + ); | ||
259 | + }; | ||
260 | + | ||
261 | + const renderArrow = (direction: 'left' | 'right') => { | ||
262 | + if (!unref(getIsMultipleImage)) { | ||
263 | + return null; | ||
264 | + } | ||
265 | + return ( | ||
266 | + <div class={[`${prefixCls}__arrow`, direction]} onClick={() => handleChange(direction)}> | ||
267 | + {direction === 'left' ? <LeftOutlined /> : <RightOutlined />} | ||
268 | + </div> | ||
269 | + ); | ||
270 | + }; | ||
271 | + | ||
272 | + return () => { | ||
273 | + return ( | ||
274 | + imgState.show && ( | ||
275 | + <div class={prefixCls} ref={wrapElRef} onMouseup={handleMouseUp}> | ||
276 | + <div class={`${prefixCls}-content`}> | ||
277 | + {/*<Spin*/} | ||
278 | + {/* indicator={<LoadingOutlined style="font-size: 24px" spin />}*/} | ||
279 | + {/* spinning={true}*/} | ||
280 | + {/* class={[*/} | ||
281 | + {/* `${prefixCls}-image`,*/} | ||
282 | + {/* {*/} | ||
283 | + {/* hidden: imgState.status !== StatueEnum.LOADING,*/} | ||
284 | + {/* },*/} | ||
285 | + {/* ]}*/} | ||
286 | + {/*/>*/} | ||
287 | + <img | ||
288 | + style={unref(getImageStyle)} | ||
289 | + class={[`${prefixCls}-image`, imgState.status === StatueEnum.DONE ? '' : 'hidden']} | ||
290 | + ref={imgElRef} | ||
291 | + src={imgState.currentUrl} | ||
292 | + onMousedown={handleAddMoveListener} | ||
293 | + /> | ||
294 | + {renderClose()} | ||
295 | + {renderIndex()} | ||
296 | + {renderController()} | ||
297 | + {renderArrow('left')} | ||
298 | + {renderArrow('right')} | ||
299 | + </div> | ||
300 | + </div> | ||
301 | + ) | ||
302 | + ); | ||
303 | + }; | ||
304 | + }, | ||
305 | +}); |
src/components/Preview/src/props.ts
0 → 100644
1 | +import { PropType } from 'vue'; | ||
2 | +export const basicProps = { | ||
3 | + show: { | ||
4 | + type: Boolean as PropType<boolean>, | ||
5 | + default: false, | ||
6 | + }, | ||
7 | + imageList: { | ||
8 | + type: [Array] as PropType<string[]>, | ||
9 | + default: null, | ||
10 | + }, | ||
11 | + index: { | ||
12 | + type: Number as PropType<number>, | ||
13 | + default: 0, | ||
14 | + }, | ||
15 | +}; |
src/components/Preview/src/types.ts
0 → 100644
1 | +export interface Options { | ||
2 | + show?: boolean; | ||
3 | + imageList: string[]; | ||
4 | + index?: number; | ||
5 | +} | ||
6 | + | ||
7 | +export interface Props { | ||
8 | + show: boolean; | ||
9 | + instance: Props; | ||
10 | + imageList: string[]; | ||
11 | + index: number; | ||
12 | +} | ||
13 | + | ||
14 | +export interface ImageProps { | ||
15 | + alt?: string; | ||
16 | + fallback?: string; | ||
17 | + src: string; | ||
18 | + width: string | number; | ||
19 | + height?: string | number; | ||
20 | + placeholder?: string | boolean; | ||
21 | + preview?: | ||
22 | + | boolean | ||
23 | + | { | ||
24 | + visible?: boolean; | ||
25 | + onVisibleChange?: (visible: boolean, prevVisible: boolean) => void; | ||
26 | + getContainer: string | HTMLElement | (() => HTMLElement); | ||
27 | + }; | ||
28 | +} | ||
29 | + | ||
30 | +export type ImageItem = string | ImageProps; |
src/views/demo/feat/img-preview/index.vue
1 | <template> | 1 | <template> |
2 | <PageWrapper title="图片预览示例"> | 2 | <PageWrapper title="图片预览示例"> |
3 | + <p @click="openImg">打开图片</p> | ||
3 | <ImagePreview :imageList="imgList" /> | 4 | <ImagePreview :imageList="imgList" /> |
4 | </PageWrapper> | 5 | </PageWrapper> |
5 | </template> | 6 | </template> |
6 | <script lang="ts"> | 7 | <script lang="ts"> |
7 | import { defineComponent } from 'vue'; | 8 | import { defineComponent } from 'vue'; |
8 | - import { ImagePreview } from '/@/components/Preview/index'; | 9 | + import { createImgPreview, ImagePreview } from '/@/components/Preview/index'; |
9 | import { PageWrapper } from '/@/components/Page'; | 10 | import { PageWrapper } from '/@/components/Page'; |
10 | 11 | ||
11 | const imgList: string[] = [ | 12 | const imgList: string[] = [ |
@@ -16,7 +17,10 @@ | @@ -16,7 +17,10 @@ | ||
16 | export default defineComponent({ | 17 | export default defineComponent({ |
17 | components: { PageWrapper, ImagePreview }, | 18 | components: { PageWrapper, ImagePreview }, |
18 | setup() { | 19 | setup() { |
19 | - return { imgList }; | 20 | + function openImg() { |
21 | + createImgPreview({ imageList: imgList }); | ||
22 | + } | ||
23 | + return { imgList, openImg }; | ||
20 | }, | 24 | }, |
21 | }); | 25 | }); |
22 | </script> | 26 | </script> |