kalkulator/static/assets/js/calculator-logic.js

208 lines
6.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// This file is responsible for all calculator animations and logic.
const buttons = document.querySelectorAll('.button');
const displayText = document.querySelector('.display-text');
let currentInput = '';
let characterSpans = []; // Array to track all spans for proper positioning
let isResultDisplayed = false; // Track if the result is displayed
let resultSpan; // For showing the result
// Add on-click event listeners to the buttons.
buttons.forEach(button => {
button.addEventListener('click', () => {
const value = button.textContent;
if (value === 'AC') {
resetCalculator();
} else if (value === 'H') {
toggleHistory();
} else if (value === 'settings') {
toggleSettings();
} else if (value === 'backspace') {
popCharacter();
} else if (value === '=') {
calculateResult();
} else {
if (isResultDisplayed) {
resetCalculator(); // Reset after "=" is pressed
}
if (currentInput === 'Error') currentInput = ''; // Clear on error
currentInput += value;
appendCharacter(value); // Append with animation
}
});
});
// This function is used to append a character to the calculator display. It's called when user clicks a button.
function appendCharacter(char) {
// Create new span element for the character
const newChar = document.createElement('span');
newChar.textContent = char;
displayText.appendChild(newChar);
characterSpans.push(newChar); // Add the new span to the array
// Move all characters smoothly
updateCharacterPositions();
// Animate the new character in from the right
anime({
targets: newChar,
opacity: [0, 1],
translateY: [0, -25], // Animating into place
scale: [0, 1],
duration: 500,
easing: 'easeOutExpo'
});
}
// This function moves already input characters to left to make space for a new one.
function updateCharacterPositions() {
const totalWidth = displayText.offsetWidth; // Width of the display
let currentOffset = totalWidth; // Start from the rightmost edge
// Loop through characters in reverse (so newest appears at the right)
for (let i = characterSpans.length - 1; i >= 0; i--) {
const span = characterSpans[i];
const spanWidth = span.offsetWidth; // Get the width of each character
currentOffset -= spanWidth; // Move left by the width of the character + some margin
anime({
targets: span,
right: (totalWidth - currentOffset - spanWidth) + 'px', // Keep them aligned right
duration: 300,
easing: 'easeOutQuint'
});
}
}
// This function calculates the result based on characters input.
function calculateResult() {
const equation = characterSpans.map(span => span.textContent).join('');
let result;
try {
result = eval(equation.replaceAll('÷', '/').replaceAll('×', '*').replaceAll('', '-')).toString();
} catch (error) {
result = 'Error';
}
// We should add 'result' to the history here.
characterSpans.forEach(span => {
span.style.fontSize = '1.5rem';
});
const totalWidth = displayText.offsetWidth; // Width of the display
let currentOffset = totalWidth; // Start from the rightmost edge
// Animate the equation upwards
// Loop through characters in reverse (so newest appears at the right)
for (let i = characterSpans.length - 1; i >= 0; i--) {
const span = characterSpans[i];
const spanWidth = span.offsetWidth; // Get the width of each character
currentOffset -= spanWidth; // Move left by the width of the character + some margin
anime({
targets: span,
translateY: -45,
opacity: [1, 0.4],
right: (totalWidth - currentOffset - spanWidth) + 'px', // Keep them aligned right
duration: 800,
easing: 'easeOutExpo'
});
}
resultSpan = document.createElement('span'); // Create a new span for the result
resultSpan.textContent = `= ${result}`;
displayText.appendChild(resultSpan);
// Animate the result to appear from below
anime({
targets: resultSpan,
opacity: [0, 1],
translateY: [50, -10], // Animating upwards
translateX: -5,
scale: [0, 1],
duration: 500,
easing: 'easeOutExpo'
});
// Set a flag to reset on the next key press
isResultDisplayed = true;
}
// This function toggles the history menu.
function toggleHistory() {
$('body').toggleClass('history-open');
if ($('body').hasClass('history-open')) { // If the history was just opened
// Animate it's elements
anime({
targets: '.history-container span',
translateY: [-100, 0],
opacity: [0, 1],
duration: 400,
delay: anime.stagger(100, { start: 200, direction: 'reverse' }),
easing: 'easeOutQuart'
})
}
}
// This function toggles the settings menu.
function toggleSettings() {
$('body').toggleClass('settings-open');
}
$('#history').on('click', toggleHistory);
// Resets everything, the result and display.
function resetCalculator() {
currentInput = '';
characterSpans = [];
displayText.innerHTML = ''; // Clear the display
isResultDisplayed = false; // Reset the result display flag
}
// popLock (funny name) is true when a cbaracter pop animation is being progress.
let popLock = false;
// This function pops the last character input to the calculator.
function popCharacter() {
if (popLock) return;
popLock = true;
if (isResultDisplayed) {
resetCalculator();
popLock = false;
return;
}
currentInput = currentInput.slice(0, -1);
characterSpans.pop();
if (displayText.innerHTML.length === 0) {
isResultDisplayed = false;
popLock = false;
return;
}
// Animate character disappearing
anime({
targets: displayText.lastChild,
opacity: [1, 0],
duration: 150,
easing: 'easeInExpo',
complete: () => {
displayText.removeChild(displayText.lastChild);
updateCharacterPositions();
popLock = false;
}
});
}
// This sets the calculator display value to some string. I'm pretty sure it's not used, but I'll leave it here just in case.
function updateDisplay(value) {
// Clear existing spans and reset positions
displayText.innerHTML = '';
characterSpans = [];
[...value].forEach(char => {
appendCharacter(char); // Append each character
});
}