Test files für LifeWave mit anime.js
This commit is contained in:
parent
c6dd063eee
commit
9c48a17152
7 changed files with 3573 additions and 0 deletions
452
test/anime-points-drag.html
Normal file
452
test/anime-points-drag.html
Normal file
|
|
@ -0,0 +1,452 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Welle Hintergrund (SVG)</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
background-color: #f0f0f0;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
svg {
|
||||
border: 1px solid #ccc; /* Optional: Rahmen zur Visualisierung */
|
||||
overflow: visible; /* Damit Punkte am Rand nicht abgeschnitten werden */
|
||||
}
|
||||
|
||||
.point {
|
||||
fill: #000000; /* Farbe der Punkte */
|
||||
opacity: 0.8; /* Punkte sichtbar machen */
|
||||
r: 3.5; /* Einheitliche Punktgröße */
|
||||
}
|
||||
|
||||
.line {
|
||||
fill: none; /* Keine Füllung für die Linie */
|
||||
stroke-width: 4; /* Dickere Hauptlinie */
|
||||
}
|
||||
|
||||
.offset-spline {
|
||||
fill: none;
|
||||
stroke-width: 2.5; /* Dickere mittlere Linien */
|
||||
stroke-opacity: 0.6; /* Angepasste Transparenz */
|
||||
}
|
||||
|
||||
.offset-spline-far {
|
||||
fill: none;
|
||||
stroke-width: 1.2; /* Etwas dickere äußere Linien */
|
||||
stroke-opacity: 0.4; /* Höhere Transparenz */
|
||||
}
|
||||
.offset-spline-far-2 {
|
||||
fill: none;
|
||||
stroke-width: 1; /* Etwas dickere äußere Linien */
|
||||
stroke-opacity: 0.2; /* Höhere Transparenz */
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Welleneffekt (SVG)</h1>
|
||||
|
||||
<svg width="600" height="200" id="point-canvas">
|
||||
<defs>
|
||||
<linearGradient id="lineGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:rgb(226, 245, 172);stop-opacity:1" />
|
||||
<stop offset="35%" style="stop-color:rgb(135, 241, 36);stop-opacity:1" />
|
||||
<stop offset="70%" style="stop-color:rgb(62, 200, 232);stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:rgb(4, 162, 254);stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', initializeApp);
|
||||
|
||||
// Konstanten und Konfiguration
|
||||
const CONFIG = {
|
||||
svgNS: "http://www.w3.org/2000/svg",
|
||||
pointRadius: 4,
|
||||
offsetNear: 5,
|
||||
offsetFar: 10,
|
||||
minOffsetFactor: 0.2,
|
||||
splineTension: 1.3,
|
||||
baseYPositions: [100, 70, 110, 80, 130, 90, 100],
|
||||
initialAnimDuration: 2500,
|
||||
contAnimDuration: 1000,
|
||||
waveAmplitude: 12,
|
||||
isDraggable: true,
|
||||
animationEnabled: true
|
||||
};
|
||||
|
||||
// Globale Variablen
|
||||
let svg, points, pointElements = [], pathElements = {}, currentAnimation, isAnimating = false;
|
||||
let draggedPointIndex = -1;
|
||||
|
||||
function initializeApp() {
|
||||
// DOM-Referenzen
|
||||
svg = document.getElementById('point-canvas');
|
||||
|
||||
// Punkte definieren
|
||||
initPoints();
|
||||
updateVisualization();
|
||||
addControls();
|
||||
|
||||
// Animation starten, wenn aktiviert
|
||||
if (CONFIG.animationEnabled) {
|
||||
startWaveAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
// Punkte initialisieren
|
||||
function initPoints() {
|
||||
const xStep = 580 / (CONFIG.baseYPositions.length - 1);
|
||||
|
||||
// Punkte erstellen
|
||||
points = CONFIG.baseYPositions.map((y, i) => {
|
||||
return { x: 20 + i * xStep, y };
|
||||
});
|
||||
|
||||
// Punktelemente erstellen
|
||||
pointElements = points.map((p, index) => {
|
||||
const circle = createSVGElement('circle', {
|
||||
'class': 'point',
|
||||
'cx': p.x,
|
||||
'cy': p.y,
|
||||
'r': CONFIG.pointRadius,
|
||||
'cursor': CONFIG.isDraggable ? 'grab' : 'default'
|
||||
});
|
||||
|
||||
// Drag-and-Drop-Funktionalität hinzufügen
|
||||
if (CONFIG.isDraggable) {
|
||||
setupDraggable(circle, index);
|
||||
}
|
||||
|
||||
svg.appendChild(circle);
|
||||
return circle;
|
||||
});
|
||||
}
|
||||
|
||||
// Hilfsfunktion zur Berechnung von Normalen
|
||||
function calculateNormals(points) {
|
||||
return points.map((p1, i, arr) => {
|
||||
let dx, dy;
|
||||
|
||||
if (i === 0) {
|
||||
dx = arr[1].x - arr[0].x;
|
||||
dy = arr[1].y - arr[0].y;
|
||||
} else if (i === arr.length - 1) {
|
||||
dx = arr[i].x - arr[i-1].x;
|
||||
dy = arr[i].y - arr[i-1].y;
|
||||
} else {
|
||||
const p0 = arr[i - 1];
|
||||
const p2 = arr[i + 1];
|
||||
dx = p2.x - p0.x;
|
||||
dy = p2.y - p0.y;
|
||||
}
|
||||
|
||||
// Normale berechnen (-dy, dx) und normalisieren
|
||||
const length = Math.sqrt(dx * dx + dy * dy);
|
||||
return length > 0
|
||||
? { x: -dy / length, y: dx / length }
|
||||
: { x: 0, y: 0 };
|
||||
});
|
||||
}
|
||||
|
||||
// Funktion zum Generieren von Offset-Spline-Pfaden
|
||||
function generateOffsetSplinePath(originalPoints, normals, offsetDistance) {
|
||||
if (originalPoints.length < 2) return "";
|
||||
|
||||
const { minOffsetFactor, splineTension } = CONFIG;
|
||||
|
||||
// Startpunkt mit Offset
|
||||
const startNormal = normals[0];
|
||||
const startOffsetX = originalPoints[0].x + startNormal.x * offsetDistance * minOffsetFactor;
|
||||
const startOffsetY = originalPoints[0].y + startNormal.y * offsetDistance * minOffsetFactor;
|
||||
let d = `M ${startOffsetX} ${startOffsetY}`;
|
||||
|
||||
// Bézier-Kurve für jeden Punkt generieren
|
||||
for (let i = 0; i < originalPoints.length - 1; i++) {
|
||||
const p0 = i > 0 ? originalPoints[i - 1] : originalPoints[0];
|
||||
const p1 = originalPoints[i];
|
||||
const p2 = originalPoints[i + 1];
|
||||
const p3 = i < originalPoints.length - 2 ? originalPoints[i + 2] : originalPoints[originalPoints.length - 1];
|
||||
|
||||
// Kontrollpunkte berechnen
|
||||
const cp1x = p1.x + (p2.x - p0.x) / 6 * splineTension;
|
||||
const cp1y = p1.y + (p2.y - p0.y) / 6 * splineTension;
|
||||
const cp2x = p2.x - (p3.x - p1.x) / 6 * splineTension;
|
||||
const cp2y = p2.y - (p3.y - p1.y) / 6 * splineTension;
|
||||
|
||||
// Normalen verwenden
|
||||
const n1 = normals[i];
|
||||
const n2 = normals[i+1];
|
||||
|
||||
// Offset-Punkte berechnen
|
||||
const offsetCp1x = cp1x + n1.x * offsetDistance;
|
||||
const offsetCp1y = cp1y + n1.y * offsetDistance;
|
||||
const offsetCp2x = cp2x + n2.x * offsetDistance;
|
||||
const offsetCp2y = cp2y + n2.y * offsetDistance;
|
||||
const offsetP2x = p2.x + n2.x * offsetDistance * minOffsetFactor;
|
||||
const offsetP2y = p2.y + n2.y * offsetDistance * minOffsetFactor;
|
||||
|
||||
// Kubisches Bézier-Segment hinzufügen
|
||||
d += ` C ${offsetCp1x} ${offsetCp1y}, ${offsetCp2x} ${offsetCp2y}, ${offsetP2x} ${offsetP2y}`;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
// Hilfsfunktion zum Erstellen von SVG-Elementen
|
||||
function createSVGElement(type, attributes = {}) {
|
||||
const element = document.createElementNS(CONFIG.svgNS, type);
|
||||
Object.entries(attributes).forEach(([key, value]) => {
|
||||
element.setAttribute(key, value);
|
||||
});
|
||||
return element;
|
||||
}
|
||||
|
||||
// Drag-and-Drop-Funktionalität
|
||||
function setupDraggable(element, pointIndex) {
|
||||
element.addEventListener('mousedown', (e) => startDrag(e, pointIndex));
|
||||
element.addEventListener('touchstart', (e) => startDrag(e, pointIndex), { passive: false });
|
||||
}
|
||||
|
||||
function startDrag(event, pointIndex) {
|
||||
event.preventDefault();
|
||||
|
||||
// Stoppe Animationen während des Ziehens
|
||||
if (currentAnimation) {
|
||||
currentAnimation.pause();
|
||||
}
|
||||
|
||||
draggedPointIndex = pointIndex;
|
||||
|
||||
// Cursor-Stil ändern
|
||||
document.querySelectorAll('.point').forEach(p => p.setAttribute('cursor', 'grab'));
|
||||
pointElements[pointIndex].setAttribute('cursor', 'grabbing');
|
||||
|
||||
// Event-Listener für Bewegung und Loslassen hinzufügen
|
||||
document.addEventListener('mousemove', moveDrag);
|
||||
document.addEventListener('touchmove', moveDrag, { passive: false });
|
||||
document.addEventListener('mouseup', endDrag);
|
||||
document.addEventListener('touchend', endDrag);
|
||||
document.addEventListener('mouseleave', endDrag);
|
||||
}
|
||||
|
||||
function moveDrag(event) {
|
||||
if (draggedPointIndex === -1) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
// Mouse- oder Touch-Position ermitteln
|
||||
const clientX = event.clientX || (event.touches && event.touches[0].clientX);
|
||||
const clientY = event.clientY || (event.touches && event.touches[0].clientY);
|
||||
|
||||
if (clientX === undefined || clientY === undefined) return;
|
||||
|
||||
// Position relativ zum SVG berechnen
|
||||
const svgRect = svg.getBoundingClientRect();
|
||||
const x = clientX - svgRect.left;
|
||||
const y = clientY - svgRect.top;
|
||||
|
||||
// Punkt aktualisieren
|
||||
points[draggedPointIndex].x = x;
|
||||
points[draggedPointIndex].y = y;
|
||||
|
||||
// Basisposition für die Animation aktualisieren
|
||||
CONFIG.baseYPositions[draggedPointIndex] = y;
|
||||
|
||||
// SVG aktualisieren
|
||||
updateVisualization();
|
||||
}
|
||||
|
||||
function endDrag() {
|
||||
if (draggedPointIndex === -1) return;
|
||||
|
||||
// Cursor zurücksetzen
|
||||
pointElements[draggedPointIndex].setAttribute('cursor', 'grab');
|
||||
|
||||
// Variablen zurücksetzen
|
||||
draggedPointIndex = -1;
|
||||
|
||||
// Event-Listener entfernen
|
||||
document.removeEventListener('mousemove', moveDrag);
|
||||
document.removeEventListener('touchmove', moveDrag);
|
||||
document.removeEventListener('mouseup', endDrag);
|
||||
document.removeEventListener('touchend', endDrag);
|
||||
document.removeEventListener('mouseleave', endDrag);
|
||||
|
||||
// Animation fortsetzen, wenn gewünscht
|
||||
if (CONFIG.animationEnabled) {
|
||||
isAnimating = true;
|
||||
startWaveAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
// Pfade aktualisieren oder erstellen
|
||||
function updateOrCreatePath(existingPath, points, normals, offsetDistance, className) {
|
||||
const d = generateOffsetSplinePath(points, normals, offsetDistance);
|
||||
|
||||
if (existingPath) {
|
||||
existingPath.setAttribute('d', d);
|
||||
return existingPath;
|
||||
} else {
|
||||
const path = createSVGElement('path', {
|
||||
'class': className,
|
||||
'stroke': 'url(#lineGradient)',
|
||||
'd': d
|
||||
});
|
||||
svg.appendChild(path);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
// Punkte aktualisieren
|
||||
function updatePointsVisualization() {
|
||||
points.forEach((p, i) => {
|
||||
if (pointElements[i]) {
|
||||
pointElements[i].setAttribute('cx', p.x);
|
||||
pointElements[i].setAttribute('cy', p.y);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Hauptfunktion zur Visualisierung
|
||||
function updateVisualization() {
|
||||
// Zeit- und Animations-Faktoren berechnen
|
||||
const time = Date.now() / 1000;
|
||||
const waveOffset1 = Math.sin(time * 1.2) * 1.5;
|
||||
const waveOffset2 = Math.sin(time * 0.8 + 1) * 2;
|
||||
|
||||
// Normalen nur einmal berechnen
|
||||
const normals = calculateNormals(points);
|
||||
const { offsetNear, offsetFar } = CONFIG;
|
||||
|
||||
// Alle Pfade aktualisieren
|
||||
const pathUpdates = [
|
||||
{ key: 'farBg2', offset: (-offsetFar * 1.5) + waveOffset2, className: 'offset-spline-far-2' },
|
||||
{ key: 'farBg1', offset: (-offsetFar * 1.35) + waveOffset2, className: 'offset-spline-far' },
|
||||
{ key: 'nearBg', offset: (-offsetNear * 1.2) + waveOffset1, className: 'offset-spline' },
|
||||
{ key: 'main', offset: 0, className: 'line' },
|
||||
{ key: 'nearFg', offset: (offsetNear * 1.2) + waveOffset1, className: 'offset-spline' },
|
||||
{ key: 'farFg1', offset: (offsetFar * 1.35) + waveOffset2, className: 'offset-spline-far' },
|
||||
{ key: 'farFg2', offset: (offsetFar * 1.5) + waveOffset2, className: 'offset-spline-far-2' }
|
||||
];
|
||||
|
||||
// Pfade aktualisieren
|
||||
pathUpdates.forEach(({ key, offset, className }) => {
|
||||
pathElements[key] = updateOrCreatePath(
|
||||
pathElements[key], points, normals, offset, className
|
||||
);
|
||||
});
|
||||
|
||||
// Punkte aktualisieren
|
||||
updatePointsVisualization();
|
||||
|
||||
// Alle Punktelemente nach vorne bringen
|
||||
pointElements.forEach(p => svg.appendChild(p));
|
||||
}
|
||||
|
||||
function startWaveAnimation() {
|
||||
// Animation beenden, falls bereits vorhanden
|
||||
if (currentAnimation) {
|
||||
currentAnimation.pause();
|
||||
}
|
||||
|
||||
// Initiale Animation erstellen
|
||||
currentAnimation = anime({
|
||||
targets: points,
|
||||
y: function(p, i) {
|
||||
return CONFIG.baseYPositions[i];
|
||||
},
|
||||
duration: CONFIG.initialAnimDuration,
|
||||
direction: 'alternate',
|
||||
loop: true,
|
||||
easing: 'easeInOutSine',
|
||||
delay: anime.stagger(100),
|
||||
update: updateVisualization,
|
||||
complete: startContinuousAnimation
|
||||
});
|
||||
}
|
||||
|
||||
function startContinuousAnimation() {
|
||||
currentAnimation = anime({
|
||||
targets: points,
|
||||
y: function(p, i) {
|
||||
const baseY = CONFIG.baseYPositions[i];
|
||||
const amplitude = CONFIG.waveAmplitude;
|
||||
return function() {
|
||||
return baseY + amplitude * Math.sin((Date.now()/1000) * Math.PI + i * 0.8);
|
||||
};
|
||||
},
|
||||
duration: CONFIG.contAnimDuration,
|
||||
autoplay: true,
|
||||
easing: 'linear',
|
||||
loop: true,
|
||||
update: function() {
|
||||
requestAnimationFrame(updateVisualization);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Steuerelemente hinzufügen
|
||||
function addControls() {
|
||||
const controlPanel = document.createElement('div');
|
||||
controlPanel.style.marginTop = '20px';
|
||||
|
||||
const toggleButton = document.createElement('button');
|
||||
toggleButton.textContent = 'Animation pausieren';
|
||||
toggleButton.addEventListener('click', function() {
|
||||
if (currentAnimation && currentAnimation.paused) {
|
||||
currentAnimation.play();
|
||||
this.textContent = 'Animation pausieren';
|
||||
} else if (currentAnimation) {
|
||||
currentAnimation.pause();
|
||||
this.textContent = 'Animation fortsetzen';
|
||||
}
|
||||
});
|
||||
|
||||
const resetButton = document.createElement('button');
|
||||
resetButton.textContent = 'Zurücksetzen';
|
||||
resetButton.style.marginLeft = '10px';
|
||||
resetButton.addEventListener('click', resetAnimation);
|
||||
|
||||
controlPanel.appendChild(toggleButton);
|
||||
controlPanel.appendChild(resetButton);
|
||||
document.body.appendChild(controlPanel);
|
||||
}
|
||||
|
||||
function resetAnimation() {
|
||||
// Originale Basispositionen wiederherstellen
|
||||
CONFIG.baseYPositions = [100, 70, 110, 80, 130, 90, 100];
|
||||
|
||||
// X-Positionen zurücksetzen
|
||||
const xStep = 580 / (CONFIG.baseYPositions.length - 1);
|
||||
points.forEach((p, i) => {
|
||||
p.x = 20 + i * xStep;
|
||||
p.y = CONFIG.baseYPositions[i];
|
||||
});
|
||||
|
||||
// Visualisierung aktualisieren
|
||||
updateVisualization();
|
||||
|
||||
// Animation neu starten
|
||||
if (CONFIG.animationEnabled) {
|
||||
isAnimating = true;
|
||||
startWaveAnimation();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue