如何使线性渐变跟随 html 中范围滑块的拇指

How can I make a linear gradient follow the thumb of a range slider in html

所以我正在制作一个 Netflix 主题的 HTML 视频播放器并且我在 javascript 中使用了线性渐变所以我的搜索栏(这是一个自定义的范围滑块)在拇指后面会有不同的颜色.手动滑动时效果很好,但是当拇指自动滑动跟随视频时,渐变保持不变

代码如下:

HTML:

<!DOCTYPE html>
<html lang="en">
<head>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Netflix video player</title>

    <link rel="stylesheet" href="https://cdnjs.cloudlflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
    <link rel="stylesheet" href="style.css">


</head>

<body>
    <div class="container">
        <div class="c-video">
            <video class="video" src="video.mp4" ></video>
            <div class="controls">
                <div class="red-bar">
                    <input class="red-juice" type="range" min="1" max="100" step="1" value="1"></input>

                </div>

            </div>
            <div class="buttons">
                <button id="play-pause"></button>
            </div>
            <div class="title"></div>
        </div>
    </div>
    <script src="script.js"></script>

</body>

</html>

css:

.container {
    display: flex;
    background-color: #000000;
    justify-content: center;
    align-items: center;
    height: 100vh;


}

.video {
    width: 100%;

}

.c-video {
    width: 100%;
    max-width: 800px;
    position: relative;
    overflow: hidden;
}

.c-video:hover .title {
    transform: translateY(0);
}

.c-video:hover .controls {
    transform: translateY(0);

}

.c-video:hover .buttons {
    top: 50%;
    left: 50%;
transform: translate(240%,-50%);
}

.title {
    display: flex;
    position: absolute;
    top: 0;
    height: 120px;
    width: 100%;
    flex-wrap: wrap;
    background-image: linear-gradient(rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.01));
    transform: translateY(-100%);
    transition: all .2s;
}

.controls {
    display: flex;
    position: absolute;
    bottom: 0;
    height: 70px;
    width: 100%;
    flex-wrap: wrap;
    background-image: linear-gradient(rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 0.9));
transform: translateY(100%);
transition: all .2s;
}

.buttons {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-999%,-50%);
}

.buttons button {

    background:none;
   height: 45px; 
    border: none;
    outline: 0;
    cursor: pointer;
    transform: translate(-300%, 0px);
}

.buttons button:before {
    content: "\f144";
    font-family: "Font Awesome 5 Free";
    display: inline-block;
    font-size: 300%;
    color: #ffff;
    -webkit-font-smoothing: antialiased;

}

.buttons button.play:before {
    content: "\f144";
    font-family: 'Font Awesome 5 Free';
}

.buttons button.pause:before {
    content: "\f28b";
    font-family: 'Font Awesome 5 Free';
}

/* Progress bar container */
.red-bar {
    height: 2px;
    margin-top:15px ;
    margin-bottom: -15px;
    background-color:rgba(0, 0, 0, 0.4);
    margin-left: 10px;
    margin-right: 10px;
    width: 100%;

}

/* This represents the progress bar */
.red-juice {
    position: relative;
    width: 100%;
    height: 2px; /* thumbHeight + (2 x thumbBorderWidth)*/
    background-image: linear-gradient(to right, red 1%, rgba(0, 0, 0, 0.4) 1%);

    -webkit-appearance: none; /*remove the line*/
    outline: none;
    top: -12px;
    margin-left: 1px;
    margin-right: 100px;

}

.red-juice::-webkit-slider-runnable-track {
    -webkit-appearance: none;
    height: 20px;
  }

    .red-juice::-webkit-slider-thumb {
        -webkit-appearance: none;
        background: red; /*thumbColor*/
        width: 15px; /* thumbHeight + (2 x thumbBorderWidth)*/
        height: 15px; /* thumbHeight + (2 x thumbBorderWidth)*/
        border-radius: 100%;
        margin-top: 1px; /* -[thumbHeight + (2 x thumbBorderWidth) - trackHeight]/2*/
        cursor: pointer;
        border: 0px solid #fff; /*border-width should be equal to thumbBorderWidth if you want same border width across all browsers and border-color should match the background*/
        transition: 0.3s;            
    }

javascript:

var video = document.querySelector(".video");
var juice = document.querySelector(".red-juice");
var btn = document.getElementById("play-pause");

function togglePlayPause() {
    if(video.paused) {
        btn.className = 'pause';
        video.play();
    } else {
        btn.className = 'play';
        video.pause();
    }
}

btn.onclick = function (params) {
    //video.fastSeek(570); // 9:30
    // video.currentTime = 570; //test
    togglePlayPause();
}

video.addEventListener('timeupdate', function() {

    if(video.ended) {
        btn.className = "play";
      // At the end of the movie, reset the position to the start and pause the playback.
        video.currentTime = 0;
        togglePlayPause();
    }
});

video.addEventListener('timeupdate', () => {
    juice.value = video.currentTime / video.duration * juice.max
  })

  juice.addEventListener('change', () => {
    video.currentTime = video.duration * juice.value / juice.max
  })

  juice.oninput = function slidingProgress() {
    juice.style.background = 'linear-gradient(to right, red ' + juice.value * 100 / juice.max + '%, rgba(0, 0, 0, 0.4) ' + this.value + '%, rgba(0, 0, 0, 0.4) 100%)'
  };

juice.addEventListener(slidingProgress);

问题是您将函数直接绑定到 oninput handler,这 不会 被触发以编程方式更改 input 的值。

