hm
This commit is contained in:
188
static/js/logo.js
Normal file
188
static/js/logo.js
Normal file
@@ -0,0 +1,188 @@
|
||||
// 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();
|
||||
});
|
||||
Reference in New Issue
Block a user