use requestAnimationFrame if available for smoother animation