样式如下,代码如下

- <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>
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |