| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- <template>
- <div class="slider-container" ref="slider-container">
- <div class="slider-track">
- <span class="slider-internalValue-text">{{ min }}</span>
- <div
- class="slider-progress"
- :style="{ width: `${progressWidth}%` }"
- @touchstart.stop="startDrag"
- @touchmove.stop="onDrag"
- @touchend.stop="endDrag">
- <span class="slider-progress-line"></span>
- </div>
- <span
- v-if="isFollow && showValue && suffixNormal"
- class="slider-internal-text"
- :style="{
- left: isFollow && progressWidth < 86 ? `calc(${progressWidth}% + 10px)` : 'auto',
- color: followColor
- }"
- >{{ showValue ? internalValue : max }}{{ suffix }}
- </span>
- <!-- <span
- v-if="isFollow && showValue && !suffixNormal"
- class="slider-internal-text"
- :style="{ left: isFollow && progressWidth < 86 ? `calc(${progressWidth}% + 10px)` : 'auto', color: followColor }"
- >{{ showValue ? internalValue : max }}<sup class="slider-internal-text-suffix">{{ suffix }}</sup>
- </span
- > -->
- <span v-else class="slider-max-text" :style="{ color: maxColor }">{{ max }}</span>
- </div>
- </div>
- </template>
- <script setup>
- import { computed, defineModel, defineProps, onMounted, ref, useTemplateRef, watch } from "vue"
- const emit = defineEmits(["onEnd", "onStart"])
- const props = defineProps({
- min: {
- type: Number,
- default: 0
- },
- max: {
- type: Number,
- default: 100
- },
- isFollow: {
- type: Boolean,
- default: false
- },
- suffixNormal: {
- type: Boolean,
- default: false
- },
- suffix: {
- type: String,
- default: ""
- },
- showValue: {
- type: Boolean,
- default: false
- }
- })
- const model = defineModel()
- watch(
- () => model.value,
- newValue => {
- if (newValue) {
- internalValue.value = newValue
- }
- }
- )
- const internalValue = ref(model.value || props.min)
- const isDragging = ref(false)
- let animationFrameId = null
- const sliderContainer = useTemplateRef("slider-container")
- const progressWidth = computed(() => {
- const percentage = ((internalValue.value - props.min) / (props.max - props.min)) * 100
- return `${Math.max(percentage, 10)}`
- })
- // const computedTrackBackground = computed(() => {
- // const percentage = ((internalValue.value - props.min) / (props.max - props.min)) * 100
- // return `linear-gradient(to right, #ff4d4f ${percentage}%, #e0e0e0 ${percentage}%)`
- // })
- const followColor = computed(() => {
- const percentage = ((internalValue.value - props.min) / (props.max - props.min)) * 100
- return percentage >= 95 ? "rgba(255, 255, 255, 0.6)" : "var(--Blue, #001428)"
- })
- const maxColor = computed(() => {
- const percentage = ((internalValue.value - props.min) / (props.max - props.min)) * 100
- return percentage >= 95 ? "rgba(255, 255, 255, 0.6)" : "rgb(116, 128, 141)"
- })
- const startDrag = event => {
- isDragging.value = true
- emit("onStart", internalValue.value)
- }
- const onDrag = event => {
- if (!isDragging.value) return
- const touch = event.touches[0]
- const sliderRect = sliderContainer.value.getBoundingClientRect()
- const offsetX = touch.clientX - sliderRect.left + 16
- // console.log(touch, sliderRect, offsetX)
- // 计算百分比并确保在 0 到 1 之间
- const percentage = Math.min(Math.max(offsetX / sliderRect.width, 0), 1)
- const newValue = Math.round(percentage * (props.max - props.min) + props.min)
- // 更新 internalValue
- if (animationFrameId) {
- cancelAnimationFrame(animationFrameId)
- }
- animationFrameId = requestAnimationFrame(() => {
- model.value = internalValue.value = newValue
- // emit('update:modelValue', internalValue.value) // 确保使用正确的事件名称
- })
- }
- const endDrag = () => {
- isDragging.value = false
- if (animationFrameId) {
- cancelAnimationFrame(animationFrameId)
- }
- emit("onEnd")
- }
- onMounted(() => {
- // 这里可以添加初始化逻辑
- })
- </script>
- <style scoped lang="scss">
- @mixin text-middle($left: auto, $right: auto) {
- position: absolute;
- top: 50%;
- left: $left;
- right: $right;
- transform: translate(0, -50%);
- font-size: 11px;
- z-index: 6;
- color: rgba(255, 255, 255, 0.6);
- pointer-events: none;
- }
- .slider {
- &-container {
- position: relative;
- box-sizing: border-box;
- width: 100%;
- height: 100%;
- border-radius: 12px;
- background: rgba(255, 255, 255, 0.6);
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
- }
- &-track {
- position: absolute;
- box-sizing: border-box;
- width: calc(100% - 4px);
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
- /* padding: 2px; */
- height: 100%; /* 设置轨道高度为 20px */
- border-radius: 10px; /* 圆角 */
- top: 50%;
- transform: translateY(-50%);
- z-index: 1;
- }
- &-progress {
- display: flex;
- align-items: center;
- color: rgba(255, 255, 255, 0.6);
- padding: 0 6px;
- font-size: 11px;
- position: absolute;
- background: linear-gradient(95.5deg, #99282b 21.44%, #a95459 84.95%);
- left: 2px;
- top: 50%;
- transform: translateY(-50%);
- z-index: 1;
- min-width: 4px;
- height: calc(100% - 4px);
- border-radius: 10px;
- position: relative;
- min-width: 30px;
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
- &-line {
- position: absolute;
- height: 12px;
- width: 2px;
- background: rgba(255, 255, 255, 0.8);
- position: absolute;
- right: 6px;
- transform: translate(-50%, 0);
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
- }
- }
- &-internalValue-text {
- @include text-middle(6px, auto);
- font-family: Jost;
- }
- &-max-text {
- @include text-middle(auto, 10px);
- color: rgb(116, 128, 141);
- font-family: Jost;
- }
- &-internal-text {
- @include text-middle(auto, 10px);
- color: #001428;
- font-family: Jost;
- font-size: 20px;
- font-style: normal;
- font-weight: 300;
- &-suffix {
- font-size: 11px;
- font-weight: 400;
- line-height: 15px;
- }
- }
- }
- </style>
|