Range input thumb doesn't align with axis tick

2019-06-11 16:36发布

问题:

The thumb a.k.a. knob in this input[type=range] codepen doesn't line up correctly with the ticks. It seems to be off by a different amount (left or right) depending on the value. Try moving the thumb to see. What CSS can I use to make the thumb line up with the current tick such that it works for all the ticks on the axis?

input.range {
  -webkit-appearance: none;
  bottom: -10px;
  position: relative;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
}
input.range:focus {
  outline: 0;
}
input.range::-moz-focus-outer {
  border: 0;
}
input.range::-webkit-slider-thumb {
  box-shadow: 1px 1px 1px black, 0px 0px 1px black;
  border: 0;
  height: 20px;
  width: 20px;
  border-radius: 50%;
  background: white;
  cursor: pointer;
  -webkit-appearance: none;
  margin-top: -7.5px;
}
input.range::-moz-range-thumb {
  box-shadow: 1px 1px 1px black, 0px 0px 1px black;
  border: 0;
  height: 20px;
  width: 20px;
  border-radius: 50%;
  background: white;
  cursor: pointer;
}
input.range::-webkit-slider-runnable-track {
  width: 100%;
  height: 5px;
  cursor: pointer;
  box-shadow: 1px 1px 1px transparent, 0px 0px 1px rgba(13, 13, 13, 0);
  background: indigo;
  border-radius: 20px;
  border: 0;
}
input.range::-moz-range-track {
  width: 100%;
  height: 5px;
  cursor: pointer;
  box-shadow: 1px 1px 1px transparent, 0px 0px 1px rgba(13, 13, 13, 0);
  background: indigo;
  border-radius: 20px;
  border: 0;
}
input.range::-ms-track {
  width: 100%;
  height: 5px;
  cursor: pointer;
  background: transparent;
  border-color: transparent;
  color: transparent;
}
input.range::-ms-thumb {
  box-shadow: 1px 1px 1px black, 0px 0px 1px black;
  border: 0;
  height: 20px;
  width: 20px;
  border-radius: 50%;
  background: white;
  cursor: pointer;
  height: 5px;
}
input.range::-ms-fill-lower,
input.range::-ms-fill-upper {
  background: indigo;
  border: 0;
  border-radius: 40px;
  box-shadow: 1px 1px 1px transparent, 0px 0px 1px rgba(13, 13, 13, 0);
}
.range__tick {
  fill: silver;
}
.range__tick:first-child {
  -webkit-transform: translateX(2px);
  -moz-transform: translateX(2px);
  -ms-transform: translateX(2px);
  -o-transform: translateX(2px);
  transform: translateX(2px);
}
.range__tick:last-child {
  -webkit-transform: translateX(-3px);
  -moz-transform: translateX(-3px);
  -ms-transform: translateX(-3px);
  -o-transform: translateX(-3px);
  transform: translateX(-3px);
}
.range__field {
  border: 0;
}
<fieldset class="range__field">
  <input class="range" type="range" min="0" max="10">
  <svg role="presentation" width="100%" height="10" xmlns="http://www.w3.org/2000/svg">
    <rect class="range__tick" x="0%" y="3" width="1" height="10"></rect>
    <rect class="range__tick" x="10%" y="3" width="1" height="10"></rect>
    <rect class="range__tick" x="20%" y="3" width="1" height="10"></rect>
    <rect class="range__tick" x="30%" y="3" width="1" height="10"></rect>
    <rect class="range__tick" x="40%" y="3" width="1" height="10"></rect>
    <rect class="range__tick" x="50%" y="3" width="1" height="10"></rect>
    <rect class="range__tick" x="60%" y="3" width="1" height="10"></rect>
    <rect class="range__tick" x="70%" y="3" width="1" height="10"></rect>
    <rect class="range__tick" x="80%" y="3" width="1" height="10"></rect>
    <rect class="range__tick" x="90%" y="3" width="1" height="10"></rect>
    <rect class="range__tick" x="100%" y="3" width="1" height="10"></rect>
  </svg>
  <svg role="presentation" width="100%" height="14" xmlns="http://www.w3.org/2000/svg">
    <text class="range__point" x="0%" y="14" text-anchor="start">0</text>
    <text class="range__point" x="10%" y="14" text-anchor="middle">1</text>
    <text class="range__point" x="20%" y="14" text-anchor="middle">2</text>
    <text class="range__point" x="30%" y="14" text-anchor="middle">3</text>
    <text class="range__point" x="40%" y="14" text-anchor="middle">4</text>
    <text class="range__point" x="50%" y="14" text-anchor="middle">5</text>
    <text class="range__point" x="60%" y="14" text-anchor="middle">6</text>
    <text class="range__point" x="70%" y="14" text-anchor="middle">7</text>
    <text class="range__point" x="80%" y="14" text-anchor="middle">8</text>
    <text class="range__point" x="90%" y="14" text-anchor="middle">9</text>
    <text class="range__point" x="100%" y="14" text-anchor="end">10</text>
  </svg>
</fieldset>

回答1:

You are not taking into account the width of the thumb in relation to the edge of the track.