您可以通过在 oninput 之外声明您的函数来解决它,然后稍后将您的函数分配给它 并且 在您的 timeupdate 中调用它听众。只要您使用 function 关键字来声明函数,在将函数分配给 oninput 之前或之后声明它并不重要,因为它得到 hoisted.

工作片段

我还在 JSFiddle 中上传了以下片段,您可以在其中更清楚地看到它。

var video = document.querySelector(".video");
var juice = document.querySelector(".red-juice");
var btn = document.getElementById("play-pause");

function togglePlayPause() {
  if (video.paused) {
    btn.className = 'pause';
    video.play();
  } else {
    btn.className = 'play';
    video.pause();
  }
}

btn.onclick = function(params) {
  //video.fastSeek(570); // 9:30
  // video.currentTime = 570; //test
  togglePlayPause();
}

video.addEventListener('timeupdate', function() {

  if (video.ended) {
    btn.className = "play";
    // At the end of the movie, reset the position to the start and pause the playback.
    video.currentTime = 0;
    togglePlayPause();
  }
});

function slidingProgress() {
  // this.value will not work here, since it points to the global window obj
  // so I'm using juice.value instead
  // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#Function_context
  juice.style.background = 'linear-gradient(to right, red ' + juice.value * 100 / juice.max + '%, rgba(0, 0, 0, 0.4) ' + juice.value + '%, rgba(0, 0, 0, 0.4) 100%)'
}

video.addEventListener('timeupdate', () => {
  juice.value = video.currentTime / video.duration * juice.max
  slidingProgress() // Call your function here to update .red-juice
})

juice.addEventListener('change', () => {
  video.currentTime = video.duration * juice.value / juice.max
})

// And finally assign it to juice.oninput
juice.oninput = slidingProgress;

// you're not specifying any events to listen to here, so it wouldn't work
// juice.addEventListener(slidingProgress);
.container {
  display: flex;
  background-color: #000000;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

.video {
  width: 100%;
}

.c-video {
  width: 100%;
  max-width: 800px;
  position: relative;
  overflow: hidden;
}

.c-video:hover .title {
  transform: translateY(0);
}

.c-video:hover .controls {
  transform: translateY(0);
}

.c-video:hover .buttons {
  top: 50%;
  left: 50%;
  transform: translate(240%, -50%);
}

.title {
  display: flex;
  position: absolute;
  top: 0;
  height: 120px;
  width: 100%;
  flex-wrap: wrap;
  background-image: linear-gradient(rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.01));
  transform: translateY(-100%);
  transition: all .2s;
}

.controls {
  display: flex;
  position: absolute;
  bottom: 0;
  height: 70px;
  width: 100%;
  flex-wrap: wrap;
  background-image: linear-gradient(rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 0.9));
  transform: translateY(100%);
  transition: all .2s;
}

.buttons {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-999%, -50%);
}

.buttons button {
  background: none;
  height: 45px;
  border: none;
  outline: 0;
  cursor: pointer;
  transform: translate(-300%, 0px);
}

.buttons button:before {
  content: "\f144";
  font-family: "Font Awesome 5 Free";
  display: inline-block;
  font-size: 300%;
  color: #ffff;
  -webkit-font-smoothing: antialiased;
}

.buttons button.play:before {
  content: "\f144";
  font-family: 'Font Awesome 5 Free';
}

.buttons button.pause:before {
  content: "\f28b";
  font-family: 'Font Awesome 5 Free';
}


/* Progress bar container */

.red-bar {
  height: 2px;
  margin-top: 15px;
  margin-bottom: -15px;
  background-color: rgba(0, 0, 0, 0.4);
  margin-left: 10px;
  margin-right: 10px;
  width: 100%;
}


/* This represents the progress bar */

.red-juice {
  position: relative;
  width: 100%;
  height: 2px;
  /* thumbHeight + (2 x thumbBorderWidth)*/
  background-image: linear-gradient(to right, red 1%, rgba(0, 0, 0, 0.4) 1%);
  -webkit-appearance: none;
  /*remove the line*/
  outline: none;
  top: -12px;
  margin-left: 1px;
  margin-right: 100px;
}

.red-juice::-webkit-slider-runnable-track {
  -webkit-appearance: none;
  height: 20px;
}

.red-juice::-webkit-slider-thumb {
  -webkit-appearance: none;
  background: red;
  /*thumbColor*/
  width: 15px;
  /* thumbHeight + (2 x thumbBorderWidth)*/
  height: 15px;
  /* thumbHeight + (2 x thumbBorderWidth)*/
  border-radius: 100%;
  margin-top: 1px;
  /* -[thumbHeight + (2 x thumbBorderWidth) - trackHeight]/2*/
  cursor: pointer;
  border: 0px solid #fff;
  /*border-width should be equal to thumbBorderWidth if you want same border width across all browsers and border-color should match the background*/
  transition: 0.3s;
}
<!DOCTYPE html>
<html lang="en">

<head>

  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Netflix video player</title>

  <link rel="stylesheet" href="https://cdnjs.cloudlflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <link rel="stylesheet" href="style.css">


</head>

<body>
  <div class="container">
    <div class="c-video">
      <video class="video" src="https://www.w3schools.com/html/mov_bbb.mp4"></video>
      <div class="controls">
        <div class="red-bar">
          <!--
            inputs are self-closing tags. You don't need a closing tag for it!
            Self-closing tags are single tagged elements - you only need to
            add a slash before '>', like so: <input />
          -->
          <input class="red-juice" type="range" min="1" max="100" step="1" value="1" />

        </div>

      </div>
      <div class="buttons">
        <button id="play-pause"></button>
      </div>
      <div class="title"></div>
    </div>
  </div>
  <script src="script.js"></script>

</body>

</html>