Спустя некоторе время объекты становятся нефизичными, также они слишком сильно реагируют на мышку
Browse files- index.html +51 -92
index.html
CHANGED
|
@@ -55,31 +55,15 @@
|
|
| 55 |
border-radius: 50%;
|
| 56 |
filter: blur(40px);
|
| 57 |
opacity: 0.3;
|
| 58 |
-
animation: float 20s infinite ease-in-out;
|
| 59 |
will-change: transform;
|
|
|
|
| 60 |
}
|
| 61 |
-
|
| 62 |
-
@keyframes float {
|
| 63 |
-
0%, 100% {
|
| 64 |
-
transform: translate(0, 0) scale(1);
|
| 65 |
-
}
|
| 66 |
-
25% {
|
| 67 |
-
transform: translate(100px, -100px) scale(1.1);
|
| 68 |
-
}
|
| 69 |
-
50% {
|
| 70 |
-
transform: translate(-100px, 100px) scale(0.9);
|
| 71 |
-
}
|
| 72 |
-
75% {
|
| 73 |
-
transform: translate(50px, 50px) scale(1.05);
|
| 74 |
-
}
|
| 75 |
-
}
|
| 76 |
-
.blob1 {
|
| 77 |
width: 300px;
|
| 78 |
height: 300px;
|
| 79 |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 80 |
top: 10%;
|
| 81 |
left: 10%;
|
| 82 |
-
animation-delay: 0s;
|
| 83 |
}
|
| 84 |
|
| 85 |
.blob2 {
|
|
@@ -88,7 +72,6 @@
|
|
| 88 |
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
| 89 |
top: 60%;
|
| 90 |
right: 10%;
|
| 91 |
-
animation-delay: 3s;
|
| 92 |
}
|
| 93 |
|
| 94 |
.blob3 {
|
|
@@ -97,7 +80,6 @@
|
|
| 97 |
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
| 98 |
bottom: 10%;
|
| 99 |
left: 30%;
|
| 100 |
-
animation-delay: 5s;
|
| 101 |
}
|
| 102 |
|
| 103 |
.blob4 {
|
|
@@ -106,7 +88,6 @@
|
|
| 106 |
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
| 107 |
top: 30%;
|
| 108 |
right: 30%;
|
| 109 |
-
animation-delay: 7s;
|
| 110 |
}
|
| 111 |
|
| 112 |
.blob5 {
|
|
@@ -115,7 +96,6 @@
|
|
| 115 |
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
|
| 116 |
bottom: 20%;
|
| 117 |
right: 20%;
|
| 118 |
-
animation-delay: 10s;
|
| 119 |
}
|
| 120 |
.header-gradient {
|
| 121 |
background: linear-gradient(135deg, #00d4ff 0%, #ff00ff 50%, #00ff88 100%);
|
|
@@ -683,52 +663,42 @@
|
|
| 683 |
</div>
|
| 684 |
</footer>
|
| 685 |
<script>
|
| 686 |
-
// Physics-based mouse interaction with floating blobs -
|
| 687 |
const demoSection = document.getElementById('demo');
|
| 688 |
const blobs = demoSection.querySelectorAll('.floating-blob');
|
| 689 |
-
// Store blob physics data with
|
| 690 |
const blobPhysics = [];
|
| 691 |
blobs.forEach((blob, index) => {
|
| 692 |
const rect = blob.getBoundingClientRect();
|
| 693 |
const demoRect = demoSection.getBoundingClientRect();
|
| 694 |
-
const angle = (Math.PI * 2 * index) / blobs.length
|
| 695 |
-
const speed =
|
| 696 |
blobPhysics.push({
|
| 697 |
element: blob,
|
| 698 |
x: rect.left - demoRect.left + rect.width / 2,
|
| 699 |
y: rect.top - demoRect.top + rect.height / 2,
|
| 700 |
-
vx: Math.cos(angle) * speed,
|
| 701 |
vy: Math.sin(angle) * speed,
|
| 702 |
baseX: rect.left - demoRect.left + rect.width / 2,
|
| 703 |
baseY: rect.top - demoRect.top + rect.height / 2,
|
| 704 |
-
mass: 1 + index * 0.
|
| 705 |
-
damping: 0.
|
| 706 |
-
springForce: 0, //
|
| 707 |
-
maxVelocity:
|
| 708 |
-
repulsionRadius:
|
| 709 |
-
inertia: 0.
|
| 710 |
-
continuousMotion:
|
| 711 |
});
|
| 712 |
});
|
| 713 |
-
|
|
|
|
| 714 |
let mouseY = 0;
|
| 715 |
let isMouseInSection = false;
|
| 716 |
-
let lastMouseX = 0;
|
| 717 |
-
let lastMouseY = 0;
|
| 718 |
-
let mouseVx = 0;
|
| 719 |
-
let mouseVy = 0;
|
| 720 |
|
| 721 |
demoSection.addEventListener('mousemove', (e) => {
|
| 722 |
const rect = demoSection.getBoundingClientRect();
|
| 723 |
-
lastMouseX = mouseX;
|
| 724 |
-
lastMouseY = mouseY;
|
| 725 |
mouseX = e.clientX - rect.left;
|
| 726 |
mouseY = e.clientY - rect.top;
|
| 727 |
-
|
| 728 |
-
// Calculate mouse velocity for dynamic interaction
|
| 729 |
-
mouseVx = mouseX - lastMouseX;
|
| 730 |
-
mouseVy = mouseY - lastMouseY;
|
| 731 |
-
|
| 732 |
isMouseInSection = true;
|
| 733 |
});
|
| 734 |
|
|
@@ -736,70 +706,61 @@ let mouseX = 0;
|
|
| 736 |
isMouseInSection = false;
|
| 737 |
});
|
| 738 |
|
| 739 |
-
// Animation loop for
|
| 740 |
function animateBlobs() {
|
| 741 |
blobPhysics.forEach((blob, index) => {
|
| 742 |
// Reset forces
|
| 743 |
let fx = 0;
|
| 744 |
let fy = 0;
|
| 745 |
-
|
|
|
|
| 746 |
if (isMouseInSection) {
|
| 747 |
const dx = blob.x - mouseX;
|
| 748 |
const dy = blob.y - mouseY;
|
| 749 |
const distance = Math.sqrt(dx * dx + dy * dy);
|
| 750 |
|
| 751 |
if (distance < blob.repulsionRadius && distance > 0) {
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
|
| 755 |
-
const baseForce = 8;
|
| 756 |
-
const force = ((blob.repulsionRadius - distance) / blob.repulsionRadius) * baseForce * forceMultiplier;
|
| 757 |
-
const repulsionX = (dx / distance) * force;
|
| 758 |
-
const repulsionY = (dy / distance) * force;
|
| 759 |
-
|
| 760 |
-
fx += repulsionX;
|
| 761 |
-
fy += repulsionY;
|
| 762 |
}
|
| 763 |
}
|
| 764 |
-
|
|
|
|
| 765 |
blobPhysics.forEach((otherBlob, otherIndex) => {
|
| 766 |
if (index !== otherIndex) {
|
| 767 |
const dx = blob.x - otherBlob.x;
|
| 768 |
const dy = blob.y - otherBlob.y;
|
| 769 |
const distance = Math.sqrt(dx * dx + dy * dy);
|
| 770 |
-
const minDistance =
|
| 771 |
|
| 772 |
if (distance < minDistance && distance > 0) {
|
| 773 |
-
const force = (minDistance - distance) / minDistance *
|
| 774 |
fx += (dx / distance) * force;
|
| 775 |
fy += (dy / distance) * force;
|
| 776 |
}
|
| 777 |
}
|
| 778 |
});
|
| 779 |
-
|
| 780 |
-
|
| 781 |
-
|
| 782 |
-
|
| 783 |
-
|
| 784 |
-
|
| 785 |
-
|
| 786 |
-
// Apply forces to velocity
|
| 787 |
blob.vx += fx / blob.mass;
|
| 788 |
blob.vy += fy / blob.mass;
|
| 789 |
-
// Apply inertia
|
| 790 |
-
blob.vx *= blob.inertia;
|
| 791 |
-
blob.vy *= blob.inertia;
|
| 792 |
|
| 793 |
-
// Apply
|
| 794 |
blob.vx *= blob.damping;
|
| 795 |
blob.vy *= blob.damping;
|
| 796 |
|
| 797 |
-
// Add small random force for
|
| 798 |
-
|
| 799 |
-
|
| 800 |
-
|
| 801 |
-
|
| 802 |
-
// Limit maximum velocity
|
| 803 |
const speed = Math.sqrt(blob.vx * blob.vx + blob.vy * blob.vy);
|
| 804 |
if (speed > blob.maxVelocity) {
|
| 805 |
blob.vx = (blob.vx / speed) * blob.maxVelocity;
|
|
@@ -809,31 +770,29 @@ let mouseX = 0;
|
|
| 809 |
// Update position
|
| 810 |
blob.x += blob.vx;
|
| 811 |
blob.y += blob.vy;
|
| 812 |
-
|
| 813 |
-
|
| 814 |
-
const
|
|
|
|
| 815 |
const demoRect = demoSection.getBoundingClientRect();
|
| 816 |
|
| 817 |
if (blob.x - blobWidth/2 < 0 || blob.x + blobWidth/2 > demoRect.width) {
|
| 818 |
-
blob.vx *= -0.
|
| 819 |
blob.x = Math.max(blobWidth/2, Math.min(demoRect.width - blobWidth/2, blob.x));
|
| 820 |
-
// Add small random velocity change on bounce
|
| 821 |
-
blob.vy += (Math.random() - 0.5) * 0.5;
|
| 822 |
}
|
| 823 |
|
| 824 |
if (blob.y - blobHeight/2 < 0 || blob.y + blobHeight/2 > demoRect.height) {
|
| 825 |
-
blob.vy *= -0.
|
| 826 |
blob.y = Math.max(blobHeight/2, Math.min(demoRect.height - blobHeight/2, blob.y));
|
| 827 |
-
// Add small random velocity change on bounce
|
| 828 |
-
blob.vx += (Math.random() - 0.5) * 0.5;
|
| 829 |
}
|
| 830 |
-
|
| 831 |
-
|
|
|
|
| 832 |
const scale = 1 + velocityScale;
|
| 833 |
-
const rotation = speed * 0.
|
| 834 |
|
| 835 |
blob.element.style.transform = `translate(${blob.x - blob.baseX}px, ${blob.y - blob.baseY}px) scale(${scale}) rotate(${rotation}deg)`;
|
| 836 |
-
});
|
| 837 |
|
| 838 |
requestAnimationFrame(animateBlobs);
|
| 839 |
}
|
|
|
|
| 55 |
border-radius: 50%;
|
| 56 |
filter: blur(40px);
|
| 57 |
opacity: 0.3;
|
|
|
|
| 58 |
will-change: transform;
|
| 59 |
+
transition: transform 0.1s ease-out;
|
| 60 |
}
|
| 61 |
+
.blob1 {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
width: 300px;
|
| 63 |
height: 300px;
|
| 64 |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 65 |
top: 10%;
|
| 66 |
left: 10%;
|
|
|
|
| 67 |
}
|
| 68 |
|
| 69 |
.blob2 {
|
|
|
|
| 72 |
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
| 73 |
top: 60%;
|
| 74 |
right: 10%;
|
|
|
|
| 75 |
}
|
| 76 |
|
| 77 |
.blob3 {
|
|
|
|
| 80 |
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
| 81 |
bottom: 10%;
|
| 82 |
left: 30%;
|
|
|
|
| 83 |
}
|
| 84 |
|
| 85 |
.blob4 {
|
|
|
|
| 88 |
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
| 89 |
top: 30%;
|
| 90 |
right: 30%;
|
|
|
|
| 91 |
}
|
| 92 |
|
| 93 |
.blob5 {
|
|
|
|
| 96 |
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
|
| 97 |
bottom: 20%;
|
| 98 |
right: 20%;
|
|
|
|
| 99 |
}
|
| 100 |
.header-gradient {
|
| 101 |
background: linear-gradient(135deg, #00d4ff 0%, #ff00ff 50%, #00ff88 100%);
|
|
|
|
| 663 |
</div>
|
| 664 |
</footer>
|
| 665 |
<script>
|
| 666 |
+
// Physics-based mouse interaction with floating blobs - Balanced version
|
| 667 |
const demoSection = document.getElementById('demo');
|
| 668 |
const blobs = demoSection.querySelectorAll('.floating-blob');
|
| 669 |
+
// Store blob physics data with balanced properties
|
| 670 |
const blobPhysics = [];
|
| 671 |
blobs.forEach((blob, index) => {
|
| 672 |
const rect = blob.getBoundingClientRect();
|
| 673 |
const demoRect = demoSection.getBoundingClientRect();
|
| 674 |
+
const angle = (Math.PI * 2 * index) / blobs.length;
|
| 675 |
+
const speed = 0.5 + Math.random() * 0.5;
|
| 676 |
blobPhysics.push({
|
| 677 |
element: blob,
|
| 678 |
x: rect.left - demoRect.left + rect.width / 2,
|
| 679 |
y: rect.top - demoRect.top + rect.height / 2,
|
| 680 |
+
vx: Math.cos(angle) * speed,
|
| 681 |
vy: Math.sin(angle) * speed,
|
| 682 |
baseX: rect.left - demoRect.left + rect.width / 2,
|
| 683 |
baseY: rect.top - demoRect.top + rect.height / 2,
|
| 684 |
+
mass: 1 + index * 0.2,
|
| 685 |
+
damping: 0.98, // Moderate damping
|
| 686 |
+
springForce: 0.002, // Gentle spring force to return to base
|
| 687 |
+
maxVelocity: 3,
|
| 688 |
+
repulsionRadius: 150, // Reduced radius
|
| 689 |
+
inertia: 0.98, // Balanced inertia
|
| 690 |
+
continuousMotion: false // Disable continuous motion for more stability
|
| 691 |
});
|
| 692 |
});
|
| 693 |
+
|
| 694 |
+
let mouseX = 0;
|
| 695 |
let mouseY = 0;
|
| 696 |
let isMouseInSection = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 697 |
|
| 698 |
demoSection.addEventListener('mousemove', (e) => {
|
| 699 |
const rect = demoSection.getBoundingClientRect();
|
|
|
|
|
|
|
| 700 |
mouseX = e.clientX - rect.left;
|
| 701 |
mouseY = e.clientY - rect.top;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 702 |
isMouseInSection = true;
|
| 703 |
});
|
| 704 |
|
|
|
|
| 706 |
isMouseInSection = false;
|
| 707 |
});
|
| 708 |
|
| 709 |
+
// Animation loop for balanced physics
|
| 710 |
function animateBlobs() {
|
| 711 |
blobPhysics.forEach((blob, index) => {
|
| 712 |
// Reset forces
|
| 713 |
let fx = 0;
|
| 714 |
let fy = 0;
|
| 715 |
+
|
| 716 |
+
// Gentle mouse repulsion force
|
| 717 |
if (isMouseInSection) {
|
| 718 |
const dx = blob.x - mouseX;
|
| 719 |
const dy = blob.y - mouseY;
|
| 720 |
const distance = Math.sqrt(dx * dx + dy * dy);
|
| 721 |
|
| 722 |
if (distance < blob.repulsionRadius && distance > 0) {
|
| 723 |
+
const force = ((blob.repulsionRadius - distance) / blob.repulsionRadius) * 2; // Reduced force
|
| 724 |
+
fx += (dx / distance) * force;
|
| 725 |
+
fy += (dy / distance) * force;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 726 |
}
|
| 727 |
}
|
| 728 |
+
|
| 729 |
+
// Blob-to-blob gentle collision/repulsion
|
| 730 |
blobPhysics.forEach((otherBlob, otherIndex) => {
|
| 731 |
if (index !== otherIndex) {
|
| 732 |
const dx = blob.x - otherBlob.x;
|
| 733 |
const dy = blob.y - otherBlob.y;
|
| 734 |
const distance = Math.sqrt(dx * dx + dy * dy);
|
| 735 |
+
const minDistance = 120; // Slightly larger minimum distance
|
| 736 |
|
| 737 |
if (distance < minDistance && distance > 0) {
|
| 738 |
+
const force = (minDistance - distance) / minDistance * 1.5; // Reduced force
|
| 739 |
fx += (dx / distance) * force;
|
| 740 |
fy += (dy / distance) * force;
|
| 741 |
}
|
| 742 |
}
|
| 743 |
});
|
| 744 |
+
|
| 745 |
+
// Spring force to return to base position
|
| 746 |
+
const springX = (blob.baseX - blob.x) * blob.springForce;
|
| 747 |
+
const springY = (blob.baseY - blob.y) * blob.springForce;
|
| 748 |
+
fx += springX;
|
| 749 |
+
fy += springY;
|
| 750 |
+
|
| 751 |
+
// Apply forces to velocity
|
| 752 |
blob.vx += fx / blob.mass;
|
| 753 |
blob.vy += fy / blob.mass;
|
|
|
|
|
|
|
|
|
|
| 754 |
|
| 755 |
+
// Apply damping and inertia
|
| 756 |
blob.vx *= blob.damping;
|
| 757 |
blob.vy *= blob.damping;
|
| 758 |
|
| 759 |
+
// Add very small random force for organic movement
|
| 760 |
+
fx += (Math.random() - 0.5) * 0.02;
|
| 761 |
+
fy += (Math.random() - 0.5) * 0.02;
|
| 762 |
+
|
| 763 |
+
// Limit maximum velocity
|
|
|
|
| 764 |
const speed = Math.sqrt(blob.vx * blob.vx + blob.vy * blob.vy);
|
| 765 |
if (speed > blob.maxVelocity) {
|
| 766 |
blob.vx = (blob.vx / speed) * blob.maxVelocity;
|
|
|
|
| 770 |
// Update position
|
| 771 |
blob.x += blob.vx;
|
| 772 |
blob.y += blob.vy;
|
| 773 |
+
|
| 774 |
+
// Boundary collision with energy loss
|
| 775 |
+
const blobWidth = 200;
|
| 776 |
+
const blobHeight = 200;
|
| 777 |
const demoRect = demoSection.getBoundingClientRect();
|
| 778 |
|
| 779 |
if (blob.x - blobWidth/2 < 0 || blob.x + blobWidth/2 > demoRect.width) {
|
| 780 |
+
blob.vx *= -0.7; // More energy loss
|
| 781 |
blob.x = Math.max(blobWidth/2, Math.min(demoRect.width - blobWidth/2, blob.x));
|
|
|
|
|
|
|
| 782 |
}
|
| 783 |
|
| 784 |
if (blob.y - blobHeight/2 < 0 || blob.y + blobHeight/2 > demoRect.height) {
|
| 785 |
+
blob.vy *= -0.7; // More energy loss
|
| 786 |
blob.y = Math.max(blobHeight/2, Math.min(demoRect.height - blobHeight/2, blob.y));
|
|
|
|
|
|
|
| 787 |
}
|
| 788 |
+
|
| 789 |
+
// Apply transform with minimal scaling
|
| 790 |
+
const velocityScale = Math.min(0.05, speed * 0.005);
|
| 791 |
const scale = 1 + velocityScale;
|
| 792 |
+
const rotation = speed * 0.1; // Very gentle rotation
|
| 793 |
|
| 794 |
blob.element.style.transform = `translate(${blob.x - blob.baseX}px, ${blob.y - blob.baseY}px) scale(${scale}) rotate(${rotation}deg)`;
|
| 795 |
+
});
|
| 796 |
|
| 797 |
requestAnimationFrame(animateBlobs);
|
| 798 |
}
|