Created
August 20, 2018 07:21
-
-
Save codernik/e5efea0c1ceb7e9e7353f793e2e9e9ac to your computer and use it in GitHub Desktop.
Horizontal timeline JS Code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(document).ready(function($) { | |
var timelines = $('.cd-horizontal-timeline'), | |
eventsMinDistance = 100; | |
(timelines.length > 0) && initTimeline(timelines); | |
function initTimeline(timelines) { | |
timelines.each(function() { | |
var timeline = $(this), | |
timelineComponents = {}; | |
//cache timeline components | |
timelineComponents['timelineWrapper'] = timeline.find('.events-wrapper'); | |
timelineComponents['eventsWrapper'] = timelineComponents['timelineWrapper'].children('.events'); | |
timelineComponents['fillingLine'] = timelineComponents['eventsWrapper'].children('.filling-line'); | |
timelineComponents['timelineEvents'] = timelineComponents['eventsWrapper'].find('a'); | |
timelineComponents['timelineDates'] = parseDate(timelineComponents['timelineEvents']); | |
timelineComponents['eventsMinLapse'] = minLapse(timelineComponents['timelineDates']); | |
timelineComponents['timelineNavigation'] = timeline.find('.cd-timeline-navigation'); | |
timelineComponents['eventsContent'] = timeline.children('.events-content'); | |
//assign a left postion to the single events along the timeline | |
setDatePosition(timelineComponents, eventsMinDistance); | |
//assign a width to the timeline | |
var timelineTotWidth = setTimelineWidth(timelineComponents, eventsMinDistance); | |
//the timeline has been initialize - show it | |
timeline.addClass('loaded'); | |
//detect click on the next arrow | |
timelineComponents['timelineNavigation'].on('click', '.next', function(event) { | |
event.preventDefault(); | |
updateSlide(timelineComponents, timelineTotWidth, 'next'); | |
}); | |
//detect click on the prev arrow | |
timelineComponents['timelineNavigation'].on('click', '.prev', function(event) { | |
event.preventDefault(); | |
updateSlide(timelineComponents, timelineTotWidth, 'prev'); | |
}); | |
//detect click on the a single event - show new event content | |
timelineComponents['eventsWrapper'].on('click', 'a', function(event) { | |
event.preventDefault(); | |
timelineComponents['timelineEvents'].removeClass('selected'); | |
$(this).addClass('selected'); | |
updateOlderEvents($(this)); | |
updateFilling($(this), timelineComponents['fillingLine'], timelineTotWidth); | |
updateVisibleContent($(this), timelineComponents['eventsContent']); | |
}); | |
//on swipe, show next/prev event content | |
timelineComponents['eventsContent'].on('swipeleft', function() { | |
var mq = checkMQ(); | |
(mq == 'mobile') && showNewContent(timelineComponents, timelineTotWidth, 'next'); | |
}); | |
timelineComponents['eventsContent'].on('swiperight', function() { | |
var mq = checkMQ(); | |
(mq == 'mobile') && showNewContent(timelineComponents, timelineTotWidth, 'prev'); | |
}); | |
//keyboard navigation | |
$(document).keyup(function(event) { | |
if (event.which == '37' && elementInViewport(timeline.get(0))) { | |
showNewContent(timelineComponents, timelineTotWidth, 'prev'); | |
} else if (event.which == '39' && elementInViewport(timeline.get(0))) { | |
showNewContent(timelineComponents, timelineTotWidth, 'next'); | |
} | |
}); | |
}); | |
} | |
function updateSlide(timelineComponents, timelineTotWidth, string) { | |
//retrieve translateX value of timelineComponents['eventsWrapper'] | |
var translateValue = getTranslateValue(timelineComponents['eventsWrapper']), | |
wrapperWidth = Number(timelineComponents['timelineWrapper'].css('width').replace('px', '')); | |
//translate the timeline to the left('next')/right('prev') | |
(string == 'next') ? | |
translateTimeline(timelineComponents, translateValue - wrapperWidth + eventsMinDistance, wrapperWidth - timelineTotWidth): translateTimeline(timelineComponents, translateValue + wrapperWidth - eventsMinDistance); | |
} | |
function showNewContent(timelineComponents, timelineTotWidth, string) { | |
//go from one event to the next/previous one | |
var visibleContent = timelineComponents['eventsContent'].find('.selected'), | |
newContent = (string == 'next') ? visibleContent.next() : visibleContent.prev(); | |
if (newContent.length > 0) { //if there's a next/prev event - show it | |
var selectedDate = timelineComponents['eventsWrapper'].find('.selected'), | |
newEvent = (string == 'next') ? selectedDate.parent('li').next('li').children('a') : selectedDate.parent('li').prev('li').children('a'); | |
updateFilling(newEvent, timelineComponents['fillingLine'], timelineTotWidth); | |
updateVisibleContent(newEvent, timelineComponents['eventsContent']); | |
newEvent.addClass('selected'); | |
selectedDate.removeClass('selected'); | |
updateOlderEvents(newEvent); | |
updateTimelinePosition(string, newEvent, timelineComponents); | |
} | |
} | |
function updateTimelinePosition(string, event, timelineComponents) { | |
//translate timeline to the left/right according to the position of the selected event | |
var eventStyle = window.getComputedStyle(event.get(0), null), | |
eventLeft = Number(eventStyle.getPropertyValue('left').replace('px', '')), | |
timelineWidth = Number(timelineComponents['timelineWrapper'].css('width').replace('px', '')), | |
timelineTotWidth = Number(timelineComponents['eventsWrapper'].css('width').replace('px', '')); | |
var timelineTranslate = getTranslateValue(timelineComponents['eventsWrapper']); | |
if ((string == 'next' && eventLeft > timelineWidth - timelineTranslate) || (string == 'prev' && eventLeft < -timelineTranslate)) { | |
translateTimeline(timelineComponents, -eventLeft + timelineWidth / 2, timelineWidth - timelineTotWidth); | |
} | |
} | |
function translateTimeline(timelineComponents, value, totWidth) { | |
var eventsWrapper = timelineComponents['eventsWrapper'].get(0); | |
value = (value > 0) ? 0 : value; //only negative translate value | |
value = (!(typeof totWidth === 'undefined') && value < totWidth) ? totWidth : value; //do not translate more than timeline width | |
setTransformValue(eventsWrapper, 'translateX', value + 'px'); | |
//update navigation arrows visibility | |
(value == 0) ? timelineComponents['timelineNavigation'].find('.prev').addClass('inactive'): timelineComponents['timelineNavigation'].find('.prev').removeClass('inactive'); | |
(value == totWidth) ? timelineComponents['timelineNavigation'].find('.next').addClass('inactive'): timelineComponents['timelineNavigation'].find('.next').removeClass('inactive'); | |
} | |
function updateFilling(selectedEvent, filling, totWidth) { | |
//change .filling-line length according to the selected event | |
var eventStyle = window.getComputedStyle(selectedEvent.get(0), null), | |
eventLeft = eventStyle.getPropertyValue('left'), | |
eventWidth = eventStyle.getPropertyValue('width'); | |
eventLeft = Number(eventLeft.replace('px', '')) + Number(eventWidth.replace('px', '')) / 2; | |
var scaleValue = eventLeft / totWidth; | |
setTransformValue(filling.get(0), 'scaleX', scaleValue); | |
} | |
function setDatePosition(timelineComponents, min) { | |
for (i = 0; i < timelineComponents['timelineDates'].length; i++) { | |
var distance = daydiff(timelineComponents['timelineDates'][0], timelineComponents['timelineDates'][i]), | |
//distanceNorm = Math.round(distance/timelineComponents['eventsMinLapse']) + 1; | |
distanceNorm = i + 1; | |
timelineComponents['timelineEvents'].eq(i).css('left', distanceNorm * min + 'px'); | |
} | |
} | |
function setTimelineWidth(timelineComponents, width) { | |
var timeSpan = daydiff(timelineComponents['timelineDates'][0], timelineComponents['timelineDates'][timelineComponents['timelineDates'].length - 1]), | |
//timeSpanNorm = timeSpan/timelineComponents['eventsMinLapse'], | |
//timeSpanNorm = Math.round(timeSpanNorm) + 4, | |
timeSpanNorm = timelineComponents['timelineDates'].length + 1; | |
totalWidth = timeSpanNorm * width; | |
timelineComponents['eventsWrapper'].css('width', totalWidth + 'px'); | |
updateFilling(timelineComponents['eventsWrapper'].find('a.selected'), timelineComponents['fillingLine'], totalWidth); | |
updateTimelinePosition('next', timelineComponents['eventsWrapper'].find('a.selected'), timelineComponents); | |
return totalWidth; | |
} | |
function updateVisibleContent(event, eventsContent) { | |
var eventDate = event.data('date'), | |
visibleContent = eventsContent.find('.selected'), | |
selectedContent = eventsContent.find('[data-date="' + eventDate + '"]'), | |
selectedContentHeight = selectedContent.height(); | |
if (selectedContent.index() > visibleContent.index()) { | |
var classEnetering = 'selected enter-right', | |
classLeaving = 'leave-left'; | |
} else { | |
var classEnetering = 'selected enter-left', | |
classLeaving = 'leave-right'; | |
} | |
selectedContent.attr('class', classEnetering); | |
visibleContent.attr('class', classLeaving).one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function() { | |
visibleContent.removeClass('leave-right leave-left'); | |
selectedContent.removeClass('enter-left enter-right'); | |
}); | |
// eventsContent.css('height', selectedContentHeight+'px'); | |
} | |
function updateOlderEvents(event) { | |
event.parent('li').prevAll('li').children('a').addClass('older-event').end().end().nextAll('li').children('a').removeClass('older-event'); | |
} | |
function getTranslateValue(timeline) { | |
var timelineStyle = window.getComputedStyle(timeline.get(0), null), | |
timelineTranslate = timelineStyle.getPropertyValue('-webkit-transform') || | |
timelineStyle.getPropertyValue('-moz-transform') || | |
timelineStyle.getPropertyValue('-ms-transform') || | |
timelineStyle.getPropertyValue('-o-transform') || | |
timelineStyle.getPropertyValue('transform'); | |
if (timelineTranslate.indexOf('(') >= 0) { | |
var timelineTranslate = timelineTranslate.split('(')[1]; | |
timelineTranslate = timelineTranslate.split(')')[0]; | |
timelineTranslate = timelineTranslate.split(','); | |
var translateValue = timelineTranslate[4]; | |
} else { | |
var translateValue = 0; | |
} | |
return Number(translateValue); | |
} | |
function setTransformValue(element, property, value) { | |
element.style['-webkit-transform'] = property + '(' + value + ')'; | |
element.style['-moz-transform'] = property + '(' + value + ')'; | |
element.style['-ms-transform'] = property + '(' + value + ')'; | |
element.style['-o-transform'] = property + '(' + value + ')'; | |
element.style['transform'] = property + '(' + value + ')'; | |
} | |
//based on http://stackoverflow.com/questions/542938/how-do-i-get-the-number-of-days-between-two-dates-in-javascript | |
function parseDate(events) { | |
var dateArrays = []; | |
events.each(function() { | |
var singleDate = $(this), | |
dateComp = singleDate.data('date').split('T'); | |
if (dateComp.length > 1) { //both DD/MM/YEAR and time are provided | |
var dayComp = dateComp[0].split('/'), | |
timeComp = dateComp[1].split(':'); | |
} else if (dateComp[0].indexOf(':') >= 0) { //only time is provide | |
var dayComp = ['2000', '0', '0'], | |
timeComp = dateComp[0].split(':'); | |
} else { //only DD/MM/YEAR | |
var dayComp = dateComp[0].split('/'), | |
timeComp = ['0', '0']; | |
} | |
var newDate = new Date(dayComp[2], dayComp[1] - 1, dayComp[0], timeComp[0], timeComp[1]); | |
dateArrays.push(newDate); | |
}); | |
return dateArrays; | |
} | |
function daydiff(first, second) { | |
return Math.round((second - first)); | |
} | |
function minLapse(dates) { | |
//determine the minimum distance among events | |
var distance = 25517440000; | |
var dateDistances = []; | |
for (i = 1; i < dates.length; i++) { | |
// distance = distance; | |
dateDistances.push(distance); | |
} | |
return Math.min.apply(null, dateDistances); | |
} | |
function elementInViewport(el) { | |
var top = el.offsetTop; | |
var left = el.offsetLeft; | |
var width = el.offsetWidth; | |
var height = el.offsetHeight; | |
while (el.offsetParent) { | |
el = el.offsetParent; | |
top += el.offsetTop; | |
left += el.offsetLeft; | |
} | |
return ( | |
top < (window.pageYOffset + window.innerHeight) && | |
left < (window.pageXOffset + window.innerWidth) && | |
(top + height) > window.pageYOffset && | |
(left + width) > window.pageXOffset | |
); | |
} | |
function checkMQ() { | |
//check if mobile or desktop device | |
return window.getComputedStyle(document.querySelector('.cd-horizontal-timeline'), '::before').getPropertyValue('content').replace(/'/g, "").replace(/'/g, ''); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment