'How do I create a simple 10 seconds countdown in Vue.js

I want to do a simple countdown from 10 to 0

I found solution online using normal javascript but let say I want to do it in Vue . The solution in Jquery

Create a simple 10 second countdown

<template>
   {{ countDown }}

</template>

<script>
  export default {
    computed: {
       countDown() {
         // How do i do the simple countdown here?
       }

    }

  }

</script>

How do I recreate the same functionality in Vue.js?

Thanks



Solution 1:[1]

Please check if this works for you.

<template>
   {{ countDown }}
</template>

<script>
    export default {
        data () {
            return {
                countDown: 10
            }
        },
        methods: {
            countDownTimer () {
                if (this.countDown > 0) {
                    setTimeout(() => {
                        this.countDown -= 1
                        this.countDownTimer()
                    }, 1000)
                }
            }
        },
        created () {
            this.countDownTimer()
        }
    }
</script>

Solution 2:[2]

Whilst the accepted answer works, and is great, it can actually be achieved in a slightly simpler way by utilising Vue.js watchers:

<template>
    {{ timerCount }}
</template>

<script>

    export default {

        data() {
            return {
                timerCount: 30
            }
        },

        watch: {

            timerCount: {
                handler(value) {

                    if (value > 0) {
                        setTimeout(() => {
                            this.timerCount--;
                        }, 1000);
                    }

                },
                immediate: true // This ensures the watcher is triggered upon creation
            }

        }
    }

</script>

The benefit of using this method is that the timer can be immediately reset by simply setting the value of timerCount.

If you would like to play/pause the timer, then you can achieve this like so (note - this is not a perfect solution as it will round to the nearest second):

<template>
    {{ timerCount }}
</template>

<script>

    export default {

        data() {
            return {
                timerEnabled: true,
                timerCount: 30
            }
        },

        watch: {

            timerEnabled(value) {
                if (value) {
                    setTimeout(() => {
                        this.timerCount--;
                    }, 1000);
                }
            },

            timerCount: {
                handler(value) {

                    if (value > 0 && this.timerEnabled) {
                        setTimeout(() => {
                            this.timerCount--;
                        }, 1000);
                    }

                },
                immediate: true // This ensures the watcher is triggered upon creation
            }

        }

        methods: {

            play() {
                this.timerEnabled = true;
            },

            pause() {
                this.timerEnabled = false;
            }

        }

    }

</script>

Solution 3:[3]

Here is a component I made for a countdown timer :

<template>
  <div>
    <slot :hour="hour" :min="min" :sec="sec"></slot>
  </div>
</template>

<script>
export default {
  props : {
    endDate : {  // pass date object till when you want to run the timer
      type : Date,
      default(){
        return new Date()
      }
    },
    negative : {  // optional, should countdown after 0 to negative
      type : Boolean,
      default : false
    }
  },
  data(){
    return{
      now : new Date(),
      timer : null
    }
  },
  computed:{
    hour(){
      let h = Math.trunc((this.endDate - this.now) / 1000 / 3600);
      return h>9?h:'0'+h;
    },
    min(){
      let m = Math.trunc((this.endDate - this.now) / 1000 / 60) % 60;
      return m>9?m:'0'+m;
    },
    sec(){
      let s = Math.trunc((this.endDate - this.now)/1000) % 60
      return s>9?s:'0'+s;
    }
  },
  watch : {
    endDate : {
      immediate : true,
      handler(newVal){
        if(this.timer){
          clearInterval(this.timer)
        }
        this.timer = setInterval(()=>{
          this.now = new Date()
          if(this.negative)
            return
          if(this.now > newVal){
            this.now = newVal
            this.$emit('endTime')
            clearInterval(this.timer)
          }
        }, 1000)
      }
    }
  },
  beforeDestroy(){
    clearInterval(this.timer)
  }
}
</script>

Solution 4:[4]

Make it a component so you can re-use it.

<body>
    <div id="app">
        <counter></counter>
        <counter></counter>
        <counter></counter>
    </div>
    <script>
        Vue.component('counter', {
            template: '<button v-on:click="countDownTimer()">{{ countDown }}</button>',
            data: function () {
                return {
                    countDown: 10,
                    countDownTimer() {
                        if (this.countDown > 0) {
                            setTimeout(() => {
                                this.countDown -= 1
                                this.countDownTimer();
                            }, 1000)
                        }
                    }
                }
            }
        })

        const app = new Vue({
            el: '#app'
        })
    </script>
</body>

Solution 5:[5]

In case if anyone uses Luxon's DateTime object instead of native JS's Date object.

<template>
  <span v-if="timer">
    {{ timeCalculated }}
  </span>
</template>

<script>
import { DateTime } from 'luxon'

export default {
  name: 'CountDownTimer',

  props: {
    endDate: {
      type: String,
      required: true
    }
  },

  data () {
    return {
      now: DateTime.local(),
      timer: null
    }
  },

  computed: {
    timeCalculated () {
      const endDateDateTimeObj = DateTime.fromISO(this.endDate)
      const theDiff = endDateDateTimeObj.diff(this.now, ['hours', 'minutes', 'seconds'])

      return `${theDiff.hours}:${theDiff.minutes}:${Math.round(theDiff.seconds)}`
    }
  },

  watch: {
    endDate: {
      immediate: true,

      handler (endDateTimeStr) {
        const endDateTimeObj = DateTime.fromISO(endDateTimeStr)

        if (this.timer) {
          clearInterval(this.timer)
        }

        this.timer = setInterval(() => {
          this.now = DateTime.local()

          if (this.now > endDateTimeObj) {
            this.now = endDateTimeObj
            clearInterval(this.timer)
          }
        }, 1000)
      }
    }
  },

  beforeDestroy () {
    clearInterval(this.timer)
  }
}
</script>

In my case endDate has String type because the value is restored from JSON. You can easily change it to the original DateTime object.

Solution 6:[6]

Use dates.

<template>
  <div>{{ time }}</div>
</template>

<script>
export default {
  name: 'Timer',

  props: ['seconds'],

  data: () => ({
    interval: undefined,
    end: new Date(0, 0, 0),
    current: new Date(0, 0, 0, 0, 0, this.seconds)
  }),

  computed: {
    time: {
      get() {
        return this.current.getSeconds();
      },

      set(d) {
        this.current = new Date(0, 0, 0, 0, 0, this.current.getSeconds() + d);
      }
    }
  },

  methods: {
    countDown() {
      this.end >= this.current
        ? clearInterval(this.interval)
        : (this.time = -1);
    }
  },

  created() {
    this.interval = setInterval(this.countDown, 1000);
  }
};
</script>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 grim
Solution 2
Solution 3 user8805101
Solution 4 Rafiq
Solution 5 TitanFighter
Solution 6 Sebastian Scholl