js-cloudimage-before-after 1.0.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/LICENSE +21 -0
- package/README.md +305 -0
- package/dist/a11y/aria.d.ts +4 -0
- package/dist/a11y/keyboard.d.ts +22 -0
- package/dist/animation/entrance.d.ts +18 -0
- package/dist/core/ci-before-after.d.ts +64 -0
- package/dist/core/config.d.ts +3 -0
- package/dist/core/types.d.ts +127 -0
- package/dist/fullscreen/fullscreen.d.ts +16 -0
- package/dist/index.d.ts +10 -0
- package/dist/js-cloudimage-before-after.cjs.js +2 -0
- package/dist/js-cloudimage-before-after.cjs.js.map +1 -0
- package/dist/js-cloudimage-before-after.esm.js +1210 -0
- package/dist/js-cloudimage-before-after.esm.js.map +1 -0
- package/dist/js-cloudimage-before-after.min.js +2 -0
- package/dist/js-cloudimage-before-after.min.js.map +1 -0
- package/dist/labels/labels.d.ts +6 -0
- package/dist/react/ci-before-after-viewer.d.ts +3 -0
- package/dist/react/index.cjs +2 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.js +179 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/types.d.ts +11 -0
- package/dist/react/use-ci-before-after.d.ts +5 -0
- package/dist/slider/gestures.d.ts +31 -0
- package/dist/slider/handle.d.ts +2 -0
- package/dist/slider/slider.d.ts +11 -0
- package/dist/utils/cloudimage.d.ts +2 -0
- package/dist/utils/dom.d.ts +8 -0
- package/dist/utils/events.d.ts +12 -0
- package/dist/zoom/controls.d.ts +12 -0
- package/dist/zoom/gestures.d.ts +21 -0
- package/dist/zoom/scroll-hint.d.ts +7 -0
- package/dist/zoom/zoom-pan.d.ts +40 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Scaleflex
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://scaleflex.cloudimg.io/v7/plugins/js-cloudimage-360-view/logo_scaleflex_on_white_bg.jpg?vh=91b12d&w=700" alt="Scaleflex" width="350">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">js-cloudimage-before-after</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
Interactive before/after image comparison slider with zoom, labels, and accessibility. Zero dependencies.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/js-cloudimage-before-after"><img src="https://img.shields.io/npm/v/js-cloudimage-before-after.svg?style=flat-square" alt="npm version"></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/js-cloudimage-before-after"><img src="https://img.shields.io/npm/dm/js-cloudimage-before-after.svg?style=flat-square" alt="npm downloads"></a>
|
|
14
|
+
<a href="https://github.com/scaleflex/js-cloudimage-before-after/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/js-cloudimage-before-after.svg?style=flat-square" alt="license"></a>
|
|
15
|
+
<a href="https://bundlephobia.com/package/js-cloudimage-before-after"><img src="https://img.shields.io/bundlephobia/minzip/js-cloudimage-before-after?style=flat-square" alt="bundle size"></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="https://scaleflex.github.io/js-cloudimage-before-after/">Live Demo</a> |
|
|
20
|
+
<a href="https://codesandbox.io/p/devbox/github/scaleflex/js-cloudimage-before-after/tree/main/examples/vanilla">Vanilla Sandbox</a> |
|
|
21
|
+
<a href="https://codesandbox.io/p/devbox/github/scaleflex/js-cloudimage-before-after/tree/main/examples/react">React Sandbox</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Why js-cloudimage-before-after?
|
|
27
|
+
|
|
28
|
+
Existing before/after sliders are often rigid, inaccessible, or missing features like zoom and vertical orientation. This library was built to fill the gap:
|
|
29
|
+
|
|
30
|
+
- **Lightweight** — under 15 KB gzipped with zero runtime dependencies
|
|
31
|
+
- **Accessible by default** — WCAG 2.1 AA compliant out of the box
|
|
32
|
+
- **Framework-agnostic** — works with vanilla JS, React, or any framework
|
|
33
|
+
- **Built-in zoom & pan** — no need for a separate zoom library
|
|
34
|
+
- **Multiple interaction modes** — drag, hover, or click
|
|
35
|
+
- **Optional Cloudimage CDN** — serve optimally-sized images automatically
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- **Three interaction modes** — Drag, hover, or click to reveal before/after
|
|
42
|
+
- **Horizontal & vertical** — Slider works in both orientations
|
|
43
|
+
- **Zoom & Pan** — CSS transform-based with mouse wheel, pinch-to-zoom, double-click, drag-to-pan
|
|
44
|
+
- **Handle styles** — Arrows, circle, or minimal line
|
|
45
|
+
- **Labels** — Customizable before/after labels with configurable position
|
|
46
|
+
- **Entrance animation** — Smooth slider animation on first view with configurable duration, delay, and easing
|
|
47
|
+
- **Fullscreen mode** — Built-in fullscreen toggle
|
|
48
|
+
- **WCAG 2.1 AA** — Full keyboard navigation, ARIA attributes, focus management, reduced motion
|
|
49
|
+
- **CSS variable theming** — Light and dark themes, fully customizable
|
|
50
|
+
- **Two init methods** — JavaScript API and HTML data-attributes
|
|
51
|
+
- **React wrapper** — Separate entry point with component, hook, and ref API
|
|
52
|
+
- **TypeScript** — Full type definitions
|
|
53
|
+
- **Cloudimage CDN** — Optional responsive image loading
|
|
54
|
+
- **Lazy loading** — IntersectionObserver-based image lazy loading
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npm install js-cloudimage-before-after
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### CDN
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<script src="https://scaleflex.cloudimg.io/v7/plugins/js-cloudimage-before-after/1.0.0/js-cloudimage-before-after.min.js?vh=ad6399&func=proxy"></script>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Quick Start
|
|
69
|
+
|
|
70
|
+
### JavaScript API
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
import CIBeforeAfter from 'js-cloudimage-before-after';
|
|
74
|
+
|
|
75
|
+
const viewer = new CIBeforeAfter('#slider', {
|
|
76
|
+
beforeSrc: 'https://example.com/kitchen-before.jpg',
|
|
77
|
+
afterSrc: 'https://example.com/kitchen-after.jpg',
|
|
78
|
+
beforeAlt: 'Kitchen before renovation',
|
|
79
|
+
afterAlt: 'Kitchen after renovation',
|
|
80
|
+
zoom: true,
|
|
81
|
+
labels: { before: 'Before', after: 'After' },
|
|
82
|
+
theme: 'light',
|
|
83
|
+
handleStyle: 'arrows',
|
|
84
|
+
animate: { duration: 800 },
|
|
85
|
+
onSlide(position) {
|
|
86
|
+
console.log('Position:', position);
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### HTML Data-Attributes
|
|
92
|
+
|
|
93
|
+
```html
|
|
94
|
+
<div
|
|
95
|
+
data-ci-before-after-before-src="https://example.com/before.jpg"
|
|
96
|
+
data-ci-before-after-after-src="https://example.com/after.jpg"
|
|
97
|
+
data-ci-before-after-before-alt="Before renovation"
|
|
98
|
+
data-ci-before-after-after-alt="After renovation"
|
|
99
|
+
data-ci-before-after-zoom="true"
|
|
100
|
+
data-ci-before-after-theme="light"
|
|
101
|
+
data-ci-before-after-handle-style="arrows"
|
|
102
|
+
data-ci-before-after-label-before="Before"
|
|
103
|
+
data-ci-before-after-label-after="After"
|
|
104
|
+
></div>
|
|
105
|
+
|
|
106
|
+
<script>CIBeforeAfter.autoInit();</script>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## API Reference
|
|
110
|
+
|
|
111
|
+
### Constructor
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
new CIBeforeAfter(element: HTMLElement | string, config: CIBeforeAfterConfig)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Config
|
|
118
|
+
|
|
119
|
+
| Option | Type | Default | Description |
|
|
120
|
+
|--------|------|---------|-------------|
|
|
121
|
+
| `beforeSrc` | `string` | — | Before image URL (required) |
|
|
122
|
+
| `afterSrc` | `string` | — | After image URL (required) |
|
|
123
|
+
| `beforeAlt` | `string` | `'Before'` | Before image alt text |
|
|
124
|
+
| `afterAlt` | `string` | `'After'` | After image alt text |
|
|
125
|
+
| `mode` | `'drag' \| 'hover' \| 'click'` | `'drag'` | Interaction mode |
|
|
126
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Slider direction |
|
|
127
|
+
| `initialPosition` | `number` | `50` | Starting position (0–100) |
|
|
128
|
+
| `handleStyle` | `'arrows' \| 'circle' \| 'line'` | `'arrows'` | Handle visual style |
|
|
129
|
+
| `labels` | `boolean \| { before?: string; after?: string }` | `true` | Show labels or custom text |
|
|
130
|
+
| `labelPosition` | `'top' \| 'bottom'` | `'top'` | Label placement |
|
|
131
|
+
| `theme` | `'light' \| 'dark'` | `'light'` | Color theme |
|
|
132
|
+
| `zoom` | `boolean` | `false` | Enable zoom & pan |
|
|
133
|
+
| `zoomMax` | `number` | `4` | Maximum zoom level |
|
|
134
|
+
| `zoomMin` | `number` | `1` | Minimum zoom level |
|
|
135
|
+
| `zoomControls` | `boolean` | `true` | Show zoom control buttons |
|
|
136
|
+
| `zoomControlsPosition` | `string` | `'bottom-right'` | Zoom controls position (`top-left`, `top-center`, `top-right`, `bottom-left`, `bottom-center`, `bottom-right`) |
|
|
137
|
+
| `scrollHint` | `boolean` | `true` | Show scroll hint when zoomed |
|
|
138
|
+
| `animate` | `boolean \| AnimateConfig` | `false` | Entrance animation |
|
|
139
|
+
| `animateOnce` | `boolean` | `true` | Animate only on first view |
|
|
140
|
+
| `fullscreenButton` | `boolean` | `true` | Show fullscreen button |
|
|
141
|
+
| `lazyLoad` | `boolean` | `true` | Lazy load images |
|
|
142
|
+
| `keyboardStep` | `number` | `1` | Arrow key step (%) |
|
|
143
|
+
| `keyboardLargeStep` | `number` | `10` | Shift+Arrow step (%) |
|
|
144
|
+
| `onSlide` | `(position: number) => void` | — | Position change callback |
|
|
145
|
+
| `onZoom` | `(level: number) => void` | — | Zoom change callback |
|
|
146
|
+
| `onFullscreenChange` | `(isFullscreen: boolean) => void` | — | Fullscreen callback |
|
|
147
|
+
| `onReady` | `() => void` | — | Ready callback |
|
|
148
|
+
| `cloudimage` | `CloudimageConfig` | — | Cloudimage CDN config |
|
|
149
|
+
|
|
150
|
+
### AnimateConfig
|
|
151
|
+
|
|
152
|
+
| Option | Type | Default | Description |
|
|
153
|
+
|--------|------|---------|-------------|
|
|
154
|
+
| `duration` | `number` | `800` | Duration in ms |
|
|
155
|
+
| `delay` | `number` | `0` | Delay before start in ms |
|
|
156
|
+
| `easing` | `string` | `'ease-out'` | CSS easing function |
|
|
157
|
+
|
|
158
|
+
### Instance Methods
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
instance.setPosition(percent: number): void // Set slider position (0–100)
|
|
162
|
+
instance.getPosition(): number // Get current position
|
|
163
|
+
instance.setZoom(level: number): void // Set zoom level
|
|
164
|
+
instance.getZoom(): number // Get current zoom level
|
|
165
|
+
instance.resetZoom(): void // Reset zoom to 1×
|
|
166
|
+
instance.enterFullscreen(): void // Enter fullscreen
|
|
167
|
+
instance.exitFullscreen(): void // Exit fullscreen
|
|
168
|
+
instance.isFullscreen(): boolean // Check fullscreen state
|
|
169
|
+
instance.update(config: Partial<Config>): void // Update config
|
|
170
|
+
instance.destroy(): void // Destroy instance
|
|
171
|
+
instance.getElements(): Elements // Get DOM elements
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Static Methods
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
CIBeforeAfter.autoInit(root?: HTMLElement): CIBeforeAfterInstance[]
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## React Usage
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
import { CIBeforeAfterViewer, useCIBeforeAfter } from 'js-cloudimage-before-after/react';
|
|
184
|
+
|
|
185
|
+
// Component
|
|
186
|
+
function ImageComparison() {
|
|
187
|
+
return (
|
|
188
|
+
<CIBeforeAfterViewer
|
|
189
|
+
beforeSrc="/kitchen-before.jpg"
|
|
190
|
+
afterSrc="/kitchen-after.jpg"
|
|
191
|
+
beforeAlt="Kitchen before renovation"
|
|
192
|
+
afterAlt="Kitchen after renovation"
|
|
193
|
+
zoom
|
|
194
|
+
labels={{ before: 'Before', after: 'After' }}
|
|
195
|
+
handleStyle="arrows"
|
|
196
|
+
animate={{ duration: 800 }}
|
|
197
|
+
onSlide={(pos) => console.log('Position:', pos)}
|
|
198
|
+
/>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Hook
|
|
203
|
+
function ImageComparison() {
|
|
204
|
+
const { containerRef, instance } = useCIBeforeAfter({
|
|
205
|
+
beforeSrc: '/kitchen-before.jpg',
|
|
206
|
+
afterSrc: '/kitchen-after.jpg',
|
|
207
|
+
zoom: true,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<>
|
|
212
|
+
<div ref={containerRef} />
|
|
213
|
+
<button onClick={() => instance.current?.setZoom(2)}>Zoom 2×</button>
|
|
214
|
+
</>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Ref API
|
|
219
|
+
function ImageComparison() {
|
|
220
|
+
const ref = useRef<CIBeforeAfterViewerRef>(null);
|
|
221
|
+
return (
|
|
222
|
+
<>
|
|
223
|
+
<CIBeforeAfterViewer
|
|
224
|
+
ref={ref}
|
|
225
|
+
beforeSrc="/kitchen-before.jpg"
|
|
226
|
+
afterSrc="/kitchen-after.jpg"
|
|
227
|
+
zoom
|
|
228
|
+
/>
|
|
229
|
+
<button onClick={() => ref.current?.setPosition(75)}>Show 75%</button>
|
|
230
|
+
</>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Theming
|
|
236
|
+
|
|
237
|
+
All visuals are customizable via CSS variables:
|
|
238
|
+
|
|
239
|
+
```css
|
|
240
|
+
.my-viewer {
|
|
241
|
+
--ci-before-after-border-radius: 12px;
|
|
242
|
+
--ci-before-after-handle-color: #0058a3;
|
|
243
|
+
--ci-before-after-handle-size: 44px;
|
|
244
|
+
--ci-before-after-handle-border: 2px solid white;
|
|
245
|
+
--ci-before-after-label-bg: rgba(0, 0, 0, 0.6);
|
|
246
|
+
--ci-before-after-label-color: #fff;
|
|
247
|
+
--ci-before-after-label-radius: 4px;
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Set `theme: 'dark'` for the built-in dark theme.
|
|
252
|
+
|
|
253
|
+
## Accessibility
|
|
254
|
+
|
|
255
|
+
- Slider handle is a focusable element with `role="slider"` and `aria-valuenow`
|
|
256
|
+
- `Arrow keys` move the slider position
|
|
257
|
+
- `Shift + Arrow` moves in larger steps
|
|
258
|
+
- `Home` / `End` jump to 0% / 100%
|
|
259
|
+
- `+` / `-` / `0` control zoom
|
|
260
|
+
- `Escape` exits fullscreen
|
|
261
|
+
- `prefers-reduced-motion` disables animations
|
|
262
|
+
- Before/after images have configurable alt text
|
|
263
|
+
|
|
264
|
+
## Cloudimage Integration
|
|
265
|
+
|
|
266
|
+
```js
|
|
267
|
+
new CIBeforeAfter('#el', {
|
|
268
|
+
beforeSrc: 'https://example.com/before.jpg',
|
|
269
|
+
afterSrc: 'https://example.com/after.jpg',
|
|
270
|
+
cloudimage: {
|
|
271
|
+
token: 'demo',
|
|
272
|
+
limitFactor: 100,
|
|
273
|
+
params: 'q=80',
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Browser Support
|
|
279
|
+
|
|
280
|
+
| Browser | Version |
|
|
281
|
+
|---------|---------|
|
|
282
|
+
| Chrome | 80+ |
|
|
283
|
+
| Firefox | 80+ |
|
|
284
|
+
| Safari | 14+ |
|
|
285
|
+
| Edge | 80+ |
|
|
286
|
+
|
|
287
|
+
## License
|
|
288
|
+
|
|
289
|
+
[MIT](./LICENSE)
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Support
|
|
294
|
+
|
|
295
|
+
If this library helped your project, consider buying me a coffee!
|
|
296
|
+
|
|
297
|
+
<a href="https://buymeacoffee.com/dzmitry.stramavus">
|
|
298
|
+
<img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee">
|
|
299
|
+
</a>
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
<p align="center">
|
|
304
|
+
Made with care by the <a href="https://www.scaleflex.com">Scaleflex</a> team
|
|
305
|
+
</p>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Orientation } from '../core/types';
|
|
2
|
+
export declare function updateAriaValue(handle: HTMLElement, position: number): void;
|
|
3
|
+
export declare function updateAriaOrientation(handle: HTMLElement, orientation: Orientation): void;
|
|
4
|
+
export declare function setContainerAria(container: HTMLElement): void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Orientation } from '../core/types';
|
|
2
|
+
export interface KeyboardCallbacks {
|
|
3
|
+
onPositionChange: (position: number) => void;
|
|
4
|
+
getPosition: () => number;
|
|
5
|
+
onZoomIn?: () => void;
|
|
6
|
+
onZoomOut?: () => void;
|
|
7
|
+
onZoomReset?: () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare class KeyboardHandler {
|
|
10
|
+
private handle;
|
|
11
|
+
private orientation;
|
|
12
|
+
private step;
|
|
13
|
+
private largeStep;
|
|
14
|
+
private zoomEnabled;
|
|
15
|
+
private callbacks;
|
|
16
|
+
private events;
|
|
17
|
+
constructor(handle: HTMLElement, orientation: Orientation, step: number, largeStep: number, zoomEnabled: boolean, callbacks: KeyboardCallbacks);
|
|
18
|
+
private bind;
|
|
19
|
+
private handleKeyDown;
|
|
20
|
+
updateConfig(orientation: Orientation, step: number, largeStep: number, zoomEnabled: boolean): void;
|
|
21
|
+
destroy(): void;
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare class EntranceAnimation {
|
|
2
|
+
private container;
|
|
3
|
+
private duration;
|
|
4
|
+
private delay;
|
|
5
|
+
private easing;
|
|
6
|
+
private animateOnce;
|
|
7
|
+
private onAnimate;
|
|
8
|
+
private observer;
|
|
9
|
+
private hasPlayed;
|
|
10
|
+
private isAnimating;
|
|
11
|
+
private delayTimer;
|
|
12
|
+
private durationTimer;
|
|
13
|
+
constructor(container: HTMLElement, duration: number, delay: number, easing: string, animateOnce: boolean, onAnimate: (skipTransition: boolean) => void);
|
|
14
|
+
private observe;
|
|
15
|
+
private play;
|
|
16
|
+
getHasPlayed(): boolean;
|
|
17
|
+
destroy(): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { CIBeforeAfterConfig, CIBeforeAfterInstance } from './types';
|
|
2
|
+
export declare class CIBeforeAfterCore implements CIBeforeAfterInstance {
|
|
3
|
+
private config;
|
|
4
|
+
private userConfig;
|
|
5
|
+
private state;
|
|
6
|
+
private elements;
|
|
7
|
+
private events;
|
|
8
|
+
private imageEvents;
|
|
9
|
+
private sliderGestures;
|
|
10
|
+
private zoomPan;
|
|
11
|
+
private zoomGestures;
|
|
12
|
+
private scrollHint;
|
|
13
|
+
private fullscreenManager;
|
|
14
|
+
private entranceAnimation;
|
|
15
|
+
private keyboardHandler;
|
|
16
|
+
private resizeObserver;
|
|
17
|
+
private zoomControlsEl;
|
|
18
|
+
private zoomControlsEvents;
|
|
19
|
+
private lazyLoadObserver;
|
|
20
|
+
private resizeDebounceTimer;
|
|
21
|
+
private animTransitionTimer;
|
|
22
|
+
private suppressCallbacks;
|
|
23
|
+
constructor(target: HTMLElement | string, userConfig: CIBeforeAfterConfig);
|
|
24
|
+
getElements(): {
|
|
25
|
+
container: HTMLElement;
|
|
26
|
+
viewport: HTMLElement;
|
|
27
|
+
beforeImage: HTMLImageElement;
|
|
28
|
+
afterImage: HTMLImageElement;
|
|
29
|
+
handle: HTMLElement;
|
|
30
|
+
};
|
|
31
|
+
setPosition(percent: number): void;
|
|
32
|
+
getPosition(): number;
|
|
33
|
+
setZoom(level: number): void;
|
|
34
|
+
getZoom(): number;
|
|
35
|
+
resetZoom(): void;
|
|
36
|
+
enterFullscreen(): void;
|
|
37
|
+
exitFullscreen(): void;
|
|
38
|
+
isFullscreen(): boolean;
|
|
39
|
+
update(newConfig: Partial<CIBeforeAfterConfig>): void;
|
|
40
|
+
destroy(): void;
|
|
41
|
+
private buildDOM;
|
|
42
|
+
private initModules;
|
|
43
|
+
private initZoom;
|
|
44
|
+
private applyZoomPositionClasses;
|
|
45
|
+
private initEntranceAnimation;
|
|
46
|
+
private loadImages;
|
|
47
|
+
/**
|
|
48
|
+
* Register load/error handlers for images. Cleans up previous handlers first.
|
|
49
|
+
* When only one image changed, the unchanged image is treated as already loaded.
|
|
50
|
+
*/
|
|
51
|
+
private registerImageLoadHandlers;
|
|
52
|
+
private onImagesReady;
|
|
53
|
+
private getClipZoomInfo;
|
|
54
|
+
private syncClip;
|
|
55
|
+
private updatePosition;
|
|
56
|
+
private onDragStart;
|
|
57
|
+
private onDragEnd;
|
|
58
|
+
private resolveImageSrc;
|
|
59
|
+
private rebuildHandle;
|
|
60
|
+
private rebuildLabels;
|
|
61
|
+
private rebuildZoom;
|
|
62
|
+
private rebuildFullscreen;
|
|
63
|
+
private initResizeObserver;
|
|
64
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
export type InteractionMode = 'drag' | 'hover' | 'click';
|
|
2
|
+
export type Orientation = 'horizontal' | 'vertical';
|
|
3
|
+
export type HandleStyle = 'arrows' | 'circle' | 'line';
|
|
4
|
+
export type Theme = 'light' | 'dark';
|
|
5
|
+
export type LabelPosition = 'top' | 'bottom';
|
|
6
|
+
export type ZoomControlsPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
|
7
|
+
export interface AnimateConfig {
|
|
8
|
+
duration?: number;
|
|
9
|
+
delay?: number;
|
|
10
|
+
easing?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface CloudimageConfig {
|
|
13
|
+
token: string;
|
|
14
|
+
apiVersion?: string;
|
|
15
|
+
domain?: string;
|
|
16
|
+
limitFactor?: number;
|
|
17
|
+
params?: string;
|
|
18
|
+
devicePixelRatioList?: number[];
|
|
19
|
+
}
|
|
20
|
+
export interface CIBeforeAfterConfig {
|
|
21
|
+
beforeSrc: string;
|
|
22
|
+
afterSrc: string;
|
|
23
|
+
beforeAlt?: string;
|
|
24
|
+
afterAlt?: string;
|
|
25
|
+
mode?: InteractionMode;
|
|
26
|
+
orientation?: Orientation;
|
|
27
|
+
initialPosition?: number;
|
|
28
|
+
zoom?: boolean;
|
|
29
|
+
zoomMax?: number;
|
|
30
|
+
zoomMin?: number;
|
|
31
|
+
theme?: Theme;
|
|
32
|
+
handleStyle?: HandleStyle;
|
|
33
|
+
labels?: boolean | {
|
|
34
|
+
before?: string;
|
|
35
|
+
after?: string;
|
|
36
|
+
};
|
|
37
|
+
labelPosition?: LabelPosition;
|
|
38
|
+
animate?: boolean | AnimateConfig;
|
|
39
|
+
animateOnce?: boolean;
|
|
40
|
+
fullscreenButton?: boolean;
|
|
41
|
+
lazyLoad?: boolean;
|
|
42
|
+
zoomControls?: boolean;
|
|
43
|
+
zoomControlsPosition?: ZoomControlsPosition;
|
|
44
|
+
scrollHint?: boolean;
|
|
45
|
+
keyboardStep?: number;
|
|
46
|
+
keyboardLargeStep?: number;
|
|
47
|
+
onSlide?: (position: number) => void;
|
|
48
|
+
onZoom?: (level: number) => void;
|
|
49
|
+
onFullscreenChange?: (isFullscreen: boolean) => void;
|
|
50
|
+
onReady?: () => void;
|
|
51
|
+
cloudimage?: CloudimageConfig;
|
|
52
|
+
}
|
|
53
|
+
export interface ResolvedConfig {
|
|
54
|
+
beforeSrc: string;
|
|
55
|
+
afterSrc: string;
|
|
56
|
+
beforeAlt: string;
|
|
57
|
+
afterAlt: string;
|
|
58
|
+
mode: InteractionMode;
|
|
59
|
+
orientation: Orientation;
|
|
60
|
+
initialPosition: number;
|
|
61
|
+
zoom: boolean;
|
|
62
|
+
zoomMax: number;
|
|
63
|
+
zoomMin: number;
|
|
64
|
+
theme: Theme;
|
|
65
|
+
handleStyle: HandleStyle;
|
|
66
|
+
labelsEnabled: boolean;
|
|
67
|
+
labelBefore: string;
|
|
68
|
+
labelAfter: string;
|
|
69
|
+
labelPosition: LabelPosition;
|
|
70
|
+
animateEnabled: boolean;
|
|
71
|
+
animateDuration: number;
|
|
72
|
+
animateDelay: number;
|
|
73
|
+
animateEasing: string;
|
|
74
|
+
animateOnce: boolean;
|
|
75
|
+
fullscreenButton: boolean;
|
|
76
|
+
lazyLoad: boolean;
|
|
77
|
+
zoomControls: boolean;
|
|
78
|
+
zoomControlsPosition: ZoomControlsPosition;
|
|
79
|
+
scrollHint: boolean;
|
|
80
|
+
keyboardStep: number;
|
|
81
|
+
keyboardLargeStep: number;
|
|
82
|
+
onSlide?: (position: number) => void;
|
|
83
|
+
onZoom?: (level: number) => void;
|
|
84
|
+
onFullscreenChange?: (isFullscreen: boolean) => void;
|
|
85
|
+
onReady?: () => void;
|
|
86
|
+
cloudimage?: CloudimageConfig;
|
|
87
|
+
}
|
|
88
|
+
export interface SliderState {
|
|
89
|
+
position: number;
|
|
90
|
+
isDragging: boolean;
|
|
91
|
+
zoomLevel: number;
|
|
92
|
+
panX: number;
|
|
93
|
+
panY: number;
|
|
94
|
+
isReady: boolean;
|
|
95
|
+
isFullscreen: boolean;
|
|
96
|
+
}
|
|
97
|
+
export interface CIBeforeAfterElements {
|
|
98
|
+
container: HTMLElement;
|
|
99
|
+
viewport: HTMLElement;
|
|
100
|
+
wrapper: HTMLElement;
|
|
101
|
+
beforeImage: HTMLImageElement;
|
|
102
|
+
afterImage: HTMLImageElement;
|
|
103
|
+
clip: HTMLElement;
|
|
104
|
+
handle: HTMLElement;
|
|
105
|
+
handleGrip: HTMLElement;
|
|
106
|
+
labelBefore: HTMLElement | null;
|
|
107
|
+
labelAfter: HTMLElement | null;
|
|
108
|
+
}
|
|
109
|
+
export interface CIBeforeAfterInstance {
|
|
110
|
+
getElements(): {
|
|
111
|
+
container: HTMLElement;
|
|
112
|
+
viewport: HTMLElement;
|
|
113
|
+
beforeImage: HTMLImageElement;
|
|
114
|
+
afterImage: HTMLImageElement;
|
|
115
|
+
handle: HTMLElement;
|
|
116
|
+
};
|
|
117
|
+
setPosition(percent: number): void;
|
|
118
|
+
getPosition(): number;
|
|
119
|
+
setZoom(level: number): void;
|
|
120
|
+
getZoom(): number;
|
|
121
|
+
resetZoom(): void;
|
|
122
|
+
enterFullscreen(): void;
|
|
123
|
+
exitFullscreen(): void;
|
|
124
|
+
isFullscreen(): boolean;
|
|
125
|
+
update(config: Partial<CIBeforeAfterConfig>): void;
|
|
126
|
+
destroy(): void;
|
|
127
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare class FullscreenManager {
|
|
2
|
+
private container;
|
|
3
|
+
private onFullscreenChange?;
|
|
4
|
+
private button;
|
|
5
|
+
private events;
|
|
6
|
+
private isActive;
|
|
7
|
+
constructor(container: HTMLElement, onFullscreenChange?: ((isFullscreen: boolean) => void) | undefined);
|
|
8
|
+
private createButton;
|
|
9
|
+
private bindEvents;
|
|
10
|
+
private handleChange;
|
|
11
|
+
enter(): Promise<void>;
|
|
12
|
+
exit(): Promise<void>;
|
|
13
|
+
toggle(): Promise<void>;
|
|
14
|
+
getIsFullscreen(): boolean;
|
|
15
|
+
destroy(): void;
|
|
16
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CIBeforeAfterCore } from './core/ci-before-after';
|
|
2
|
+
import type { CIBeforeAfterConfig, CIBeforeAfterInstance } from './core/types';
|
|
3
|
+
export type { CIBeforeAfterConfig, CIBeforeAfterInstance, InteractionMode, Orientation, HandleStyle, Theme, LabelPosition, ZoomControlsPosition, AnimateConfig, CloudimageConfig, SliderState, } from './core/types';
|
|
4
|
+
export { CIBeforeAfterCore };
|
|
5
|
+
declare class CIBeforeAfter extends CIBeforeAfterCore {
|
|
6
|
+
static autoInit(root?: HTMLElement): CIBeforeAfterInstance[];
|
|
7
|
+
constructor(target: HTMLElement | string, config: CIBeforeAfterConfig);
|
|
8
|
+
}
|
|
9
|
+
export default CIBeforeAfter;
|
|
10
|
+
export { CIBeforeAfter };
|