Change its animation-duration to 8 seconds:
setTimeout(() => {
box.style.setProperty('--xe', '400px');
box.style.animationDuration = '8s';
}, 1000);
Originally, the box was meant to move 200 pixels in 4 seconds; by a simple division we know that its speed is set to 50px/s.
┌──────┐
│ 50/s │ (4s away)
└──────┘
|-------------------|-------------------|
0 100 200
After one second, when the callback is fired, the length suddenly doubles to 400; this causes the box's speed to increase to 100px/s. However, since it is already 50px from the original position, it must "teleport" to 100px to be able to travel the rest in 3 seconds.
┌──────┐
│ 50/s │ (3s away)
└──────┘
|---------|---------|-------------------|-------------------|-------------------|
0 50 100 200 300 400
┌──────┐
│ 100/s│ (3s away)
└──────┘
|---------|---------|-------------------|-------------------|-------------------|
0 50 100 200 300 400
So, to prevent it from teleporting we need to give it more time, making its speed remain consistent. Again, we need math:
( 400 - 50 ) / 50 = 7 (seconds)
To_be_traveled / Speed = Time
Add it to 1 second we already used to travel the first 50 pixels and we have the final answer: 8 seconds.
And that's all there is to it! Should your actual input changes, you can simply convert the formula above to a function. Note that this won't work with any animation-timing-function other than linear.
Try it:
var box = document.getElementById('box');
box.style.setProperty('--xs', '0px');
box.style.setProperty('--xe', '200px');
box.classList.add('anim');
setTimeout(() => {
box.style.setProperty('--xe', '400px');
box.style.animationDuration = '8s';
}, 1000)
#box {
width: 40px;
height: 40px;
background-color: red;
border-radius: 15px;
position: relative;
}
.anim {
animation-name: anim;
animation-duration: 4s;
animation-timing-function: linear;
}
@keyframes anim {
0% { left: var(--xs); }
100% { left: var(--xe); }
}
<div id="box"></div>