如何创建带进度条的动画倒计时器

问题描述 投票:0回答:1

我有这个 JS 倒计时器,但它不能按我想要的方式工作。我需要一个正常的时间,例如 60 秒。绿色进度条应从左向右移动。当剩余时间为 10 秒时,进度条应变为黄色,当剩余时间为 5 秒时,进度条应变为红色。时间结束后,它将保持红色。

紧要关头,到了23秒的时候,进度条就停在这个地方了。

const FULL_DASH_ARRAY = 283;
const WARNING_THRESHOLD = 10;
const ALERT_THRESHOLD = 5;

const COLOR_CODES = {
  info: {
    color: "green"
  },
  warning: {
    color: "orange",
    threshold: WARNING_THRESHOLD
  },
  alert: {
    color: "red",
    threshold: ALERT_THRESHOLD
  }
};

const TIME_LIMIT = 20;
let timePassed = 0;
let timeLeft = TIME_LIMIT;
let timerInterval = null;
let remainingPathColor = COLOR_CODES.info.color;

document.getElementById("app").innerHTML = `
<div class="base-timer">
  <svg class="base-timer__svg" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
    <g class="base-timer__circle">
      <circle class="base-timer__path-elapsed" cx="50" cy="50" r="45"></circle>
      <path
        id="base-timer-path-remaining"
        stroke-dasharray="283"
        class="base-timer__path-remaining ${remainingPathColor}"
        d="
          M 50, 50
          m -45, 0
          a 45,45 0 1,0 90,0
          a 45,45 0 1,0 -90,0
        "
      ></path>
    </g>
  </svg>
  <span id="base-timer-label" class="base-timer__label">${formatTime(
    timeLeft
  )}</span>
</div>
`;

startTimer();

function onTimesUp() {
  clearInterval(timerInterval);
}

function startTimer() {
  timerInterval = setInterval(() => {
    timePassed = timePassed += 1;
    timeLeft = TIME_LIMIT - timePassed;
    document.getElementById("base-timer-label").innerHTML = formatTime(
      timeLeft
    );
    setCircleDasharray();
    setRemainingPathColor(timeLeft);

    if (timeLeft === 0) {
      onTimesUp();
    }
  }, 1000);
}

function formatTime(time) {
  const minutes = Math.floor(time / 60);
  let seconds = time % 60;

  if (seconds < 10) {
    seconds = `0${seconds}`;
  }

  return `${minutes}:${seconds}`;
}

function setRemainingPathColor(timeLeft) {
  const {
    alert,
    warning,
    info
  } = COLOR_CODES;
  if (timeLeft <= alert.threshold) {
    document
      .getElementById("base-timer-path-remaining")
      .classList.remove(warning.color);
    document
      .getElementById("base-timer-path-remaining")
      .classList.add(alert.color);
  } else if (timeLeft <= warning.threshold) {
    document
      .getElementById("base-timer-path-remaining")
      .classList.remove(info.color);
    document
      .getElementById("base-timer-path-remaining")
      .classList.add(warning.color);
  }
}

function calculateTimeFraction() {
  const rawTimeFraction = timeLeft / TIME_LIMIT;
  return rawTimeFraction - (1 / TIME_LIMIT) * (1 - rawTimeFraction);
}

function setCircleDasharray() {
  const circleDasharray = `${(
    calculateTimeFraction() * FULL_DASH_ARRAY
  ).toFixed(0)} 283`;
  document
    .getElementById("base-timer-path-remaining")
    .setAttribute("stroke-dasharray", circleDasharray);
}
body {
  font-family: sans-serif;
  display: grid;
  height: 100vh;
  place-items: center;
}

.base-timer {
  position: relative;
  width: 300px;
  height: 300px;
}

.base-timer__svg {
  transform: scaleX(-1);
}

.base-timer__circle {
  fill: none;
  stroke: none;
}

.base-timer__path-elapsed {
  stroke-width: 7px;
  stroke: grey;
}

.base-timer__path-remaining {
  stroke-width: 7px;
  stroke-linecap: round;
  transform: rotate(90deg);
  transform-origin: center;
  transition: 1s linear all;
  fill-rule: nonzero;
  stroke: currentColor;
}

.base-timer__path-remaining.green {
  color: rgb(65, 184, 131);
}

.base-timer__path-remaining.orange {
  color: orange;
}

.base-timer__path-remaining.red {
  color: red;
}

.base-timer__label {
  position: absolute;
  width: 300px;
  height: 300px;
  top: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 48px;
}
<div id="app"></div>

javascript html css timer progress-bar
1个回答
0
投票

这是一个示例,说明我如何开始制作不是圆形的进度条,并在不同的阈值上变成不同的颜色。

首先,我制作一个用于时间显示和进度条的容器。然后我为进度条容器创建一个子级,这是实际的进度。然后,我使用js更新进度的css变量,该变量用于更新变换scaleX。我使用scaleX而不是width的原因是它更适合过渡并防止回流dom。

然后剩下的就是在js中使用一些异步魔法,每秒循环一次,检查进度以查看颜色是否需要更新,并更新进度条的比例。

如果您想要进度条更平滑的过渡,您可以进行过渡以缩放计时器的长度,但是如果您打算将计时器用于游戏或其他内容,那么它会给您带来不太精细的控制。另外,这种方法开启了暂停、改变时间的能力,我只是喜欢滴答作响而不是逐渐减少的美感。就好像游戏是 1 秒间隔更新而不是连续更新。不过,可以根据您的意图以任何一种方式编写。

希望这个演示有帮助!如果您还有其他疑问,请随时询问。

我利用 css 变量来保存我的描述性颜色

const get = query => document.querySelector(query);
const wait = s => new Promise(r => setTimeout(r, s * 1000));

const display = get(".timer .display");
const progress = get(".timer .progress");

const WARN_THRESHOLD = .4;
const DANGER_THRESHOLD = .2;

const INIT_TIME = 15;
let timeRemaining = INIT_TIME;



(async () => {
  while (timeRemaining > 0) {
    timeRemaining--;
    display.innerText = `${timeRemaining}s`;
    const newProgress = timeRemaining / INIT_TIME;
    progress.style.setProperty("--progress", newProgress);
    if (newProgress > WARN_THRESHOLD) {
      progress.style.setProperty("--color", "var(--safe)");
    } else if (newProgress > DANGER_THRESHOLD) {
      progress.style.setProperty("--color", "var(--warn)");
    } else {
      progress.style.setProperty("--color", "var(--danger)");
    }
    await wait(1);
  }
})();
/* CENTER TIMER */
.timer {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

/* FLOW TIMER CONTENT */
.timer {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  width: 200px;
  border: 5px solid lightblue;
}

.timer .display {
  
}

.timer .progress-container {
  width: 100%;
  border: 1px solid black;
  --height: 20px;
  height: var(--height);
}

:root {
  --safe: lightgreen;
  --warn: yellow;
  --danger: red;
}

.timer .progress {
  --color: var(--safe);
  --progress: 1;
  background-color: var(--color);
  width: 100%;
  height: var(--height);
  transform: scaleX(var(--progress));
  transform-origin: left;
  transition: transform 0.1s, color 0.1s;
}
<div class="timer">
  <div class="display"></div>
  <div class="progress-container">
    <div class="progress">
    </div>
  </div>
</div>

© www.soinside.com 2019 - 2024. All rights reserved.