✨ feat(mindmap): aktualisiere die Mindmap-Datenstruktur mit neuen Knoten und Kanten, um die Benutzerinteraktion zu verbessern. Füge dynamische Knotenbeschreibungen hinzu und implementiere eine Funktion zur Aktualisierung der Mindmap, die das Layout optimiert und die Benutzererfahrung verbessert.
This commit is contained in:
@@ -3,570 +3,239 @@
|
||||
* Verbessert die Interaktion mit der Mindmap und steuert die Seitenleisten-Anzeige
|
||||
*/
|
||||
|
||||
// Stellt sicher, dass das Dokument geladen ist, bevor Aktionen ausgeführt werden
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('Mindmap-Interaktionsverbesserungen werden initialisiert...');
|
||||
|
||||
// Auf das Laden der Mindmap warten
|
||||
document.addEventListener('mindmap-loaded', setupInteractionEnhancements);
|
||||
|
||||
// Sofortiges Setup für statische Interaktionen
|
||||
setupStaticInteractions();
|
||||
// Globale Variablen
|
||||
let cy;
|
||||
let selectedNode = null;
|
||||
let isLegendVisible = true;
|
||||
|
||||
// Direkten Event-Listener für Knotenauswahl einrichten
|
||||
setupNodeSelectionListener();
|
||||
|
||||
// Neuronales Netzwerk-Hintergrund-Effekt aktivieren
|
||||
setupNeuralNetworkEffect();
|
||||
// Initialisierung der Mindmap
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Cytoscape-Container initialisieren
|
||||
cy = cytoscape({
|
||||
container: document.getElementById('cy'),
|
||||
style: [
|
||||
{
|
||||
selector: 'node',
|
||||
style: {
|
||||
'background-color': '#60a5fa',
|
||||
'label': 'data(label)',
|
||||
'text-valign': 'center',
|
||||
'text-halign': 'center',
|
||||
'text-wrap': 'wrap',
|
||||
'text-max-width': '100px',
|
||||
'font-size': '12px',
|
||||
'color': '#fff',
|
||||
'text-outline-color': '#000',
|
||||
'text-outline-width': '2px',
|
||||
'width': '40px',
|
||||
'height': '40px',
|
||||
'border-width': '2px',
|
||||
'border-color': '#fff',
|
||||
'border-opacity': '0.5',
|
||||
'padding': '10px',
|
||||
'text-events': 'yes'
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'edge',
|
||||
style: {
|
||||
'width': '2px',
|
||||
'line-color': 'rgba(255, 255, 255, 0.3)',
|
||||
'target-arrow-color': 'rgba(255, 255, 255, 0.3)',
|
||||
'target-arrow-shape': 'triangle',
|
||||
'curve-style': 'bezier',
|
||||
'label': 'data(label)',
|
||||
'font-size': '10px',
|
||||
'color': '#fff',
|
||||
'text-outline-color': '#000',
|
||||
'text-outline-width': '2px',
|
||||
'text-rotation': 'autorotate'
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: ':selected',
|
||||
style: {
|
||||
'background-color': '#8b5cf6',
|
||||
'line-color': '#8b5cf6',
|
||||
'target-arrow-color': '#8b5cf6',
|
||||
'source-arrow-color': '#8b5cf6',
|
||||
'text-outline-color': '#000',
|
||||
'text-outline-width': '2px',
|
||||
'border-width': '3px',
|
||||
'border-color': '#fff',
|
||||
'border-opacity': '1'
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: '.highlighted',
|
||||
style: {
|
||||
'background-color': '#10b981',
|
||||
'line-color': '#10b981',
|
||||
'target-arrow-color': '#10b981',
|
||||
'source-arrow-color': '#10b981',
|
||||
'transition-property': 'background-color, line-color, target-arrow-color',
|
||||
'transition-duration': '0.3s'
|
||||
}
|
||||
}
|
||||
],
|
||||
layout: {
|
||||
name: 'cose',
|
||||
idealEdgeLength: 100,
|
||||
nodeOverlap: 20,
|
||||
refresh: 20,
|
||||
fit: true,
|
||||
padding: 30,
|
||||
randomize: false,
|
||||
componentSpacing: 100,
|
||||
nodeRepulsion: 400000,
|
||||
edgeElasticity: 100,
|
||||
nestingFactor: 5,
|
||||
gravity: 80,
|
||||
numIter: 1000,
|
||||
initialTemp: 200,
|
||||
coolingFactor: 0.95,
|
||||
minTemp: 1.0
|
||||
}
|
||||
});
|
||||
|
||||
// Event-Listener für Knoten
|
||||
cy.on('tap', 'node', function(evt) {
|
||||
const node = evt.target;
|
||||
updateNodeInfo(node);
|
||||
highlightConnectedNodes(node);
|
||||
});
|
||||
|
||||
// Event-Listener für Hintergrund-Klick
|
||||
cy.on('tap', function(evt) {
|
||||
if (evt.target === cy) {
|
||||
resetHighlighting();
|
||||
hideNodeInfo();
|
||||
}
|
||||
});
|
||||
|
||||
// Zoom-Kontrollen
|
||||
document.getElementById('zoom-in').addEventListener('click', function() {
|
||||
cy.zoom({
|
||||
level: cy.zoom() * 1.2,
|
||||
renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 }
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('zoom-out').addEventListener('click', function() {
|
||||
cy.zoom({
|
||||
level: cy.zoom() / 1.2,
|
||||
renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 }
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('reset-view').addEventListener('click', function() {
|
||||
cy.fit();
|
||||
});
|
||||
|
||||
// Legende ein-/ausblenden
|
||||
document.getElementById('toggle-legend').addEventListener('click', function() {
|
||||
const legend = document.querySelector('.category-legend');
|
||||
isLegendVisible = !isLegendVisible;
|
||||
legend.style.display = isLegendVisible ? 'flex' : 'none';
|
||||
});
|
||||
|
||||
// Tastatursteuerung
|
||||
document.addEventListener('keydown', function(evt) {
|
||||
switch(evt.key) {
|
||||
case '+':
|
||||
cy.zoom({
|
||||
level: cy.zoom() * 1.2,
|
||||
renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 }
|
||||
});
|
||||
break;
|
||||
case '-':
|
||||
cy.zoom({
|
||||
level: cy.zoom() / 1.2,
|
||||
renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 }
|
||||
});
|
||||
break;
|
||||
case 'Escape':
|
||||
resetHighlighting();
|
||||
hideNodeInfo();
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Erzeugt subtile Hintergrundeffekte für neuronales Netzwerk
|
||||
function setupNeuralNetworkEffect() {
|
||||
const cyContainer = document.getElementById('cy');
|
||||
if (!cyContainer) return;
|
||||
// Knoteninformationen aktualisieren
|
||||
function updateNodeInfo(node) {
|
||||
const infoPanel = document.getElementById('node-info');
|
||||
const infoContent = infoPanel.querySelector('.info-content');
|
||||
|
||||
// Dendrite Animation CSS
|
||||
const dendriteStyle = document.createElement('style');
|
||||
dendriteStyle.textContent = `
|
||||
.neuron-pulse {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(139, 92, 246, 0.1) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: 0;
|
||||
animation: neuronPulse 6s ease-in-out infinite;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
@keyframes neuronPulse {
|
||||
0%, 100% { opacity: 0; transform: translate(-50%, -50%) scale(0.7); }
|
||||
50% { opacity: 0.8; transform: translate(-50%, -50%) scale(1.2); }
|
||||
}
|
||||
|
||||
.synapse-line {
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent, rgba(139, 92, 246, 0.3), transparent);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: 0;
|
||||
transform-origin: 0% 50%;
|
||||
animation: synapseFlow 8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes synapseFlow {
|
||||
0%, 100% { opacity: 0; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
// Knotendaten abrufen
|
||||
const nodeData = node.data();
|
||||
|
||||
// Info-Panel aktualisieren
|
||||
infoContent.innerHTML = `
|
||||
<h4 class="text-lg font-semibold mb-2">${nodeData.label}</h4>
|
||||
<p class="mb-3">${nodeData.description || 'Keine Beschreibung verfügbar.'}</p>
|
||||
<div class="mt-4">
|
||||
<h5 class="text-sm font-semibold mb-2">Verknüpfte Konzepte:</h5>
|
||||
<ul class="space-y-1">
|
||||
${getConnectedNodesList(node)}
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
document.head.appendChild(dendriteStyle);
|
||||
|
||||
// Zufällige Pulsierende Dendrite-Effekte
|
||||
setInterval(() => {
|
||||
if (Math.random() > 0.7) {
|
||||
const pulse = document.createElement('div');
|
||||
pulse.className = 'neuron-pulse';
|
||||
|
||||
// Zufällige Größe und Position
|
||||
const size = Math.random() * 200 + 100;
|
||||
pulse.style.width = `${size}px`;
|
||||
pulse.style.height = `${size}px`;
|
||||
pulse.style.left = `${Math.random() * 100}%`;
|
||||
pulse.style.top = `${Math.random() * 100}%`;
|
||||
|
||||
// Animation-Eigenschaften variieren
|
||||
pulse.style.animationDuration = `${Math.random() * 4 + 3}s`;
|
||||
pulse.style.animationDelay = `${Math.random() * 2}s`;
|
||||
|
||||
cyContainer.appendChild(pulse);
|
||||
|
||||
// Element nach Animation entfernen
|
||||
setTimeout(() => pulse.remove(), 7000);
|
||||
}
|
||||
|
||||
// Zufällige Synapse-Linien-Effekte
|
||||
if (Math.random() > 0.8) {
|
||||
const synapse = document.createElement('div');
|
||||
synapse.className = 'synapse-line';
|
||||
|
||||
// Zufällige Position und Größe
|
||||
const startX = Math.random() * 100;
|
||||
const startY = Math.random() * 100;
|
||||
const length = Math.random() * 200 + 50;
|
||||
const angle = Math.random() * 360;
|
||||
|
||||
synapse.style.width = `${length}px`;
|
||||
synapse.style.left = `${startX}%`;
|
||||
synapse.style.top = `${startY}%`;
|
||||
synapse.style.transform = `rotate(${angle}deg)`;
|
||||
|
||||
// Animation-Eigenschaften
|
||||
synapse.style.animationDuration = `${Math.random() * 3 + 5}s`;
|
||||
synapse.style.animationDelay = `${Math.random() * 2}s`;
|
||||
|
||||
cyContainer.appendChild(synapse);
|
||||
|
||||
// Element nach Animation entfernen
|
||||
setTimeout(() => synapse.remove(), 9000);
|
||||
}
|
||||
}, 800);
|
||||
// Panel anzeigen
|
||||
infoPanel.classList.add('visible');
|
||||
}
|
||||
|
||||
// Richtet grundlegende statische Interaktionen ein
|
||||
function setupStaticInteractions() {
|
||||
// Initialisiert die Hover-Effekte für die Seitenleisten-Panels
|
||||
initializePanelEffects();
|
||||
// Verbundene Knoten hervorheben
|
||||
function highlightConnectedNodes(node) {
|
||||
// Vorherige Hervorhebungen zurücksetzen
|
||||
resetHighlighting();
|
||||
|
||||
// Prevent default Zoom bei CTRL + Mausrad
|
||||
document.addEventListener('wheel', function(e) {
|
||||
if (e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}, { passive: false });
|
||||
// Ausgewählten Knoten hervorheben
|
||||
node.addClass('highlighted');
|
||||
|
||||
// Initialisiert verbesserten Zoom-Handler direkt nach dem Laden
|
||||
initializeZoomHandler();
|
||||
// Verbundene Knoten und Kanten hervorheben
|
||||
const connectedElements = node.neighborhood();
|
||||
connectedElements.addClass('highlighted');
|
||||
}
|
||||
|
||||
// Richtet erweiterte Interaktionen mit der geladenen Mindmap ein
|
||||
function setupInteractionEnhancements() {
|
||||
console.log('Mindmap geladen - verbesserte Interaktionen werden eingerichtet');
|
||||
|
||||
// Cytoscape-Instanz
|
||||
const cy = window.cy;
|
||||
if (!cy) {
|
||||
console.warn('Cytoscape-Instanz nicht gefunden!');
|
||||
return;
|
||||
// Hervorhebungen zurücksetzen
|
||||
function resetHighlighting() {
|
||||
cy.elements().removeClass('highlighted');
|
||||
}
|
||||
|
||||
// Info-Panel ausblenden
|
||||
function hideNodeInfo() {
|
||||
const infoPanel = document.getElementById('node-info');
|
||||
infoPanel.classList.remove('visible');
|
||||
}
|
||||
|
||||
// Liste der verbundenen Knoten generieren
|
||||
function getConnectedNodesList(node) {
|
||||
const connectedNodes = node.neighborhood('node');
|
||||
if (connectedNodes.length === 0) {
|
||||
return '<li class="text-gray-400">Keine direkten Verbindungen</li>';
|
||||
}
|
||||
|
||||
// Hover-Effekte für Knoten
|
||||
cy.on('mouseover', 'node', function(evt) {
|
||||
const node = evt.target;
|
||||
|
||||
// Nur anwenden, wenn der Knoten nicht ausgewählt ist
|
||||
if (!node.selected()) {
|
||||
node.style({
|
||||
'shadow-opacity': 0.8,
|
||||
'shadow-blur': 'mapData(neuronActivity, 0.3, 1, 10, 20)',
|
||||
'background-opacity': 1
|
||||
});
|
||||
}
|
||||
|
||||
// Verbundene Kanten hervorheben
|
||||
node.connectedEdges().style({
|
||||
'line-opacity': 0.7,
|
||||
'width': 'mapData(strength, 0.2, 0.8, 1.5, 2.5)'
|
||||
});
|
||||
});
|
||||
|
||||
cy.on('mouseout', 'node', function(evt) {
|
||||
const node = evt.target;
|
||||
|
||||
// Nur zurücksetzen, wenn nicht ausgewählt
|
||||
if (!node.selected()) {
|
||||
node.removeStyle();
|
||||
}
|
||||
|
||||
// Verbundene Kanten zurücksetzen, wenn nicht mit ausgewähltem Knoten verbunden
|
||||
node.connectedEdges().forEach(edge => {
|
||||
const sourceSelected = edge.source().selected();
|
||||
const targetSelected = edge.target().selected();
|
||||
|
||||
if (!sourceSelected && !targetSelected) {
|
||||
edge.removeStyle();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Verhindere, dass der Browser die Seite scrollt, wenn über der Mindmap gezoomt wird
|
||||
preventScrollWhileZooming();
|
||||
|
||||
// Tastaturkürzel für Mindmap-Interaktionen
|
||||
setupKeyboardShortcuts(cy);
|
||||
return connectedNodes.map(connectedNode => {
|
||||
const data = connectedNode.data();
|
||||
return `
|
||||
<li class="flex items-center space-x-2">
|
||||
<span class="w-2 h-2 rounded-full" style="background-color: ${getNodeColor(data.category)}"></span>
|
||||
<span>${data.label}</span>
|
||||
</li>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// Initialisiert speziellen Zoom-Handler für sanften Zoom
|
||||
function initializeZoomHandler() {
|
||||
// Auf das Laden der Mindmap warten
|
||||
document.addEventListener('mindmap-loaded', function() {
|
||||
if (!window.cy) return;
|
||||
|
||||
const cy = window.cy;
|
||||
|
||||
// Laufenden AnimationsFrame-Request speichern
|
||||
let zoomAnimationFrame = null;
|
||||
let targetZoom = cy.zoom();
|
||||
let currentZoom = targetZoom;
|
||||
let zoomCenter = { x: 0, y: 0 };
|
||||
let zoomTime = 0;
|
||||
|
||||
// Aktuellen Zoom überwachen und sanft anpassen
|
||||
function updateZoom() {
|
||||
// Sanfter Übergang zum Ziel-Zoom-Level
|
||||
zoomTime += 0.08;
|
||||
|
||||
// Easing-Funktion für flüssigere Bewegung
|
||||
const easedProgress = 1 - Math.pow(1 - Math.min(zoomTime, 1), 3);
|
||||
|
||||
if (currentZoom !== targetZoom) {
|
||||
currentZoom += (targetZoom - currentZoom) * easedProgress;
|
||||
|
||||
// Zoom mit Position anwenden
|
||||
cy.zoom({
|
||||
level: currentZoom,
|
||||
position: zoomCenter
|
||||
});
|
||||
|
||||
// Loop fortsetzen, bis wir sehr nahe am Ziel sind
|
||||
if (Math.abs(currentZoom - targetZoom) > 0.001 && zoomTime < 1) {
|
||||
zoomAnimationFrame = requestAnimationFrame(updateZoom);
|
||||
} else {
|
||||
// Endgültigen Zoom setzen, um sicherzustellen, dass wir genau das Ziel erreichen
|
||||
cy.zoom({
|
||||
level: targetZoom,
|
||||
position: zoomCenter
|
||||
});
|
||||
zoomAnimationFrame = null;
|
||||
}
|
||||
} else {
|
||||
zoomAnimationFrame = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Überschreibe den Standard-mousewheel-Handler von Cytoscape
|
||||
cy.removeAllListeners('mousewheel');
|
||||
cy.on('mousewheel', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const delta = e.originalEvent.deltaY;
|
||||
const mousePosition = e.position || e.cyPosition;
|
||||
|
||||
// Glätten und Limitieren des Zoom-Faktors
|
||||
const factor = delta > 0 ? 0.97 : 1.03;
|
||||
|
||||
// Neues Zoom-Level berechnen mit Begrenzung
|
||||
const maxZoom = cy.maxZoom() || 3;
|
||||
const minZoom = cy.minZoom() || 0.2;
|
||||
targetZoom = Math.min(maxZoom, Math.max(minZoom, cy.zoom() * factor));
|
||||
|
||||
// Position für Zoom setzen
|
||||
zoomCenter = mousePosition;
|
||||
|
||||
// Zeit zurücksetzen
|
||||
zoomTime = 0;
|
||||
|
||||
// Laufende Animation abbrechen und neue starten
|
||||
if (zoomAnimationFrame) {
|
||||
cancelAnimationFrame(zoomAnimationFrame);
|
||||
}
|
||||
|
||||
zoomAnimationFrame = requestAnimationFrame(updateZoom);
|
||||
});
|
||||
|
||||
// Panning auch flüssiger gestalten
|
||||
cy.on('pan', function() {
|
||||
cy.style().selector('node').style({
|
||||
'transition-property': 'none',
|
||||
}).update();
|
||||
});
|
||||
|
||||
cy.on('panend', function() {
|
||||
cy.style().selector('node').style({
|
||||
'transition-property': 'background-color, shadow-color, shadow-opacity, shadow-blur',
|
||||
'transition-duration': '0.3s'
|
||||
}).update();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Verhindert Browser-Scrolling während Zoom in der Mindmap
|
||||
function preventScrollWhileZooming() {
|
||||
const cyContainer = document.getElementById('cy');
|
||||
if (cyContainer) {
|
||||
cyContainer.addEventListener('wheel', function(e) {
|
||||
// Verhindern des Standard-Scrollens während des Zooms
|
||||
e.preventDefault();
|
||||
}, { passive: false });
|
||||
}
|
||||
}
|
||||
|
||||
// Initialisiert Effekte für Seitenleisten-Panels
|
||||
function initializePanelEffects() {
|
||||
// Selektiert alle Panel-Elemente
|
||||
const panels = document.querySelectorAll('.sidebar-panel');
|
||||
|
||||
panels.forEach(panel => {
|
||||
// Hover-Effekt für Panels
|
||||
panel.addEventListener('mouseenter', function() {
|
||||
this.style.transform = 'translateY(-5px)';
|
||||
this.style.boxShadow = '0 10px 25px rgba(0, 0, 0, 0.3), 0 0 15px rgba(139, 92, 246, 0.3)';
|
||||
});
|
||||
|
||||
panel.addEventListener('mouseleave', function() {
|
||||
this.style.transform = '';
|
||||
this.style.boxShadow = '';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Richtet Tastaturkürzel für Mindmap-Interaktionen ein
|
||||
function setupKeyboardShortcuts(cy) {
|
||||
document.addEventListener('keydown', function(e) {
|
||||
// Nur fortfahren, wenn keine Texteingabe im Fokus ist
|
||||
if (document.activeElement.tagName === 'INPUT' ||
|
||||
document.activeElement.tagName === 'TEXTAREA' ||
|
||||
document.activeElement.isContentEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Tastaturkürzel
|
||||
switch(e.key) {
|
||||
case '+':
|
||||
case '=':
|
||||
// Einzoomen (sanfter)
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
e.preventDefault();
|
||||
smoothZoom(cy, 1.15, 400);
|
||||
}
|
||||
break;
|
||||
|
||||
case '-':
|
||||
// Auszoomen (sanfter)
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
e.preventDefault();
|
||||
smoothZoom(cy, 0.85, 400);
|
||||
}
|
||||
break;
|
||||
|
||||
case '0':
|
||||
// Zoom auf Gesamtansicht
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
e.preventDefault();
|
||||
smoothFit(cy);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Escape':
|
||||
// Ausgewählten Knoten abwählen
|
||||
cy.nodes().unselect();
|
||||
resetNodeSelection();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Sanften Zoom mit Animation anwenden
|
||||
function smoothZoom(cy, factor, duration = 400) {
|
||||
const currentZoom = cy.zoom();
|
||||
const targetZoom = currentZoom * factor;
|
||||
|
||||
// Mittelpunkt der Ansicht verwenden
|
||||
const center = {
|
||||
x: cy.width() / 2,
|
||||
y: cy.height() / 2
|
||||
// Farbe basierend auf Kategorie
|
||||
function getNodeColor(category) {
|
||||
const colors = {
|
||||
'Philosophie': '#60a5fa',
|
||||
'Wissenschaft': '#8b5cf6',
|
||||
'Technologie': '#10b981',
|
||||
'Künste': '#f59e0b',
|
||||
'Psychologie': '#ef4444'
|
||||
};
|
||||
|
||||
// Sanftes Zoomen mit Animation
|
||||
cy.animation({
|
||||
zoom: {
|
||||
level: targetZoom,
|
||||
position: center
|
||||
},
|
||||
duration: duration,
|
||||
easing: 'cubic-bezier(0.19, 1, 0.22, 1)'
|
||||
}).play();
|
||||
}
|
||||
|
||||
// Sanftes Anpassen der Ansicht mit Animation
|
||||
function smoothFit(cy, padding = 50) {
|
||||
cy.animation({
|
||||
fit: {
|
||||
eles: cy.elements(),
|
||||
padding: padding
|
||||
},
|
||||
duration: 600,
|
||||
easing: 'cubic-bezier(0.19, 1, 0.22, 1)'
|
||||
}).play();
|
||||
}
|
||||
|
||||
// Richtet den Event-Listener für die Knotenauswahl ein
|
||||
function setupNodeSelectionListener() {
|
||||
document.addEventListener('mindmap-loaded', function() {
|
||||
if (!window.cy) return;
|
||||
|
||||
window.cy.on('tap', 'node', function(evt) {
|
||||
handleNodeSelection(evt.target);
|
||||
});
|
||||
|
||||
window.cy.on('tap', function(evt) {
|
||||
if (evt.target === window.cy) {
|
||||
resetNodeSelection();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Verarbeitet die Knotenauswahl
|
||||
function handleNodeSelection(node) {
|
||||
if (!node) return;
|
||||
|
||||
// Stelle sicher, dass nur dieser Knoten ausgewählt ist
|
||||
window.cy.nodes().unselect();
|
||||
node.select();
|
||||
|
||||
// Speichere ausgewählten Knoten global
|
||||
window.mindmapInstance.selectedNode = node;
|
||||
|
||||
// Zeige Informationen in der Sidebar an
|
||||
showNodeDescriptionSidebar(node);
|
||||
|
||||
// Informationspanel aktualisieren
|
||||
updateNodeInfoPanel(node);
|
||||
|
||||
// Node zentrieren mit Animation
|
||||
window.cy.animation({
|
||||
center: {
|
||||
eles: node
|
||||
},
|
||||
zoom: 1.3,
|
||||
duration: 800,
|
||||
easing: 'cubic-bezier(0.19, 1, 0.22, 1)'
|
||||
}).play();
|
||||
|
||||
// Hervorhebung für den ausgewählten Knoten mit Leuchteffekt
|
||||
node.style({
|
||||
'background-opacity': 1,
|
||||
'shadow-opacity': 1,
|
||||
'shadow-blur': 20,
|
||||
'shadow-color': node.data('color')
|
||||
});
|
||||
|
||||
// Verbindungen hervorheben
|
||||
node.connectedEdges().style({
|
||||
'line-color': '#a78bfa',
|
||||
'line-opacity': 0.7,
|
||||
'width': 2,
|
||||
'line-style': 'solid'
|
||||
});
|
||||
}
|
||||
|
||||
// Setzt die Knotenauswahl zurück
|
||||
function resetNodeSelection() {
|
||||
const nodeInfoPanel = document.getElementById('node-info-panel');
|
||||
if (nodeInfoPanel) {
|
||||
nodeInfoPanel.classList.remove('visible');
|
||||
}
|
||||
|
||||
showDefaultSidebar();
|
||||
|
||||
// Globale Referenz zurücksetzen
|
||||
if (window.mindmapInstance) {
|
||||
window.mindmapInstance.selectedNode = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Zeigt das Informationspanel für einen Knoten an
|
||||
function updateNodeInfoPanel(node) {
|
||||
const nodeInfoPanel = document.getElementById('node-info-panel');
|
||||
const nodeDescription = document.getElementById('node-description');
|
||||
const connectedNodes = document.getElementById('connected-nodes');
|
||||
const panelTitle = nodeInfoPanel ? nodeInfoPanel.querySelector('.info-panel-title') : null;
|
||||
|
||||
if (!nodeInfoPanel || !nodeDescription || !connectedNodes || !panelTitle) return;
|
||||
|
||||
// Titel und Beschreibung aktualisieren
|
||||
panelTitle.textContent = node.data('name');
|
||||
nodeDescription.textContent = node.data('description') || 'Keine Beschreibung verfügbar.';
|
||||
|
||||
// Verbundene Knoten auflisten
|
||||
connectedNodes.innerHTML = '';
|
||||
|
||||
// Verbundene Knoten ermitteln
|
||||
const connectedNodesList = [];
|
||||
node.connectedEdges().forEach(edge => {
|
||||
let connectedNode;
|
||||
if (edge.source().id() === node.id()) {
|
||||
connectedNode = edge.target();
|
||||
} else {
|
||||
connectedNode = edge.source();
|
||||
}
|
||||
|
||||
if (!connectedNodesList.some(n => n.id() === connectedNode.id())) {
|
||||
connectedNodesList.push(connectedNode);
|
||||
}
|
||||
});
|
||||
|
||||
// Verbundene Knoten im Panel anzeigen
|
||||
if (connectedNodesList.length > 0) {
|
||||
connectedNodesList.forEach(connectedNode => {
|
||||
const nodeLink = document.createElement('span');
|
||||
nodeLink.className = 'inline-block px-2 py-1 text-xs rounded-md m-1 cursor-pointer opacity-80 hover:opacity-100 transition-opacity';
|
||||
nodeLink.style.backgroundColor = connectedNode.data('color');
|
||||
nodeLink.textContent = connectedNode.data('name');
|
||||
|
||||
// Beim Klick auf den verbundenen Knoten zu diesem navigieren
|
||||
nodeLink.addEventListener('click', function() {
|
||||
handleNodeSelection(connectedNode);
|
||||
});
|
||||
|
||||
connectedNodes.appendChild(nodeLink);
|
||||
});
|
||||
} else {
|
||||
connectedNodes.innerHTML = '<span class="text-sm italic">Keine verbundenen Knoten</span>';
|
||||
}
|
||||
|
||||
// Panel anzeigen mit Animation
|
||||
nodeInfoPanel.classList.add('visible');
|
||||
}
|
||||
|
||||
// Zeigt die Standard-Seitenleiste an
|
||||
function showDefaultSidebar() {
|
||||
// Alle Seitenleisten-Panels anzeigen/ausblenden
|
||||
const allPanels = document.querySelectorAll('[data-sidebar]');
|
||||
allPanels.forEach(panel => {
|
||||
if (panel.getAttribute('data-sidebar') === 'node-description') {
|
||||
panel.classList.add('hidden');
|
||||
} else {
|
||||
panel.classList.remove('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Zeigt die Knotenbeschreibung in der Seitenleiste an
|
||||
function showNodeDescriptionSidebar(node) {
|
||||
// Das Knotenbeschreibungs-Panel finden
|
||||
const nodeDescPanel = document.querySelector('[data-sidebar="node-description"]');
|
||||
|
||||
if (nodeDescPanel) {
|
||||
// Panel sichtbar machen
|
||||
nodeDescPanel.classList.remove('hidden');
|
||||
|
||||
// Titel und Beschreibung aktualisieren
|
||||
const nodeTitleElement = nodeDescPanel.querySelector('[data-node-title]');
|
||||
const nodeDescElement = nodeDescPanel.querySelector('[data-node-description]');
|
||||
|
||||
if (nodeTitleElement) {
|
||||
nodeTitleElement.textContent = node.data('name');
|
||||
}
|
||||
|
||||
if (nodeDescElement) {
|
||||
// Beschreibung mit HTML-Formatierung anzeigen
|
||||
nodeDescElement.innerHTML = generateNodeDescription(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generiert eine formatierte HTML-Beschreibung für einen Knoten
|
||||
function generateNodeDescription(node) {
|
||||
let description = node.data('description') || 'Keine Beschreibung verfügbar.';
|
||||
|
||||
// Einfache HTML-Formatierung (kann erweitert werden)
|
||||
description = description
|
||||
.replace(/\n/g, '<br>')
|
||||
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
||||
.replace(/\*(.*?)\*/g, '<em>$1</em>')
|
||||
.replace(/`(.*?)`/g, '<code>$1</code>');
|
||||
|
||||
return description;
|
||||
return colors[category] || '#60a5fa';
|
||||
}
|
||||
@@ -17,10 +17,200 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// Auf das Laden der Mindmap warten
|
||||
document.addEventListener('mindmap-loaded', function() {
|
||||
console.log('Mindmap geladen, wende neuronales Netzwerk-Design an...');
|
||||
enhanceMindmap();
|
||||
updateMindmap();
|
||||
});
|
||||
});
|
||||
|
||||
// Mindmap-Daten
|
||||
const mindmapData = {
|
||||
nodes: [
|
||||
// Philosophie
|
||||
{
|
||||
id: 'philosophy',
|
||||
label: 'Philosophie',
|
||||
category: 'Philosophie',
|
||||
description: 'Die Lehre vom Denken und der Erkenntnis'
|
||||
},
|
||||
{
|
||||
id: 'epistemology',
|
||||
label: 'Erkenntnistheorie',
|
||||
category: 'Philosophie',
|
||||
description: 'Untersuchung der Natur und Grenzen menschlicher Erkenntnis'
|
||||
},
|
||||
{
|
||||
id: 'ethics',
|
||||
label: 'Ethik',
|
||||
category: 'Philosophie',
|
||||
description: 'Lehre vom moralisch richtigen Handeln'
|
||||
},
|
||||
|
||||
// Wissenschaft
|
||||
{
|
||||
id: 'science',
|
||||
label: 'Wissenschaft',
|
||||
category: 'Wissenschaft',
|
||||
description: 'Systematische Erforschung der Natur und Gesellschaft'
|
||||
},
|
||||
{
|
||||
id: 'physics',
|
||||
label: 'Physik',
|
||||
category: 'Wissenschaft',
|
||||
description: 'Lehre von der Materie und ihren Wechselwirkungen'
|
||||
},
|
||||
{
|
||||
id: 'biology',
|
||||
label: 'Biologie',
|
||||
category: 'Wissenschaft',
|
||||
description: 'Lehre von den Lebewesen und ihren Lebensprozessen'
|
||||
},
|
||||
|
||||
// Technologie
|
||||
{
|
||||
id: 'technology',
|
||||
label: 'Technologie',
|
||||
category: 'Technologie',
|
||||
description: 'Anwendung wissenschaftlicher Erkenntnisse'
|
||||
},
|
||||
{
|
||||
id: 'ai',
|
||||
label: 'Künstliche Intelligenz',
|
||||
category: 'Technologie',
|
||||
description: 'Maschinelles Lernen und intelligente Systeme'
|
||||
},
|
||||
{
|
||||
id: 'robotics',
|
||||
label: 'Robotik',
|
||||
category: 'Technologie',
|
||||
description: 'Entwicklung und Steuerung von Robotern'
|
||||
},
|
||||
|
||||
// Künste
|
||||
{
|
||||
id: 'arts',
|
||||
label: 'Künste',
|
||||
category: 'Künste',
|
||||
description: 'Kreativer Ausdruck und künstlerische Gestaltung'
|
||||
},
|
||||
{
|
||||
id: 'music',
|
||||
label: 'Musik',
|
||||
category: 'Künste',
|
||||
description: 'Kunst der Töne und Klänge'
|
||||
},
|
||||
{
|
||||
id: 'literature',
|
||||
label: 'Literatur',
|
||||
category: 'Künste',
|
||||
description: 'Schriftliche Werke und Dichtkunst'
|
||||
},
|
||||
|
||||
// Psychologie
|
||||
{
|
||||
id: 'psychology',
|
||||
label: 'Psychologie',
|
||||
category: 'Psychologie',
|
||||
description: 'Wissenschaft vom Erleben und Verhalten'
|
||||
},
|
||||
{
|
||||
id: 'cognitive',
|
||||
label: 'Kognitive Psychologie',
|
||||
category: 'Psychologie',
|
||||
description: 'Studium mentaler Prozesse'
|
||||
},
|
||||
{
|
||||
id: 'behavioral',
|
||||
label: 'Verhaltenspsychologie',
|
||||
category: 'Psychologie',
|
||||
description: 'Analyse von Verhaltensmustern'
|
||||
}
|
||||
],
|
||||
|
||||
edges: [
|
||||
// Philosophie-Verbindungen
|
||||
{ source: 'philosophy', target: 'epistemology', label: 'umfasst' },
|
||||
{ source: 'philosophy', target: 'ethics', label: 'umfasst' },
|
||||
{ source: 'philosophy', target: 'science', label: 'beeinflusst' },
|
||||
|
||||
// Wissenschaft-Verbindungen
|
||||
{ source: 'science', target: 'physics', label: 'umfasst' },
|
||||
{ source: 'science', target: 'biology', label: 'umfasst' },
|
||||
{ source: 'science', target: 'technology', label: 'fördert' },
|
||||
|
||||
// Technologie-Verbindungen
|
||||
{ source: 'technology', target: 'ai', label: 'umfasst' },
|
||||
{ source: 'technology', target: 'robotics', label: 'umfasst' },
|
||||
{ source: 'ai', target: 'cognitive', label: 'beeinflusst' },
|
||||
|
||||
// Künste-Verbindungen
|
||||
{ source: 'arts', target: 'music', label: 'umfasst' },
|
||||
{ source: 'arts', target: 'literature', label: 'umfasst' },
|
||||
{ source: 'arts', target: 'psychology', label: 'beeinflusst' },
|
||||
|
||||
// Psychologie-Verbindungen
|
||||
{ source: 'psychology', target: 'cognitive', label: 'umfasst' },
|
||||
{ source: 'psychology', target: 'behavioral', label: 'umfasst' },
|
||||
{ source: 'psychology', target: 'ethics', label: 'beeinflusst' },
|
||||
|
||||
// Interdisziplinäre Verbindungen
|
||||
{ source: 'cognitive', target: 'ai', label: 'inspiriert' },
|
||||
{ source: 'physics', target: 'technology', label: 'ermöglicht' },
|
||||
{ source: 'literature', target: 'philosophy', label: 'reflektiert' }
|
||||
]
|
||||
};
|
||||
|
||||
// Mindmap aktualisieren
|
||||
function updateMindmap() {
|
||||
if (!cy) return;
|
||||
|
||||
// Bestehende Elemente entfernen
|
||||
cy.elements().remove();
|
||||
|
||||
// Neue Knoten hinzufügen
|
||||
mindmapData.nodes.forEach(node => {
|
||||
cy.add({
|
||||
group: 'nodes',
|
||||
data: {
|
||||
id: node.id,
|
||||
label: node.label,
|
||||
category: node.category,
|
||||
description: node.description
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Neue Kanten hinzufügen
|
||||
mindmapData.edges.forEach(edge => {
|
||||
cy.add({
|
||||
group: 'edges',
|
||||
data: {
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
label: edge.label
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Layout anwenden
|
||||
cy.layout({
|
||||
name: 'cose',
|
||||
idealEdgeLength: 100,
|
||||
nodeOverlap: 20,
|
||||
refresh: 20,
|
||||
fit: true,
|
||||
padding: 30,
|
||||
randomize: false,
|
||||
componentSpacing: 100,
|
||||
nodeRepulsion: 400000,
|
||||
edgeElasticity: 100,
|
||||
nestingFactor: 5,
|
||||
gravity: 80,
|
||||
numIter: 1000,
|
||||
initialTemp: 200,
|
||||
coolingFactor: 0.95,
|
||||
minTemp: 1.0
|
||||
}).run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Erweitert die Mindmap mit dem neuronalen Netzwerk-Design
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user