浏览代码

Set up TotpLooper & Dots components

Bubka 1 年之前
父节点
当前提交
ed1a653b37
共有 3 个文件被更改,包括 174 次插入0 次删除
  1. 4 0
      resources/js_vue3/assets/app.scss
  2. 49 0
      resources/js_vue3/components/Dots.vue
  3. 121 0
      resources/js_vue3/components/TotpLooper.vue

+ 4 - 0
resources/js_vue3/assets/app.scss

@@ -447,6 +447,10 @@ figure.no-icon {
   /* green */
 }
 
+.dots.off li {
+  background: $black-bis;
+}
+
 // .dots li:nth-child(3n+2), .dots li:nth-child(3n+3) {
 //     display:none;
 // }

+ 49 - 0
resources/js_vue3/components/Dots.vue

@@ -0,0 +1,49 @@
+<script setup>
+    const props = defineProps({
+        stepCount: {
+            type: Number,
+            default: 10
+        },
+        initialIndex: {
+            type: Number,
+            default: null
+        },
+    })
+
+    const activeDot = ref(0)
+    const isOff = computed(() => {
+        return activeDot.value == -1
+    })
+
+    /**
+     * Turns On dots
+     */
+    function turnOn(index) {
+        activeDot.value = index < props.stepCount ? index + 1 : 1
+    }
+
+    /**
+     * Turns Off all dots
+     */
+    function turnOff() {
+        activeDot.value = -1
+    }
+
+    onMounted(() => {
+        if (props.initialIndex != null) {
+                turnOn(props.initialIndex)
+            }
+    })
+
+    defineExpose({
+        turnOn,
+        turnOff
+    })
+
+</script>
+
+<template>
+    <ul class="dots" :class="{'off' : isOff}">
+        <li v-for="n in stepCount" :key="n" :data-is-active="n == activeDot ? true : null"></li>
+    </ul>
+</template>

+ 121 - 0
resources/js_vue3/components/TotpLooper.vue

@@ -0,0 +1,121 @@
+<script setup>
+    const props = defineProps({
+        step_count: {
+            type: Number,
+            default: 10
+        },
+        period : Number,
+        generated_at: Number,
+        autostart: {
+            type: Boolean,
+            default: true
+        },
+    })
+
+    const generatedAt = ref(null)
+    const remainingTimeout = ref(null)
+    const initialStepToNextStepTimeout = ref(null)
+    const stepToStepInterval = ref(null)
+    const stepIndex = ref(null)
+
+    //                              |<----period p----->|
+    //     |                        |                   |
+    //     |------- ··· ------------|--------|----------|---------->
+    //     |                        |        |          |
+    //  unix T0                 Tp.start   Tgen_at    Tp.end
+    //                              |        |          |
+    //  elapsedTimeInCurrentPeriod--|<------>|          |
+    //  (in ms)                     |        |          |
+    //                              ● ● ● ● ●|● ◌ ◌ ◌ ◌ |
+    //                              | |      ||         |
+    //                              | |      |<-------->|--remainingTimeBeforeEndOfPeriod (for remainingTimeout)
+    //    durationBetweenTwoSteps-->|-|<     ||         
+    //   (for stepToStepInterval)   | |     >||<---durationFromInitialToNextStep (for initialStepToNextStepTimeout)
+    //                                        |
+    //                                        |
+    //                                    stepIndex
+
+    const elapsedTimeInCurrentPeriod = computed(() => {
+        return generatedAt.value % props.period
+    })
+
+    const remainingTimeBeforeEndOfPeriod = computed(() => {
+        return props.period - elapsedTimeInCurrentPeriod.value
+    })
+
+    const durationBetweenTwoSteps = computed(() => {
+        return props.period / props.step_count
+    })
+
+    const initialStepIndex = computed(() => {
+        let relativePosition = (elapsedTimeInCurrentPeriod.value * props.step_count) / props.period
+
+        return (Math.floor(relativePosition) + 0)
+    })
+
+    const emit = defineEmits(['loop-started', 'loop-ended', 'stepped-up'])
+
+    /**
+     * Starts looping
+     */
+    const startLoop = () => {
+        clearLooper()
+        generatedAt.value = props.generated_at
+
+        emit('loop-started', initialStepIndex.value)
+
+        stepIndex.value = initialStepIndex.value
+
+        // Main timeout that runs until the end of the period
+        remainingTimeout.value = setTimeout(function() {
+            clearLooper()
+            emit('loop-ended')
+        }, remainingTimeBeforeEndOfPeriod.value * 1000);
+
+        // During the remainingTimeout countdown we emit an event every durationBetweenTwoSteps seconds,
+        // except for the first next dot
+        let durationFromInitialToNextStep =  (Math.ceil(elapsedTimeInCurrentPeriod.value / durationBetweenTwoSteps.value) * durationBetweenTwoSteps.value) - elapsedTimeInCurrentPeriod.value
+
+        initialStepToNextStepTimeout.value = setTimeout(function() {
+            if( durationFromInitialToNextStep > 0 ) {
+                stepIndex.value += 1
+                emit('stepped-up', stepIndex.value)
+            }
+            stepToStepInterval.value = setInterval(function() {
+                stepIndex.value += 1
+                emit('stepped-up', stepIndex.value)
+            }, durationBetweenTwoSteps.value * 1000)
+        }, durationFromInitialToNextStep * 1000)
+    }
+
+    /**
+     * Resets all timers and internal vars
+     */
+    const clearLooper = () => {
+        clearTimeout(remainingTimeout.value)
+        clearTimeout(initialStepToNextStepTimeout.value)
+        clearInterval(stepToStepInterval.value)
+        stepIndex.value = generatedAt.value = null
+    }
+
+    onMounted(() => {
+        if (props.autostart == true) {
+            startLoop()
+        }
+    })
+
+    onUnmounted(() => {
+        clearLooper()
+    })
+
+    defineExpose({
+        startLoop,
+        clearLooper
+    })
+
+</script>
+
+<template>
+    <div>
+    </div>
+</template>