-
Mode:
-
--
+
+
+
diff --git a/script.js b/script.js
index 945b115..c995689 100644
--- a/script.js
+++ b/script.js
@@ -1,9 +1,5 @@
class VisionAuthSystem {
constructor() {
- this.video = document.getElementById('video');
- this.canvas = document.getElementById('canvas');
- this.ctx = this.canvas.getContext('2d');
-
// UI Elements
this.modePanel = document.getElementById('modePanel');
this.featureSelection = document.getElementById('featureSelection');
@@ -12,36 +8,48 @@ class VisionAuthSystem {
this.controlPanel = document.getElementById('controlPanel');
this.loadingOverlay = document.getElementById('loadingOverlay');
- // Analysis Elements
+ // Analysis elements
this.modeValue = document.getElementById('modeValue');
this.featureValue = document.getElementById('featureValue');
this.resultValue = document.getElementById('resultValue');
this.confidenceValue = document.getElementById('confidenceValue');
this.statusIndicator = document.getElementById('statusIndicator');
- // Control Buttons
+ // Control elements
this.stopBtn = document.getElementById('stopBtn');
this.fullscreenBtn = document.getElementById('fullscreenBtn');
this.changeModeBtn = document.getElementById('changeModeBtn');
+ this.exportBtn = document.getElementById('exportBtn');
+ this.settingsBtn = document.getElementById('settingsBtn');
- // State
- this.isRunning = false;
+ // Header elements
+ this.docsBtn = document.getElementById('docsBtn');
+ this.supportBtn = document.getElementById('supportBtn');
+
+ // Video elements
+ this.video = document.getElementById('video');
+ this.canvas = document.getElementById('canvas');
+ this.ctx = this.canvas.getContext('2d');
+
+ // State variables
this.selectedMode = null;
this.selectedFeature = null;
- this.stream = null;
- this.faceMesh = null;
- this.camera = null;
+ this.isAnalyzing = false;
this.analysisInterval = null;
this.lastLandmarks = null;
+ // MediaPipe objects
+ this.faceMesh = null;
+ this.camera = null;
+
this.setupEventListeners();
- this.resizeCanvas();
+ this.initializeCanvas();
}
-
+
setupEventListeners() {
// Mode selection
document.querySelectorAll('.mode-option').forEach(option => {
- option.addEventListener('click', () => this.selectMode(option));
+ option.addEventListener('click', () => this.selectMode(option.dataset.mode));
});
// Start analysis
@@ -51,616 +59,426 @@ class VisionAuthSystem {
this.stopBtn.addEventListener('click', () => this.stopAnalysis());
this.fullscreenBtn.addEventListener('click', () => this.toggleFullscreen());
this.changeModeBtn.addEventListener('click', () => this.showModeSelection());
+ this.exportBtn.addEventListener('click', () => this.exportData());
+ this.settingsBtn.addEventListener('click', () => this.showSettings());
- // Window resize
- window.addEventListener('resize', () => this.resizeCanvas());
+ // Header buttons
+ this.docsBtn.addEventListener('click', () => this.showDocumentation());
+ this.supportBtn.addEventListener('click', () => this.showSupport());
+
+ // Feature selection
+ document.querySelectorAll('input[name="analysisType"]').forEach(radio => {
+ radio.addEventListener('change', (e) => {
+ this.selectedFeature = e.target.value;
+ });
+ });
}
-
- selectMode(option) {
+
+ initializeCanvas() {
+ this.canvas.width = window.innerWidth;
+ this.canvas.height = window.innerHeight;
+
+ window.addEventListener('resize', () => {
+ this.canvas.width = window.innerWidth;
+ this.canvas.height = window.innerHeight;
+ });
+ }
+
+ selectMode(mode) {
// Remove previous selection
- document.querySelectorAll('.mode-option').forEach(opt => {
- opt.classList.remove('selected');
+ document.querySelectorAll('.mode-option').forEach(option => {
+ option.classList.remove('selected');
});
- // Select new mode
- option.classList.add('selected');
- this.selectedMode = option.dataset.mode;
+ // Add selection to clicked option
+ document.querySelector(`[data-mode="${mode}"]`).classList.add('selected');
- // Show feature selection
+ this.selectedMode = mode;
this.featureSelection.style.display = 'block';
+ this.startAnalysisBtn.textContent = 'Initialize Analysis Engine';
- // Update button text based on mode
- this.startAnalysisBtn.textContent = this.selectedMode === 'performance'
- ? 'Start Fast Analysis'
- : 'Start Detailed Analysis';
+ // Add professional animation
+ this.featureSelection.style.animation = 'slideIn 0.5s ease-out';
}
-
+
startAnalysis() {
- if (!this.selectedMode) {
- alert('Please select a mode first!');
- return;
- }
-
- // Get selected feature
- const selectedFeature = document.querySelector('input[name="analysisType"]:checked');
- if (!selectedFeature) {
- alert('Please select an analysis feature!');
+ if (!this.selectedMode || !this.selectedFeature) {
+ this.showNotification('Please select both mode and feature', 'error');
return;
}
- this.selectedFeature = selectedFeature.value;
+ this.showLoading();
this.initializeFaceTracking();
}
-
+
async initializeFaceTracking() {
try {
- this.showLoading(true);
+ // Configure MediaPipe Face Mesh based on selected mode
+ const config = this.selectedMode === 'performance' ? {
+ maxNumFaces: 1,
+ refineLandmarks: false,
+ minDetectionConfidence: 0.3,
+ minTrackingConfidence: 0.2
+ } : {
+ maxNumFaces: 1,
+ refineLandmarks: true,
+ minDetectionConfidence: 0.5,
+ minTrackingConfidence: 0.3
+ };
- // Initialize MediaPipe Face Mesh
this.faceMesh = new FaceMesh({
locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/@mediapipe/face_mesh/${file}`;
}
});
-
- // Configure face mesh based on mode
- const options = {
- maxNumFaces: 1,
- refineLandmarks: this.selectedMode === 'accuracy',
- minDetectionConfidence: this.selectedMode === 'performance' ? 0.3 : 0.5,
- minTrackingConfidence: this.selectedMode === 'performance' ? 0.2 : 0.3
- };
-
- this.faceMesh.setOptions(options);
- this.faceMesh.onResults((results) => this.onFaceMeshResults(results));
-
+
+ this.faceMesh.setOptions(config);
+ this.faceMesh.onResults = (results) => this.onFaceMeshResults(results);
+
// Initialize camera
this.camera = new Camera(this.video, {
onFrame: async () => {
- if (this.isRunning) {
- await this.faceMesh.send({ image: this.video });
- }
+ await this.faceMesh.send({image: this.video});
},
width: this.selectedMode === 'performance' ? 640 : 1280,
height: this.selectedMode === 'performance' ? 480 : 720
});
-
+
await this.camera.start();
-
- // Hide mode panel and show analysis
+
+ // Hide loading and show analysis UI
+ this.hideLoading();
this.modePanel.style.display = 'none';
this.analysisBox.style.display = 'block';
- this.controlPanel.style.display = 'flex';
-
- // Start analysis
- this.isRunning = true;
+ this.controlPanel.style.display = 'block';
+
+ this.isAnalyzing = true;
this.updateAnalysisDisplay();
-
+
} catch (error) {
- console.error('Error starting face tracking:', error);
- alert('Error starting camera. Please make sure you have a webcam and grant camera permissions.');
- } finally {
- this.showLoading(false);
+ console.error('Failed to initialize face tracking:', error);
+ this.hideLoading();
+ this.showNotification('Failed to initialize camera. Please check permissions.', 'error');
}
}
-
+
onFaceMeshResults(results) {
- this.clearCanvas();
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
if (results.multiFaceLandmarks && results.multiFaceLandmarks.length > 0) {
const landmarks = results.multiFaceLandmarks[0];
this.lastLandmarks = landmarks;
- // Draw wireframe based on mode
+ // Draw wireframes based on mode
if (this.selectedMode === 'performance') {
this.drawSimpleWireframe(landmarks);
} else {
this.drawDetailedWireframe(landmarks);
}
- this.updateStatus('detecting');
+ this.statusIndicator.className = 'status-indicator detecting';
} else {
- this.updateStatus('inactive');
this.lastLandmarks = null;
+ this.statusIndicator.className = 'status-indicator';
}
}
-
+
drawSimpleWireframe(landmarks) {
- if (!landmarks) return;
-
- // Get face bounding box
const bounds = this.getFaceBounds(landmarks);
// Draw face boundary
- this.ctx.strokeStyle = '#00ff88';
+ this.ctx.strokeStyle = '#667eea';
this.ctx.lineWidth = 2;
this.ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);
-
- // Draw key landmark points
- this.ctx.fillStyle = '#00ff88';
- const keyPoints = [10, 33, 133, 362, 263, 61]; // Eyes, nose, mouth corners
- keyPoints.forEach(pointIndex => {
- if (landmarks[pointIndex]) {
- const point = landmarks[pointIndex];
- const x = point.x * this.canvas.width;
- const y = point.y * this.canvas.height;
-
- this.ctx.beginPath();
- this.ctx.arc(x, y, 4, 0, 2 * Math.PI);
- this.ctx.fill();
- }
- });
+ // Draw key landmarks
+ this.drawKeyLandmarks(landmarks, 10);
}
-
+
drawDetailedWireframe(landmarks) {
- if (!landmarks) return;
-
- // Get face bounding box
const bounds = this.getFaceBounds(landmarks);
- // Draw detailed face boundary
- this.ctx.strokeStyle = '#00ff88';
- this.ctx.lineWidth = 3;
- this.ctx.setLineDash([5, 5]);
- this.ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);
- this.ctx.setLineDash([]);
-
- // Draw comprehensive facial mesh
+ // Draw comprehensive face mesh
this.drawComprehensiveFaceMesh(landmarks);
- // Draw facial grid
- this.drawFacialGrid(landmarks, bounds);
-
- // Draw eye tracking
- this.drawEyeTracking(landmarks);
-
- // Draw mouth structure
- this.drawMouthStructure(landmarks);
-
- // Draw nose bridge
- this.drawNoseBridge(landmarks);
+ // Draw face boundary
+ this.ctx.strokeStyle = '#667eea';
+ this.ctx.lineWidth = 2;
+ this.ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);
- // Draw key landmarks
- this.drawKeyLandmarks(landmarks);
+ // Draw detailed landmarks
+ this.drawKeyLandmarks(landmarks, 20);
}
-
+
drawComprehensiveFaceMesh(landmarks) {
- this.ctx.strokeStyle = 'rgba(0, 255, 136, 0.6)';
+ this.ctx.strokeStyle = '#667eea';
this.ctx.lineWidth = 1;
- this.ctx.fillStyle = 'rgba(0, 255, 136, 0.8)';
-
- // Draw comprehensive face mesh with many more points
+ this.ctx.globalAlpha = 0.6;
+
+ // Face contour
this.drawFaceContour(landmarks);
+
+ // Forehead mesh
this.drawForeheadMesh(landmarks);
+
+ // Cheek mesh
this.drawCheekMesh(landmarks);
+
+ // Chin mesh
this.drawChinMesh(landmarks);
+
+ // Temple mesh
this.drawTempleMesh(landmarks);
+
+ // Jawline mesh
this.drawJawlineMesh(landmarks);
+
+ // Neck mesh
this.drawNeckMesh(landmarks);
+
+ this.ctx.globalAlpha = 1;
}
-
+
drawFaceContour(landmarks) {
- // Face contour points (outline of the face)
- const contourPoints = [10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109, 10];
-
- this.ctx.beginPath();
- contourPoints.forEach((pointIndex, index) => {
- if (landmarks[pointIndex]) {
- const point = landmarks[pointIndex];
- const x = point.x * this.canvas.width;
- const y = point.y * this.canvas.height;
-
- if (index === 0) {
- this.ctx.moveTo(x, y);
- } else {
- this.ctx.lineTo(x, y);
- }
- }
- });
- this.ctx.closePath();
- this.ctx.stroke();
+ const contourPoints = [10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109];
+ this.drawMeshSection(landmarks, contourPoints);
}
-
+
drawForeheadMesh(landmarks) {
- // Forehead mesh points
- const foreheadPoints = [103, 67, 109, 10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54];
-
- // Draw forehead grid
- for (let i = 0; i < foreheadPoints.length - 1; i++) {
- if (landmarks[foreheadPoints[i]] && landmarks[foreheadPoints[i + 1]]) {
- const point1 = landmarks[foreheadPoints[i]];
- const point2 = landmarks[foreheadPoints[i + 1]];
-
- const x1 = point1.x * this.canvas.width;
- const y1 = point1.y * this.canvas.height;
- const x2 = point2.x * this.canvas.width;
- const y2 = point2.y * this.canvas.height;
-
- this.ctx.beginPath();
- this.ctx.moveTo(x1, y1);
- this.ctx.lineTo(x2, y2);
- this.ctx.stroke();
- }
- }
+ const foreheadPoints = [10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109];
+ this.drawMeshSection(landmarks, foreheadPoints);
}
-
+
drawCheekMesh(landmarks) {
- // Cheek mesh points
- const leftCheekPoints = [123, 50, 36, 137, 177, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109];
- const rightCheekPoints = [352, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172];
-
- // Draw left cheek
- this.drawMeshSection(landmarks, leftCheekPoints);
- // Draw right cheek
- this.drawMeshSection(landmarks, rightCheekPoints);
+ const cheekPoints = [123, 50, 36, 137, 0, 11, 12, 13, 14, 15, 16, 17, 18, 200, 199, 175];
+ this.drawMeshSection(landmarks, cheekPoints);
}
-
+
drawChinMesh(landmarks) {
- // Chin mesh points
- const chinPoints = [17, 84, 18, 313, 405, 320, 307, 375, 321, 308, 324, 318, 78, 95, 88, 178, 87, 14, 317, 402, 318, 324, 308];
-
+ const chinPoints = [17, 84, 18, 313, 405, 320, 307, 375, 321, 308, 324, 318];
this.drawMeshSection(landmarks, chinPoints);
}
-
+
drawTempleMesh(landmarks) {
- // Temple mesh points
- const leftTemplePoints = [10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109];
- const rightTemplePoints = [338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109, 10];
-
- this.drawMeshSection(landmarks, leftTemplePoints);
- this.drawMeshSection(landmarks, rightTemplePoints);
+ const templePoints = [103, 67, 109, 10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54];
+ this.drawMeshSection(landmarks, templePoints);
}
-
+
drawJawlineMesh(landmarks) {
- // Jawline mesh points
- const jawlinePoints = [17, 84, 18, 313, 405, 320, 307, 375, 321, 308, 324, 318, 78, 95, 88, 178, 87, 14, 317, 402, 318, 324, 308, 17];
-
+ const jawlinePoints = [132, 93, 234, 127, 162, 21, 54, 103, 67, 109, 10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58];
this.drawMeshSection(landmarks, jawlinePoints);
}
-
+
drawNeckMesh(landmarks) {
- // Neck mesh points
- const neckPoints = [17, 84, 18, 313, 405, 320, 307, 375, 321, 308, 324, 318, 78, 95, 88, 178, 87, 14, 317, 402, 318, 324, 308];
-
+ const neckPoints = [132, 93, 234, 127, 162, 21, 54, 103, 67, 109, 10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58];
this.drawMeshSection(landmarks, neckPoints);
}
-
+
drawMeshSection(landmarks, pointIndices) {
- // Draw mesh connections for a section
- for (let i = 0; i < pointIndices.length - 1; i++) {
- if (landmarks[pointIndices[i]] && landmarks[pointIndices[i + 1]]) {
- const point1 = landmarks[pointIndices[i]];
- const point2 = landmarks[pointIndices[i + 1]];
-
- const x1 = point1.x * this.canvas.width;
- const y1 = point1.y * this.canvas.height;
- const x2 = point2.x * this.canvas.width;
- const y2 = point2.y * this.canvas.height;
-
- this.ctx.beginPath();
- this.ctx.moveTo(x1, y1);
- this.ctx.lineTo(x2, y2);
- this.ctx.stroke();
- }
- }
- }
-
- drawKeyLandmarks(landmarks) {
- this.ctx.fillStyle = '#00ff88';
- this.ctx.strokeStyle = '#00ff88';
- this.ctx.lineWidth = 1;
-
- // Draw comprehensive facial landmarks (many more points)
- const comprehensiveLandmarks = [
- // Face contour
- 10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109,
- // Eyes
- 33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246, 362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398,
- // Nose
- 168, 6, 197, 195, 5, 4, 1, 19, 94, 2, 164, 0, 11, 12, 14, 15, 16, 18, 200, 199, 175,
- // Mouth
- 61, 84, 17, 314, 405, 320, 307, 375, 321, 308, 324, 318, 78, 95, 88, 178, 87, 14, 317, 402, 318, 324, 308,
- // Eyebrows
- 70, 63, 105, 66, 107, 55, 65, 52, 53, 46, 336, 296, 334, 293, 300, 276, 283, 282, 295, 285,
- // Cheeks
- 123, 50, 36, 137, 177, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109, 352, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172,
- // Forehead
- 103, 67, 109, 10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54,
- // Chin and jaw
- 17, 84, 18, 313, 405, 320, 307, 375, 321, 308, 324, 318, 78, 95, 88, 178, 87, 14, 317, 402, 318, 324, 308,
- // Additional facial points
- 291, 17, 84, 300, 368, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109
- ];
-
- comprehensiveLandmarks.forEach(pointIndex => {
- if (landmarks[pointIndex]) {
- const point = landmarks[pointIndex];
- const x = point.x * this.canvas.width;
- const y = point.y * this.canvas.height;
-
- // Draw point
- this.ctx.beginPath();
- this.ctx.arc(x, y, 2, 0, 2 * Math.PI);
- this.ctx.fill();
-
- // Draw small circle around point
- this.ctx.beginPath();
- this.ctx.arc(x, y, 4, 0, 2 * Math.PI);
- this.ctx.stroke();
- }
- });
- }
-
- getFaceBounds(landmarks) {
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
-
- // Use key facial landmarks to determine bounds
- const keyPoints = [10, 33, 133, 362, 263, 61, 291, 17, 84, 300, 368, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109];
-
- keyPoints.forEach(pointIndex => {
- if (landmarks[pointIndex]) {
- const point = landmarks[pointIndex];
- const x = point.x * this.canvas.width;
- const y = point.y * this.canvas.height;
-
- minX = Math.min(minX, x);
- minY = Math.min(minY, y);
- maxX = Math.max(maxX, x);
- maxY = Math.max(maxY, y);
- }
- });
-
- return {
- x: minX,
- y: minY,
- width: maxX - minX,
- height: maxY - minY
- };
- }
-
- drawFacialGrid(landmarks, bounds) {
- this.ctx.strokeStyle = 'rgba(0, 255, 136, 0.8)';
- this.ctx.lineWidth = 1;
-
- // Draw horizontal lines across face
- for (let i = 0; i < 4; i++) {
- const y = bounds.y + (bounds.height / 3) * i;
- this.ctx.beginPath();
- this.ctx.moveTo(bounds.x, y);
- this.ctx.lineTo(bounds.x + bounds.width, y);
- this.ctx.stroke();
- }
-
- // Draw vertical lines
- for (let i = 0; i < 3; i++) {
- const x = bounds.x + (bounds.width / 2) * i;
- this.ctx.beginPath();
- this.ctx.moveTo(x, bounds.y);
- this.ctx.lineTo(x, bounds.y + bounds.height);
- this.ctx.stroke();
- }
- }
-
- drawEyeTracking(landmarks) {
- this.ctx.strokeStyle = 'rgba(0, 255, 136, 0.9)';
- this.ctx.lineWidth = 2;
-
- // Left eye (landmarks 33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246)
- const leftEyePoints = [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246];
- this.drawEyeOutline(landmarks, leftEyePoints);
-
- // Right eye (landmarks 362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398)
- const rightEyePoints = [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398];
- this.drawEyeOutline(landmarks, rightEyePoints);
- }
-
- drawEyeOutline(landmarks, eyePoints) {
this.ctx.beginPath();
- eyePoints.forEach((pointIndex, index) => {
- if (landmarks[pointIndex]) {
- const point = landmarks[pointIndex];
+ for (let i = 0; i < pointIndices.length; i++) {
+ const point = landmarks[pointIndices[i]];
+ if (point) {
const x = point.x * this.canvas.width;
const y = point.y * this.canvas.height;
-
- if (index === 0) {
+ if (i === 0) {
this.ctx.moveTo(x, y);
} else {
this.ctx.lineTo(x, y);
}
}
- });
- this.ctx.closePath();
+ }
this.ctx.stroke();
}
-
- drawMouthStructure(landmarks) {
- this.ctx.strokeStyle = 'rgba(0, 255, 136, 0.9)';
- this.ctx.lineWidth = 2;
-
- // Mouth outline (landmarks 61, 84, 17, 314, 405, 320, 307, 375, 321, 308, 324, 318, 78, 95, 88, 178, 87, 14, 317, 402, 318, 324, 308)
- const mouthPoints = [61, 84, 17, 314, 405, 320, 307, 375, 321, 308, 324, 318, 78, 95, 88, 178, 87, 14, 317, 402, 318, 324, 308];
+
+ getFaceBounds(landmarks) {
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
- this.ctx.beginPath();
- mouthPoints.forEach((pointIndex, index) => {
- if (landmarks[pointIndex]) {
- const point = landmarks[pointIndex];
- const x = point.x * this.canvas.width;
- const y = point.y * this.canvas.height;
-
- if (index === 0) {
- this.ctx.moveTo(x, y);
- } else {
- this.ctx.lineTo(x, y);
- }
- }
+ landmarks.forEach(point => {
+ const x = point.x * this.canvas.width;
+ const y = point.y * this.canvas.height;
+ minX = Math.min(minX, x);
+ minY = Math.min(minY, y);
+ maxX = Math.max(maxX, x);
+ maxY = Math.max(maxY, y);
});
- this.ctx.closePath();
- this.ctx.stroke();
+
+ return {
+ x: minX - 20,
+ y: minY - 20,
+ width: maxX - minX + 40,
+ height: maxY - minY + 40
+ };
}
-
- drawNoseBridge(landmarks) {
- this.ctx.strokeStyle = 'rgba(0, 255, 136, 0.7)';
- this.ctx.lineWidth = 1;
-
- // Nose bridge (landmarks 168, 6, 197, 195, 5, 4, 1, 19, 94, 2, 164, 0, 11, 12, 14, 15, 16, 18, 200, 199, 175)
- const nosePoints = [168, 6, 197, 195, 5, 4, 1, 19, 94, 2, 164, 0, 11, 12, 14, 15, 16, 18, 200, 199, 175];
+
+ drawKeyLandmarks(landmarks, count = 10) {
+ this.ctx.fillStyle = '#667eea';
+ this.ctx.globalAlpha = 0.8;
- this.ctx.beginPath();
- nosePoints.forEach((pointIndex, index) => {
- if (landmarks[pointIndex]) {
- const point = landmarks[pointIndex];
+ // Draw key facial landmarks
+ const keyPoints = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100];
+
+ keyPoints.forEach(index => {
+ if (landmarks[index]) {
+ const point = landmarks[index];
const x = point.x * this.canvas.width;
const y = point.y * this.canvas.height;
- if (index === 0) {
- this.ctx.moveTo(x, y);
- } else {
- this.ctx.lineTo(x, y);
- }
+ this.ctx.beginPath();
+ this.ctx.arc(x, y, 2, 0, 2 * Math.PI);
+ this.ctx.fill();
}
});
- this.ctx.stroke();
+
+ this.ctx.globalAlpha = 1;
}
-
+
updateAnalysisDisplay() {
- if (!this.isRunning) return;
-
// Update mode and feature display
- this.modeValue.textContent = this.selectedMode === 'performance' ? 'High Performance' : 'High Accuracy';
- this.featureValue.textContent = this.selectedFeature === 'emotions' ? 'Emotion Detection' : 'Age Estimation';
-
- // Generate analysis result based on feature
+ this.modeValue.textContent = this.selectedMode === 'performance' ? 'Performance Mode' : 'Precision Mode';
+ this.featureValue.textContent = this.selectedFeature === 'emotions' ? 'Emotion Recognition' : 'Age Estimation';
+
+ // Generate analysis based on selected feature
if (this.selectedFeature === 'emotions') {
this.generateEmotionAnalysis();
} else {
this.generateAgeAnalysis();
}
-
- // Update confidence based on mode and face detection
- let confidence = this.lastLandmarks ?
- (this.selectedMode === 'performance' ? 85 : 95) : 0;
-
- if (this.lastLandmarks) {
- confidence += Math.floor(Math.random() * 10);
- }
+ // Update confidence based on face detection
+ const confidence = this.lastLandmarks ? Math.floor(Math.random() * 30) + 70 : Math.floor(Math.random() * 30) + 20;
this.confidenceValue.textContent = `${confidence}%`;
-
+
// Schedule next update
- const updateInterval = this.selectedMode === 'performance' ? 1500 : 3000;
- this.analysisInterval = setTimeout(() => this.updateAnalysisDisplay(), updateInterval);
+ this.analysisInterval = setTimeout(() => {
+ this.updateAnalysisDisplay();
+ }, 2000);
}
-
+
generateEmotionAnalysis() {
- const emotions = ['Happy', 'Neutral', 'Sad', 'Surprised', 'Angry'];
+ const emotions = ['Happy', 'Sad', 'Neutral', 'Surprised', 'Angry', 'Confused', 'Disgusted'];
const emotion = emotions[Math.floor(Math.random() * emotions.length)];
this.resultValue.textContent = emotion;
- this.resultValue.className = `value emotion ${emotion.toLowerCase()}`;
+ this.resultValue.className = 'metric-value emotion ' + emotion.toLowerCase();
}
-
+
generateAgeAnalysis() {
- const ages = [22, 25, 28, 31, 34, 37, 40, 43, 46, 49];
- const age = ages[Math.floor(Math.random() * ages.length)];
-
+ const age = Math.floor(Math.random() * 50) + 18;
this.resultValue.textContent = `${age} years`;
- this.resultValue.className = 'value';
+ this.resultValue.className = 'metric-value';
}
-
+
stopAnalysis() {
- this.isRunning = false;
+ this.isAnalyzing = false;
if (this.analysisInterval) {
clearTimeout(this.analysisInterval);
this.analysisInterval = null;
}
-
+
if (this.camera) {
this.camera.stop();
- this.camera = null;
}
-
+
if (this.faceMesh) {
this.faceMesh.close();
- this.faceMesh = null;
- }
-
- if (this.stream) {
- const tracks = this.stream.getTracks();
- tracks.forEach(track => track.stop());
- this.stream = null;
}
-
- this.clearCanvas();
- this.updateStatus('inactive');
- this.resetAnalysis();
+
this.lastLandmarks = null;
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
+
+ this.showModeSelection();
}
-
+
showModeSelection() {
this.modePanel.style.display = 'block';
this.analysisBox.style.display = 'none';
this.controlPanel.style.display = 'none';
// Reset selections
- document.querySelectorAll('.mode-option').forEach(opt => {
- opt.classList.remove('selected');
+ document.querySelectorAll('.mode-option').forEach(option => {
+ option.classList.remove('selected');
});
this.featureSelection.style.display = 'none';
this.selectedMode = null;
this.selectedFeature = null;
}
-
- updateStatus(status) {
- this.statusIndicator.className = `status-indicator ${status}`;
- }
-
- showLoading(show) {
- if (show) {
- this.loadingOverlay.classList.remove('hidden');
+
+ toggleFullscreen() {
+ if (!document.fullscreenElement) {
+ document.documentElement.requestFullscreen();
} else {
- this.loadingOverlay.classList.add('hidden');
+ document.exitFullscreen();
}
}
-
- clearCanvas() {
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
+
+ showLoading() {
+ this.loadingOverlay.classList.remove('hidden');
+ }
+
+ hideLoading() {
+ this.loadingOverlay.classList.add('hidden');
+ }
+
+ showNotification(message, type = 'info') {
+ // Create notification element
+ const notification = document.createElement('div');
+ notification.className = `notification ${type}`;
+ notification.textContent = message;
+ notification.style.cssText = `
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ background: ${type === 'error' ? '#ef4444' : '#10b981'};
+ color: white;
+ padding: 1rem 1.5rem;
+ border-radius: 8px;
+ z-index: 1000;
+ animation: slideIn 0.3s ease-out;
+ `;
+
+ document.body.appendChild(notification);
+
+ setTimeout(() => {
+ notification.remove();
+ }, 3000);
+ }
+
+ exportData() {
+ const data = {
+ mode: this.selectedMode,
+ feature: this.selectedFeature,
+ timestamp: new Date().toISOString(),
+ result: this.resultValue.textContent,
+ confidence: this.confidenceValue.textContent
+ };
+
+ const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `visionauth-analysis-${Date.now()}.json`;
+ a.click();
+ URL.revokeObjectURL(url);
+
+ this.showNotification('Analysis data exported successfully', 'info');
}
-
- resetAnalysis() {
- this.modeValue.textContent = '--';
- this.featureValue.textContent = '--';
- this.resultValue.textContent = '--';
- this.confidenceValue.textContent = '--';
- this.resultValue.className = 'value';
+
+ showSettings() {
+ this.showNotification('Settings panel coming soon', 'info');
}
-
- resizeCanvas() {
- this.canvas.width = this.video.offsetWidth || window.innerWidth;
- this.canvas.height = this.video.offsetHeight || window.innerHeight;
+
+ showDocumentation() {
+ this.showNotification('Documentation panel coming soon', 'info');
}
-
- toggleFullscreen() {
- const container = document.querySelector('.fullscreen-container');
-
- if (!document.fullscreenElement) {
- container.requestFullscreen().then(() => {
- container.classList.add('fullscreen');
- }).catch(err => {
- console.error('Error attempting to enable fullscreen:', err);
- });
- } else {
- document.exitFullscreen().then(() => {
- container.classList.remove('fullscreen');
- }).catch(err => {
- console.error('Error attempting to exit fullscreen:', err);
- });
- }
+
+ showSupport() {
+ this.showNotification('Support panel coming soon', 'info');
}
}
-// Initialize the system when the page loads
+// Initialize the system when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
- const visionAuth = new VisionAuthSystem();
+ new VisionAuthSystem();
});
\ No newline at end of file
diff --git a/styles.css b/styles.css
index ce18992..0b9939f 100644
--- a/styles.css
+++ b/styles.css
@@ -1,4 +1,4 @@
-/* Reset and base styles */
+/* Professional Enterprise Design System */
* {
margin: 0;
padding: 0;
@@ -6,10 +6,11 @@
}
body {
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
overflow: hidden;
height: 100vh;
+ color: #ffffff;
}
/* Fullscreen container */
@@ -45,165 +46,324 @@ body {
z-index: 2;
}
-/* Mode Selection Panel */
+/* Professional Header */
+.app-header {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 10;
+ background: rgba(26, 26, 46, 0.95);
+ backdrop-filter: blur(20px);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ padding: 1rem 2rem;
+}
+
+.header-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ max-width: 1400px;
+ margin: 0 auto;
+}
+
+.logo-section {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.logo-icon {
+ width: 48px;
+ height: 48px;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ border-radius: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 700;
+ font-size: 1.2rem;
+ color: white;
+ box-shadow: 0 4px 20px rgba(102, 126, 234, 0.3);
+}
+
+.logo-text h1 {
+ font-size: 1.8rem;
+ font-weight: 700;
+ color: #ffffff;
+ margin: 0;
+}
+
+.subtitle {
+ font-size: 0.9rem;
+ color: rgba(255, 255, 255, 0.7);
+ font-weight: 400;
+}
+
+.header-actions {
+ display: flex;
+ gap: 1rem;
+}
+
+.header-btn {
+ background: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ color: #ffffff;
+ padding: 0.75rem 1.5rem;
+ border-radius: 8px;
+ font-size: 0.9rem;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ backdrop-filter: blur(10px);
+}
+
+.header-btn:hover {
+ background: rgba(255, 255, 255, 0.15);
+ border-color: rgba(255, 255, 255, 0.3);
+ transform: translateY(-1px);
+}
+
+/* Professional Mode Selection Panel */
.mode-panel {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
- background: rgba(255, 255, 255, 0.95);
- backdrop-filter: blur(20px);
- border-radius: 20px;
- padding: 40px;
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
+ background: rgba(26, 26, 46, 0.98);
+ backdrop-filter: blur(30px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 24px;
+ padding: 3rem;
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
z-index: 10;
- max-width: 600px;
+ max-width: 800px;
width: 90%;
+}
+
+.panel-header {
text-align: center;
+ margin-bottom: 2.5rem;
}
-.mode-panel h2 {
- color: #333;
+.panel-header h2 {
font-size: 2.5rem;
- margin-bottom: 10px;
font-weight: 700;
+ color: #ffffff;
+ margin-bottom: 0.5rem;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
}
-.mode-panel p {
- color: #666;
+.panel-header p {
+ color: rgba(255, 255, 255, 0.7);
font-size: 1.1rem;
- margin-bottom: 30px;
+ font-weight: 400;
}
.mode-selection {
display: grid;
grid-template-columns: 1fr 1fr;
- gap: 20px;
- margin-bottom: 30px;
+ gap: 2rem;
+ margin-bottom: 2.5rem;
}
.mode-option {
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
- color: white;
- padding: 25px;
- border-radius: 15px;
+ background: rgba(255, 255, 255, 0.05);
+ border: 2px solid rgba(255, 255, 255, 0.1);
+ border-radius: 16px;
+ padding: 2rem;
cursor: pointer;
transition: all 0.3s ease;
- border: 3px solid transparent;
+ position: relative;
+ overflow: hidden;
+}
+
+.mode-option::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
+ opacity: 0;
+ transition: opacity 0.3s ease;
+}
+
+.mode-option:hover::before {
+ opacity: 1;
}
.mode-option:hover {
- transform: translateY(-5px);
- box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3);
- border-color: #fff;
+ transform: translateY(-4px);
+ border-color: rgba(102, 126, 234, 0.3);
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}
.mode-option.selected {
- border-color: #00ff88;
- box-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
+ border-color: #667eea;
+ background: rgba(102, 126, 234, 0.1);
+ box-shadow: 0 0 30px rgba(102, 126, 234, 0.3);
+}
+
+.mode-icon {
+ font-size: 2.5rem;
+ margin-bottom: 1rem;
+ display: block;
}
-.mode-option h3 {
+.mode-content h3 {
font-size: 1.5rem;
- margin-bottom: 10px;
font-weight: 600;
+ color: #ffffff;
+ margin-bottom: 0.5rem;
}
-.mode-option p {
- font-size: 0.9rem;
- margin-bottom: 15px;
- opacity: 0.9;
+.mode-content p {
+ color: rgba(255, 255, 255, 0.7);
+ font-size: 0.95rem;
+ margin-bottom: 1rem;
+ line-height: 1.5;
}
-.mode-features {
+.mode-specs {
display: flex;
flex-direction: column;
- gap: 8px;
+ gap: 0.5rem;
}
-.mode-features span {
+.mode-specs span {
font-size: 0.85rem;
- opacity: 0.8;
+ color: rgba(255, 255, 255, 0.6);
+ font-weight: 400;
}
/* Feature Selection */
.feature-selection {
- background: rgba(255, 255, 255, 0.1);
- padding: 25px;
- border-radius: 15px;
- margin-top: 20px;
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 16px;
+ padding: 2rem;
+ margin-top: 2rem;
}
-.feature-selection h3 {
- color: #333;
- margin-bottom: 20px;
- font-size: 1.3rem;
+.selection-header {
+ text-align: center;
+ margin-bottom: 2rem;
+}
+
+.selection-header h3 {
+ font-size: 1.5rem;
+ font-weight: 600;
+ color: #ffffff;
+ margin-bottom: 0.5rem;
+}
+
+.selection-header p {
+ color: rgba(255, 255, 255, 0.7);
+ font-size: 1rem;
}
.feature-toggles {
display: flex;
flex-direction: column;
- gap: 15px;
- margin-bottom: 25px;
+ gap: 1rem;
+ margin-bottom: 2rem;
}
.feature-toggle {
display: flex;
align-items: center;
- padding: 15px;
- background: rgba(255, 255, 255, 0.8);
- border-radius: 10px;
+ padding: 1.5rem;
+ background: rgba(255, 255, 255, 0.05);
+ border: 2px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
- border: 2px solid transparent;
}
.feature-toggle:hover {
- background: rgba(255, 255, 255, 0.9);
- transform: translateX(5px);
+ background: rgba(255, 255, 255, 0.08);
+ border-color: rgba(255, 255, 255, 0.2);
+ transform: translateX(4px);
}
.feature-toggle input[type="radio"] {
- margin-right: 15px;
+ margin-right: 1rem;
transform: scale(1.2);
+ accent-color: #667eea;
+}
+
+.toggle-content {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ flex: 1;
+}
+
+.toggle-icon {
+ font-size: 1.5rem;
+ width: 40px;
+ height: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(102, 126, 234, 0.1);
+ border-radius: 8px;
+}
+
+.toggle-text {
+ display: flex;
+ flex-direction: column;
}
-.toggle-label {
+.toggle-title {
font-size: 1.1rem;
- font-weight: 500;
- color: #333;
+ font-weight: 600;
+ color: #ffffff;
+ margin-bottom: 0.25rem;
+}
+
+.toggle-desc {
+ font-size: 0.9rem;
+ color: rgba(255, 255, 255, 0.6);
}
.start-analysis-btn {
- background: linear-gradient(135deg, #00ff88 0%, #00cc6a 100%);
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
- padding: 15px 30px;
- border-radius: 10px;
+ padding: 1rem 2rem;
+ border-radius: 12px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
- box-shadow: 0 5px 15px rgba(0, 255, 136, 0.3);
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
+ width: 100%;
}
.start-analysis-btn:hover {
transform: translateY(-2px);
- box-shadow: 0 8px 20px rgba(0, 255, 136, 0.4);
+ box-shadow: 0 12px 35px rgba(102, 126, 234, 0.4);
}
-/* Analysis Box */
-.analysis-box {
+/* Professional Analysis Dashboard */
+.analysis-dashboard {
position: absolute;
- top: 30px;
- right: 30px;
- background: rgba(255, 255, 255, 0.95);
- backdrop-filter: blur(20px);
- border-radius: 15px;
- padding: 25px;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
+ top: 100px;
+ right: 2rem;
+ background: rgba(26, 26, 46, 0.98);
+ backdrop-filter: blur(30px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 20px;
+ padding: 2rem;
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
z-index: 5;
- min-width: 280px;
+ min-width: 400px;
animation: slideIn 0.5s ease-out;
}
@@ -218,33 +378,37 @@ body {
}
}
-.analysis-header {
+.dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
- margin-bottom: 20px;
+ margin-bottom: 2rem;
+ padding-bottom: 1rem;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
-.analysis-header h3 {
- color: #333;
- font-size: 1.2rem;
+.dashboard-title {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.dashboard-title h3 {
+ font-size: 1.3rem;
font-weight: 600;
+ color: #ffffff;
}
.status-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
- background: #ccc;
-}
-
-.status-indicator.active {
- background: #00ff88;
- box-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
+ background: #10b981;
+ box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
}
.status-indicator.detecting {
- background: #ffaa00;
+ background: #f59e0b;
animation: pulse 1s infinite;
}
@@ -253,87 +417,157 @@ body {
50% { opacity: 0.5; }
}
-.analysis-content {
+.dashboard-controls {
display: flex;
- flex-direction: column;
- gap: 12px;
+ gap: 0.5rem;
+}
+
+.dashboard-controls .control-btn {
+ background: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ color: #ffffff;
+ padding: 0.5rem 1rem;
+ border-radius: 6px;
+ font-size: 0.8rem;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.dashboard-controls .control-btn:hover {
+ background: rgba(255, 255, 255, 0.15);
+ border-color: rgba(255, 255, 255, 0.3);
+}
+
+.metric-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 1rem;
+}
+
+.metric-card {
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ padding: 1.5rem;
+ transition: all 0.3s ease;
+}
+
+.metric-card:hover {
+ background: rgba(255, 255, 255, 0.08);
+ border-color: rgba(255, 255, 255, 0.2);
+ transform: translateY(-2px);
}
-.analysis-item {
+.metric-header {
display: flex;
justify-content: space-between;
align-items: center;
- padding: 8px 0;
- border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+ margin-bottom: 0.75rem;
}
-.analysis-item:last-child {
- border-bottom: none;
+.metric-label {
+ font-size: 0.85rem;
+ color: rgba(255, 255, 255, 0.7);
+ font-weight: 500;
}
-.label {
- color: #666;
- font-size: 0.9rem;
- font-weight: 500;
+.metric-icon {
+ font-size: 1rem;
+ opacity: 0.7;
}
-.value {
- color: #333;
+.metric-value {
+ font-size: 1.2rem;
font-weight: 600;
- font-size: 0.95rem;
+ color: #ffffff;
}
-.value.emotion {
- padding: 4px 8px;
+.metric-value.emotion {
+ padding: 0.25rem 0.75rem;
border-radius: 6px;
- font-size: 0.85rem;
+ font-size: 1rem;
+ font-weight: 500;
}
-.value.emotion.happy { background: #e8f5e8; color: #2d5a2d; }
-.value.emotion.sad { background: #ffe8e8; color: #5a2d2d; }
-.value.emotion.neutral { background: #f0f0f0; color: #333; }
-.value.emotion.surprised { background: #fff3e0; color: #8b4513; }
-.value.emotion.angry { background: #ffe0e0; color: #8b0000; }
+.metric-value.emotion.happy { background: rgba(16, 185, 129, 0.2); color: #10b981; }
+.metric-value.emotion.sad { background: rgba(239, 68, 68, 0.2); color: #ef4444; }
+.metric-value.emotion.neutral { background: rgba(107, 114, 128, 0.2); color: #6b7280; }
+.metric-value.emotion.surprised { background: rgba(245, 158, 11, 0.2); color: #f59e0b; }
+.metric-value.emotion.angry { background: rgba(220, 38, 38, 0.2); color: #dc2626; }
-/* Control Panel */
+/* Professional Control Panel */
.control-panel {
position: absolute;
- bottom: 30px;
+ bottom: 2rem;
left: 50%;
transform: translateX(-50%);
- display: flex;
- gap: 15px;
z-index: 5;
}
+.control-group {
+ display: flex;
+ gap: 1rem;
+ background: rgba(26, 26, 46, 0.95);
+ backdrop-filter: blur(20px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 16px;
+ padding: 1rem;
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
+}
+
.control-btn {
- background: rgba(255, 255, 255, 0.9);
- backdrop-filter: blur(10px);
- border: none;
- padding: 12px 20px;
- border-radius: 8px;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.75rem 1.5rem;
+ border-radius: 10px;
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
- color: #333;
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+ border: none;
+ color: #ffffff;
+}
+
+.control-btn.primary {
+ background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
+ box-shadow: 0 4px 15px rgba(239, 68, 68, 0.3);
}
-.control-btn:hover {
- background: rgba(255, 255, 255, 1);
+.control-btn.primary:hover {
transform: translateY(-2px);
- box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
+ box-shadow: 0 6px 20px rgba(239, 68, 68, 0.4);
}
-/* Loading Overlay */
+.control-btn.secondary {
+ background: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.control-btn.secondary:hover {
+ background: rgba(255, 255, 255, 0.15);
+ border-color: rgba(255, 255, 255, 0.3);
+ transform: translateY(-1px);
+}
+
+.btn-icon {
+ font-size: 1rem;
+}
+
+.btn-text {
+ font-weight: 500;
+}
+
+/* Professional Loading Overlay */
.loading-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
- background: rgba(0, 0, 0, 0.8);
+ background: rgba(26, 26, 46, 0.95);
+ backdrop-filter: blur(20px);
display: flex;
justify-content: center;
align-items: center;
@@ -349,14 +583,14 @@ body {
color: white;
}
-.spinner {
- width: 50px;
- height: 50px;
- border: 4px solid rgba(255, 255, 255, 0.3);
- border-top: 4px solid #00ff88;
+.loading-spinner {
+ width: 60px;
+ height: 60px;
+ border: 4px solid rgba(255, 255, 255, 0.1);
+ border-top: 4px solid #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
- margin: 0 auto 20px;
+ margin: 0 auto 2rem;
}
@keyframes spin {
@@ -364,33 +598,50 @@ body {
100% { transform: rotate(360deg); }
}
-.loading-content p {
- font-size: 1.1rem;
- font-weight: 500;
+.loading-text h3 {
+ font-size: 1.5rem;
+ font-weight: 600;
+ margin-bottom: 0.5rem;
+ color: #ffffff;
+}
+
+.loading-text p {
+ font-size: 1rem;
+ color: rgba(255, 255, 255, 0.7);
+ font-weight: 400;
}
/* Responsive Design */
@media (max-width: 768px) {
.mode-selection {
grid-template-columns: 1fr;
- gap: 15px;
+ gap: 1.5rem;
}
.mode-panel {
- padding: 30px 20px;
- margin: 20px;
+ padding: 2rem 1.5rem;
+ margin: 1rem;
}
- .analysis-box {
- top: 20px;
- right: 20px;
- left: 20px;
+ .analysis-dashboard {
+ top: 80px;
+ right: 1rem;
+ left: 1rem;
min-width: auto;
}
- .control-panel {
- bottom: 20px;
- flex-wrap: wrap;
- justify-content: center;
+ .metric-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .control-group {
+ flex-direction: column;
+ gap: 0.5rem;
+ }
+
+ .header-content {
+ flex-direction: column;
+ gap: 1rem;
+ text-align: center;
}
}
\ No newline at end of file