Search code examples
javascriptvue.jsvuetify.jsprogress-bar

How to complete Vuetify's circular progress bar on middle percentage value?


I am using Vuetify's progress circular component that takes a value prop which is the percentage of current progress and the circle gets completed when the percentage value reaches 100.
My use case is to create a countdown and I can have a max percentage value of let's say 10, which means the circle should show completed on a value of 10. But I can't figure out a way to complete the circle on the middle percentage value.

Is this possible anyhow?

Here is my example code where I want my 10-value and 20-value circles to be filled like the 100-value circle.

Note- The value could be any multiple of 10, like 10, 20, 30, 50, etc.

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data() {
    return {
      second_10: 10,
      second_20: 20,
      second_100: 100,
      interval_10: null,
      interval_20: null,
      interval_100: null,
    }
  },
  methods: {
    countDown(val) {
      this[`interval_${val}`] = setInterval(() => {
         if(this[`second_${val}`] == 0) {
           this[`second_${val}`] = val
         } else {
           this[`second_${val}`]--;
         }
      }, 1000)
    },
    stopCountDown() {
      clearInterval(this.interval_10);
      clearInterval(this.interval_20);
      clearInterval(this.interval_100)
    }
  }
})
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.3.1/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vuetify@2.3.1/dist/vuetify.min.css"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons"/>
<div id="app">
  <v-app id="inspire">
    <v-container>
      <v-row>
        <v-col cols="12" align="center">
          <v-btn small color="error" @click="stopCountDown()">Stop Timer</v-btn>
        </v-col>
        <v-col>
          <v-btn small @click="countDown(100)">start timer</v-btn>
          <v-progress-circular
            :rotate="360"
            :size="50"
            :width="5"
            :value="second_100"
            color="success"
            class="mt-3"
            >
            {{ second_100 }}
          </v-progress-circular>
        </v-col>
        <v-col>
          <v-btn small @click="countDown(10)">start timer</v-btn>
          <v-progress-circular
            :rotate="360"
            :size="50"
            :width="5"
            :value="second_10"
            color="success"
            class="mt-3"
            >
            {{ second_10 }}
          </v-progress-circular>
        </v-col>
        <v-col>
          <v-btn small @click="countDown(20)">start timer</v-btn>
          <v-progress-circular
            :rotate="360"
            :size="50"
            :width="5"
            :value="second_20"
            color="success"
            class="mt-3"
            >
            {{ second_20 }}
          </v-progress-circular>
        </v-col>
      </v-row>
    </v-container>
  </v-app>
</div>


Solution

  • You need to find the multiplier to reach 100 from your initial countdown value. Just do counter * (100 / initialCounterValue) to get it.

    This only concern here is that the initial counter value must be a multiple of 100. It won't work well with like 18 haha

    I'd suggest to make a component so that this logic is in a single place:

    <template>
      <v-col>
         <v-btn small @click="startCountdown">start timer</v-btn>
         <v-progress-circular
            :rotate="360"
            :size="50"
            :width="5"
            :value="counter * (100 / from)"
            color="success"
            class="ms-4"
         >
           {{ counter }}
         </v-progress-circular>
       </v-col>
    </template>
    <script>
    export default {
      props: {
        from: { type: Number, default: 10 }
      },
      data() {
        return {
           counter: this.from,
           interval: null,
        }
      },
      methods: {
        startCountdown() {
           this.interval = setInterval(() => {
              if (this.counter <= 0 && this.interval) {
                 clearInterval(this.interval)
              } else {
                 this.counter--;
              }
           }, 1000)
        }
      }
    }
    </script>
    

    Example with the formula:

    new Vue({
      el: '#app',
      vuetify: new Vuetify(),
      data() {
        return {
          second_100: 100,
          second_20: 20,
        }
      },
      methods: {
        countDown(val) {
          setInterval(() => {
             if(this[`second_${val}`] == 0) {
               this[`second_${val}`] = val
             } else {
               this[`second_${val}`]--;
             }
          }, 1000)
        },
      }
    })
    <script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vuetify@2.3.1/dist/vuetify.min.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vuetify@2.3.1/dist/vuetify.min.css"/>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons"/>
    <div id="app">
      <v-app id="inspire">
        <v-container>
          <v-row>
            <v-col>
              <v-btn small @click="countDown(100)">start timer</v-btn>
              <v-progress-circular
                :rotate="360"
                :size="50"
                :width="5"
                :value="second_100"
                color="success"
                class="ms-4"
                >
                {{ second_100 }}
              </v-progress-circular>
            </v-col>
            <v-col>
              <v-btn small @click="countDown(20)">start timer</v-btn>
              <v-progress-circular
                :rotate="360"
                :size="50"
                :width="5"
                :value="second_20 * (100/20)"
                color="success"
                class="ms-4"
                >
                {{ second_20 }}
              </v-progress-circular>
            </v-col>
          </v-row>
        </v-container>
      </v-app>
    </div>