react-native-gesture-image-viewer
Version:
🖼️ React Native Image Viewer - Reanimated-powered image gestures with full control
252 lines (251 loc) • 7.02 kB
JavaScript
"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