37

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

sinusGob
  • 4,053
  • 12
  • 46
  • 82

7 Answers7

72

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>
tony19
  • 125,647
  • 18
  • 229
  • 307
Ben Carey
  • 16,540
  • 19
  • 87
  • 169
65

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>
grim
  • 6,669
  • 11
  • 38
  • 57
Tomonso Ejang
  • 1,336
  • 3
  • 15
  • 27
9

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>
user8805101
  • 93
  • 1
  • 5
  • hi @user8805101 when i try your code i got error : Invalid prop: type check failed for prop "endDate". Expected Date, got String with value "2021-11-03 15:23". How to send date from parent component. – user6085744 Nov 03 '21 at 10:04
1

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>
Rafiq
  • 8,987
  • 4
  • 35
  • 35
1

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.

TitanFighter
  • 4,582
  • 3
  • 45
  • 73
0

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>
Sebastian Scholl
  • 855
  • 7
  • 11
0

The cleanest way to do this is with "setInterval". It is recursive by itself, preventing unnecessary boilerplate. Put it in a variable ''interval'' so inside it you can stop the recursion with "clearInterval"

Answer to question:

data () {
   return {
      countDown: 10
   }
},

interval = setInterval(() => { 
    if(this.countDown == 0) clearInterval(interval)
    this.countDown--; 
}, 1000)

For those who came here looking for a normal timer:

// mounted
interval = setInterval(() => { this.seconds += 1;  }

//html or computed
Math.floor(this.seconds/ 60) % 60 // seconds
Math.floor(this.recordTimer / (60 * 60)) % 60 // hours
Mithsew
  • 1,129
  • 8
  • 20