...Before
We're now onto implementing the screen animations when eval runs.
However, minor changes we need to do before this:
~ updating the output of eval to float precision of 3.
const calculateScreen = () => {
/* ... */
result.value = `${Number(eval(temp).toFixed(3))}`
/* ... */
}
}
~ setting superscript to false when "=" is pressed (calculateScreen is called), and adding superscript in updateScreen conditionality, to allow powers to work on immediately evaluated answers.
const calculateScreen = () => {
if (result.value) {
/* ... */
superscript.value = false
}
}
/* ... */
const updateScreen = (_in: string) => {
if (result.value) {
if (immediate.value && !superscript.value) {
if (/^\d+$/.test(_in)) {
/* ... */
~ on immediate evaluation, if clearScreen is pressed, and the result data is wholly a digit (integer or double), delete the whole number.
const clearScreen = () => {
if (result.value) {
if (immediate.value && /^-?\d+\.?\d*$/.test(result.value)) {
result.value = undefined
return
}
let index;
/* ... */
}
}
App animations
We need immediate variable in AppScreen since it's essential in the animation renders. Let us pass it as prop then.
<!-- ... -->
<app-screen
:immediate="immediate"
:result="result"></app-screen>
<!-- ... -->
From Google calculator, we know that initially, the smaller display at the top right of the main screen is not visible until any button is pressed. We'll use init ref for that. We'll watch for active_ops which is a computed property of props.result and if anything, init changes to true, making the smaller display visible.
import { ref, computed, watch } from 'vue';
const props = defineProps<{
immediate: boolean
result?: string
}>()
const init = ref(false)
const active_ops = computed(() => props.result )
watch(active_ops, ()=>{
if (props.result) init.value = true
})
In the template part of the component, create the div just above the main display div, with the following tailwind classes
<div class="absolute top-2 right-2 text-sm tracking-tighter
text-gray-400"
v-show="init">
</div>
I want to track n and n-1 states of the immediate flag, that is present and the just present flag. When computation is being done, the smaller display is updated, also after it has just been done, it's updated.
The difference is that during computation (when immediate is true), it's updated with the operands and the operators, and appears before the "=", and after computation has just been done, immediate_state_before will be used to help change the display state to the just computed answer.
The immediate flag is essential for toggling the options of display in the smaller display.
<script setup lang="ts">
/* ... */
const immediate_state_before = ref(false)
const answer_ops = ref<string>()
const active_ops = computed(() => {
if (props.immediate || immediate_state_before.value){
answer_ops.value = active_ops.value
}
immediate_state_before.value = props.immediate
return props.result
})
/* ... */
</script>
<template>
<!-- ... -->
<div class="absolute top-2 right-2 text-sm tracking-tighter
text-gray-400 origin-top-right"
v-show="init"
:class="{ 'answer-scale-down': immediate || immediate_state_before }">
<span v-if="immediate" v-html="answer_ops"></span>
<span v-else>Ans</span>
<span class="mx-1">=</span>
<span v-if="!immediate" v-html="answer_ops ?? '0'"></span>
</div>
<div class="absolute bottom-2 right-2 text-4xl tracking-tighter"
:class="{ 'screen-from-down': immediate || immediate_state_before }"
v-html="result ?? '0'">
</div>
<!-- ... -->
</template>
The classes answer-scale-down and screen-from-down are used to trigger animation keyframes when immediate flag is triggered, computation is done, whoch will influence everything after.
<!-- ... -->
<style scoped>
@keyframes answer-scale-down {
0% { transform: scale(1.8); }
100% { transform: scale(1); }
}
@keyframes screen-from-down {
0% { bottom: -100px; }
100% { bottom: 8px; }
}
.answer-scale-down {
animation: answer-scale-down 0.3s forwards;
}
.screen-from-down {
animation: screen-from-down 0.25s forwards;
}
</style>