效果预览
概述
本文教你如何用像素值(px)表示时间(如:2023-5-6 10:00:00),并且让时间随着鼠标的操作而变化。你只需要掌握一个简单的转换公式,就可以编写出一个漂亮的时间戳进度条。
计算逻辑
假设有一个长度为300px的进度条,并且已知开始时间和结束时间(时间戳需要为秒),当鼠标点击进度条某一段区域时,使用offsetX获取鼠标位置,再进行如下计算:
- 用 offsetX / 进度条长度 * 间隔时长(结束时间 - 开始时间) = 时间(秒)
- new Date(开始时间 + 时间(秒) * 1000)
你可能会好奇,为什么这样的计算能够准确地显示鼠标所在的时间。别着急,下面就告诉你答案。
- 鼠标在进度条上的进度 = e.offsetX / 进度条长度 * 100;
- 小数百分比转为百分数(%),需要乘以100。计算方式和结果为 0.1 * 100 = 10% ... 1 * 100 = 100;
- 我们可以利用这种形式去乘以“间隔时长”,以换算出鼠标所在位置为几秒;
- 假设变量 duration = 7200,e.offsetX / 进度条长度 * duration,持续递进。计算方式和结果为 0.1 * 7200 = 720 ... 1 * 7200 = 7200
- new Date(开始时间 + 时间(秒) * 1000) 这个就很好理解了,开始时间 + 时间(秒)生成完整的时间戳(秒),乘以 1000 ,则是为了转换为毫秒;
如:
let time = offsetX / 进度条长度 * 间隔时长(结束时间 - 开始时间)
2023/10/10 10:00:00 = new Date(startTime + time * 1000).toLocaleString()
代码预览
源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="referrer" content="no-referrer">
<title>Title</title>
<style>
#bar-box {
position: relative;
margin: 50px;
}
#progress-bar {
position: relative;
margin: 50px;
width: 300px;
height: 10px;
background: #7aa1d2;
border-radius: 5px;
cursor: pointer;
}
#progress-bar #completed-progress {
border-radius: 5px;
height: 100%;
position: absolute;
top: 0;
left: 0;
background: #6c5e86;
z-index: 100;
}
#progress-bar span {
position: absolute;
top: -1px;
left: 0;
height: 12px;
width: 12px;
border-radius: 50%;
background: #a0522d;
z-index: 101;
}
#top-tip, #bottom-tip {
position: absolute;
}
#top-tip {
top: -25px;
}
#bottom-tip {
top: 15px;
}
</style>
</head>
<body>
<img src="https://img-blog.csdnimg.cn/6d15082ac7234ec7a16065e74f689590.jpeg" alt="" style="width: 200px;height: 200px">
<div>
<p>
进度条开始时间:<span class="time"></span>
</p>
<p>
进度条结束时间:<span class="time"></span>
</p>
</div>
<div id="bar-box">
<div id="progress-bar">
<div id="completed-progress"></div>
<span id="progress-button"></span>
</div>
<span id="top-tip"></span>
<span id="bottom-tip"></span>
</div>
<button id="button">开始播放</button>
<button id="stop">停止播放</button>
<button id="reset">重置</button>
<script>
const doc = document
const timeDom = doc.getElementsByClassName('time')
const topTip = doc.getElementById('top-tip')
const bottomTip = doc.getElementById('bottom-tip')
const progressBar = doc.getElementById('progress-bar')
const progressButton = doc.getElementById('progress-button')
const completedProgress = doc.getElementById('completed-progress')
const button = doc.getElementById('button')
const stop = doc.getElementById('stop')
const reset = doc.getElementById('reset')
const progressBarW = progressBar.offsetWidth
const progressButtonW = progressButton.offsetWidth
let frame = null
let offsetPx = 0
let date = new Date()
let startTime = date.getTime()
let endTime = date.getTime() + 2 * 60 * 60 * 1000 // 小时
// let endTime = date.getTime() + 2 * 24 * 60 * 60 * 1000 // 天
timeDom[0].innerText = new Date(startTime).toLocaleString()
timeDom[1].innerText = new Date(endTime).toLocaleString()
progressBar.onmousemove = mousemoveBar
progressBar.onclick = clickBar
button.onclick = clickButton
stop.onclick = clickStop
reset.onclick = clickReset
let offsetX = 0
function mousemoveBar(e) {
// 鼠标移动不能精确的达到 e.offsetX = progressBarW 或 e.offsetX = 0,此时需要判断是否移动到末尾或起始位置
if (e.offsetX === 1)
offsetX = 0
else
offsetX = e.offsetX === progressBarW - 1 ? progressBarW : e.offsetX
topTip.innerText = createTime(offsetX)
}
function clickBar(e) {
offsetPx = e.offsetX - progressButtonW / 2
// progressButton.style.left = `${offsetPx}px`
progressButton.style.left = `${offsetPx / progressBarW * 100}%`
completedProgress.style.width = `${e.offsetX / progressBarW * 100}%`
bottomTip.innerText = createTime(e.offsetX)
}
function clickButton() {
if (offsetPx !== progressButtonW / 2) offsetPx += progressButtonW / 2 // 点击播放时,避免出现一瞬间回退几个像素的的情况
start()
}
function start() {
if (offsetPx > progressBarW) {
cancelAnimationFrame(frame)
return
}
frame = requestAnimationFrame(start)
// progressButton.style.left = `${offsetPx - progressButtonW / 2}px`
progressButton.style.left = `calc(${offsetPx / progressBarW * 100}% - ${progressButtonW / 2}px)`
completedProgress.style.width = `${offsetPx / progressBarW * 100}%`
bottomTip.innerText = createTime(offsetPx)
offsetPx += 0.5
}
function clickStop() {
cancelAnimationFrame(frame)
}
function clickReset() {
cancelAnimationFrame(frame)
offsetPx = 0
progressButton.style.left = offsetPx
completedProgress.style.width = '0%'
bottomTip.innerText = ''
topTip.innerText = ''
}
// 秒 = 毫秒 / 1000
// 间隔时长(秒) = 结束时间 - 开始时间
const duration = endTime / 1000 - startTime / 1000
/**
* 鼠标在进度条上的进度(小数百分比) = e.offsetX / progressBarW
* 我们知道,小数百分比转为百分数(%),需要乘以100,计算方式为 0.1 * 100 = 10% 1 * 100 = 100
* 我们可以利用这种形式去乘以”间隔时长“,以换算出鼠标所在的位置,是多少秒
* 如:假设 duration = 7200,e.offsetX / progressBarW * duration 持续递进,计算方式为 0.1 * 7200 = 720 ... 1 * 7200 = 7200,
**/
function createTime(x) {
let time = x / progressBarW * duration
return new Date(startTime + time * 1000).toLocaleString()
}
</script>
</body>
</html>