为什么当持续时间减少 JavaScript 时动画不完成其路径

Why animation don't complete its path when duration is decreased by JavaScript

我有一个动画,它的 duration 每次 black 跳跃(使用 space)超过 red,后续跳转减少 duration.

效果很好

但是时间减少了一定时间后,比如说减少到4s,3.9s,3.8s...之后,动画就不是从最右端开始了这应该是。因为在 @keyframes animateVillan

中决定了 path(110vw)

有没有我做错了什么,首先认为这是一个小故障,并且决定仅在 red 达到小于 10 时才更改持续时间并尝试以下部分

if (ourVillanFigXValue < 10) {
      ourVillanFig.style.animationDuration = ourVillanAnimeDuration - 0.1 + "s";
    }

但这并没有解决问题,red

没有完全追踪路径

Sorry have to jump a little, 4 or 5 jumps only to see the error plz

let ourHeroFig = document.getElementById("ourHero");
let ourVillanFig = document.getElementById("obstacleBar");
let gameScoreDigits = document.getElementById("gameScoreDigits");
let valueXCoordinate = "";
let obstacleBarCrossed = true;

document.body.addEventListener('keydown', function(e) {
  let ourHeroFigXValue = parseInt(getComputedStyle(ourHeroFig).getPropertyValue('left'));
  let ourHeroFigYValue = parseInt(getComputedStyle(ourHeroFig).getPropertyValue('bottom'));

  if (e.code === "ArrowRight") {
    valueXCoordinate = ourHeroFigXValue + 100;

  } else if (e.code === "KeyA" || e.code === "ArrowLeft") {
    if (ourHeroFigXValue > ourHeroFig.offsetWidth + 90) {
      valueXCoordinate = ourHeroFigXValue - 100;
    } else {
      valueXCoordinate = 0;
    }
  } else if (e.code === "Space") {
    ourHeroFig.classList.add("animateHero");
    setTimeout(function() {
      ourHeroFig.classList.remove("animateHero");
    }, 700)
  }
  changePosition();

})

function changePosition() {
  ourHeroFig.style.left = valueXCoordinate + 'px'
}

let delayInAnimeSub = ""
setInterval(
  function() {
    let ourHeroFigXValue = parseInt(getComputedStyle(ourHeroFig).getPropertyValue('left'));
    let ourHeroFigYValue = parseInt(getComputedStyle(ourHeroFig).getPropertyValue('bottom'));
    let ourVillanFigXValue = parseInt(getComputedStyle(ourVillanFig).getPropertyValue('left'));
    let ourVillanFigYValue = parseInt(getComputedStyle(ourVillanFig).getPropertyValue('bottom'));
    let gameOverValueX = Math.abs(ourVillanFigXValue - ourHeroFigXValue);
    let gameOverValueY = Math.abs(ourVillanFigYValue - ourHeroFigYValue);

    if (ourVillanFigXValue < 10) {
      ourVillanFig.style.animationDuration = ourVillanAnimeDuration - 0.3 + "s";
    }
    if (gameOverValueX < ourVillanFig.offsetWidth && gameOverValueY < ourVillanFig.offsetHeight) {
      console.log("yes touched");
      ourVillanFig.classList.remove("animateVillan");
      obstacleBarCrossed = false;
    } else if (obstacleBarCrossed && gameOverValueX < ourVillanFig.offsetWidth) {
      ourVillanAnimeDuration = parseFloat(getComputedStyle(ourVillanFig).getPropertyValue('animation-duration'));
      console.log(ourVillanFigXValue < 0, ourVillanAnimeDuration)

      if (ourVillanAnimeDuration <= 2) {
        ourVillanAnimeDuration = 2
      }
    }
    // console.log(gameOverValueX,gameOverValueY)
  }, 10);
#ourHero {
  width: 20px;
  height: 180px;
  background-color: black;
  position: fixed;
  bottom: 0;
  left: 0;
  transition: 0.1s;
}

.animateHero {
  animation: animateHero 0.7s linear;
}

@keyframes animateHero {
  0% {
    bottom: 0;
  }
  50% {
    bottom: 350px;
  }
  100% {
    bottom: 0;
  }
}

#obstacleBar {
  width: 20px;
  height: 180px;
  background-color: red;
  position: fixed;
  bottom: 0;
  left: 50vw;
}

.animateVillan {
  animation: animateVillan 5s linear infinite;
}

@keyframes animateVillan {
  0% {
    left: 110vw;
  }
  100% {
    left: 0;
  }
}
<div id="ourHero"></div>
<div id="obstacleBar" class="animateVillan"></div>

提前感谢您的帮助

首先让红色的东西从最右边开始,所以将 left: 50vw; 更改为 left: 110vw; 之类的东西。此外,不是无限动画,而是在红色离开屏幕后删除 animateVillan class 然后重新添加它。可以通过使用 animationend 事件处理程序来完成:

