offsetWidth de cada elemento por 2 para que se calcule que esté centrado, pero no estoy seguro de dónde/cómo hacerlo.Enlace a JS Fiddle: https://jsfiddle.net/nb5a92x3/
window.onload = function() { const tabsContainer = document.querySelector('.tabsContainer'); const tabs = tabsContainer.querySelectorAll('.tab'); function animate(target) { let siblings = []; let elm = target; let keyframes = []; let offset; // Push the widths of each previous element to an array while (elm = elm.previousElementSibling) { siblings.push(elm.offsetWidth); } // Add the sum of all previous elements offset = siblings.reduce((prev, current) => prev + current, 0); // Animate the container to the left setTimeout(function() { tabsContainer.style.left = `calc(50% - ${offset}px)`; }, i * 800); } // Run the code for (i = 0; i < tabs.length; i++) { animate(tabs[i]); } } body { background: #ccc; } section { text-align: center; width: 375px; background: white; margin: 0 auto; padding: 16px; position: relative; display: flex; overflow: hidden; } .tabsContainer { display: flex; position: relative; margin: 0; padding: 0; list-style: none; gap: 0 16px; transition: left 0.2s ease; .tab { border-radius: 25px; background: gray; text-align: center; height: 34px; display: inline-flex; align-items: center; padding: 0 16px; } } <section> <ul class="tabsContainer"> <li class="tab">TAB ONE</li> <li class="tab">ANOTHER TAB HERE</li> <li class="tab">A REALLY LONG TAB HERE</li> <li class="tab">LAST TAB</li> </ul> </section>Si configura en CSS su contenedor al margin-left: 50%
entonces todo lo que necesita es x = EL_curr.offsetLeft + EL_curr.offsetWidth / 2
y aplique ese valor x al CSS translateX del contenedor deslizante (que tiene mucho más rendimiento que animar la propiedad left de CSS, ya que la transform puede acelerarse por hardware ).
// Utility functions: const EL = (sel, par) => (par || document).querySelector(sel); const ELS = (sel, par) => (par || document).querySelectorAll(sel); // Tabs animator: const tabsAnimator = (EL_slider) => { const ELS_items = ELS(".tab", EL_slider); const tot = ELS_items.length; let curr = 0; let tOut; const anim = () => { const EL_curr = ELS_items[curr]; const x = EL_curr.offsetLeft + EL_curr.offsetWidth / 2; EL_slider.style.transform = `translateX(-${x}px)`; curr += 1; // Increment counter if (curr > tot - 1) curr = 0; // Reset counter tOut = setTimeout(anim, 1500); }; anim(); // Init! }; ELS(".tabsContainer").forEach(tabsAnimator); * { margin: 0; box-sizing: border-box; } .tabsWrapper { width: 375px; margin: 20px auto; padding: 16px 0; position: relative; overflow: hidden; background: #ccc; border-radius: 3em; } .tabsContainer { position: relative; padding: 0; display: inline-flex; list-style: none; gap: 0 15px; transition: transform 0.3s; margin-left: 50%; } .tabsContainer .tab { border-radius: 25px; background: gold; white-space: nowrap; padding: 5px 15px; } <section class="tabsWrapper"> <ul class="tabsContainer"> <li class="tab">5 OFF FRIDAY</li> <li class="tab">10 OFFSUMMER</li> <li class="tab">SAVE MORE</li> <li class="tab">HALF OFF EVERYTHING</li> </ul> </section> <section class="tabsWrapper"> <ul class="tabsContainer"> <li class="tab">JavaScript</li> <li class="tab">HTML</li> <li class="tab">CSS</li> </ul> </section>section como selector. Es demasiado común. Utilice más bien una clase específica como .tabsWrapper en el contenedor padre. Además, si desea pausar la animación en "mouseenter" , use clearTimeout(tOut); — y simplemente anim() en el "mouseleave" .