BarkID-Dog-Identifier / static /js /animations.js
JanithDeshan24's picture
feat: Initial project commit for BarkID
b574826 verified
/**
* Dog Breed Identifier - Custom JS
* Developer: Janith Deshan
* Last Updated: 2025-10-20
*/
document.addEventListener('DOMContentLoaded', function() {
// Initialize AOS animation library
AOS.init({
duration: 800,
easing: 'ease-out',
once: false,
mirror: true
});
// Initialize all components
initClearButton();
createParticles();
createPawPrints();
initFileUpload();
initNavbarScroll();
updateTimeDisplay();
setInterval(updateTimeDisplay, 1000);
initSmoothScrolling();
initShareButtons();
// Progress bar animation for results
animateProgressBar();
// Hide loading when page loads (in case of a refresh during loading)
hideLoading();
});
/**
* Animates the confidence progress bar in results
*/
function animateProgressBar() {
const progressBar = document.querySelector('.progress-bar');
if (progressBar) {
// Find the confidence text for the *first* prediction
const confidenceElement = document.querySelector('.result-details .confidence-text');
const confidenceText = confidenceElement ? confidenceElement.textContent : '0%';
const match = confidenceText.match(/(\d+(\.\d+)?)%/);
if (match) {
const percentage = parseFloat(match[1]);
progressBar.style.width = percentage + '%';
// Add dynamic color class based on percentage
if (percentage >= 80) {
progressBar.classList.add('high-confidence');
progressBar.style.background = 'linear-gradient(to right, var(--primary), var(--accent))';
} else if (percentage >= 40) {
progressBar.classList.add('medium-confidence');
progressBar.style.background = 'linear-gradient(to right, var(--primary-light), var(--accent))';
} else {
progressBar.classList.add('low-confidence');
progressBar.style.background = 'linear-gradient(to right, var(--secondary), var(--secondary-light))';
}
}
}
}
/**
* Initialize the 'Clear Results' button
*/
function initClearButton() {
const clearBtn = document.getElementById('clear-results-btn');
const resultSection = document.querySelector('.result-section');
const fileInput = document.querySelector('.file-input');
const uploadForm = document.getElementById('upload-form');
const previewContainer = document.querySelector('.preview-container');
const previewImage = document.getElementById('image-preview');
const previewText = document.getElementById('preview-text');
const selectedFileContainer = document.querySelector('.selected-file');
const submitBtn = uploadForm ? uploadForm.querySelector('button[type="submit"]') : null;
if (clearBtn) {
clearBtn.addEventListener('click', function() {
// Hide the result section
if (resultSection) {
resultSection.style.display = 'none';
}
// Hide the preview
if (previewContainer) {
previewContainer.style.display = 'none';
previewImage.style.display = 'none';
previewText.style.display = 'block';
previewImage.src = '#';
}
// Reset the file input
if (fileInput) {
fileInput.value = '';
}
// Clear selected file text
if (selectedFileContainer) {
selectedFileContainer.textContent = '';
}
// Reset the submit button
if (submitBtn) {
submitBtn.innerHTML = '<i class="fas fa-search"></i> Identify Breed';
submitBtn.disabled = false;
}
// Remove any error messages
const errorAlert = document.querySelector('.upload-error');
if (errorAlert && errorAlert.parentNode) {
errorAlert.parentNode.removeChild(errorAlert);
}
// Optional: Scroll back to the form
document.getElementById('identifier').scrollIntoView({ behavior: 'smooth' });
});
}
}
/**
* Create background particles
*/
function createParticles() {
const container = document.querySelector('.particle-container');
if (!container) return;
const colors = ['rgba(74, 111, 165, 0.3)', 'rgba(255, 139, 94, 0.3)', 'rgba(84, 209, 189, 0.3)'];
// Create particles
for (let i = 0; i < 30; i++) {
const particle = document.createElement('div');
particle.classList.add('particle');
// Random properties
const size = 3 + Math.random() * 8;
const color = colors[Math.floor(Math.random() * colors.length)];
const left = Math.random() * 100;
const delay = Math.random() * 15;
const duration = 15 + Math.random() * 30;
// Set styles
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
particle.style.backgroundColor = color;
particle.style.left = `${left}vw`;
particle.style.top = '100vh';
particle.style.animationDuration = `${duration}s`;
particle.style.animationDelay = `${delay}s`;
container.appendChild(particle);
}
}
/**
* Create paw print animations
*/
function createPawPrints() {
const container = document.querySelector('.paw-prints');
if (!container) return;
// Create paw prints
for (let i = 0; i < 15; i++) {
const paw = document.createElement('div');
paw.classList.add('paw');
// Random properties
const size = 15 + Math.random() * 15;
const left = Math.random() * 100;
const top = Math.random() * 100;
const rotation = Math.random() * 360;
// Set styles
paw.style.width = `${size}px`;
paw.style.height = `${size}px`;
paw.style.left = `${left}vw`;
paw.style.top = `${top}vh`;
paw.style.transform = `rotate(${rotation}deg)`;
container.appendChild(paw);
}
}
/**
* Initialize file upload functionality with validation and preview
*/
function initFileUpload() {
const fileInput = document.querySelector('.file-input');
const uploadArea = document.querySelector('.upload-area');
const browseBtn = document.querySelector('.browse-btn');
const selectedFileContainer = document.querySelector('.selected-file');
const form = document.getElementById('upload-form');
if (!fileInput || !uploadArea) return;
// Click the hidden file input when browse button is clicked
if (browseBtn) {
browseBtn.addEventListener('click', function() {
fileInput.click();
});
}
// Click the hidden file input when upload area is clicked
uploadArea.addEventListener('click', function(e) {
// Prevent clicking on the file input itself from triggering this
if (e.target !== fileInput) {
fileInput.click();
}
});
// Handle file selection with validation
fileInput.addEventListener('change', function() {
if (this.files.length > 0) {
const file = this.files[0];
if (validateFile(file)) {
displayFilePreview(file);
uploadArea.classList.add('file-selected');
// Remove any existing errors
const errorAlert = document.querySelector('.upload-error');
if (errorAlert && errorAlert.parentNode) {
errorAlert.parentNode.removeChild(errorAlert);
}
} else {
// Reset file input
this.value = '';
uploadArea.classList.remove('file-selected');
}
}
});
// Handle drag and drop
['dragover', 'dragenter'].forEach(event => {
uploadArea.addEventListener(event, function(e) {
e.preventDefault();
e.stopPropagation();
uploadArea.classList.add('drag-over');
});
});
['dragleave', 'dragend'].forEach(event => {
uploadArea.addEventListener(event, function(e) {
e.preventDefault();
e.stopPropagation();
uploadArea.classList.remove('drag-over');
});
});
uploadArea.addEventListener('drop', function(e) {
e.preventDefault();
e.stopPropagation();
uploadArea.classList.remove('drag-over');
if (e.dataTransfer.files.length > 0) {
const file = e.dataTransfer.files[0];
if (validateFile(file)) {
fileInput.files = e.dataTransfer.files;
displayFilePreview(file);
uploadArea.classList.add('file-selected');
}
}
});
// Add loading state when form is submitted
if (form) {
form.addEventListener('submit', function() {
const submitBtn = this.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing...';
submitBtn.disabled = true;
}
showLoading(); // Show the loading overlay
});
}
}
/**
* Validate uploaded file type and size
* @param {File} file - The file to validate
* @returns {boolean} - Whether the file is valid
*/
function validateFile(file) {
// Check file type
const validTypes = ['image/jpeg', 'image/jpg', 'image/png'];
if (!validTypes.includes(file.type)) {
showError('Please select a valid image file (JPG, JPEG, or PNG).');
return false;
}
// Check file size (limit to 5MB)
const maxSize = 5 * 1024 * 1024; // 5MB
if (file.size > maxSize) {
showError('File size exceeds 5MB. Please choose a smaller image.');
return false;
}
return true;
}
/**
* Display error message
* @param {string} message - The error message to display
*/
function showError(message) {
// Create error alert if it doesn't exist
let errorAlert = document.querySelector('.upload-error');
if (!errorAlert) {
errorAlert = document.createElement('div');
errorAlert.className = 'alert alert-danger mt-3 upload-error';
const uploadContainer = document.querySelector('.upload-container');
if (uploadContainer) {
uploadContainer.appendChild(errorAlert);
}
}
errorAlert.innerHTML = `<i class="fas fa-exclamation-triangle"></i> ${message}`;
// Remove after 5 seconds
setTimeout(() => {
if (errorAlert.parentNode) {
errorAlert.parentNode.removeChild(errorAlert);
}
}, 5000);
}
/**
* Display image preview
* @param {File} file - The file to preview
*/
function displayFilePreview(file) {
const previewContainer = document.querySelector('.preview-container');
const previewImage = document.getElementById('image-preview');
const previewText = document.getElementById('preview-text');
const selectedFileContainer = document.querySelector('.selected-file');
if (!previewContainer || !previewImage || !previewText) return;
// Show loading indicator
previewText.textContent = "Loading preview...";
previewText.style.display = 'block';
previewImage.style.display = 'none';
previewContainer.style.display = 'block';
// Use FileReader to read the file
const reader = new FileReader();
reader.onload = function(e) {
previewImage.src = e.target.result;
previewImage.style.display = 'block';
previewText.style.display = 'none';
// Add file name display
if (selectedFileContainer) {
selectedFileContainer.textContent = `Selected: ${file.name} (${formatFileSize(file.size)})`;
}
};
reader.onerror = function() {
previewText.textContent = "Error loading preview";
};
reader.readAsDataURL(file);
}
/**
* Format file size for display
* @param {number} bytes - File size in bytes
* @returns {string} - Formatted file size
*/
function formatFileSize(bytes) {
if (bytes < 1024) return bytes + " bytes";
else if (bytes < 1048576) return (bytes / 1024).toFixed(1) + " KB";
else return (bytes / 1048576).toFixed(1) + " MB";
}
/**
* Show loading overlay
*/
function showLoading() {
const overlay = document.getElementById('loading-overlay');
if (overlay) {
overlay.classList.add('active');
}
}
/**
* Hide loading overlay
*/
function hideLoading() {
const overlay = document.getElementById('loading-overlay');
if (overlay) {
overlay.classList.remove('active');
}
}
/**
* Initialize navbar scroll effect
*/
function initNavbarScroll() {
const navbar = document.querySelector('.app-header');
if (!navbar) return;
window.addEventListener('scroll', function() {
if (window.scrollY > 10) {
navbar.classList.add('scrolled');
navbar.style.padding = '10px 0';
navbar.style.boxShadow = '0 5px 20px rgba(0, 0, 0, 0.08)';
} else {
navbar.classList.remove('scrolled');
navbar.style.padding = '15px 0';
navbar.style.boxShadow = '0 2px 15px rgba(0, 0, 0, 0.05)';
}
});
}
/**
* Update time display in footer with current UTC time
*/
function updateTimeDisplay() {
const timeDisplay = document.getElementById('current-time');
if (!timeDisplay) return;
const now = new Date();
const year = now.getUTCFullYear();
const month = String(now.getUTCMonth() + 1).padStart(2, '0');
const day = String(now.getUTCDate()).padStart(2, '0');
const hours = String(now.getUTCHours()).padStart(2, '0');
const minutes = String(now.getUTCMinutes()).padStart(2, '0');
const seconds = String(now.getUTCSeconds()).padStart(2, '0');
timeDisplay.textContent = `${year}-${month}-${day} ${hours}:${minutes}:${seconds} UTC`;
}
/**
* Initialize smooth scrolling for navigation links
*/
function initSmoothScrolling() {
const navLinks = document.querySelectorAll('a[href^="#"]');
navLinks.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
if (targetId === '#') return;
const targetElement = document.querySelector(targetId);
if (!targetElement) return;
// Get navbar height for offset
const navbar = document.querySelector('.app-header');
const navbarHeight = navbar ? navbar.offsetHeight : 0;
// Calculate scroll position
const targetPosition = targetElement.getBoundingClientRect().top + window.scrollY - navbarHeight;
// Smooth scroll to target
window.scrollTo({
top: targetPosition,
behavior: 'smooth'
});
// Close mobile nav if open
const navbarToggler = document.querySelector('.navbar-toggler');
const navbarCollapse = document.querySelector('.navbar-collapse');
if (navbarCollapse && navbarCollapse.classList.contains('show')) {
navbarToggler.click();
}
// Update active state in nav
navLinks.forEach(link => link.classList.remove('active'));
this.classList.add('active');
});
});
// Update active nav item on scroll
window.addEventListener('scroll', function() {
const scrollPosition = window.scrollY;
const navbar = document.querySelector('.app-header');
const navbarHeight = navbar ? navbar.offsetHeight : 0;
document.querySelectorAll('section').forEach(section => {
const sectionTop = section.offsetTop - navbarHeight - 10;
const sectionBottom = sectionTop + section.offsetHeight;
if (scrollPosition >= sectionTop && scrollPosition < sectionBottom) {
const sectionId = section.getAttribute('id');
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === `#${sectionId}`) {
link.classList.add('active');
}
});
}
});
});
}
/**
* Initialize social sharing buttons functionality
*/
function initShareButtons() {
const shareButtons = document.querySelectorAll('.share-btn');
shareButtons.forEach(button => {
button.addEventListener('click', function() {
const platform = this.getAttribute('data-platform');
const resultImg = document.querySelector('.result-image img');
const resultText = document.querySelector('.item-value .prediction-tag');
if (!resultImg || !resultText) return;
const imageUrl = resultImg.src;
const breedText = resultText.textContent;
const shareText = `I just identified a ${breedText} using the Dog Breed Identifier!`;
const siteUrl = window.location.href;
let shareUrl = '';
switch(platform) {
case 'facebook':
shareUrl = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(siteUrl)}`;
break;
case 'twitter':
shareUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(shareText)}&url=${encodeURIComponent(siteUrl)}`;
break;
case 'pinterest':
shareUrl = `https://pinterest.com/pin/create/button/?url=${encodeURIComponent(siteUrl)}&media=${encodeURIComponent(imageUrl)}&description=${encodeURIComponent(shareText)}`;
break;
case 'email':
shareUrl = `mailto:?subject=Dog Breed Identification&body=${encodeURIComponent(shareText + '\n\n' + siteUrl)}`;
break;
}
if (shareUrl) {
window.open(shareUrl, '_blank', 'width=600,height=400');
}
});
});
}