样式如下,代码如下
<script setup>
import { computed, defineEmits, defineProps, onMounted, ref, watch } from 'vue'
// 定义 props
const props = defineProps({
// 初始百分比
initialPercentage: {
type: Number,
default: 0,
},
})
// 定义 emits
const emits = defineEmits(['drag-percentage-change'])
// 定义圆圈数量
const circleCount = 5
// 进度条容器引用
const progressBarContainer = ref(null)
// 可拖动圆圈的位置(百分比)
const draggableCircleLeftPercentage = ref(props.initialPercentage)
// 可拖动圆圈的位置样式
const draggableCircleLeft = computed(() => `${draggableCircleLeftPercentage.value}%`)
// 进度条直线宽度
const progressLineWidth = computed(() => '100%')
// 已完成部分直线宽度
const completedLineWidth = computed(() => `${draggableCircleLeftPercentage.value}%`)
// 圆圈位置计算
const circles = ref([])
onMounted(() => {
if (progressBarContainer.value) {
const containerWidth = progressBarContainer.value.offsetWidth
for (let i = 0; i < circleCount; i++) {
const leftPercentage = (i / (circleCount - 1)) * 100
circles.value.push({
left: `${leftPercentage}%`,
})
}
}
})
// 开始拖动
const isDragging = ref(false)
const startDragging = (e) => {
if (!progressBarContainer.value)
return
isDragging.value = true
const containerRect = progressBarContainer.value.getBoundingClientRect()
const startX = e.clientX
const handleMouseMove = (e) => {
if (isDragging.value) {
const offsetX = e.clientX - containerRect.left
const containerWidth = containerRect.width
let newLeft = (offsetX / containerWidth) * 100
// 取整并确保在 0 到 100 之间
newLeft = Math.min(100, Math.max(0, Math.round(newLeft)))
draggableCircleLeftPercentage.value = newLeft
// 抛出事件,返回当前百分比
emits('drag-percentage-change', newLeft)
}
}
const handleMouseUp = () => {
if (isDragging.value) {
isDragging.value = false
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
}
}
document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', handleMouseUp)
}
// 点击进度条
const handleBarClick = (e) => {
if (!progressBarContainer.value)
return
const containerRect = progressBarContainer.value.getBoundingClientRect()
const offsetX = e.clientX - containerRect.left
const containerWidth = containerRect.width
let clickPercentage = (offsetX / containerWidth) * 100
// 取整并确保在 0 到 100 之间
clickPercentage = Math.min(100, Math.max(0, Math.round(clickPercentage)))
draggableCircleLeftPercentage.value = clickPercentage
// 抛出事件,返回当前百分比
emits('drag-percentage-change', clickPercentage)
}
// 点击圆圈
const handleCircleClick = (index) => {
const percentage = (index / (circleCount - 1)) * 100
draggableCircleLeftPercentage.value = percentage
emits('drag-percentage-change', percentage)
}
// 监听 initialPercentage 的变化
watch(() => props.initialPercentage, (newValue) => {
// 取整并确保在 0 到 100 之间
const validValue = Math.min(100, Math.max(0, Math.round(newValue)))
draggableCircleLeftPercentage.value = validValue
})
</script>
<template>
<div ref="progressBarContainer" class="progress-bar-container" @mousedown="handleBarClick">
<div class="progress-line" :style="{ width: progressLineWidth }" />
<div class="completed-line" :style="{ width: completedLineWidth }" />
<div
v-for="(circle, index) in circles"
:key="index"
class="progress-circle"
:class="{ active: index * (100 / (circleCount - 1)) <= draggableCircleLeftPercentage }"
:style="{ left: `calc(${circle.left} - 5px)` }"
@click="handleCircleClick(index)"
/>
<div
class="draggable-circle"
:style="{ left: `calc(${draggableCircleLeft} - 7px)` }"
@mousedown="startDragging"
/>
</div>
</template>
<style scoped>
.progress-bar-container {
position: relative;
width: 100%;
height: 20px;
}
.progress-line {
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 0;
width: 100%;
height: 2px;
background-color: gray;
z-index: 1;
}
.completed-line {
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 0;
height: 2px;
background-color: white;
z-index: 2;
}
.progress-circle {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #808080;
z-index: 3;
cursor: pointer;
}
.progress-circle.active {
background-color: white;
}
.draggable-circle {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 14px;
height: 14px;
border-radius: 50%;
background-color: white;
z-index: 4;
cursor: pointer;
}
</style>
<template>
<div>
<CircleProgressBar
:initialPercentage="initialValue"
@drag-percentage-change="handlePercentageChange"
/>
</div>
</template>
<script setup>
import { ref } from 'vue';
import CircleProgressBar from './CircleProgressBar.vue';
const initialValue = ref(20); // 初始百分比为 20%
const handlePercentageChange = (percentage) => {
console.log('当前百分比:', percentage);
// 你可以在这里处理接收到的百分比
};
</script>