|
<!DOCTYPE html> |
|
<html lang="ru"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Интерактивная графическая демонстрация</title> |
|
<style> |
|
body { |
|
margin: 0; |
|
overflow: hidden; |
|
background: linear-gradient(135deg, #1a1a2e, #16213e); |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
height: 100vh; |
|
font-family: 'Arial', sans-serif; |
|
color: rgba(255, 255, 255, 0.8); |
|
} |
|
|
|
canvas { |
|
display: block; |
|
position: fixed; |
|
top: 0; |
|
left: 0; |
|
z-index: 1; |
|
} |
|
|
|
.info { |
|
position: absolute; |
|
bottom: 20px; |
|
left: 20px; |
|
z-index: 2; |
|
background-color: rgba(0, 0, 0, 0.5); |
|
padding: 15px; |
|
border-radius: 10px; |
|
max-width: 300px; |
|
} |
|
|
|
h1 { |
|
margin: 0 0 10px 0; |
|
font-size: 18px; |
|
color: #fff; |
|
} |
|
|
|
p { |
|
margin: 5px 0; |
|
font-size: 14px; |
|
line-height: 1.4; |
|
} |
|
|
|
.controls { |
|
position: absolute; |
|
top: 20px; |
|
right: 20px; |
|
z-index: 2; |
|
display: flex; |
|
flex-direction: column; |
|
gap: 10px; |
|
} |
|
|
|
button { |
|
background: rgba(0, 0, 0, 0.5); |
|
color: white; |
|
border: 1px solid rgba(255, 255, 255, 0.2); |
|
border-radius: 5px; |
|
padding: 8px 12px; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
button:hover { |
|
background: rgba(255, 255, 255, 0.1); |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<canvas id="canvas"></canvas> |
|
|
|
<div class="info"> |
|
<h1>Интерактивная частичная система</h1> |
|
<p>Двигайте мышью, чтобы влиять на частицы. Нажмите для создания взрыва.</p> |
|
<p>Частицы реагируют на гравитацию, трение и друг на друга.</p> |
|
</div> |
|
|
|
<div class="controls"> |
|
<button id="explode">Создать взрыв</button> |
|
<button id="changeColor">Изменить цвет</button> |
|
<button id="toggleLines">Переключить соединения</button> |
|
</div> |
|
|
|
<script> |
|
|
|
const canvas = document.getElementById('canvas'); |
|
const ctx = canvas.getContext('2d'); |
|
|
|
|
|
canvas.width = window.innerWidth; |
|
canvas.height = window.innerHeight; |
|
|
|
|
|
const settings = { |
|
particleCount: 150, |
|
particleSize: 3, |
|
lineDistance: 100, |
|
showLines: true, |
|
gravity: 0.05, |
|
friction: 0.98, |
|
mouseInfluence: 20, |
|
colors: [ |
|
'#ff3366', '#00ffcc', '#ffcc00', '#33ccff', '#cc33ff', |
|
'#ff9933', '#66ff33', '#3399ff', '#ff33cc', '#33ff99' |
|
], |
|
currentColorIndex: 0, |
|
maxVelocity: 5 |
|
}; |
|
|
|
|
|
function random(min, max) { |
|
return Math.random() * (max - min) + min; |
|
} |
|
|
|
|
|
class Particle { |
|
constructor(x, y) { |
|
this.x = x || random(0, canvas.width); |
|
this.y = y || random(0, canvas.height); |
|
this.velocityX = random(-1, 1); |
|
this.velocityY = random(-1, 1); |
|
this.size = random(1, settings.particleSize); |
|
this.baseSize = this.size; |
|
this.color = settings.colors[ |
|
Math.floor(random(0, settings.colors.length)) |
|
]; |
|
this.hue = Math.floor(random(0, 360)); |
|
this.life = 0; |
|
this.maxLife = random(200, 1000); |
|
} |
|
|
|
|
|
update(mouseX, mouseY) { |
|
|
|
const dx = mouseX - this.x; |
|
const dy = mouseY - this.y; |
|
const distance = Math.sqrt(dx * dx + dy * dy); |
|
|
|
|
|
if (distance < 100) { |
|
const angle = Math.atan2(dy, dx); |
|
const force = (100 - distance) / settings.mouseInfluence; |
|
|
|
this.velocityX += Math.cos(angle) * force; |
|
this.velocityY += Math.sin(angle) * force; |
|
} |
|
|
|
|
|
const speed = Math.sqrt(this.velocityX * this.velocityX + this.velocityY * this.velocityY); |
|
if (speed > settings.maxVelocity) { |
|
this.velocityX = (this.velocityX / speed) * settings.maxVelocity; |
|
this.velocityY = (this.velocityY / speed) * settings.maxVelocity; |
|
} |
|
|
|
|
|
this.velocityY += settings.gravity; |
|
|
|
|
|
this.velocityX *= settings.friction; |
|
this.velocityY *= settings.friction; |
|
|
|
|
|
this.x += this.velocityX; |
|
this.y += this.velocityY; |
|
|
|
|
|
if (this.x < 0 || this.x > canvas.width) { |
|
this.velocityX *= -0.8; |
|
this.x = this.x < 0 ? 0 : canvas.width; |
|
} |
|
|
|
if (this.y < 0 || this.y > canvas.height) { |
|
this.velocityY *= -0.8; |
|
this.y = this.y < 0 ? 0 : canvas.height; |
|
} |
|
|
|
|
|
this.life++; |
|
if (this.life > this.maxLife) this.life = 0; |
|
const pulse = Math.sin(this.life * 0.05) * 0.5 + 1; |
|
this.size = this.baseSize * pulse; |
|
|
|
|
|
this.hue = (this.hue + 0.2) % 360; |
|
} |
|
|
|
|
|
draw() { |
|
ctx.fillStyle = `hsla(${this.hue}, 100%, 50%, 0.8)`; |
|
ctx.beginPath(); |
|
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); |
|
ctx.fill(); |
|
|
|
|
|
ctx.shadowBlur = 15; |
|
ctx.shadowColor = `hsla(${this.hue}, 100%, 50%, 0.6)`; |
|
} |
|
} |
|
|
|
|
|
let particles = []; |
|
for (let i = 0; i < settings.particleCount; i++) { |
|
particles.push(new Particle()); |
|
} |
|
|
|
|
|
let mouseX = canvas.width / 2; |
|
let mouseY = canvas.height / 2; |
|
|
|
|
|
canvas.addEventListener('mousemove', (e) => { |
|
mouseX = e.clientX; |
|
mouseY = e.clientY; |
|
}); |
|
|
|
|
|
canvas.addEventListener('mousedown', createExplosion); |
|
|
|
|
|
document.getElementById('explode').addEventListener('click', createExplosion); |
|
|
|
|
|
document.getElementById('changeColor').addEventListener('click', () => { |
|
settings.currentColorIndex = (settings.currentColorIndex + 1) % settings.colors.length; |
|
}); |
|
|
|
|
|
document.getElementById('toggleLines').addEventListener('click', () => { |
|
settings.showLines = !settings.showLines; |
|
}); |
|
|
|
|
|
function createExplosion() { |
|
for (let i = 0; i < particles.length; i++) { |
|
const angle = random(0, Math.PI * 2); |
|
const force = random(5, 15); |
|
|
|
particles[i].velocityX = Math.cos(angle) * force; |
|
particles[i].velocityY = Math.sin(angle) * force; |
|
} |
|
} |
|
|
|
|
|
function animate() { |
|
|
|
ctx.fillStyle = 'rgba(10, 10, 20, 0.2)'; |
|
ctx.fillRect(0, 0, canvas.width, canvas.height); |
|
|
|
|
|
particles.forEach(particle => particle.update(mouseX, mouseY)); |
|
|
|
|
|
if (settings.showLines) { |
|
drawConnections(); |
|
} |
|
|
|
|
|
particles.forEach(particle => particle.draw()); |
|
|
|
|
|
ctx.shadowBlur = 0; |
|
|
|
requestAnimationFrame(animate); |
|
} |
|
|
|
|
|
function drawConnections() { |
|
for (let i = 0; i < particles.length; i++) { |
|
for (let j = i + 1; j < particles.length; j++) { |
|
const dx = particles[i].x - particles[j].x; |
|
const dy = particles[i].y - particles[j].y; |
|
const distance = Math.sqrt(dx * dx + dy * dy); |
|
|
|
if (distance < settings.lineDistance) { |
|
const opacity = 1 - (distance / settings.lineDistance); |
|
ctx.strokeStyle = `rgba(200, 200, 255, ${opacity * 0.2})`; |
|
ctx.lineWidth = opacity * 1.5; |
|
ctx.beginPath(); |
|
ctx.moveTo(particles[i].x, particles[i].y); |
|
ctx.lineTo(particles[j].x, particles[j].y); |
|
ctx.stroke(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
window.addEventListener('resize', () => { |
|
canvas.width = window.innerWidth; |
|
canvas.height = window.innerHeight; |
|
}); |
|
|
|
|
|
animate(); |
|
|
|
|
|
setTimeout(createExplosion, 500); |
|
</script> |
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: absolute; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">This website has been generated by <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body> |
|
</html> |