Reputation: 13397
I have this slider, when I click one of the dots it slides to the center of the screen. The problem I have is that if I click a dot that is already close to the center, it moves very slowly. If I click a dot that is much further away, it takes the same amount of time to reach the center (but appears to be moving faster; obviously this is an illusion). What I would like to do is for them all to travel at the same speed. So the further one away takes longer to get to the center, but the closest one is must faster.
Currently my code looks like this:
// Moves the current active item to the center
moveToCenter: function (e, dragOptions, animate) {
// Get the central position
var center = $window.innerWidth / 2,
rect = e.target.getBoundingClientRect(),
distance = center - rect.left - ((50 - 18) / 2), // Make these variables?
positive = rect.left > center;
// If we should animation
if (animate) {
// Get our fake mouse position
var clientX = center,
target = center + distance,
offset = clientX / $window.innerWidth,
length = distance / $window.innerWidth;
// Initiate the first move
_setupMove(dragOptions, offset);
// Execute every 1 millisecond
var timer = $interval(function () {
// Get our new clientX
clientX = clientX + length;
offset = clientX / $window.innerWidth;
// Move
_moveElements(dragOptions, offset, service.updateDragElement);
// Should we stop?
var stop = positive ? clientX <= target : clientX >= target;
// If we should stop
if (stop) {
// Cancel the interval
$interval.cancel(timer);
}
}, 1);
// Otherwise
} else {
// Update the current position
dragOptions.currentX = distance / dragOptions.speed;
// Move our elements
service.updateDragElement(dragOptions);
}
},
I know it's to do with setting clientX. I thought that maybe I needed to work out how far I should travel based on time, so I created this:
// Get our fake mouse position
var clientX = center,
target = center + distance,
offset = clientX / windowWidth,
percent = distance / windowWidth,
interval = 1000 * percent;
// Initiate the first move
_setupMove(dragOptions, offset);
// Execute every 1 millisecond
var timer = $interval(function () {
// Get our new clientX
clientX = clientX + (distance * percent);
offset = clientX / windowWidth;
// Move
_moveElements(dragOptions, offset, service.updateDragElement);
// Should we stop?
var stop = positive ? clientX <= target : clientX >= target;
// If we should stop
if (stop) {
// Cancel the interval
$interval.cancel(timer);
}
}, interval);
In theory it should travel at the same speed regardless of the distance it has to travel. But this didn't work.
Does anyone have some idea how I can solve this?
I have changed my code to this thanks to the proposed answer:
// Animates the slider
animate: function (startTime, distance, duration, options) {
// Animate
requestAnimationFrame(function () {
// Get our offset
var now = Date.now(),
timeDelta = Math.min(now - startTime, duration),
timePercent = timeDelta / duration,
offset = timePercent * distance;
var finalX = options.finalX + offset;
// Create the transform
options.transform = 'translateX(' + finalX + 'px)';
// Add to our element
options.element.style.transform = options.transform;
if (timeDelta < duration) {
service.animate(startTime, distance, duration, options);
} else {
options.finalX = finalX;
options.currentX = options.xOffset + (distance / $window.innerWidth);
}
});
},
// Moves the current active item to the center
moveToCenter: function (e, dragOptions, animate) {
// Get the central position
var windowWidth = $window.innerWidth,
center = windowWidth / 2,
rect = e.target.getBoundingClientRect(),
distance = center - rect.left - ((50 - 18) / 2), // Make these variables?
positive = rect.left > center;
// If we should animation
if (animate) {
var start = center,
distance = center - rect.left - ((50 - 18) / 2), // Make these variables?
now = Date.now(),
duration = distance / 1;
// Animate our elements
service.animate(now, distance, duration, dragOptions);
// Otherwise
} else {
// Update the current position
dragOptions.currentX = distance / dragOptions.speed;
// Move our elements
service.updateDragElement(dragOptions);
}
},
This now scrolls properly if I select anything left of the center. When it is right of the center it just jumps to the point instead of animating it.
Upvotes: 1
Views: 54
Reputation: 78920
Here would be my recommended approach. First figure out the velocity you'd like:
var velocity = 1; // pixels/millisecond
...then using a requestAnimationFrame
"loop":
var startPoint = center;
var endPoint = rect.left - 16;
var distance = endPoint - startPoint;
var startTime = Date.now();
var duration = distance / velocity;
animate();
function animate() {
requestAnimationFrame(function () {
var now = Date.now();
var timeDelta = Math.min(now - startTime, duration);
var timePercent = timeDelta / duration;
var offset = timePercent * distance;
_moveElements(dragOptions, offset, service.updateDragElement);
if (timeDelta < duration) {
animate();
}
});
}
Using setTimeout
/setInterval
can add artificial lag to an animation and will not perform consistently across devices.
Upvotes: 1