@evoke-ui/zsort3d
Version:
TypeScript z-plane rendering engine with 3D depth simulation using Canvas 2D and mouse-based navigation
310 lines • 11.4 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.TouchManager = void 0;
const ITouchConfig_1 = require("../interfaces/ITouchConfig");
let Hammer = null;
const loadHammer = async () => {
if (typeof window === 'undefined') {
return null;
}
if (!Hammer) {
Hammer = (await Promise.resolve().then(() => __importStar(require('hammerjs')))).default;
}
return Hammer;
};
class TouchManager {
constructor(element, config = {}) {
this.hammer = null;
this.eventHandlers = new Map();
this.isDestroyed = false;
this.lastPanEvent = null;
this.hammerLoaded = false;
this.element = element;
this.config = { ...ITouchConfig_1.DEFAULT_TOUCH_CONFIG, ...this.deepMergeConfig(ITouchConfig_1.DEFAULT_TOUCH_CONFIG, config) };
this.devicePixelRatio = typeof window !== 'undefined' ? (window.devicePixelRatio || 1) : 1;
if (typeof window !== 'undefined') {
this.init();
}
}
deepMergeConfig(defaultConfig, userConfig) {
const merged = { ...defaultConfig };
if (userConfig.pan) {
merged.pan = { ...defaultConfig.pan, ...userConfig.pan };
}
if (userConfig.pinch) {
merged.pinch = { ...defaultConfig.pinch, ...userConfig.pinch };
}
if (userConfig.tap) {
merged.tap = { ...defaultConfig.tap, ...userConfig.tap };
}
if (userConfig.rotate) {
merged.rotate = { ...defaultConfig.rotate, ...userConfig.rotate };
}
if (userConfig.performance) {
merged.performance = { ...defaultConfig.performance, ...userConfig.performance };
}
if (userConfig.device) {
merged.device = { ...defaultConfig.device, ...userConfig.device };
}
return merged;
}
async init() {
if (!this.config.enabled || this.isDestroyed || typeof window === 'undefined') {
return;
}
const HammerClass = await loadHammer();
if (!HammerClass) {
console.warn('TouchManager: Hammer.js could not be loaded');
return;
}
this.hammerLoaded = true;
this.setupCSSTouchAction();
this.createHammerInstance(HammerClass);
this.setupGestures(HammerClass);
this.setupEventListeners();
this.setupOrientationHandler();
}
setupCSSTouchAction() {
if (this.config.performance.useCSSTouchAction) {
this.element.style.touchAction = 'none';
this.element.style.userSelect = 'none';
this.element.style.webkitUserSelect = 'none';
}
}
createHammerInstance(HammerClass) {
this.hammer = new HammerClass.Manager(this.element, {
recognizers: [],
inputClass: HammerClass.TouchInput,
touchAction: 'none'
});
}
setupGestures(HammerClass) {
if (!this.hammer)
return;
if (this.config.pan.enabled) {
const pan = new HammerClass.Pan({
direction: HammerClass.DIRECTION_ALL,
threshold: this.config.pan.threshold,
pointers: this.config.pan.pointers
});
this.hammer.add(pan);
}
if (this.config.pinch.enabled) {
const pinch = new HammerClass.Pinch({
threshold: this.config.pinch.threshold
});
this.hammer.add(pinch);
}
if (this.config.tap.enabled) {
const tap = new HammerClass.Tap({
time: this.config.tap.time,
threshold: this.config.tap.threshold,
taps: this.config.tap.taps,
pointers: this.config.tap.pointers
});
this.hammer.add(tap);
}
if (this.config.rotate.enabled) {
const rotate = new HammerClass.Rotate({
threshold: this.config.rotate.threshold
});
this.hammer.add(rotate);
if (this.config.pinch.enabled) {
const pinchRecognizer = this.hammer.get('pinch');
if (pinchRecognizer) {
rotate.recognizeWith(pinchRecognizer);
}
}
}
if (this.config.pan.enabled && this.config.pinch.enabled) {
const panRecognizer = this.hammer.get('pan');
const pinchRecognizer = this.hammer.get('pinch');
if (panRecognizer && pinchRecognizer) {
panRecognizer.recognizeWith(pinchRecognizer);
}
}
}
setupEventListeners() {
if (!this.hammer)
return;
if (this.config.pan.enabled) {
this.hammer.on('panstart panmove panend', this.handlePanEvent.bind(this));
}
if (this.config.pinch.enabled) {
this.hammer.on('pinchstart pinchmove pinchend', this.handlePinchEvent.bind(this));
}
if (this.config.tap.enabled) {
this.hammer.on('tap', this.handleTapEvent.bind(this));
}
if (this.config.rotate.enabled) {
this.hammer.on('rotatestart rotatemove rotateend', this.handleRotateEvent.bind(this));
}
}
setupOrientationHandler() {
if (this.config.device.handleOrientationChange && typeof window !== 'undefined') {
window.addEventListener('orientationchange', this.handleOrientationChange.bind(this));
window.addEventListener('resize', this.handleOrientationChange.bind(this));
}
}
handlePanEvent(e) {
if (this.config.performance.preventDefault) {
e.preventDefault();
}
const now = Date.now();
const baseSensitivity = this.config.pan.sensitivity ?? 1.0;
const sensitivity = this.config.device.adaptToPixelRatio
? baseSensitivity * this.devicePixelRatio
: baseSensitivity;
const throttleTime = this.config.performance.throttle ?? 16;
if (this.lastPanEvent && (now - this.lastPanEvent.time) < throttleTime) {
return;
}
const eventData = {
type: 'pan',
deltaX: e.deltaX * sensitivity,
deltaY: e.deltaY * sensitivity,
center: { x: e.center.x, y: e.center.y },
velocity: e.velocity,
direction: e.direction,
originalEvent: e
};
this.lastPanEvent = { x: e.center.x, y: e.center.y, time: now };
this.emit('pan', eventData);
}
handlePinchEvent(e) {
if (this.config.performance.preventDefault) {
e.preventDefault();
}
const baseSensitivity = this.config.pinch.sensitivity ?? 1.0;
const sensitivity = this.config.device.adaptToPixelRatio
? baseSensitivity * this.devicePixelRatio
: baseSensitivity;
const eventData = {
type: 'pinch',
scale: e.scale * sensitivity,
center: { x: e.center.x, y: e.center.y },
originalEvent: e
};
this.emit('pinch', eventData);
}
handleTapEvent(e) {
if (this.config.performance.preventDefault) {
e.preventDefault();
}
const eventData = {
type: 'tap',
center: { x: e.center.x, y: e.center.y },
originalEvent: e
};
this.emit('tap', eventData);
}
handleRotateEvent(e) {
if (this.config.performance.preventDefault) {
e.preventDefault();
}
const sensitivity = this.config.rotate.sensitivity ?? 1.0;
const eventData = {
type: 'rotate',
rotation: e.rotation * sensitivity,
center: { x: e.center.x, y: e.center.y },
originalEvent: e
};
this.emit('rotate', eventData);
}
handleOrientationChange() {
if (typeof window !== 'undefined') {
setTimeout(() => {
this.devicePixelRatio = window.devicePixelRatio || 1;
}, 100);
}
}
on(eventType, handler) {
if (!this.eventHandlers.has(eventType)) {
this.eventHandlers.set(eventType, []);
}
this.eventHandlers.get(eventType).push(handler);
}
off(eventType, handler) {
const handlers = this.eventHandlers.get(eventType);
if (handlers) {
const index = handlers.indexOf(handler);
if (index > -1) {
handlers.splice(index, 1);
}
}
}
emit(eventType, data) {
const handlers = this.eventHandlers.get(eventType);
if (handlers) {
handlers.forEach(handler => handler(data));
}
}
async updateConfig(newConfig) {
this.config = { ...this.config, ...this.deepMergeConfig(this.config, newConfig) };
if (newConfig.enabled !== undefined) {
this.destroy();
await this.init();
}
}
getConfig() {
return { ...this.config };
}
async setEnabled(enabled) {
await this.updateConfig({ enabled });
}
isEnabled() {
return this.config.enabled && !this.isDestroyed && this.hammerLoaded;
}
destroy() {
if (this.isDestroyed)
return;
this.isDestroyed = true;
if (this.hammer) {
this.hammer.destroy();
this.hammer = null;
}
this.eventHandlers.clear();
if (this.config.device.handleOrientationChange && typeof window !== 'undefined') {
window.removeEventListener('orientationchange', this.handleOrientationChange.bind(this));
window.removeEventListener('resize', this.handleOrientationChange.bind(this));
}
this.element.style.touchAction = '';
this.element.style.userSelect = '';
this.element.style.webkitUserSelect = '';
}
}
exports.TouchManager = TouchManager;
//# sourceMappingURL=TouchManager.js.map