@hyper-proto/iv-viewer 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Slider.js ADDED
@@ -0,0 +1,97 @@
1
+ import { noop } from './util';
2
+
3
+ class Slider {
4
+ constructor (container, {
5
+ onStart, onMove, onEnd, isSliderEnabled,
6
+ }) {
7
+ this.container = container;
8
+ this.isSliderEnabled = isSliderEnabled;
9
+ this.onStart = onStart || noop;
10
+ this.onMove = onMove || noop;
11
+ this.onEnd = onEnd || noop;
12
+ }
13
+
14
+ startHandler = (eStart) => {
15
+ if (!this.isSliderEnabled()) return;
16
+
17
+ this.removeListeners();
18
+
19
+ eStart.preventDefault();
20
+
21
+ const { moveHandler, endHandler, onStart } = this;
22
+
23
+ const isTouchEvent = eStart.type === 'touchstart' || eStart.type === 'touchend';
24
+
25
+ this.touchMoveEvent = isTouchEvent ? 'touchmove' : 'mousemove';
26
+ this.touchEndEvent = isTouchEvent ? 'touchend' : 'mouseup';
27
+
28
+ this.sx = isTouchEvent ? eStart.touches[0].clientX : eStart.clientX;
29
+
30
+ this.sy = isTouchEvent ? eStart.touches[0].clientY : eStart.clientY;
31
+
32
+ onStart(eStart, {
33
+ x: this.sx,
34
+ y: this.sy,
35
+ });
36
+
37
+ // add listeners
38
+ document.addEventListener(this.touchMoveEvent, moveHandler);
39
+ document.addEventListener(this.touchEndEvent, endHandler);
40
+ /*
41
+ add end handler in context menu as well.
42
+ As mouseup event is not trigger on context menu open
43
+ https://bugs.chromium.org/p/chromium/issues/detail?id=506801
44
+ */
45
+ document.addEventListener('contextmenu', endHandler);
46
+ };
47
+
48
+ moveHandler = (eMove) => {
49
+ if (!this.isSliderEnabled()) return;
50
+
51
+ eMove.preventDefault();
52
+ const { sx, sy, onMove } = this;
53
+
54
+ const isTouchEvent = this.touchMoveEvent === 'touchmove';
55
+
56
+ // get the coordinates
57
+ const mx = isTouchEvent ? eMove.touches[0].clientX : eMove.clientX;
58
+ const my = isTouchEvent ? eMove.touches[0].clientY : eMove.clientY;
59
+
60
+ onMove(eMove, {
61
+ dx: mx - sx,
62
+ dy: my - sy,
63
+ mx,
64
+ my,
65
+ });
66
+ };
67
+
68
+ endHandler = () => {
69
+ if (!this.isSliderEnabled()) return;
70
+ this.removeListeners();
71
+ this.onEnd();
72
+ };
73
+
74
+ // remove previous events if it's not removed
75
+ // - Case when while sliding mouse moved out of document and released there
76
+ removeListeners () {
77
+ if (!this.touchMoveEvent) return;
78
+ document.removeEventListener(this.touchMoveEvent, this.moveHandler);
79
+ document.removeEventListener(this.touchEndEvent, this.endHandler);
80
+ document.removeEventListener('contextmenu', this.endHandler);
81
+ }
82
+
83
+ init () {
84
+ ['touchstart', 'mousedown'].forEach((evt) => {
85
+ this.container.addEventListener(evt, this.startHandler);
86
+ });
87
+ }
88
+
89
+ destroy () {
90
+ ['touchstart', 'mousedown'].forEach((evt) => {
91
+ this.container.removeEventListener(evt, this.startHandler);
92
+ });
93
+ this.removeListeners();
94
+ }
95
+ }
96
+
97
+ export default Slider;
package/src/dist.js ADDED
@@ -0,0 +1,6 @@
1
+ import ImageViewer from './ImageViewer';
2
+ import FullScreenViewer from './FullScreen';
3
+
4
+ ImageViewer.FullScreenViewer = FullScreenViewer;
5
+
6
+ export default ImageViewer;
package/src/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import ImageViewer from './ImageViewer';
2
+
3
+ export default ImageViewer;
4
+ export { ImageViewer };
5
+ export { default as FullScreenViewer } from './FullScreen';
@@ -0,0 +1,268 @@
1
+ @use './variables' as *;
2
+
3
+ .iv {
4
+ &-container {
5
+ overflow: hidden;
6
+ position: relative;
7
+ user-select: none;
8
+ }
9
+
10
+ &-fullscreen {
11
+ position: fixed;
12
+ background: $iv-dark-gray;
13
+ width: 100%;
14
+ height: 100%;
15
+ top: 0;
16
+ left: 0;
17
+ display: none;
18
+ z-index: 1000;
19
+ }
20
+
21
+ &-fullscreen-container {
22
+ position: relative;
23
+ height: 100%;
24
+ width: 100%;
25
+ }
26
+
27
+ &-fullscreen-close {
28
+ position: absolute;
29
+ width: $iv-close-icon-size;
30
+ height: $iv-close-icon-size;
31
+ right: calc($iv-boundary-space / 2);
32
+ top: calc($iv-boundary-space / 2);
33
+ padding: calc($iv-boundary-space / 2);
34
+ cursor: pointer;
35
+ text-align: center;
36
+ overflow: hidden;
37
+ text-shadow: $iv-fullscreen-close-text-shadow;
38
+ transition: all ease 200ms;
39
+
40
+ &:after,
41
+ &:before {
42
+ content: "";
43
+ height: $iv-fullscreen-close-height;
44
+ width: $iv-close-icon-size;
45
+ background: $iv-white;
46
+ position: absolute;
47
+ top: 50%;
48
+ left: 50%;
49
+ }
50
+
51
+ &:before {
52
+ transform: translate(-50%, -50%) rotate(45deg);
53
+ }
54
+ &:after {
55
+ transform: translate(-50%, -50%) rotate(-45deg);
56
+ }
57
+ &:hover {
58
+ transform: rotate(90deg);
59
+ transform-origin: 50% 50%;
60
+ }
61
+ }
62
+
63
+ /***** snap view css *****/
64
+ &-snap-view {
65
+ width: calc($iv-snap-view-width + 2px);
66
+ height: calc($iv-snap-view-height + 2px);
67
+ position: absolute;
68
+ top: $iv-boundary-space;
69
+ border: 1px solid $iv-snap-view-border-color;
70
+ background: $iv-black;
71
+ z-index: 100;
72
+ box-sizing: border-box;
73
+ transition: opacity ease 400ms;
74
+ opacity: 0;
75
+
76
+ @if ($iv-snap-view-on-left) {
77
+ left: $iv-boundary-space;
78
+ } @else {
79
+ right: $iv-boundary-space;
80
+ }
81
+ }
82
+
83
+ &-snap-image-wrap {
84
+ display: inline-block;
85
+ position: absolute;
86
+ max-width: $iv-snap-view-width;
87
+ max-height: $iv-snap-view-height;
88
+ top: 50%;
89
+ left: 50%;
90
+ transform: translate(-50%, -50%);
91
+ overflow: hidden;
92
+ }
93
+
94
+ &-snap-image {
95
+ position: relative;
96
+ touch-action: none;
97
+ }
98
+
99
+ &-snap-handle {
100
+ box-sizing: border-box;
101
+ position: absolute;
102
+ border: $iv-zoom-handle-border;
103
+ transform: translate3d(0, 0, 0);
104
+ box-shadow: 0 0 0 200px rgba($iv-black, 0.5);
105
+ cursor: pointer;
106
+ cursor: grab;
107
+
108
+ &:active {
109
+ cursor: grabbing;
110
+ }
111
+ }
112
+
113
+ /*** zoom actions ***/
114
+
115
+ &-zoom-actions {
116
+ width: 100%;
117
+ box-sizing: content-box;
118
+ position: absolute;
119
+ top: 150px;
120
+ left: -1px;
121
+ height: $iv-zoom-slider-height;
122
+ border: $iv-zoom-handle-border;
123
+ border-top: 0;
124
+ background: rgba($iv-black, 0.3);
125
+
126
+ &--has-buttons {
127
+ .iv-zoom-slider {
128
+ left: $iv-zoom-button-width;
129
+ right: $iv-zoom-button-width;
130
+ }
131
+ }
132
+ }
133
+
134
+ &-zoom-handle {
135
+ width: $iv-zoom-handle-width;
136
+ height: $iv-zoom-handle-height;
137
+ background: $iv-handle-color;
138
+ position: absolute;
139
+ cursor: pointer;
140
+ cursor: grab;
141
+
142
+ &:active {
143
+ cursor: grabbing;
144
+ }
145
+ }
146
+
147
+ &-zoom-slider {
148
+ box-sizing: inherit;
149
+ position: absolute;
150
+ top: 0;
151
+ bottom: 0;
152
+ left: 0;
153
+ right: 0;
154
+ }
155
+
156
+ &-button-zoom {
157
+ position: absolute;
158
+ top: 0;
159
+ bottom: 0;
160
+ width: $iv-zoom-button-width;
161
+ cursor: pointer;
162
+ display: flex;
163
+ align-items: center;
164
+ justify-content: center;
165
+ &:before,
166
+ &:after {
167
+ content: "";
168
+ height: $iv-zoom-button-icon-height;
169
+ width: $iv-zoom-button-icon-width;
170
+ background: $iv-zoom-button-color;
171
+ position: absolute;
172
+ }
173
+ &--in,
174
+ &--out {
175
+ @extend .iv-button-zoom;
176
+ }
177
+
178
+ &--in {
179
+ right: 0;
180
+ &:after {
181
+ transform: rotate(90deg);
182
+ }
183
+ }
184
+ &--out {
185
+ left: 0;
186
+ }
187
+ }
188
+
189
+ /**** snap view css end *****/
190
+ &-image-mode {
191
+ display: $iv-image-mode-display;
192
+ }
193
+
194
+ &-image-view {
195
+ position: absolute;
196
+ height: 100%;
197
+ width: 100%;
198
+ top: 0;
199
+ left: 0;
200
+ }
201
+
202
+ &-image-wrap {
203
+ display: inline-block;
204
+ &:active {
205
+ cursor: move;
206
+ }
207
+ }
208
+
209
+ &-image {
210
+ max-width: 100%;
211
+ max-height: 100%;
212
+ position: absolute;
213
+ touch-action: none;
214
+ transform: translate3d(0, 0, 0);
215
+ }
216
+
217
+ &-loader {
218
+ top: 50%;
219
+ left: 50%;
220
+ border-radius: 50%;
221
+ width: $iv-loader-width;
222
+ height: $iv-loader-width;
223
+ z-index: 100;
224
+ margin-top: -16px;
225
+ margin-left: -16px;
226
+ font-size: 5px;
227
+ position: absolute;
228
+ text-indent: -9999em;
229
+ border: $iv-loader-border-color;
230
+ border-left: $iv-loader-active-border-color;
231
+ transform: translateZ(0);
232
+ animation: loading-icon 1.1s infinite linear;
233
+ }
234
+
235
+ &-loader:after {
236
+ width: 10em;
237
+ height: 10em;
238
+ border-radius: 50%;
239
+ }
240
+
241
+ @-webkit-keyframes loading-icon {
242
+ 0% {
243
+ -webkit-transform: rotate(0deg);
244
+ transform: rotate(0deg);
245
+ }
246
+ 100% {
247
+ -webkit-transform: rotate(360deg);
248
+ transform: rotate(360deg);
249
+ }
250
+ }
251
+ @keyframes loading-icon {
252
+ 0% {
253
+ -webkit-transform: rotate(0deg);
254
+ transform: rotate(0deg);
255
+ }
256
+ 100% {
257
+ -webkit-transform: rotate(360deg);
258
+ transform: rotate(360deg);
259
+ }
260
+ }
261
+
262
+ @media screen and (max-width: 767px) {
263
+ &-snap-view {
264
+ z-index: -1;
265
+ visibility: hidden;
266
+ }
267
+ }
268
+ }
@@ -0,0 +1,43 @@
1
+ //=========colors=========//
2
+ $iv-white: #fff !default;
3
+ $iv-light-gray: #aaa !default;
4
+ $iv-gray: #6d6d6d !default;
5
+ $iv-dark-gray: #0d0d0d !default;
6
+ $iv-black: black;
7
+
8
+ //========variables========//
9
+ $iv-close-icon-size: 24px !default;
10
+ $iv-boundary-space: 20px !default;
11
+ $iv-handle-color: $iv-white !default;
12
+
13
+
14
+ //========snapview========//
15
+ $iv-snap-view-border-color: $iv-light-gray !default;
16
+ $iv-snap-view-width: 150px !default;
17
+ $iv-snap-view-height: 150px !default;
18
+ $iv-snap-view-on-left: true !default;
19
+
20
+ //========zoom========//
21
+ $iv-zoom-slider-height: 20px !default;
22
+ $iv-zoom-handle-height: $iv-zoom-slider-height !default;
23
+ $iv-zoom-handle-width: 20px !default;
24
+ $iv-zoom-handle-border: 1px solid $iv-light-gray !default;
25
+ $iv-zoom-button-width: 24px !default;
26
+ $iv-zoom-button-icon-height: 2px !default;
27
+ $iv-zoom-button-icon-width: 10px !default;
28
+ $iv-zoom-button-color: $iv-white !default;
29
+ $iv-zoom-button-spacing: 4px !default;
30
+
31
+ //========fullscreen========//
32
+ $iv-fullscreen-bg-color: $iv-dark-gray !default;
33
+ $iv-fullscreen-close-text-shadow: 0 0 3px $iv-gray !default;
34
+ $iv-fullscreen-close-height: 4px !default;
35
+
36
+ //========imagemode========//
37
+ $iv-image-mode-display: inline-block !default;
38
+
39
+ //========loader========//
40
+ $iv-loader-width: 32px !default;
41
+ $iv-loader-height: 32px !default;
42
+ $iv-loader-border-color: 1.1em solid rgba($iv-black, 0.2) !default;
43
+ $iv-loader-active-border-color: 1.1em solid $iv-white !default;
@@ -0,0 +1 @@
1
+ @use './iv-viewer';
package/src/util.js ADDED
@@ -0,0 +1,158 @@
1
+ // constants
2
+ export const ZOOM_CONSTANT = 15; // increase or decrease value for zoom on mouse wheel
3
+ export const MOUSE_WHEEL_COUNT = 5; // A mouse delta after which it should stop preventing default behaviour of mouse wheel
4
+
5
+ export function noop () {}
6
+ export function preventDefault (e) {
7
+ e.preventDefault();
8
+ }
9
+
10
+ // ease out method
11
+ /*
12
+ t : current time,
13
+ b : initial value,
14
+ c : changed value,
15
+ d : duration
16
+ */
17
+ export function easeOutQuart (t, b, c, d) {
18
+ t /= d;
19
+ t -= 1;
20
+ return -c * (t * t * t * t - 1) + b;
21
+ }
22
+
23
+ export function createElement (options) {
24
+ const elem = document.createElement(options.tagName);
25
+ if (options.id) elem.id = options.id;
26
+ if (options.html) elem.innerHTML = options.html;
27
+ if (options.className) elem.className = options.className;
28
+ if (options.src) elem.src = options.src;
29
+ if (options.style) elem.style.cssText = options.style;
30
+ if (options.child) elem.appendChild(options.child);
31
+
32
+ // Insert before
33
+ if (options.insertBefore) {
34
+ options.parent.insertBefore(elem, options.insertBefore);
35
+
36
+ // Standard append
37
+ } else {
38
+ options.parent.appendChild(elem);
39
+ }
40
+
41
+ return elem;
42
+ }
43
+
44
+ // method to add class
45
+ export function addClass (el, className) {
46
+ const classNameAry = className.split(' ');
47
+
48
+ if (classNameAry.length > 1) {
49
+ classNameAry.forEach((classItem) => addClass(el, classItem));
50
+ } else if (el.classList) {
51
+ el.classList.add(className);
52
+ } else {
53
+ el.className += ` ${className}`;
54
+ }
55
+ }
56
+
57
+ // method to remove class
58
+ export function removeClass (el, className) {
59
+ const classNameAry = className.split(' ');
60
+ if (classNameAry.length > 1) {
61
+ classNameAry.forEach((classItem) => removeClass(el, classItem));
62
+ } else if (el.classList) {
63
+ el.classList.remove(className);
64
+ } else {
65
+ el.className = el.className.replace(new RegExp(`(^|\\b)${className.split(' ').join('|')}(\\b|$)`, 'gi'), ' ');
66
+ }
67
+ }
68
+
69
+ // function to check if image is loaded
70
+ export function imageLoaded (img) {
71
+ return img.complete && (typeof img.naturalWidth === 'undefined' || img.naturalWidth !== 0);
72
+ }
73
+
74
+ export function toArray (list) {
75
+ if (!(list instanceof NodeList || list instanceof HTMLCollection)) return [list];
76
+ return Array.prototype.slice.call(list);
77
+ }
78
+
79
+ export function assign (target, ...rest) {
80
+ rest.forEach((obj) => {
81
+ Object.keys(obj).forEach((key) => {
82
+ target[key] = obj[key];
83
+ });
84
+ });
85
+ return target;
86
+ }
87
+
88
+ export function css (elements, properties) {
89
+ const elmArray = toArray(elements);
90
+
91
+ if (typeof properties === 'string') {
92
+ return window.getComputedStyle(elmArray[0])[properties];
93
+ }
94
+
95
+ elmArray.forEach((element) => {
96
+ Object.keys(properties).forEach((key) => {
97
+ const value = properties[key];
98
+ element.style[key] = value;
99
+ });
100
+ });
101
+
102
+ return undefined;
103
+ }
104
+
105
+ export function removeCss (element, property) {
106
+ element.style.removeProperty(property);
107
+ }
108
+
109
+ export function wrap (element, { tag = 'div', className, id, style }) {
110
+ const wrapper = document.createElement(tag);
111
+ if (className) wrapper.className = className;
112
+ if (id) wrapper.id = id;
113
+ if (style) wrapper.style = style;
114
+ element.parentNode.insertBefore(wrapper, element);
115
+ element.parentNode.removeChild(element);
116
+ wrapper.appendChild(element);
117
+ return wrapper;
118
+ }
119
+
120
+ export function unwrap (element) {
121
+ const parent = element.parentNode;
122
+
123
+ if (parent !== document.body) {
124
+ parent.parentNode.insertBefore(element, parent);
125
+ parent.parentNode.removeChild(parent);
126
+ }
127
+ }
128
+
129
+ export function remove (elements) {
130
+ const elmArray = toArray(elements);
131
+ elmArray.forEach((element) => {
132
+ element.parentNode.removeChild(element);
133
+ });
134
+ }
135
+
136
+ export function clamp (num, min, max) {
137
+ return Math.min(Math.max(num, min), max);
138
+ }
139
+
140
+ export function assignEvent (element, events, handler) {
141
+ if (typeof events === 'string') events = [events];
142
+
143
+ events.forEach((event) => {
144
+ element.addEventListener(event, handler);
145
+ });
146
+
147
+ return () => {
148
+ events.forEach((event) => {
149
+ element.removeEventListener(event, handler);
150
+ });
151
+ };
152
+ }
153
+
154
+ export function getTouchPointsDistance (touches) {
155
+ const touch0 = touches[0];
156
+ const touch1 = touches[1];
157
+ return Math.sqrt((touch1.pageX - touch0.pageX) ** 2 + (touch1.pageY - touch0.pageY) ** 2);
158
+ }