Ползунок у range input двигается неверно

Буду благодарен, подскажите, пожалуйста, почему ползунок двигается не синхронно с линией прогресса? введите сюда описание изображения

То есть при максимально раздвинутой линии прогресса все правильно отображается, но когда они стремятся друг к другу, ползунок постепенно начинает отставать от линии прогресса.

     <template>
      <div class="container">
        <div class="slider">

          <div class="slider__inner">
            <div class="sliders_control">
              <span :style="progressStyle" class="slider__progress"></span>
    

              <input
                ref="fromSlider"
                v-model="fromValue"
                id="fromSlider"
                type="range"
                min="0"
                max="100"
                :step="visibleMounth ? stepValue : stepValueYearAndMonth"
              />

              <input
                ref="toSlider"
                v-model="toValue"
                id="toSlider"
                type="range"
                min="0"
                max="100"
                :step="visibleMounth ? stepValue : stepValueYearAndMonth"
              />
            </div>
            <ul class="slider__year-list" v-if="visibleMounth">
              <li
                class="slider__year-item"
                v-for="(months, year) in date"
                :key="year"
              >
                <div class="slider__month">
                  {{ year }}
                  <template v-if="false">
                    <ul
                      class="slider__mouth-list"
                      v-for="(month, index) in months"
                      :key="index"
                    >
                      <li class="slider__mouth-item">{{ month }}</li>
                    </ul>
                  </template>
                </div>
              </li>
            </ul>

            <ul class="slider__year-list" v-if="!visibleMounth">
              <li
                class="slider__year-item"
                v-for="(months, year) in dateYearAndMounth"
                :key="year"
              >
                <div class="slider__month">
                  {{ year }}

                  <ul
                    class="slider__mouth-list"
                    v-for="(month, index) in months"
                    :key="index"
                  >
                    <li class="slider__mouth-item">{{ month }}</li>
                  </ul>
                </div>
              </li>
            </ul>
          </div>
        </div>
      </div>
    </template>




<script setup>
      import { ref, computed, watch } from "vue";

      const mouth = ["янв","фев","мар","апр","май","июн","июл","авг","сен","окт","ноя","дек",];

      const date = {
        2014: mouth,
        2015: mouth,
        2016: mouth,
        2017: mouth,
        2018: mouth,
        2019: mouth,
        2020: mouth,
        2021: mouth,
        2022: mouth,
        2023: mouth,
        2030: [],
      };

      let dateYearAndMounth = {};

      const fromSlider = ref(null);
      const toSlider = ref(null);

      const fromValue = ref(5);
      const toValue = ref(70);

      const visibleMounth = ref(true);


      const stepValue = computed(() => {
        const numberOfYears = Object.keys(date).length;
        let numberOfMouth = 12;

        const numberOfSteps = numberOfMouth * numberOfYears - 12 + numberOfYears;
        console.log((numberOfSteps > 1 ? 100 / (numberOfSteps - 1) : 1).toFixed(0));

        return (numberOfSteps > 1 ? 100 / (numberOfSteps - 1) : 1).toFixed(3);
      });

      const stepValueYearAndMonth = computed(() => {
        const numberOfYears = Object.keys(dateYearAndMounth).length;
        let numberOfMouth = 12;

        const numberOfSteps = numberOfMouth * numberOfYears - 12 + numberOfYears;

        return (numberOfSteps > 1 ? 100 / (numberOfSteps - 1) : 1).toFixed(3);
      });

      const progressStyle = computed(() => {
        const left = fromValue.value;
        const width = toValue.value - fromValue.value;

        console.log(width);

        return {
          left: `${left}%`,
          width: `${width}%`,
        };
      });


      watch([fromValue, toValue], ([newFromValue, newToValue]) => {
        if (+newFromValue > +newToValue) {
          [fromValue.value, toValue.value] = [newToValue, newFromValue];
        }
 });




<style scoped>
   .container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 15px;
  }

  .slider {
    display: flex;
    width: 90%;
    margin: 35% auto;
    grid-gap: 20px;
    align-items: center;
  }

  .slider__text-date {
    width: 100px;
  }

  .slider__text {
    margin-bottom: 10px;
  }

  .slider__inner {
    width: 100%;
  }

  .sliders_control {
    position: relative;
    min-height: 50px;
    display: flex;
    align-items: center;
  }

  .slider__progress {
    position: absolute;
    width: 100%;
    height: 10px;
    background-color: #5cadea;
    z-index: 1;
  }

  input[type="range"] {
    appearance: none;
    height: 10px;
    width: 100%;
    position: absolute;
    background-color: #edf1f1;
    pointer-events: none;
    left: 0;
    right: 0;
  }

  input[type="range"]::-webkit-slider-thumb {
    appearance: none;
    position: relative;
    pointer-events: all;
    width: 20px;
    height: 20px;
    background-color: #5cadea;
    border-radius: 50%;
    z-index: 2;
    cursor: pointer;
  }

  input[type="range"]::-webkit-slider-thumb::after {
    display: flex;
    align-items: center;
    width: 10px;
    height: 10px;
    background-color: #152121;
    position: absolute;
    z-index: 4;
  }

  #fromSlider::-webkit-slider-thumb {
    transform: translateX(-30%);
  }

  #toSlider::-webkit-slider-thumb {
    transform: translateX(20%);
  }

  #fromSlider {
    height: 0;
    z-index: 2;
  }

  .slider__year-list {
    display: flex;
    justify-content: space-between;
    width: 102%;
    margin-left: -8px;
  }

  .slider__year-item:not(:last-child) {
    width: 100%;
    margin-right: 10px;
  }

  .slider__month {
    display: flex;
    justify-content: space-between;
  }
