// Draggable Logo with Click-to-Navigate Functionality class DraggableLogo { constructor() { this.logo = document.querySelector('.logo'); this.isDragging = false; this.dragOffset = { x: 0, y: 0 }; this.position = { x: 0, y: 0 }; this.clickStartTime = 0; this.startPosition = { x: 0, y: 0 }; this.clickThreshold = 200; // ms this.website = 'https://computertech.dev'; if (this.logo) { this.init(); } } init() { this.loadPosition(); this.setupEventListeners(); } setupEventListeners() { // Mouse events this.logo.addEventListener('mousedown', this.handleStart.bind(this)); document.addEventListener('mousemove', this.handleMove.bind(this)); document.addEventListener('mouseup', this.handleEnd.bind(this)); // Touch events for mobile this.logo.addEventListener('touchstart', this.handleStart.bind(this), { passive: false }); document.addEventListener('touchmove', this.handleMove.bind(this), { passive: false }); this.logo.addEventListener('touchend', this.handleEnd.bind(this)); // Prevent context menu this.logo.addEventListener('contextmenu', (e) => e.preventDefault()); // Double-click to snap to corner this.logo.addEventListener('dblclick', (e) => { e.preventDefault(); this.snapToNearestCorner(); }); // Click handler (fallback) this.logo.addEventListener('click', (e) => { if (!this.isDragging) { e.preventDefault(); window.open(this.website, '_blank'); } }); } handleStart(e) { e.preventDefault(); this.clickStartTime = Date.now(); this.isDragging = false; this.startPosition = { x: 0, y: 0 }; const rect = this.logo.getBoundingClientRect(); const clientX = e.clientX || (e.touches && e.touches[0].clientX); const clientY = e.clientY || (e.touches && e.touches[0].clientY); this.dragOffset.x = clientX - rect.left; this.dragOffset.y = clientY - rect.top; this.startPosition.x = clientX; this.startPosition.y = clientY; this.logo.classList.add('dragging'); this.logo.classList.remove('clickable'); } handleMove(e) { if (!this.logo.classList.contains('dragging')) return; const clientX = e.clientX || (e.touches && e.touches[0].clientX); const clientY = e.clientY || (e.touches && e.touches[0].clientY); const deltaX = Math.abs(clientX - this.startPosition.x); const deltaY = Math.abs(clientY - this.startPosition.y); const dragThreshold = 5; if (deltaX > dragThreshold || deltaY > dragThreshold) { this.isDragging = true; e.preventDefault(); this.position.x = clientX - this.dragOffset.x; this.position.y = clientY - this.dragOffset.y; // Keep within viewport bounds const logoRect = this.logo.getBoundingClientRect(); const margin = 10; this.position.x = Math.max(margin, Math.min(window.innerWidth - logoRect.width - margin, this.position.x)); this.position.y = Math.max(margin, Math.min(window.innerHeight - logoRect.height - margin, this.position.y)); this.logo.style.left = this.position.x + 'px'; this.logo.style.top = this.position.y + 'px'; } } handleEnd(e) { this.logo.classList.remove('dragging'); const clickDuration = Date.now() - this.clickStartTime; if (!this.isDragging && clickDuration < this.clickThreshold) { // It was a click, navigate to website this.logo.classList.add('clickable'); setTimeout(() => { window.open(this.website, '_blank'); }, 100); } else if (this.isDragging) { // It was a drag, save position this.savePosition(); this.addBounceEffect(); } // Reset dragging state with small delay setTimeout(() => { this.isDragging = false; this.logo.classList.remove('clickable'); }, 50); } savePosition() { localStorage.setItem('logoPosition', JSON.stringify(this.position)); // Update CSS variables for immediate positioning on reload document.documentElement.style.setProperty('--logo-x', this.position.x + 'px'); document.documentElement.style.setProperty('--logo-y', this.position.y + 'px'); } loadPosition() { const savedPosition = localStorage.getItem('logoPosition'); if (savedPosition) { this.position = JSON.parse(savedPosition); this.logo.style.left = this.position.x + 'px'; this.logo.style.top = this.position.y + 'px'; } } snapToNearestCorner() { const logoRect = this.logo.getBoundingClientRect(); const margin = 20; const corners = [ { x: margin, y: margin }, { x: window.innerWidth - logoRect.width - margin, y: margin }, { x: margin, y: window.innerHeight - logoRect.height - margin }, { x: window.innerWidth - logoRect.width - margin, y: window.innerHeight - logoRect.height - margin } ]; let nearestCorner = corners[0]; let minDistance = Infinity; corners.forEach(corner => { const distance = Math.sqrt( Math.pow(corner.x - this.position.x, 2) + Math.pow(corner.y - this.position.y, 2) ); if (distance < minDistance) { minDistance = distance; nearestCorner = corner; } }); this.position = nearestCorner; this.logo.style.transition = 'all 0.5s cubic-bezier(0.4, 0, 0.2, 1)'; this.logo.style.left = nearestCorner.x + 'px'; this.logo.style.top = nearestCorner.y + 'px'; setTimeout(() => { this.logo.style.transition = ''; this.savePosition(); }, 500); } addBounceEffect() { this.logo.style.animation = 'bounce 0.6s ease-out'; setTimeout(() => { this.logo.style.animation = ''; }, 600); } } // Initialize draggable logo when DOM is loaded document.addEventListener('DOMContentLoaded', () => { window.draggableLogo = new DraggableLogo(); });