Timer component

Hi Everyone,

I have a timer component that starts and pauses on clicking the buttons. It’s working fine when I am on the same page. If I move to another page and come back to the same page the timer reset to its default state. How do I keep the timer running even if I switch to a different page and come back?

The timer will start once clicked on the play icon and pause on click of pause icon. I want to keep the timer running between the pages if I’ve not clicked on the pause icon.

Here is the code:

<script setup lang="ts">
const timeBegan = ref();
const timeStopped = ref();
const stoppedDuration = ref();
const started = ref();
const running = ref(false);
const time = ref('00:00:00:00');

onMounted(() => {
  clockRunning();
});

function start() {
  if (running.value) return;

  if (timeBegan.value === undefined) {
    reset();
    timeBegan.value = new Date();
  }

  if (timeStopped.value !== undefined) {
    stoppedDuration.value += new Date().valueOf() - timeStopped.value;
  }

  started.value = setInterval(clockRunning, 10);
  running.value = true;
}

function stop() {
  running.value = false;
  timeStopped.value = new Date();
  clearInterval(started.value);
}

function reset() {
  running.value = false;
  clearInterval(started.value);
  stoppedDuration.value = 0;
  timeBegan.value = undefined;
  timeStopped.value = undefined;
  time.value = '00:00:00:00';
}

function clockRunning() {
  if (!running.value) return;
  const currentTime = new Date(),
    timeElapsed = new Date(
      currentTime.valueOf() - timeBegan.value - stoppedDuration.value
    ),
    days = timeElapsed.getUTCHours() * 24,
    hour = timeElapsed.getUTCHours(),
    min = timeElapsed.getUTCMinutes(),
    sec = timeElapsed.getUTCSeconds();

  time.value =
    zeroPrefix(days, 2) +
    ':' +
    zeroPrefix(hour, 2) +
    ':' +
    zeroPrefix(min, 2) +
    ':' +
    zeroPrefix(sec, 2);
}

function zeroPrefix(num: number, digit: number) {
  let zero = '';
  for (let i = 0; i < digit; i++) {
    zero += '0';
  }
  return (zero + num).slice(-digit);
}
</script>
<template>
  <div id="clock">
    <span class="time">{{ time }}</span>

    <div class="btn-container">
      <!-- pause -->
      <font-awesome-icon
        class="text-2xl text-primary w-2rem cursor-pointer"
        :icon="['far', 'circle-pause']"
        @click="stop"
      />
      <!-- play -->
      <font-awesome-icon
        class="text-2xl text-primary w-2rem cursor-pointer"
        :icon="['far', 'circle-play']"
        @click="start"
      />
    </div>
  </div>
</template>

<style lang="scss" scoped>
#clock {
  order: 0;
  flex: 0 1 auto;
  align-self: center;

  .time {
    font-size: 2.5em;
  }

  .text {
    margin-top: 30px;
    font-size: 1em;
    text-align: center;

    a {
      text-decoration: none;
      color: inherit;

      transition: color 0.1s ease-out;
    }
  }

  .btn-container {
    display: flex;
    margin-top: 15px;

    a {
      text-align: center;
      font-family: 'Share Tech Mono', sans-serif;
      background: transparent;
      border: none;
      padding: 10px 15px;
      margin: 0 10px;
      text-transform: uppercase;
      font-size: 2em;
      cursor: pointer;
      flex-grow: 1;
      transition: color 0.1s ease-out;
    }
  }
}
</style>

You can use keep-alive tag to cache the component instance of the page, so instance get preserved even when you navigate to other page with vue-router.

<router-view v-slot="{ Component }">
  <transition>
    <keep-alive include="componentname">
      <component :is="Component" />
    </keep-alive>
  </transition>
</router-view>

you use include attribute to apply to only page component you want. For more on it refer to KeepAlive | Vue.js

I have one example you can refer Vue 3 + Vue Router + Vite Starter + keep-alive - StackBlitz

Hi @Jayesh,
Thank you for the reply. In my case the timer component is not in views it’s a standalone component can be used anywhere in the app. It would be helpful if you could guide how I can use the keep-alive for the component other than the components in the views.

Thank you.

You can also use KeepAlive without vue router, where you use dynamic component tag and need to wrap it inside KeepAlive tag and use the component tag to load your component. Like in below link

Hi @kathirr007,
Another option would be moving the timer state to an external state management such as a Vuex or Pinia store. That should help prevent the timer state from resetting everytime the component is re-mounted.