mirror of https://github.com/mkrhere/pw2
1 changed files with 218 additions and 0 deletions
@ -0,0 +1,218 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8" /> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
|
<title>Document</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div id="container"> |
||||
|
<div id="rectangle"></div> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
#container { |
||||
|
position: relative; |
||||
|
width: 100%; |
||||
|
height: 600px; |
||||
|
overflow: hidden; |
||||
|
background-color: #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
#rectangle { |
||||
|
position: absolute; |
||||
|
width: 200px; |
||||
|
height: 120px; |
||||
|
background-color: #3498db; |
||||
|
border-radius: 8px; |
||||
|
cursor: grab; |
||||
|
user-select: none; |
||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); |
||||
|
transform-origin: center; |
||||
|
transition: box-shadow 0.2s ease; |
||||
|
} |
||||
|
|
||||
|
#rectangle:active { |
||||
|
cursor: grabbing; |
||||
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3); |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<script> |
||||
|
document.addEventListener("DOMContentLoaded", function () { |
||||
|
const rectangle = document.getElementById("rectangle"); |
||||
|
const container = document.getElementById("container"); |
||||
|
|
||||
|
// Position and movement state |
||||
|
let isDragging = false; |
||||
|
let currentX = 50; |
||||
|
let currentY = 50; |
||||
|
let rotation = 0; |
||||
|
|
||||
|
// Drag state tracking |
||||
|
let dragPointX = 0; |
||||
|
let dragPointY = 0; |
||||
|
let prevMouseX = 0; |
||||
|
let prevMouseY = 0; |
||||
|
|
||||
|
// Physics constants |
||||
|
const friction = 0.95; // General friction (higher = less friction) |
||||
|
const rotationFactor = 0.1; // How much rotation is applied (lower = less rotation) |
||||
|
|
||||
|
// Momentum tracking |
||||
|
let momentumX = 0; |
||||
|
let momentumY = 0; |
||||
|
let angularMomentum = 0; |
||||
|
let momentumAnimationId = null; |
||||
|
|
||||
|
// Initialize position |
||||
|
setTransform(currentX, currentY, rotation); |
||||
|
|
||||
|
function setTransform(x, y, rot) { |
||||
|
currentX = x; |
||||
|
currentY = y; |
||||
|
rotation = rot; |
||||
|
rectangle.style.transform = `translate(${x}px, ${y}px) rotate(${rot}deg)`; |
||||
|
} |
||||
|
|
||||
|
function getRectCenter() { |
||||
|
const rect = rectangle.getBoundingClientRect(); |
||||
|
return { |
||||
|
x: rect.left + rect.width / 2, |
||||
|
y: rect.top + rect.height / 2, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
// Start dragging |
||||
|
rectangle.addEventListener("mousedown", dragStart); |
||||
|
rectangle.addEventListener("touchstart", dragStart, { passive: false }); |
||||
|
|
||||
|
function dragStart(e) { |
||||
|
e.preventDefault(); |
||||
|
if (momentumAnimationId !== null) { |
||||
|
cancelAnimationFrame(momentumAnimationId); |
||||
|
momentumAnimationId = null; |
||||
|
} |
||||
|
|
||||
|
// Get event coordinates |
||||
|
const eventX = |
||||
|
e.type === "touchstart" ? e.touches[0].clientX : e.clientX; |
||||
|
const eventY = |
||||
|
e.type === "touchstart" ? e.touches[0].clientY : e.clientY; |
||||
|
|
||||
|
// Record where on the card we're dragging from (relative to its center) |
||||
|
const center = getRectCenter(); |
||||
|
dragPointX = eventX - center.x; |
||||
|
dragPointY = eventY - center.y; |
||||
|
|
||||
|
// Initialize previous mouse position for momentum calculations |
||||
|
prevMouseX = eventX; |
||||
|
prevMouseY = eventY; |
||||
|
|
||||
|
isDragging = true; |
||||
|
momentumX = 0; |
||||
|
momentumY = 0; |
||||
|
angularMomentum = 0; |
||||
|
} |
||||
|
|
||||
|
// Drag movement |
||||
|
document.addEventListener("mousemove", drag); |
||||
|
document.addEventListener("touchmove", drag, { passive: false }); |
||||
|
|
||||
|
function drag(e) { |
||||
|
if (!isDragging) return; |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
// Get event coordinates |
||||
|
const eventX = |
||||
|
e.type === "touchmove" ? e.touches[0].clientX : e.clientX; |
||||
|
const eventY = |
||||
|
e.type === "touchmove" ? e.touches[0].clientY : e.clientY; |
||||
|
|
||||
|
// Calculate movement delta |
||||
|
const dx = eventX - prevMouseX; |
||||
|
const dy = eventY - prevMouseY; |
||||
|
|
||||
|
// Update position based on direct movement |
||||
|
const newX = currentX + dx; |
||||
|
const newY = currentY + dy; |
||||
|
|
||||
|
// Calculate rotation based on the movement direction and drag point |
||||
|
// The further from center, the more leverage for rotation |
||||
|
const dragDistance = Math.sqrt( |
||||
|
dragPointX * dragPointX + dragPointY * dragPointY, |
||||
|
); |
||||
|
|
||||
|
// Calculate the tangential component of movement |
||||
|
// We use the cross product to determine how much the movement is perpendicular to the radius |
||||
|
const tangentialForce = |
||||
|
(dx * dragPointY - dy * dragPointX) / (dragDistance || 1); |
||||
|
|
||||
|
// Apply rotation with distance-based scaling |
||||
|
// This mimics the lever effect - further from center = more rotation |
||||
|
const rotationDelta = |
||||
|
tangentialForce * rotationFactor * (dragDistance / 100 + 0.5); |
||||
|
const newRotation = rotation + rotationDelta; |
||||
|
|
||||
|
// Update momentum for after drag ends |
||||
|
momentumX = dx * 0.9; // Reduced to prevent excessive sliding |
||||
|
momentumY = dy * 0.9; |
||||
|
angularMomentum = rotationDelta * 0.9; |
||||
|
|
||||
|
// Apply the transforms |
||||
|
setTransform(newX, newY, newRotation); |
||||
|
|
||||
|
// Update previous mouse position |
||||
|
prevMouseX = eventX; |
||||
|
prevMouseY = eventY; |
||||
|
} |
||||
|
|
||||
|
// End dragging |
||||
|
document.addEventListener("mouseup", dragEnd); |
||||
|
document.addEventListener("touchend", dragEnd); |
||||
|
|
||||
|
function dragEnd() { |
||||
|
if (isDragging) { |
||||
|
isDragging = false; |
||||
|
applyMomentum(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Apply momentum after release with friction |
||||
|
function applyMomentum() { |
||||
|
if ( |
||||
|
Math.abs(momentumX) < 0.1 && |
||||
|
Math.abs(momentumY) < 0.1 && |
||||
|
Math.abs(angularMomentum) < 0.01 |
||||
|
) { |
||||
|
momentumAnimationId = null; |
||||
|
return; // Stop if momentum is very small |
||||
|
} |
||||
|
|
||||
|
// Apply friction |
||||
|
momentumX *= friction; |
||||
|
momentumY *= friction; |
||||
|
angularMomentum *= friction; |
||||
|
|
||||
|
// Apply momentum |
||||
|
setTransform( |
||||
|
currentX + momentumX, |
||||
|
currentY + momentumY, |
||||
|
rotation + angularMomentum, |
||||
|
); |
||||
|
|
||||
|
// Continue applying momentum |
||||
|
momentumAnimationId = requestAnimationFrame(applyMomentum); |
||||
|
} |
||||
|
|
||||
|
// Handle edge cases |
||||
|
document.addEventListener("mouseleave", function () { |
||||
|
if (isDragging) { |
||||
|
isDragging = false; |
||||
|
applyMomentum(); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
</script> |
||||
|
</body> |
||||
|
</html> |
Loading…
Reference in new issue