In een vorige post heb ik je laten zien hoe je een responsieve verticale tijdlijn helemaal opnieuw kunt opbouwen. Vandaag zal ik het proces van het maken van de bijbehorende bespreken horizontaal tijdlijn.
Zoals gewoonlijk, om een idee te krijgen van wat we gaan bouwen, kun je de gerelateerde CodePen-demo bekijken (bekijk de grotere versie voor een betere ervaring):
We hebben veel te dekken, dus laten we aan de slag gaan!
De opmaak is identiek aan de opmaak die we hebben gedefinieerd voor de verticale tijdlijn, met uitzondering van drie kleine dingen:
.pijlen
) Die verantwoordelijk is voor de tijdlijnnavigatie.Dit is de vereiste markup:
Sommige inhoud hier
De begintoestand van de tijdlijn ziet er als volgt uit:
Na enkele eenvoudige letterstijlen, kleurstijlen, enz. Die ik hier omwille van de eenvoud heb weggelaten, specificeren we enkele structurele CSS-regels:
.tijdlijn white-space: nowrap; overflow-x: verborgen; .timeline ol font-size: 0; breedte: 100vw; opvulling: 250 px 0; overgang: alle 1s; .timeline ol li position: relative; weergave: inline-block; list-style-type: none; breedte: 160 px; hoogte: 3px; achtergrond: #fff; .timeline ol li: last-child width: 280px; .timeline ol li: not (: first-child) margin-left: 14px; .timeline ol li: not (: last-child) :: after content: "; position: absolute; top: 50%; left: calc (100% + 1px); bottom: 0; width: 12px; height: 12px; transform: translateY (-50%); grensradius: 50%; achtergrond: # F45B69;
Het belangrijkst hier, zult u twee dingen opmerken:
breedte: 100vw
en de ouder heeft overflow-x: verborgen
. Dit "maskeert" effectief de lijstitems. Dankzij de tijdlijnnavigatie kunnen we later door de items navigeren.Met deze regels is hier de huidige staat van de tijdlijn (zonder enige inhoud, om dingen duidelijk te houden):
Op dit punt zullen we de stijl stylen div
elementen (we zullen ze vanaf nu 'tijdlijnelementen' noemen) die deel uitmaken van de lijstitems, evenals hun ::voor
pseudo-elementen.
Daarnaast gebruiken we de : N-kind (oneven)
en : N-kind (zelfs)
CSS pseudo-klassen om onderscheid te maken tussen de stijlen voor de oneven en even divs.
Dit zijn de algemene stijlen voor de tijdlijnelementen:
.tijdlijn ol li div position: absolute; links: calc (100% + 7px); breedte: 280 px; opvulling: 15px; lettergrootte: 1rem; witruimte: normaal; de kleur zwart; achtergrond: wit; .timeline ol li div :: before content: "; position: absolute; top: 100%; left: 0; width: 0; height: 0; border-style: solid;
Dan enkele stijlen voor de oneven:
.tijdlijn ol li: nth-child (oneven) div top: -16px; transform: translateY (-100%); .timeline ol li: nth-child (oneven) div :: before top: 100%; grensbreedte: 8px 8px 0 0; randkleur: wit transparant transparant transparant;
En tot slot enkele stijlen voor de evene:
.tijdlijn ol li: nth-child (even) div top: calc (100% + 16px); .timeline ol li: nth-child (even) div :: before top: -8px; grensbreedte: 8px 0 0 8px; randkleur: transparant transparant transparant wit;
Dit is de nieuwe staat van de tijdlijn, met opnieuw toegevoegde content:
Zoals je waarschijnlijk hebt gemerkt, zijn de tijdlijnelementen absoluut gepositioneerd. Dat betekent dat ze uit de normale documentstroom worden verwijderd. Met dat in gedachten, om ervoor te zorgen dat de hele tijdlijn verschijnt, moeten we grote opvulwaarden voor boven en onder instellen voor de lijst. Als we geen paddings toepassen, wordt de tijdlijn bijgesneden:
Het is nu tijd om de navigatieknoppen te stylen. Onthoud dat we standaard de vorige pijl uitschakelen en hem de klasse geven invalide
.
Dit zijn de bijbehorende CSS-stijlen:
.tijdlijn .arrows display: flex; justify-content: center; margin-bottom: 20px; .timeline .arrows .arrow__prev margin-right: 20px; .timeline .disabled opacity: .5; .timeline .arrows img width: 45px; hoogte: 45 px;
De bovenstaande regels geven ons deze tijdlijn:
De basisstructuur van de tijdlijn is gereed. Laten we er wat interactiviteit aan toevoegen!
Allereerst moeten we een aantal variabelen instellen die we later zullen gebruiken.
const tijdlijn = document.querySelector (". timeline ol"), elH = document.querySelectorAll (".tijdlijn li> div"), arrows = document.querySelectorAll (".tijdlijn .arrows .arrow"), arrowPrev = document.querySelector (".timeline .arrows .arrow__prev"), arrowNext = document.querySelector (".tijdlijn .arrows .arrow__next"), firstItem = document.querySelector (". timeline li: first-child"), lastItem = document.querySelector ( ".timeline li: last-child"), xScrolling = 280, disabledClass = "disabled";
Wanneer alle pagina-items gereed zijn, de in het
functie wordt aangeroepen.
window.addEventListener ("load", init);
Deze functie triggert vier subfuncties:
functie init () setEqualHeights (elH); animateTl (xScrolling, arrows, timeline); setSwipeFn (tijdlijn, arrowPrev, arrowNext); setKeyboardFn (arrowPrev, arrowNext);
Zoals we zo dadelijk zullen zien, volbrengt elk van deze functies een bepaalde taak.
Als u terugkeert naar de laatste demo, merkt u dat de tijdlijnelementen geen gelijke hoogte hebben. Dit heeft geen invloed op de hoofdfunctionaliteit van onze tijdlijn, maar u kunt er de voorkeur aan geven als alle elementen dezelfde hoogte hebben. Om dit te bereiken, kunnen we ze een vaste hoogte geven via CSS (eenvoudige oplossing) of een dynamische hoogte die overeenkomt met de hoogte van het hoogste element via JavaScript.
De tweede optie is flexibeler en stabieler, dus hier is een functie die dit gedrag implementeert:
functiesetEqualHeights (el) let counter = 0; for (let i = 0; i < el.length; i++) const singleHeight = el[i].offsetHeight; if (counter < singleHeight) counter = singleHeight; for (let i = 0; i < el.length; i++) el[i].style.height = '$counterpx';
Met deze functie wordt de hoogte van het hoogste tijdlijnelement opgehaald en wordt deze ingesteld als de standaardhoogte voor alle elementen.
Hier ziet u hoe de demo eruitziet:
Laten we ons nu concentreren op de tijdlijnanimatie. We zullen de functie bouwen die dit gedrag stap voor stap implementeert.
Eerst registreren we een klikgebeurtenislistener voor de tijdlijnknoppen:
function animateTl (scrollen, el, tl) for (let i = 0; i < el.length; i++) el[i].addEventListener("click", function() // code here );
Telkens wanneer op een knop wordt geklikt, controleren we de uitgeschakelde status van de tijdlijnknoppen en als ze niet zijn uitgeschakeld, schakelen we ze uit. Dit zorgt ervoor dat er slechts één keer op beide knoppen wordt geklikt totdat de animatie is voltooid.
Dus, in termen van code, bevat de click handler aanvankelijk deze regels:
if (! arrowPrev.disabled) arrowPrev.disabled = true; if (! arrowNext.disabled) arrowNext.disabled = true;
De volgende stappen zijn:
transformeren
eigenschap om de tijdlijn 280 px naar rechts te verplaatsen. De waarde van de xScrolling
variabele bepaalt de hoeveelheid beweging. transformeren
waarde van de tijdlijn en voeg toe of verwijder aan die waarde de gewenste hoeveelheid beweging (d.w.z. 280px). Dus, zolang we op de klikken voorgaand knop, de waarde van de transformeren
eigenschap neemt af en de tijdlijn wordt van links naar rechts verplaatst. Echter, wanneer het volgende knop is geklikt, de waarde van de transformeren
eigenschap neemt toe en de tijdlijn wordt van rechts naar links verplaatst.De code die deze functionaliteit implementeert is als volgt:
laat teller = 0; for (let i = 0; i < el.length; i++) el[i].addEventListener("click", function() // other code here const sign = (this.classList.contains("arrow__prev")) ? "" : "-"; if (counter === 0) tl.style.transform = 'translateX(-$scrollingpx)'; else const tlStyle = getComputedStyle(tl); // add more browser prefixes if needed here const tlTransform = tlStyle.getPropertyValue("-webkit-transform") || tlStyle.getPropertyValue("transform"); const values = parseInt(tlTransform.split(",")[4]) + parseInt('$sign$scrolling'); tl.style.transform = 'translateX($valuespx)'; counter++; );
Goed werk! We hebben zojuist een manier gedefinieerd om de tijdlijn te animeren. De volgende uitdaging is om erachter te komen wanneer deze animatie moet stoppen. Dit is onze aanpak:
Onthoud dat het laatste element een leeg element is met een breedte gelijk aan de breedte van de tijdlijnelementen (d.w.z. 280px). We geven deze waarde (of een hogere) omdat we willen zorgen dat het laatste tijdlijnelement zichtbaar is voordat de functie wordt uitgeschakeld. volgende knop.
Om te detecteren of de doelelementen volledig zichtbaar zijn in het huidige venster, gebruiken we dezelfde code die we hebben gebruikt voor de verticale tijdlijn. De vereiste code die afkomstig is van deze Stack Overflow-thread is als volgt:
function isElementInViewport (el) const rect = el.getBounding ClientRect (); return (rect.top> = 0 && rect.left> = 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) );
Naast de bovenstaande functie definiëren we een andere helper:
function setBtnState (el, flag = true) if (vlag) el.classList.add (disabledClass); else if (el.classList.contains (disabledClass)) el.classList.remove (disabledClass); el.disabled = false;
Met deze functie wordt het. Toegevoegd of verwijderd invalide
klasse van een element op basis van de waarde van de vlag
parameter. Bovendien kan het de uitgeschakelde status voor dit element wijzigen.
Gegeven wat we hierboven hebben beschreven, hier is de code die we definiëren om te controleren of de animatie moet stoppen of niet:
for (let i = 0; i < el.length; i++) el[i].addEventListener("click", function() // other code here // code for stopping the animation setTimeout(() => isElementInViewport (firstItem)? setBtnState (arrowPrev): setBtnState (arrowPrev, false); isElementInViewport (lastItem)? setBtnState (arrowNext): setBtnState (arrowNext, false); , 1100); // andere code hier);
Merk op dat er een vertraging van 1,1 seconde is voordat deze code wordt uitgevoerd. Waarom gebeurt dit?
Als we teruggaan naar onze CSS, zien we deze regel:
.tijdlijn ol transition: all 1s;
De tijdlijnanimatie heeft dus 1 seconde nodig om te voltooien. Zolang het voltooid is, wachten we 100 milliseconden en voeren we onze controles uit.
Dit is de tijdlijn met animaties:
Tot nu toe reageert de tijdlijn niet op aanraakgebeurtenissen. Het zou leuk zijn als we deze functionaliteit zouden kunnen toevoegen. Om dit te bereiken, kunnen we onze eigen JavaScript-implementatie schrijven of een van de gerelateerde bibliotheken (bijvoorbeeld Hammer.js, TouchSwipe.js) gebruiken die er zijn.
Voor onze demo houden we dit eenvoudig en gebruiken we Hammer.js, dus eerst nemen we deze bibliotheek op in onze pen:
Vervolgens verklaren we de bijbehorende functie:
functie setSwipeFn (tl, vorige, volgende) const hammer = new Hammer (tl); hammer.on ("swipeleft", () => next.click ()); hammer.on ("swiperight", () => vorige klik ());
Binnen de bovenstaande functie doen we het volgende:
swipeleft
en veeg naar rechts
events. De tijdlijn met swipe-ondersteuning:
Laten we de gebruikerservaring verder verbeteren door ondersteuning te bieden voor toetsenbordnavigatie. Onze doelen:
De bijbehorende functie is de volgende:
function setKeyboardFn (vorige, volgende) document.addEventListener ("keydown", (e) => if ((e.which === 37) || (e.which === 39)) const timelineOfTop = tijdlijn .offsetTop; const y = window.pageYOffset; if (timelineOfTop! == y) window.scrollTo (0, timelineOfTop); if (e.which === 37) prev.click (); else if ( e.which === 39) next.click (););
De tijdlijn met toetsenbordondersteuning:
We zijn bijna klaar! Last but not least, laten we de tijdlijn responsief maken. Wanneer het kijkvenster minder dan 600 px is, zou het de volgende gestapelde lay-out moeten hebben:
Aangezien we een desktop-first benadering gebruiken, zijn hier de CSS-regels die we moeten overschrijven:
@media-scherm en (max-width: 599px) .timeline ol, .timeline ol li width: auto; .timeline ol opvulling: 0; transformeren: geen! belangrijk; .timeline ol li display: block; hoogte: auto; achtergrond: transparant; .timeline ol li: first-child margin-top: 25px; .timeline ol li: not (: first-child) margin-left: auto; .timeline ol li div width: 94%; hoogte: auto! belangrijk; marge: 0 auto 25px; .timeline ol li: nth-child div position: static; .timeline ol li: nth-child (oneven) div transform: none; .timeline ol li: nth-child (oneven) div :: before, .timeline ol li: nth-child (even) div :: before left: 50%; top 100%; transformatie: translateX (-50%); rand: geen; border-links: 1px effen wit; hoogte: 25px; .timeline ol li: last-child, .timeline ol li: nth-last-child (2) div :: before, .timeline ol li: not (: last-child) :: after, .timeline .arrows display : geen;
Notitie: Voor twee van de bovenstaande regels moesten we de !belangrijk
regel om de gerelateerde inline stijlen te overschrijven die worden toegepast via JavaScript.
De uiteindelijke status van onze tijdlijn:
De demo werkt goed in alle recente browsers en apparaten. Zoals je misschien hebt gemerkt, gebruiken we Babel om onze ES6-code te compileren naar ES5.
Het enige kleine probleem dat ik tegenkwam tijdens het testen ervan, is de tekstvernieuwing die optreedt wanneer de tijdlijn wordt geanimeerd. Hoewel ik verschillende benaderingen probeerde die in verschillende Stack Overflow-threads werden voorgesteld, vond ik geen eenvoudige oplossing voor alle besturingssystemen en browsers. Houd dus rekening met het feit dat er kleine lettertypeweergaveproblemen optreden als de tijdlijn wordt geanimeerd.
In deze redelijk omvangrijke tutorial begonnen we met een eenvoudige geordende lijst en creëerde een responsieve horizontale tijdlijn. We hebben ongetwijfeld heel wat interessante dingen behandeld, maar ik hoop dat je het leuk vond om naar het eindresultaat toe te werken en dat het je heeft geholpen nieuwe kennis op te doen..
Als je vragen hebt of als er iets is dat je niet begrijpt, laat het me weten in de reacties hieronder!
Als u deze tijdlijn verder wilt verbeteren of uitbreiden, kunt u het volgende doen: