在 Vue3 项目中实现计时器组件的使用(Vite+Vue3+Node+npm+Element-plus,附测试代码)

一、概述

记录时间 [2024-12-26]

本文讲述如何在 Vue3 项目中使用计时器组件。具体包括开发环境的配置,Vite+Vue 项目的创建,Element Plus 插件的使用,以及计时器组件的创建和使用。

想要直接实现计时器组件,查看文章的第四部分。

文末附有项目的测试代码,可以直接上手哦。


1. 计时器效果图

计时停止状态

在这里插入图片描述


计时启动状态

在这里插入图片描述


开发工具及文档

  • Node.js - 下载地址;Node.js 安装和配置 - 参考文章
  • VS Code - 下载地址;写前端代码的软件,直接安装即可。或者通过压缩包解压缩安装。
  • Vue3 - 文档
  • Vite - 文档
  • Element Plus - 文档


二、创建 + 配置项目

Vite 是一种新型前端构建工具,意在提供开箱即用的配置,能够显著提升前端开发体验。

1. 创建

在 cmd 中,通过 Vite,构建一个 Vite + Vue 项目。

自行指定项目存放位置。

# check npm, 检查 npm 版本
npm -v

# 7.0 以上版本使用该语句
npm create vite@latest my-vue-app

# choose vue + js
# 选择创建 Vue 项目,使用 JavaScript 语言

# 执行以下命令即可运行该模板项目
cd my-vue-app	# 跳转到项目路径
npm install		# 安装依赖
npm run dev		# 执行运行

2. 项目预览

通过浏览器,访问对应的 http 路径,就可以查看这个模板项目。

# 通过浏览器访问
Local:   http://localhost:5173/

# Ctrl + C 终止项目

在这里插入图片描述


3. 修改项目模板

使用 VS Code 打开项目 my-vue-app,观察左边四个文件。

  • 删除 HelloWorld.vuestyle.css
  • 再把 App.vuemain.js 里面没用的代码注释掉。

在这里插入图片描述


就剩下一个基本框架,方便我们后续写内容。

在这里插入图片描述



修改 v3 创建模板(可选)

文件路径:C:\Users\user\.vscode\extensions\sdras.vue-vscode-snippets\snippets\vue.json

修改 vbase-3-setup 模板,后续创建 vue 组件时会用到。

  "Vue Single File Component Setup Composition API": {
    "prefix": "vbase-3-setup",
    "body": [
	  "<script setup>",
      "",
      "</script>",
      "",
      "<template>",
      "",
      "</template>",
      "",
      "<style scoped>",
      "",
      "</style>"
    ],
    "description": "Base for Vue File Setup Composition API"
  },

修改后,vbase-3-setup 模板效果如下,输入 v3 即可使用。

重启 VS Code 生效。

<script setup>

</script>

<template>

</template>

<style scoped>

</style>

4. 安装前端组件

安装组件

Element Plus 是基于 Vue 3,面向设计师和开发者的组件库。里面有一些基础的前端组件样式,如布局、按钮、输入框等,可用于简化开发。

通过 npm 安装 Element Plus:

可以用 cmd,也可以通过 VS Code 中的终端 Terminal 来执行项目。后续 Terminal 用的比较多。

# Ctrl + C 终止项目

# 安装 Element Plus
npm install element-plus --save

会安装在项目的 node_modules 目录下。


导入组件

然后在 main.js 中导入组件。

import { createApp } from 'vue'

import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

import App from './App.vue'

const app = createApp(App)

app.use(ElementPlus)
app.mount('#app')


三、整体布局

1. 测试

经过上面的修改,运行项目后,我们在浏览器中看到的是一个空白的页面。

npm install		# 安装依赖
npm run dev		# 执行运行

App.vue 中写点内容测试一下:

<script setup>
</script>

<template>
  <h3>test</h3>
</template>

<style scoped></style>

保存修改后的文件,然后刷新浏览器的页面。就可以观察到更新后的内容。


2. 栅格布局

使用 Element Plus,通过基础的 24 分栏,迅速简便地创建布局。

挑选一个左中右 3 分的布局。

App.vue 中实现这个效果。


HTML 显示

此时布局中,“左中右” 分布为 8:8:8(24),也就是均分,我们可以手动调整为 6:12:6(24),这样中间的空间多一些。

<template>
  <el-row>

    <!-- left -->

    <el-col :span="8">
      <div class="grid-content ep-bg-purple" />
      <h1>left</h1>
    </el-col>

    <!-- center -->

    <el-col :span="8">
      <div class="grid-content ep-bg-purple-light" />
      <h1>center</h1>
      <h1>计时器组件</h1>
    </el-col>

    <!-- right -->

    <el-col :span="8">
      <div class="grid-content ep-bg-purple" />
      <h1>right</h1>
    </el-col>
  </el-row>
</template>

CSS 样式

<style scoped>
h1 {
  text-align: center;
}

.el-row {
  margin-bottom: 20px;
}

.el-row:last-child {
  margin-bottom: 0;
}

.el-col {
  border-radius: 4px;
}

.grid-content {
  border-radius: 4px;
  min-height: 36px;
}
</style>

3. 效果预览

在浏览器中呈现的效果如下图所示。

在这里插入图片描述



四、计时器组件

1. 创建 + 导入组件

Vue 组件的创建位置一般在项目的 my-vue-app\src\components 目录下。

在该目录下,创建计时器组件 Timer.vue

<script setup>

</script>

<template>
    <h3>计时器组件 Timer.vue 导入到 App.vue 中</h3>
</template>

<style scoped></style>

将计时器组件导入到 App.vue

  • <script> 标签里导入;
  • <template> 中应用。
<script setup>
import Timer from './components/Timer.vue'
</script>

<template>
	<!-- template 里面有其他代码的,Timer 放到对应位置即可 -->
	<!-- 放到 center 布局里 -->
	<Timer />
</template>

2. 计时器的布局

先在网页上绘制计时器的静态布局,就是先画出一个样子来。

  • 内 / 外边框,按钮,使用 Element Plus 组件;
  • “时分秒” 显示 2 位数,“毫秒” 显示 3 位数;
  • 各种文字调整成合适大小。

效果预览

在这里插入图片描述


代码编写

外边框

外边框在 App.vue 中实现,在 <style> 中编写一个 css 样式,使用 Element Plus 组件。

.box {
  width: 90%;
  border: 2px solid var(--el-border-color);
  border-radius: 0;
  padding: 30px;
}

<template> 中使用:

<div class="box">
	<Timer1 />
</div>


其他布局在 Timer.vue 中编写代码。



内边框 + 边框内的文字 + 三个按钮

  • 使用 Element Plus 边框组件,在一行中循环显示 4 个小边框。
  • 边框内的文字分两部分:显示时间;时间单位。
  • 使用 Element Plus 按钮组件,设置 “开始”,“暂停”,“重置” 3 个按钮。

<template>
    <h1>计时器</h1>

    <!-- display: inline; 让 div 在同一行 -->
    <!-- 循环显示 4 个小边框 -->
    <div v-for="i in 4" style="display: inline;">
        <div class="radius" :style="{
            borderRadius: 'small'
                ? `var(--el-border-radius-small)`
                : '',
        }">
            <div class="show-size">
                00
                <span style="font-size: 20px;">小时</span>
                
                <!-- 通过总耗时获取对应的时分秒 -->
                <!-- {{ formatTime(elapsedTime, time[i - 1]) }} -->

                <!-- 通过数组获取对应的时间单位 -->
                <!-- <span style="font-size: 20px;">{{ timeShow[i - 1] }}</span> -->
            </div>
        </div>
    </div>

    <!-- 框 和 button 使用 element-plus -->

    <div style="margin-top: 120px;">
        <el-button type="primary">开始</el-button>
        <el-button type="info">暂停</el-button>
        <el-button>重置</el-button>
    </div>
</template>

<style scoped>
.show-size {
    font-size: 2em;
    margin: 10px 0;
    text-align: center;
}

.radius {
    height: 60px;
    width: 20%;
    border: 1px solid var(--el-border-color);
    border-radius: 0;
    float: left;
}
</style>

3. 计时器的初步逻辑

接下来在 Timer.vue 中实现计时器的初步逻辑:

  • 使用定时器函数累计耗时,确定时间概念(时分秒)。
  • 能通过网页与计时器组件进行交互。
  • 点击启动,计时器开始工作;点击暂停,计时器暂停工作;点击重置,计时器重置。

定时器函数

setInterval() 是 JavaScript 中的一个定时器函数,用于按照指定的时间间隔(以毫秒为单位)重复执行一段代码或函数。它会持续调用指定的函数,直到 clearInterval() 被调用或者页面被卸载。

setInterval() 会返回一个 intervalID,是正整数形式的标识符,可用于清除定时器

参考用法(观察一下)

// intervalID: id
let intervalID = null;

// 通过 id 启动定时器
// func: 每次定时器到期时要调用的函数或要执行的代码
// delay: 两次调用之间的时间间隔, 以毫秒为单位, 1s = 1000ms
intervalID = setInterval({func}, delay);

// 通过 id 清除定时器
clearInterval(intervalId);

定时器:规定一段时间,每隔一段时间完成一个动作,比如执行一段代码;

计时器:统计累计消耗的时间。


定义变量

Timer.vue 中实现这个逻辑:

  • elapsedTime:累计耗时(以毫秒为单位);
  • intervalId:定时器的 ID
  • setInterval():启动定时器;
  • clearInterval():清除定时器。
  • isRunning:判断计时器是否启动的标志。(计时器和定时器同启同停)

当你在模板中使用了一个 ref,然后改变了这个 ref 的值时,Vue 会自动检测到这个变化,并且相应地更新 DOM。

<script> 标签中导入 ref,定义变量。

<script setup>
import { ref } from 'vue';

// 计时器的启动状态 false 暂停计时;ture 正在计时
const isRunning = ref(false);

// 计时器的耗时(以毫秒为单位)
const elapsedTime = ref(0);

// 定时器的 id,用于调用定时器的函数,如 setInterval,clearInterval
let intervalId = null;

// 循环获取数组的内容, 配合小边框使用, 用来渲染页面内容
const time = [
    'hours', 'minutes', 'seconds', 'millis'
];

const timeShow = [
    '小时', '分钟', '秒', '毫秒'
];
</script>

启动计时器

编写方法 startTimer(),用于启动计时器。

  • 判断计时器的启动状态,如果已经在计时,直接返回;
  • 更新计时器的启动状态为 ture
  • 通过 id 启动定时器,累加耗时,从而相当于启动了计时器,每隔 5ms 在 elapsedTime 中更新累计耗时。
// 启动计时器
const startTimer = () => {
    // 如果已经在计时,直接返回
    if (isRunning.value) return;

    // 更新计时器的状态为 ture 正在计时
    isRunning.value = true;

    // 通过 id 启动定时器,累加耗时,从而相当于启动了计时器,1s = 1000ms,每 5ms 累加 5ms
    intervalId = setInterval(() => {
        elapsedTime.value += 5;
    }, 5);
}

暂停计时器

编写方法 pauseTimer(),用于暂停计时器。

  • 更新计时器的启动状态为 false
  • 通过 id 清除定时器,暂停耗时的累加,从而相当于暂停了计时器。
// 暂停计时器
const pauseTimer = () => {
    // 更新计时器的状态为 false 暂停计时
    isRunning.value = false;

    // 通过 id 清除定时器,暂停耗时的累加,从而相当于暂停了计时器
    clearInterval(intervalId);
}

重置计时器

编写方法 resetTimer(),用于重置计时器。

  • 更新计时器的启动状态为 false
  • 通过 id 清除定时器,停止耗时的累加;
  • 将总耗时 elapsedTime 的值清空。
  • 停止耗时的累加,且清空总耗时,相当于重置了计时器。
// 重置计时器
const resetTimer = () => {
    // 更新计时器的状态为 false
    isRunning.value = false;

    // 通过 id 清除定时器,暂停耗时的累加
    clearInterval(intervalId);

    // 将已耗时设置为 0
    elapsedTime.value = 0;

    // 重置起始时间
    // for (let i = 0; i < 4; i++) {
    //     startTime.value[i] = 0;
    // }
}

耗时换算

我们通过 elapsedTime 累加耗时,它是以毫秒为单位的,我们要实现 “时,分,秒,毫秒” 分区显示。

需要通过简单的数学计算,来完成耗时换算。
1 s = 1000 m s 1 m i n = 60 s = ( 60 × 1000 ) m s 1 h = 60 m i n = ( 60 × 60 ) s = ( 60 × 60 × 1000 ) m s 1 s = 1000 ms \\ 1 min = 60 s = (60×1000) ms \\ 1 h = 60 min = (60×60) s = (60×60×1000) ms 1s=1000ms1min=60s=(60×1000)ms1h=60min=(60×60)s=(60×60×1000)ms


倒过来计算,通过毫秒换算成 “时,分,秒,毫秒” 显示。

注意:程序中整数做除法,会直接省略小数点后面的数值(3739220/1000 = 3739)
e g : t o t a l T i m e = 3739220 m s s h o w : m i l l i s = t o t a l T i m e % 1000 = 3739220 % 1000 = 220 m s s h o w : s e c o n d s = ( t o t a l T i m e / 1000 ) % 60 = 19 s s h o w : m i n u t e s = ( t o t a l T i m e / ( 1000 ∗ 60 ) ) % 60 = 2 m i n s h o w : h o u r s = ( t o t a l T i m e / ( 1000 ∗ 60 ∗ 60 ) ) % 24 = 1 h e n d S h o w : 1 h : 2 m i n : 19 s : 220 m s eg: totalTime = 3739220 ms \\ show: millis = totalTime \% 1000 = 3739220 \% 1000 = 220ms \\ show: seconds = (totalTime / 1000) \% 60 = 19s \\ show: minutes = (totalTime / (1000 * 60)) \% 60 = 2min \\ show: hours = (totalTime / (1000 * 60 * 60)) \% 24 = 1h \\ endShow:1h:2min:19s:220ms eg:totalTime=3739220msshow:millis=totalTime%1000=3739220%1000=220msshow:seconds=(totalTime/1000)%60=19sshow:minutes=(totalTime/(100060))%60=2minshow:hours=(totalTime/(10006060))%24=1hendShow:1h:2min:19s:220ms


在代码中编写耗时换算函数 formatTime

// 时分秒分区显示
const formatTime = (milliseconds, timeString) => {
    const millis = Math.floor(milliseconds % 1000);
    const seconds = Math.floor((milliseconds / 1000) % 60);
    const minutes = Math.floor((milliseconds / (1000 * 60)) % 60);
    const hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24);

    // 配合数组 time 使用,确定获取的是 millis/seconds/minutes/hours 其中一部分
    // String(obj).padStart(3, '0') 格式化数据的显示,毫秒 3 位,其他 2 位
    
    if (timeString == 'millis') {
        return `${String(millis).padStart(3, '0')}`;
    } else if (timeString == 'seconds') {
        return `${String(seconds).padStart(2, '0')}`;
    } else if (timeString == 'minutes') {
        return `${String(minutes).padStart(2, '0')}`;
    } else if (timeString == 'hours') {
        return `${String(hours).padStart(2, '0')}`;
    }

}

4. 更新页面渲染

动态显示

Timer.vue<template> 标签中,使用插值表达式 {{}} 来完成页面内容的动态显示。

  • 通过总耗时获取对应的 “时分秒”
<!-- i 用于循环遍历 -->
{{ formatTime(elapsedTime, time[i - 1]) }}

  • 通过数组获取对应的时间单位
<!-- i 用于循环遍历 -->
{{ timeShow[i - 1] }}

  • 绑定按钮点击事件可点击状态
<!-- @click="startTimer", 点击按钮触发 startTimer() 方法 -->
<!-- :disabled="isRunning", 根据 isRunning 的值确定按钮是否被禁用, true 禁用, false 不禁用 -->

<el-button @click="startTimer" :disabled="isRunning" type="primary">开始</el-button>

<!-- 另外两个按钮同理 -->

<el-button @click="pauseTimer" :disabled="!isRunning" type="info">暂停</el-button>
<el-button @click="resetTimer">重置</el-button>

效果预览

点击启动,计时器开始工作;点击暂停,计时器暂停工作;点击重置,计时器重置。

在这里插入图片描述


计时器不动时,“暂停” 按钮不可点击;计时器运行时,“开始” 按钮不可点击。

(按钮被禁用时,颜色会变浅一些)

在这里插入图片描述



接下来开始优化计时器,增加设置起始时间功能,同时设置运行时特效。



5. 设置起始时间

效果预览

在计时器的下方,可以设置计时器的起始时间。

需要达到的效果:按照 “时分秒” 的格式设置起始时间,点击 “提交” 按钮后,上方计时器显示的起始时间,就是我们提交的时间。

在这里插入图片描述


逻辑实现

Timer.vue<script> 标签中编写逻辑代码。

  • startTime:存放 “时分秒” 格式的初始时间的数组,默认为 [0, 0, 0, 0]
  • setStartTime():将提交的 startTime 换算成 “毫秒”,然后赋值给计时器的耗时 elapsedTime
  • 重置计时器的同时,将 startTime 的值重置为 [0, 0, 0, 0]
// 起始时间
const startTime = ref([0, 0, 0, 0]);

// 设置起始时间,换算成毫秒,赋值给计时器的耗时
const setStartTime = () => {
    elapsedTime.value = (((startTime.value[1] * 60) + startTime.value[2]) * 60 + startTime.value[3]) * 1000;
}

// 重置计时器
const resetTimer = () => {
    // other codes...

    // 重置起始时间
    for (let i = 0; i < 4; i++) {
        startTime.value[i] = 0;
    }
}

样式设计

使用 Element Plus 组件,挑选 3 个下拉选项框,分别对应选择的 “时分秒”。

  • 在下拉选项框中,使用 v-modelstartTime 数组内容双向绑定,这样会将选中的起始时间保存到这个数组中。
  • 使用 :disabled 绑定 isRunning 的值,来决定下拉选项框是否被禁用。在计时器启动时,禁用;计时器停止时,不禁用。

再使用一个 “提交” 按钮,点击按钮,就执行 setStartTime() 方法。

  • “提交” 按钮的点击事件绑定 setStartTime() 方法,将提交的 startTime 换算成 “毫秒”,然后赋值给计时器的耗时 elapsedTime
  • 由于使用了 refDOM 会自动更新,也就是计时器上的时间会更新成所提交的起始时间。
  • 使用 :disabled 绑定 isRunning 的值,在计时器启动时,禁用 “提交” 按钮,不允许修改时间。

Timer.vue<template> 标签中编写。

<!-- set Start Time -->

<h3>设置起始时间</h3>

<div v-for="i in 3" style="display: inline;">
    <el-select v-model="startTime[i]" :disabled="isRunning" placeholder="Select" class="select-box">
        <el-option v-for="item in 60" :key="item - 1" :label="`${item - 1} ${timeShow[i - 1]}`" :value="item - 1" />
    </el-select>

</div>

<!-- 计时器启动时不允许修改时间 -->

<el-button type="success" @click="setStartTime" :disabled="isRunning">提交</el-button>

给下拉选项框设置一个 css 样式,在 Timer.vue<style> 标签中编写。

.select-box {
    width: 100px;
    margin-right: 10px;
}

6. 设置运行时特效

在计时器启动时,计时器上显示的时间会发生变化。在此时,我们给计时器设计一个特别的 css 样式。

  • 在计时器暂停 / 停止时,计时器上显示的时间不动,颜色是默认的;
  • 在计时器启动时,计时器上显示的时间会发生变化,颜色也发生变化,变成海棠红色。

效果预览

计时器启动时的效果。

在这里插入图片描述


代码编写

Timer.vue<template> 标签中修改代码。该效果是使用 v-if 来实现的。

<!-- set running css v-if -->

<div class="show-size">

    <span v-if="isRunning" style="color:#db5a6b;">

        <!-- 通过总耗时获取对应的时分秒 -->
        {{ formatTime(elapsedTime, time[i - 1]) }}

        <!-- 通过数组获取对应的时间单位 -->
        <span style="font-size: 20px;">{{ timeShow[i - 1] }}</span>
    </span>

    <span v-else>
        {{ formatTime(elapsedTime, time[i - 1]) }}
        <span style="font-size: 20px;">{{ timeShow[i - 1] }}</span>
    </span>

</div>


五、运行项目

至此,在 Vue3 项目中使用计时器组件就成功实现了,点击 VS Code 左上角的 Terminal -> New Terminal,转到 powershell 中,执行以下命令,即可运行此项目。

npm install		# 安装依赖
npm run dev		# 执行运行

# 通过浏览器访问
Local:   http://localhost:5173/

# Ctrl + C 终止项目


六、完整测试代码

1. create

# check npm
npm -v

# npm 7+, 创建 Vite+Vue3 项目
npm create vite@latest my-vue-app

# choose vue + js
# 选择创建 Vue 项目,使用 JavaScript 语言

# 跳转到项目路径
cd my-vue-app

# 安装 element-plus
npm install element-plus --save

npm install		# 安装依赖
npm run dev		# 执行运行

# 通过浏览器访问
Local:   http://localhost:5173/

# Ctrl + C 终止项目

2. main.js

import { createApp } from 'vue'

// 引入 element-plus
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

import App from './App.vue'

const app = createApp(App)

app.use(ElementPlus)
app.mount('#app')

3. App.vue

<script setup>
import Timer from './components/Timer.vue'
</script>

<template>
  <el-row>

    <!-- left -->

    <el-col :span="6">
      <div class="grid-content ep-bg-purple" />
    </el-col>

    <!-- center -->

    <el-col :span="12">
      <div class="grid-content ep-bg-purple-light" />

      <div class="box">
        <Timer />
      </div>

    </el-col>

    <!-- right -->

    <el-col :span="6">
      <div class="grid-content ep-bg-purple" />
    </el-col>
  </el-row>
</template>

<style scoped>
h1 {
  text-align: center;
}

.box {
  width: 90%;
  border: 2px solid var(--el-border-color);
  border-radius: 0;
  padding: 30px;
}

.el-row {
  margin-bottom: 20px;
}

.el-row:last-child {
  margin-bottom: 0;
}

.el-col {
  border-radius: 4px;
}

.grid-content {
  border-radius: 4px;
  min-height: 36px;
}
</style>

4. Timer.vue

<script setup>
import { ref } from 'vue';

// 计时器的启动状态 false 暂停计时;ture 正在计时
const isRunning = ref(false);

// 计时器的耗时(以毫秒为单位)
const elapsedTime = ref(0);

// 定时器的 id,用于调用定时器的函数,如 setInterval,clearInterval
let intervalId = null;

// 循环获取数组的内容, 配合小边框使用, 用来渲染页面内容
const time = [
    'hours', 'minutes', 'seconds', 'millis'
];

const timeShow = [
    '小时', '分钟', '秒', '毫秒'
];

// 起始时间
const startTime = ref([0, 0, 0, 0]);

// 设置起始时间,换算成毫秒,赋值给计时器的耗时
const setStartTime = () => {
    elapsedTime.value = (((startTime.value[1] * 60) + startTime.value[2]) * 60 + startTime.value[3]) * 1000;
}

// 时分秒分区显示
const formatTime = (milliseconds, timeString) => {
    const millis = Math.floor(milliseconds % 1000);
    const seconds = Math.floor((milliseconds / 1000) % 60);
    const minutes = Math.floor((milliseconds / (1000 * 60)) % 60);
    const hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24);

    // 配合数组 time 使用,确定获取的是 millis/seconds/minutes/hours 其中一部分
    // String(obj).padStart(3, '0') 格式化数据的显示,毫秒 3 位,其他 2 位

    if (timeString == 'millis') {
        return `${String(millis).padStart(3, '0')}`;
    } else if (timeString == 'seconds') {
        return `${String(seconds).padStart(2, '0')}`;
    } else if (timeString == 'minutes') {
        return `${String(minutes).padStart(2, '0')}`;
    } else if (timeString == 'hours') {
        return `${String(hours).padStart(2, '0')}`;
    }

}

// 启动计时器
const startTimer = () => {
    // 如果已经在计时,直接返回
    if (isRunning.value) return;

    // 更新计时器的状态为 ture 正在计时
    isRunning.value = true;

    // 通过 id 启动定时器,累加耗时,从而相当于启动了计时器,1s = 1000ms,每 5ms 累加 5ms
    intervalId = setInterval(() => {
        elapsedTime.value += 5;
    }, 5);
}

// 暂停计时器
const pauseTimer = () => {
    // 更新计时器的状态为 false 暂停计时
    isRunning.value = false;

    // 通过 id 清除定时器,暂停耗时的累加,从而相当于暂停了计时器
    clearInterval(intervalId);
}

// 重置计时器
const resetTimer = () => {
    // 更新计时器的状态为 false
    isRunning.value = false;

    // 通过 id 清除定时器,暂停耗时的累加
    clearInterval(intervalId);

    // 将已耗时设置为 0
    elapsedTime.value = 0;

    // 重置起始时间
    for (let i = 0; i < 4; i++) {
        startTime.value[i] = 0;
    }
}

</script>

<template>
    <h1>计时器</h1>

    <!-- display: inline; 让 div 在同一行 -->
    <!-- 循环取数 -->
    <div v-for="i in 4" style="display: inline;">
        <div class="radius" :style="{
            borderRadius: 'small'
                ? `var(--el-border-radius-small)`
                : '',
        }">

            <!-- set running css v-if -->

            <div class="show-size">

                <span v-if="isRunning" style="color:#db5a6b;">

                    <!-- 通过总耗时获取对应的时分秒 -->
                    {{ formatTime(elapsedTime, time[i - 1]) }}

                    <!-- 通过数组获取对应的时间单位 -->
                    <span style="font-size: 20px;">{{ timeShow[i - 1] }}</span>
                </span>

                <span v-else>
                    {{ formatTime(elapsedTime, time[i - 1]) }}
                    <span style="font-size: 20px;">{{ timeShow[i - 1] }}</span>
                </span>

            </div>
        </div>
    </div>

    <!-- 框 和 button 使用 element-plus -->

    <div style="margin-top: 120px;">

        <!-- @click 绑定按钮点击事件;:disabled 绑定按钮是否可以点击 -->

        <el-button @click="startTimer" :disabled="isRunning" type="primary">开始</el-button>
        <el-button @click="pauseTimer" :disabled="!isRunning" type="info">暂停</el-button>
        <el-button @click="resetTimer">重置</el-button>
    </div>

    <!-- set Start Time -->

    <h3>设置起始时间</h3>

    <div v-for="i in 3" style="display: inline;">
        <el-select v-model="startTime[i]" :disabled="isRunning" placeholder="Select" class="select-box">
            <el-option v-for="item in 60" :key="item - 1" :label="`${item - 1} ${timeShow[i - 1]}`" :value="item - 1" />
        </el-select>

    </div>

    <!-- 计时器启动时不允许修改时间 -->

    <el-button type="success" @click="setStartTime" :disabled="isRunning">提交</el-button>
</template>

<style scoped>
.show-size {
    font-size: 2em;
    margin: 10px 0;
    text-align: center;
}

.radius {
    height: 60px;
    width: 20%;
    border: 1px solid var(--el-border-color);
    border-radius: 0;
    float: left;
}

.select-box {
    width: 100px;
    margin-right: 10px;
}
</style>


参考资料

Vue3 文档:https://cn.vuejs.org/guide/introduction.html

Vite 文档:https://vitejs.cn/vite3-cn/guide/#scaffolding-your-first-vite-project

Element-plus 文档:https://element-plus.org/zh-CN/component/overview.html

色彩工具:https://tool.lu/index.php/color/palette.html?id=2

VS Code 官网:https://code.visualstudio.com/

Node.js 官网:https://nodejs.org/zh-cn/download/prebuilt-binaries

参考文章 - Node.js 安装和配置:https://blog.csdn.net/Sareur_1879/article/details/144729321

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/943446.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

图神经网络_图嵌入_Struc2Vec

0 背景 之前的node embedding方式&#xff0c;都是基于近邻关系&#xff0c;但是有些节点没有近邻&#xff0c;也有结构相似性。如图中的u、v节点。 struc2vec算法适用于捕获结构相似性。 1 相似度&#xff08;距离&#xff09;计算 1.1 公式 f k ( u , v ) f k − 1 ( u …

JZ31 栈的压入、弹出序列

题目来源&#xff1a;栈的压入、弹出序列_牛客题霸_牛客网 题目&#xff1a;如下 输入两个整数序列&#xff0c;第一个序列表示栈的压入顺序&#xff0c;请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序&#xf…

Android 蓝牙开发-传输数据

概述 传统蓝牙是通过建立REFCCOM sockect来进行通信的&#xff0c;类似于socket通信&#xff0c;一台设备需要开放服务器套接字并处于listen状态&#xff0c;而另一台设备使用服务器的MAC地址发起连接。连接建立后&#xff0c;服务器和客户端就都通过对BluetoothSocket进行读写…

Java圣诞树

目录 写在前面 技术需求 程序设计 代码分析 一、代码结构与主要功能概述 二、代码功能分解与分析 1. 类与常量定义 2. 绘制树的主逻辑 3. 彩色球的绘制 4. 动态效果的实现 5. 窗口初始化 三、关键特性与优点 四、总结 写在后面 写在前面 Java语言绘制精美圣诞树…

认识计算机网络

单单看这一个词语&#xff0c;有熟悉又陌生&#xff0c;让我们来重新认识一下这位大角色——计算机网络。 一、是什么 以及 怎么来的 计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路和通信设备连接起来&#xff0c;在网络操作…

【再谈设计模式】享元模式~对象共享的优化妙手

一、引言 在软件开发过程中&#xff0c;我们常常面临着创建大量细粒度对象的情况&#xff0c;这可能会导致内存占用过高、性能下降等问题。享元模式&#xff08;Flyweight Pattern&#xff09;就像是一位空间管理大师&#xff0c;它能够在不影响功能的前提下&#xff0c;有效地…

用Python写炸金花游戏

文章目录 **代码分解与讲解**1. **扑克牌的生成与洗牌**2. **给玩家发牌**3. **打印玩家的手牌**4. **定义牌的优先级**5. **判断牌型**6. **确定牌型优先级**7. **比较两手牌的大小**8. **打印结果** 完整代码 以下游戏规则&#xff1a; 那么我们要实现的功能&#xff0c;就是…

WebRTC服务质量(07)- 重传机制(04) 接收NACK消息

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

Cadence学习笔记 11 PCB中器件放置