One way to solve this is to wrap your two svg elements in a div, then provide padding on both sides equal to half of the thumb width.

Modify HTML:

<fieldset class="range__field">
    <input class="range" type="range" min="0" max="10" />
    <div class="timeline-wrapper">
        <!-- svgs go here -->
    </div>
</fieldset>

Append to CSS:

.timeline-wrapper {
    padding: 0 10px;
}

.timeline-wrapper {
  padding: 0 10px;
}
input.range {
  -webkit-appearance: none;
  bottom: -10px;
  position: relative;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
}
input.range:focus {
  outline: 0;
}
input.range::-moz-focus-outer {
  border: 0;
}
input.range::-webkit-slider-thumb {
  box-shadow: 1px 1px 1px black, 0px 0px 1px black;
  border: 0;
  height: 20px;
  width: 20px;
  border-radius: 50%;
  background: white;
  cursor: pointer;
  -webkit-appearance: none;
  margin-top: -7.5px;
}
input.range::-moz-range-thumb {
  box-shadow: 1px 1px 1px black, 0px 0px 1px black;
  border: 0;
  height: 20px;
  width: 20px;
  border-radius: 50%;
  background: white;
  cursor: pointer;
}
input.range::-webkit-slider-runnable-track {
  width: 100%;
  height: 5px;
  cursor: pointer;
  box-shadow: 1px 1px 1px transparent, 0px 0px 1px rgba(13, 13, 13, 0);
  background: indigo;
  border-radius: 20px;
  border: 0;
}
input.range::-moz-range-track {
  width: 100%;
  height: 5px;
  cursor: pointer;
  box-shadow: 1px 1px 1px transparent, 0px 0px 1px rgba(13, 13, 13, 0);
  background: indigo;
  border-radius: 20px;
  border: 0;
}
input.range::-ms-track {
  width: 100%;
  height: 5px;
  cursor: pointer;
  background: transparent;
  border-color: transparent;
  color: transparent;
}
input.range::-ms-thumb {
  box-shadow: 1px 1px 1px black, 0px 0px 1px black;
  border: 0;
  height: 20px;
  width: 20px;
  border-radius: 50%;
  background: white;
  cursor: pointer;
  height: 5px;
}
input.range::-ms-fill-lower,
input.range::-ms-fill-upper {
  background: indigo;
  border: 0;
  border-radius: 40px;
  box-shadow: 1px 1px 1px transparent, 0px 0px 1px rgba(13, 13, 13, 0);
}
.range__tick {
  fill: silver;
}
.range__tick:first-child {
  -webkit-transform: translateX(2px);
  -moz-transform: translateX(2px);
  -ms-transform: translateX(2px);
  -o-transform: translateX(2px);
  transform: translateX(2px);
}
.range__tick:last-child {
  -webkit-transform: translateX(-3px);
  -moz-transform: translateX(-3px);
  -ms-transform: translateX(-3px);
  -o-transform: translateX(-3px);
  transform: translateX(-3px);
}
.range__field {
  border: 0;
}
<fieldset class="range__field">
  <input class="range" type="range" min="0" max="10">
  <div class="timeline-wrapper">
    <svg role="presentation" width="100%" height="10" xmlns="http://www.w3.org/2000/svg">
      <rect class="range__tick" x="0%" y="3" width="1" height="10"></rect>
      <rect class="range__tick" x="10%" y="3" width="1" height="10"></rect>
      <rect class="range__tick" x="20%" y="3" width="1" height="10"></rect>
      <rect class="range__tick" x="30%" y="3" width="1" height="10"></rect>
      <rect class="range__tick" x="40%" y="3" width="1" height="10"></rect>
      <rect class="range__tick" x="50%" y="3" width="1" height="10"></rect>
      <rect class="range__tick" x="60%" y="3" width="1" height="10"></rect>
      <rect class="range__tick" x="70%" y="3" width="1" height="10"></rect>
      <rect class="range__tick" x="80%" y="3" width="1" height="10"></rect>
      <rect class="range__tick" x="90%" y="3" width="1" height="10"></rect>
      <rect class="range__tick" x="100%" y="3" width="1" height="10"></rect>
    </svg>
    <svg role="presentation" width="100%" height="14" xmlns="http://www.w3.org/2000/svg">
      <text class="range__point" x="0%" y="14" text-anchor="start">0</text>
      <text class="range__point" x="10%" y="14" text-anchor="middle">1</text>
      <text class="range__point" x="20%" y="14" text-anchor="middle">2</text>
      <text class="range__point" x="30%" y="14" text-anchor="middle">3</text>
      <text class="range__point" x="40%" y="14" text-anchor="middle">4</text>
      <text class="range__point" x="50%" y="14" text-anchor="middle">5</text>
      <text class="range__point" x="60%" y="14" text-anchor="middle">6</text>
      <text class="range__point" x="70%" y="14" text-anchor="middle">7</text>
      <text class="range__point" x="80%" y="14" text-anchor="middle">8</text>
      <text class="range__point" x="90%" y="14" text-anchor="middle">9</text>
      <text class="range__point" x="100%" y="14" text-anchor="end">10</text>
    </svg>
  </div>
</fieldset>