UNPKG

react-native-gesture-image-viewer

Version:

🖼️ React Native Image Viewer - Reanimated-powered image gestures with full control

252 lines (251 loc) 7.02 kB
"use strict"; import { withTiming } from 'react-native-reanimated'; import { createBoundsConstraint, createScrollAction } from "./utils.js"; class GestureViewerManager { currentIndex = 0; dataLength = 0; width = 0; height = 0; maxZoomScale = 2; enableSwipeGesture = true; enableLoop = false; listRef = null; scale = null; rotation = null; translateX = null; translateY = null; loopCallback = null; listeners = new Set(); eventListeners = new Map(); notifyListeners() { const state = this.getState(); this.listeners.forEach(listener => listener(state)); } subscribe(listener) { this.listeners.add(listener); return () => { this.listeners.delete(listener); }; } addEventListener(eventType, callback) { if (!this.eventListeners.has(eventType)) { this.eventListeners.set(eventType, new Set()); } this.eventListeners.get(eventType)?.add(callback); return () => { const listeners = this.eventListeners.get(eventType); if (listeners) { listeners.delete(callback); if (listeners.size === 0) { this.eventListeners.delete(eventType); } } }; } emitEvent(eventType, data) { const listeners = this.eventListeners.get(eventType); if (listeners) { listeners.forEach(callback => callback(data)); } } emitZoomChange = (scale, previousScale) => { this.emitEvent('zoomChange', { scale, previousScale }); }; emitRotationChange = (rotation, previousRotation) => { this.emitEvent('rotationChange', { rotation, previousRotation }); }; getState() { return { currentIndex: this.currentIndex, totalCount: this.dataLength }; } setEnableLoop(enabled) { this.enableLoop = enabled; } setWidth(width) { this.width = width; } setHeight(height) { this.height = height; } setListRef(ref) { this.listRef = ref; } setDataLength(length) { this.dataLength = length; } setEnableSwipeGesture(enabled) { this.enableSwipeGesture = enabled; } setCurrentIndex(index) { if (index !== this.currentIndex) { this.currentIndex = index; } } setZoomSharedValues(scale, translateX, translateY, maxZoomScale) { this.scale = scale; this.translateX = translateX; this.translateY = translateY; this.maxZoomScale = maxZoomScale; } notifyStateChange() { this.notifyListeners(); } setRotation(rotation) { this.rotation = rotation; } rotate = (angle = 90, clockwise = true) => { const MAX_ANGLE = 360; if (!this.rotation || angle < 0 || angle > MAX_ANGLE || angle !== 0 && this.rotation.value % angle !== 0 && angle !== 360) { return; } if (angle === 0) { const nextAngle = Math.floor(this.rotation.value / MAX_ANGLE) * MAX_ANGLE; this.rotation.value = withTiming(clockwise ? nextAngle : nextAngle - MAX_ANGLE); return; } if (angle === 360) { this.rotation.value = withTiming(clockwise ? this.rotation.value + MAX_ANGLE : this.rotation.value - MAX_ANGLE); return; } const nextAngle = clockwise ? this.rotation.value + angle : this.rotation.value - angle; this.rotation.value = withTiming(nextAngle); }; zoomIn = (multiplier = 0.25) => { if (!this.scale || !this.translateX || !this.translateY || multiplier < 0.01 || multiplier > 1) { return; } const nextScale = Math.min(this.scale.value * (1 + multiplier), this.maxZoomScale); this.scale.value = withTiming(nextScale); const { translateX, translateY } = createBoundsConstraint({ width: this.width, height: this.height })({ translateX: this.translateX.value, translateY: this.translateY.value, scale: nextScale }); this.translateX.value = withTiming(translateX); this.translateY.value = withTiming(translateY); }; zoomOut = (multiplier = 0.25) => { if (!this.scale || !this.translateX || !this.translateY || multiplier < 0.01 || multiplier > 1) { return; } const nextScale = Math.max(this.scale.value / (1 + multiplier), 1); this.scale.value = withTiming(nextScale); if (nextScale === 1) { this.translateX.value = withTiming(0); this.translateY.value = withTiming(0); return; } const { translateX, translateY } = createBoundsConstraint({ width: this.width, height: this.height })({ translateX: this.translateX.value, translateY: this.translateY.value, scale: nextScale }); this.translateX.value = withTiming(translateX); this.translateY.value = withTiming(translateY); }; resetZoom = (scale = 1) => { if (!this.scale || !this.translateX || !this.translateY || scale <= 0 || scale > this.maxZoomScale) { return; } this.scale.value = withTiming(scale); this.translateX.value = withTiming(0); this.translateY.value = withTiming(0); }; goToIndex = index => { if (!this.enableSwipeGesture || !this.listRef) { return; } this.loopCallback = null; const { scrollTo } = createScrollAction(this.listRef, this.width); if (this.enableLoop && this.dataLength > 1) { if (index < 0) { this.loopCallback = () => { scrollTo(this.dataLength, false); this.updateCurrentIndex(this.dataLength - 1); this.loopCallback = null; }; scrollTo(0, true); return; } if (index >= this.dataLength) { this.loopCallback = () => { scrollTo(1, false); this.updateCurrentIndex(0); this.loopCallback = null; }; scrollTo(this.dataLength + 1, true); return; } scrollTo(index + 1, true); this.updateCurrentIndex(index); return; } if (index < 0 || index >= this.dataLength) { return; } scrollTo(index, true); this.updateCurrentIndex(index); }; handleMomentumScrollEnd = scrollIndex => { if (!this.loopCallback) { return false; } if (scrollIndex === 0 || scrollIndex === this.dataLength + 1) { this.loopCallback(); return true; } this.loopCallback = null; return true; }; handleScrollBeginDrag = () => { this.loopCallback = null; }; goToPrevious = () => { this.goToIndex(this.currentIndex - 1); }; goToNext = () => { this.goToIndex(this.currentIndex + 1); }; cleanUp() { this.loopCallback = null; this.listeners.clear(); this.listRef = null; this.enableSwipeGesture = true; this.currentIndex = 0; this.dataLength = 0; this.maxZoomScale = 2; this.scale = null; this.translateX = null; this.translateY = null; this.rotation = null; this.eventListeners.clear(); } updateCurrentIndex = targetIndex => { this.currentIndex = targetIndex; this.notifyListeners(); }; } export default GestureViewerManager; //# sourceMappingURL=GestureViewerManager.js.map