html+js+css实现一个圆形滑块,可以拖动,可以点击,先看效果再讲原理,最后附上源码:
产品经理设计了这样一个需求,通过拖动圆形滑块实现时间的设置功能,虽然看着有点复杂,但是确实有点复杂。
实现思路
需求分析:
-
一个圆盘,一个滑块;
-
以圆盘为圆心,点击圆盘任意位置或者拖动滑块,滑块会移动到指定位置;
-
同步保存滑块的值
实现逻辑:
获取鼠标点击或者移动的位置到圆盘圆心水平x和垂直y的距离;
然后使用Math.atan2函数根据x和y计算出这个点到圆心这条线和x轴的角度angle,angle的范围为 -π 到 π;
当点击的位置位于x轴上方时,angle小于零,位于x轴下方时,angle大于零
最后根据这个angle,就可以获取一个相对于这个圆形360°的百分比,配合设置的最大值和最小值计算出当前滑块的值。
代码实现
这里就不说太多废话了,直接把完整代码放下面,注释很清楚😉
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style>#container{width: 200px;height: 200px;border-radius: 100px;background: aqua;margin: 50px;}#bar{width: 20px;height: 20px;background: red;border-radius: 10px;pointer-events: none;text-align: center;font-size: 12px;line-height: 20px;color: white;}</style>
</head>
<body>
<div id="container"><!-- <div class="track"></div>--><div id="bar"></div>
</div><script>// 滑块容器const slider = document.getElementById('container')// 滑块const bar = document.getElementById('bar')// 滑动轨道半径const radius = 100// 滑块的值,以及最小和最大let value = 50, min = 0, max = 100// 当前是否正在拖动let isDragging = false// 鼠标按下事件slider.addEventListener('mousedown', (event) => {event.preventDefault();// 开始拖动isDragging = true;// 监听鼠标移动事件slider.addEventListener('mousemove', handleMouseMove);// 鼠标松开事件slider.addEventListener('mouseup', endDrag);// 鼠标移出区域slider.addEventListener('mouseleave', endDrag);})// 处理鼠标移动事件const handleMouseMove = (event) => {if (isDragging) {updateValue(event)}}// 更新滑块数值const updateValue = event => {// 获取容器中心点const centerX = slider.offsetWidth / 2;const centerY = slider.offsetHeight / 2;// 鼠标位置到中心x和y方向的距离const deltaX = event.offsetX - centerXconst deltaY = event.offsetY - centerY// 根据距离计算鼠标到中心点的角度,Math.atan2返回值为 -π 到 π ,导致滑动的位置时从圆形的最右端开始滑动的,所以用 + Math.PI/2处理一些初始位置let angle = Math.atan2(deltaY, deltaX) + Math.PI/2// 转换为角度 0-360let newValue = ((angle * 180) / Math.PI + 360) % 360;// 将360分段newValue = Math.round(newValue/3.6)*3.6// 根据百分比计算滑块的值newValue = (newValue / 360) * (max - min) + min;if (newValue <= min) {newValue = max;} else if (newValue > max) {newValue = max;}value = newValue.toFixed(0)bar.innerText = value// 更新滑块位置updatePosition()}// 鼠标点击时也更新滑块的值slider.addEventListener('click', updateValue);// 更新滑块位置const updatePosition = () => {// 根据值计算角度,然后计算出滑块的位置let angle = ((value - min) / (max - min)) * 360 - 90;const x = Math.cos((angle * Math.PI) / 180) * radius + 90;const y = Math.sin((angle * Math.PI) / 180) * radius + 90;bar.style.transform = `translate(${x}px, ${y}px)`}// 根据默认值更新滑块位置bar.innerText = valueupdatePosition()// 结束拖动const endDrag = () => {isDragging = false;// 移除相关监听事件slider.removeEventListener('mousemove', handleMouseMove);slider.removeEventListener('mouseup', endDrag);slider.removeEventListener('mouseleave', endDrag);}</script>
</body>
</html>
大家有任何问题都可以在评论区留言交流,相互学习!