</style>

Ответы (1 шт):

Автор решения: BlackStar1991

Могу точно сказать, что это где-то твой косяк в вычислениях смещений элемента. (Сначала думал что браузерный, но нет) https://codesandbox.io/p/sandbox/rang-fn468d

<template>
  <div class="container">
    <div class="slider">

      <div class="slider__inner">
        <div class="sliders_control">
          <span :style="progressStyle" class="slider__progress"></span>


          <input
            ref="fromSlider"
            v-model="fromValue"
            id="fromSlider"
            type="range"
            min="0"
            max="100"
            :step="visibleMounth ? stepValue : stepValueYearAndMonth"
          />

          <input
            ref="toSlider"
            v-model="toValue"
            id="toSlider"
            type="range"
            min="0"
            max="100"
            :step="visibleMounth ? stepValue : stepValueYearAndMonth"
          />
        </div>
        <ul class="slider__year-list" v-if="visibleMounth">
          <li
            class="slider__year-item"
            v-for="(months, year) in date"
            :key="year"
          >
            <div class="slider__month">
              {{ year }}
              <template v-if="false">
                <ul
                  class="slider__mouth-list"
                  v-for="(month, index) in months"
                  :key="index"
                >
                  <li class="slider__mouth-item">{{ month }}</li>
                </ul>
              </template>
            </div>
          </li>
        </ul>

        <ul class="slider__year-list" v-if="!visibleMounth">
          <li
            class="slider__year-item"
            v-for="(months, year) in dateYearAndMounth"
            :key="year"
          >
            <div class="slider__month">
              {{ year }}

              <ul
                class="slider__mouth-list"
                v-for="(month, index) in months"
                :key="index"
              >
                <li class="slider__mouth-item">{{ month }}</li>
              </ul>
            </div>
          </li>
        </ul>
      </div>
    </div>
  </div>
</template>




<script setup>
  import { ref, computed, watch } from "vue";

  const mouth = ["янв","фев","мар","апр","май","июн","июл","авг","сен","окт","ноя","дек",];

  const date = {
    2014: mouth,
    2015: mouth,
    2016: mouth,
    2017: mouth,
    2018: mouth,
    2019: mouth,
    2020: mouth,
    2021: mouth,
    2022: mouth,
    2023: mouth,
    2030: [],
  };

  let dateYearAndMounth = {};

  const fromSlider = ref(null);
  const toSlider = ref(null);

  const fromValue = ref(5);
  const toValue = ref(70);

  const visibleMounth = ref(true);


  const stepValue = computed(() => {
    const numberOfYears = Object.keys(date).length;
    let numberOfMouth = 12;

    const numberOfSteps = numberOfMouth * numberOfYears - 12 + numberOfYears;
    console.log((numberOfSteps > 1 ? 100 / (numberOfSteps - 1) : 1).toFixed(0));

    return (numberOfSteps > 1 ? 100 / (numberOfSteps - 1) : 1).toFixed(3);
  });

  const stepValueYearAndMonth = computed(() => {
    const numberOfYears = Object.keys(dateYearAndMounth).length;
    let numberOfMouth = 12;

    const numberOfSteps = numberOfMouth * numberOfYears - 12 + numberOfYears;

    return (numberOfSteps > 1 ? 100 / (numberOfSteps - 1) : 1).toFixed(3);
  });

  const progressStyle = computed(() => {
    const left = fromValue.value - 0.98;
    const width = toValue.value - fromValue.value;

    console.log(width);

    return {
      left: `${left}%`,
      width: `${width}%`,
    };
  });


  watch([fromValue, toValue], ([newFromValue, newToValue]) => {
    if (+newFromValue > +newToValue) {
      [fromValue.value, toValue.value] = [newToValue, newFromValue];
    }
});

</script>


<style scoped>
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}

.slider {
display: flex;
width: 90%;
margin: 35% auto;
grid-gap: 20px;
align-items: center;
}

.slider__text-date {
width: 100px;
}

.slider__text {
margin-bottom: 10px;
}

.slider__inner {
width: 100%;
}

.sliders_control {
position: relative;
min-height: 50px;
display: flex;
align-items: center;
}

.slider__progress {
position: absolute;
width: 100%;
height: 10px;
background-color: #5cadea;
z-index: 1;
}

input[type="range"] {
appearance: none;
height: 10px;
width: 100%;
position: absolute;
background-color: #edf1f1;
pointer-events: none;
left: 0;
right: 0;
}

input[type="range"]::-webkit-slider-thumb {
appearance: none;
position: relative;
pointer-events: all;
width: 20px;
height:20px;
background-color: #5cadea;
border-radius: 50%;
z-index: 2;
cursor: pointer;
}



#fromSlider::-webkit-slider-thumb {
transform: translateX(-30%);
}

#toSlider::-webkit-slider-thumb {
transform: translateX(20%);
}

#fromSlider {
height: 0;
z-index: 2;
}

.slider__year-list {
display: flex;
justify-content: space-between;
width: 102%;
margin-left: -8px;
}

.slider__year-item:not(:last-child) {
width: 100%;
margin-right: 10px;
}

.slider__month {
display: flex;
justify-content: space-between;
}
</style>

Я решил вот этой корректировкой const left = fromValue.value - 0.98; но теперь там сначала этот ползунок вылазит и атрибут step у тебя не очень хороший, там желательно цельные числа передовать.

Вообщем проблема в параметрах а не в стилях.

→ Ссылка