feat(skill-tree): add touch support and improve modal handling

This commit is contained in:
TehRiehlDeal committed 2026-01-10 11:04:35 -08:00
1 parent f091bf6d55
commit 7f0f86d14e
1 file changed
+57 -8
+57 -8
View File
@@ -18,14 +18,23 @@
let windowWidth = 0;
let windowHeight = 0;
let modalOpenedAt = 0;
function openModal(node: TreeNode) {
selectedNode = node;
modalVisible = true;
modalOpenedAt = Date.now();
// Hide tooltip when modal opens
hoveredNode = null;
}
function closeModal() {
// Prevent closing if modal just opened (within 300ms)
// This fixes touch events "bleeding through" on mobile
if (Date.now() - modalOpenedAt < 300) {
return;
}
modalVisible = false;
setTimeout(() => {
selectedNode = null;
@@ -37,7 +46,7 @@
origin: '#ffd700',
experience: '#4ecdc4',
projects: '#ff6b6b',
skills: '#95e1d3',
skills: '#f9a825',
education: '#dda0dd',
};
return colorMap[category];
@@ -54,6 +63,8 @@
handleResize();
window.addEventListener('resize', handleResize);
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
const app = new Application();
await app.init({
@@ -245,16 +256,45 @@
}
}, { passive: true });
// ========== TOUCH EVENTS FOR PANNING & ZOOMING ==========
let touchStartedOnNode = false;
app.canvas.addEventListener('touchstart', (e: TouchEvent) => {
// Check if touch started on a node by seeing if the target is interactive
touchStartedOnNode = false;
if (e.touches.length === 1) {
// Single touch - prepare for potential panning
isDragging = true;
hasMoved = false;
isTouchZooming = false;
dragStartX = e.touches[0].clientX;
dragStartY = e.touches[0].clientY;
containerStartX = treeContainer.x;
containerStartY = treeContainer.y;
} else if (e.touches.length === 2) {
// Two touches - start pinch zoom
isDragging = false;
isTouchZooming = true;
hasMoved = true; // Pinch zoom counts as movement
const dx = e.touches[0].clientX - e.touches[1].clientX;
const dy = e.touches[0].clientY - e.touches[1].clientY;
lastTouchDistance = Math.sqrt(dx * dx + dy * dy);
}
}, { passive: true });
app.canvas.addEventListener('touchmove', (e: TouchEvent) => {
if (e.touches.length === 1 && isDragging && !isTouchZooming) {
// Single touch - panning
const dx = e.touches[0].clientX - dragStartX;
const dy = e.touches[0].clientY - dragStartY;
if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
// Only count as moved if we've moved more than a small threshold
if (Math.abs(dx) > 10 || Math.abs(dy) > 10) {
hasMoved = true;
}
treeContainer.x = containerStartX + dx;
treeContainer.y = containerStartY + dy;
}
} else if (e.touches.length === 2 && isTouchZooming) {
// Two touches - pinch zoom
const dx = e.touches[0].clientX - e.touches[1].clientX;
@@ -296,6 +336,7 @@
// Went from 2 touches to 1 - reset pan start
isTouchZooming = false;
isDragging = true;
hasMoved = true; // Consider this movement since we were zooming
dragStartX = e.touches[0].clientX;
dragStartY = e.touches[0].clientY;
containerStartX = treeContainer.x;
@@ -332,7 +373,7 @@
case 'large':
return 30 * mobileFactor;
case 'medium':
return 20 * mobileFactor;
return 25 * mobileFactor;
case 'small':
return 20 * mobileFactor;
default:
@@ -426,6 +467,10 @@
profileContainer.eventMode = 'static';
profileContainer.cursor = 'pointer';
profileContainer.on('pointerdown', () => {
hasMoved = false;
});
profileContainer.on('pointerup', () => {
if (!hasMoved) {
openModal(node);
@@ -435,13 +480,13 @@
profileContainer.on('pointerover', () => {
gsap.to(profileContainer.scale, { x: 1.2, y: 1.2, duration: 0.2 });
gsap.to(glowGraphics, { alpha: 1.5, duration: 0.2 });
if (!isMobile) hoveredNode = node;
if (!isTouchDevice) hoveredNode = node;
});
profileContainer.on('pointerout', () => {
gsap.to(profileContainer.scale, { x: 1, y: 1, duration: 0.2 });
gsap.to(glowGraphics, { alpha: 1, duration: 0.2 });
hoveredNode = null;
if (!isTouchDevice) hoveredNode = null;
});
treeContainer.addChild(profileContainer);
@@ -488,6 +533,10 @@
nodeContainer.eventMode = 'static';
nodeContainer.cursor = 'pointer';
nodeContainer.on('pointerdown', () => {
hasMoved = false;
});
nodeContainer.on('pointerup', () => {
if (!hasMoved) {
openModal(node);
@@ -497,13 +546,13 @@
nodeContainer.on('pointerover', () => {
gsap.to(nodeContainer.scale, { x: 1.2, y: 1.2, duration: 0.2 });
gsap.to(glowGraphics, { alpha: 1.5, duration: 0.2 });
if (!isMobile) hoveredNode = node;
if (!isTouchDevice) hoveredNode = node;
});
nodeContainer.on('pointerout', () => {
gsap.to(nodeContainer.scale, { x: 1, y: 1, duration: 0.2 });
gsap.to(glowGraphics, { alpha: 1, duration: 0.2 });
hoveredNode = null;
if (!isTouchDevice) hoveredNode = null;
});
treeContainer.addChild(nodeContainer);