基于Cadence 17.4&#xff0c;四层板4路HDMI电路 更多Cadence学习笔记&#xff1a;Cadence学习笔记 1 原理图库绘制Cadence学习笔记 2 PCB封装绘制Cadence学习笔记 3 MCU主控原理图绘制Cadence学习笔记 4 单片机原理图绘制Cadence学习笔记 5 四路HDMI原理图绘制Cadence学习笔记…

Docker 入门:如何使用 Docker 容器化 AI 项目(二)

四、将 AI 项目容器化&#xff1a;示例实践 - 完整的图像分类与 API 服务 让我们通过一个更完整的 AI 项目示例&#xff0c;展示如何将 AI 项目容器化。我们以一个基于 TensorFlow 的图像分类模型为例&#xff0c;演示如何将训练、推理、以及 API 服务过程容器化。 4.1 创建 …

三层交换机配置

一&#xff0c;三层交换 概念&#xff1a;三层交换技术就是&#xff1a;二层交换技术三层转发技术(路由器功能)。它解决了局域网中网段划分之后&#xff0c;网段中子网必须依赖路由器进行管理的局面&#xff0c;解决了传统路由器低速&#xff0c;复杂所造成的网络瓶颈问题。 …

LabVIEW应用在工业车间

LabVIEW作为一种图形化编程语言&#xff0c;以其强大的数据采集和硬件集成功能广泛应用于工业自动化领域。在工业车间中&#xff0c;LabVIEW不仅能够实现快速开发&#xff0c;还能通过灵活的硬件接口和直观的用户界面提升生产效率和设备管理水平。尽管其高成本和初期学习门槛可…

【CSS in Depth 2 精译_094】16.2:CSS 变换在动效中的应用(下)——导航菜单的文本标签“飞入”特效与交错渲染效果的实现

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第五部分 添加动效 ✔️【第 16 章 变换】 ✔️ 16.1 旋转、平移、缩放与倾斜 16.1.1 变换原点的更改16.1.2 多重变换的设置16.1.3 单个变换属性的设置 16.2 变换在动效中的应用 16.2.1 放大图标&am…

Qt使用QZipWriter和QZipReader来解压、压缩文件

首先感谢这位博主的无私奉献&#xff1a;Qt - 实现压缩文件、文件夹和解压缩操作 - [BORUTO] - 博客园 多文件和目录压缩时&#xff0c;不改变原始文件和目录的相对位置结构&#xff0c;需要在addFile和addDirectory时&#xff0c;需要带上相对路径&#xff0c;如下&#xff1…

PH热榜 | 2024-12-23

1. Websparks 标语&#xff1a;让你的创意变为现实的AI软件工程师 介绍&#xff1a;现在&#xff0c;构建网页应用从未如此简单快捷&#xff01;WebSparks是一个基于人工智能的平台&#xff0c;它能让开发者、设计师&#xff0c;甚至不懂编程的人&#xff0c;都能在很短的时间…

Opencv之对图片的处理和运算

Opencv实现对图片的处理和修改 目录 Opencv实现对图片的处理和修改灰度图读取灰度图转换灰度图 RBG图单通道图方法一方法二 单通道图显色合并单通道图 图片截取图片打码图片组合缩放格式1格式2 图像运算图像ma[m:n,x:y]b[m1:n1,x1:y1] add加权运算 灰度图 读取灰度图 imread(‘…

OpenLinkSaas使用手册-Git工具

在OpenLinkSaas的工具箱里面&#xff0c;最基础的一个就是Git仓库管理。Git仓库功能让git使用更加简单和强大&#xff0c;不仅可以使用常规的commit/pull/push/branch等功能外&#xff0c;还连接了Git仓库供应商的能力。 OpenLinkSass支持使用国内主流的Git仓库供应商的账号登录…

WebRTC服务质量(12)- Pacer机制(04) 向Pacer中插入数据

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

protobuf学习使用

1、概述 protobuf是Google开发的一种语言中立、平台无关、可扩展的序列化结构数据格式。允许定义一次数据结构&#xff0c;然后可以使用各种支持的语言来生成代码&#xff0c;以轻松地读写这些结构到一个二进制流中&#xff0c;如网络传输或文件&#xff0c;Protobuf支持多种编…

CTFHUB-web进阶-php

我们用蚁剑中的这个插件来做这些关卡 一.LD_PRELOAD 发现这里有一句话木马&#xff0c;并且把ant给了我们&#xff0c;我们直接连接蚁剑 右键 选择模式&#xff0c;都可以试一下&#xff0c;这里第一个就可以 点击开始 我们进入到目录&#xff0c;刷新一下&#xff0c;会有一个…