ourVillanFig.addEventListener('animationend', () => {
  ourVillanFig.classList.remove('animateVillan')
  ourVillanFig.clientHeight // just to cause a reflow
  ourVillanFig.classList.add('animateVillan')
})

或者可能通过检查它的 x 位置,看看它什么时候出来。

这是结果。它似乎工作正常,没有任何故障:

编辑: 要使红色在接触黑色时停在原处,您可以进行以下 css class:

.pauseVillan {
      animation-play-state: paused;
    }

然后当红色接触时只需添加 pauseVillan class 即可:

ourVillanFig.classList.add('pauseVillan')

这是更新后的代码段:

let ourHeroFig = document.getElementById('ourHero')
let ourVillanFig = document.getElementById('obstacleBar')
let gameScoreDigits = document.getElementById('gameScoreDigits')
let valueXCoordinate = ''
let obstacleBarCrossed = true

document.body.addEventListener('keydown', function(e) {
  let ourHeroFigXValue = parseInt(
    getComputedStyle(ourHeroFig).getPropertyValue('left')
  )
  let ourHeroFigYValue = parseInt(
    getComputedStyle(ourHeroFig).getPropertyValue('bottom')
  )

  if (e.code === 'ArrowRight') {
    valueXCoordinate = ourHeroFigXValue + 100
  } else if (e.code === 'KeyA' || e.code === 'ArrowLeft') {
    if (ourHeroFigXValue > ourHeroFig.offsetWidth + 90) {
      valueXCoordinate = ourHeroFigXValue - 100
    } else {
      valueXCoordinate = 0
    }
  } else if (e.code === 'Space') {
    ourHeroFig.classList.add('animateHero')
    setTimeout(function() {
      ourHeroFig.classList.remove('animateHero')
    }, 700)
  }
  changePosition()
})

ourVillanFig.addEventListener('animationend', () => {
  ourVillanFig.classList.remove('animateVillan')
  ourVillanFig.clientHeight // just to cause a reflow
  ourVillanFig.classList.add('animateVillan')
})

function changePosition() {
  ourHeroFig.style.left = valueXCoordinate + 'px'
}

let delayInAnimeSub = ''
setInterval(function() {
  let ourHeroFigXValue = parseInt(
    getComputedStyle(ourHeroFig).getPropertyValue('left')
  )
  let ourHeroFigYValue = parseInt(
    getComputedStyle(ourHeroFig).getPropertyValue('bottom')
  )
  let ourVillanFigXValue = parseInt(
    getComputedStyle(ourVillanFig).getPropertyValue('left')
  )
  let ourVillanFigYValue = parseInt(
    getComputedStyle(ourVillanFig).getPropertyValue('bottom')
  )
  let gameOverValueX = Math.abs(ourVillanFigXValue - ourHeroFigXValue)
  let gameOverValueY = Math.abs(ourVillanFigYValue - ourHeroFigYValue)

  if (ourVillanFigXValue < 10) {
    ourVillanFig.style.animationDuration =
      ourVillanAnimeDuration - 0.3 + 's'
  }
  if (
    gameOverValueX < ourVillanFig.offsetWidth &&
    gameOverValueY < ourVillanFig.offsetHeight
  ) {
    console.log('yes touched')
    ourVillanFig.classList.add('pauseVillan')
    obstacleBarCrossed = false
  } else if (
    obstacleBarCrossed &&
    gameOverValueX < ourVillanFig.offsetWidth
  ) {
    ourVillanAnimeDuration = parseFloat(
      getComputedStyle(ourVillanFig).getPropertyValue('animation-duration')
    )
    console.log(ourVillanFigXValue < 0, ourVillanAnimeDuration)

    if (ourVillanAnimeDuration <= 2) {
      ourVillanAnimeDuration = 2
    }
  }
  // console.log(gameOverValueX,gameOverValueY)
}, 10)
#ourHero {
  width: 20px;
  height: 180px;
  background-color: black;
  position: fixed;
  bottom: 0;
  left: 0;
  transition: 0.1s;
}

.animateHero {
  animation: animateHero 0.7s linear;
}

@keyframes animateHero {
  0% {
    bottom: 0;
  }
  50% {
    bottom: 350px;
  }
  100% {
    bottom: 0;
  }
}

#obstacleBar {
  width: 20px;
  height: 180px;
  background-color: red;
  position: fixed;
  bottom: 0;
  left: 110vw;
}

.animateVillan {
  animation: animateVillan 4s linear;
}

.pauseVillan {
  animation-play-state: paused;
}

@keyframes animateVillan {
  0% {
    left: 110vw;
  }
  100% {
    left: 0;
  }
}
<div id="ourHero"></div>
<div id="obstacleBar" class="animateVillan"></div>