Instead of using a simple timeline, use an animated timeline to enhance the appeal of your work. Like the code below, we have a horizontal timeline that displays text if we click on each timeline. It can be used when we want to show the roadmap of something as a question and answer and the user can see the answer of each time by clicking on each.
HTML
<!-- This script got from www.devanswer.com -->
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css'>
<div id="content" class="content">
<div class="container-fluid timeline">
<!-- ghost elements for background -->
<div class="row background">
<div class="col-lg-5 year-2018"></div>
<div class="col-lg-7 year-2019"></div>
</div>
<!-- ghost elements for background: END -->
<div class="container timeline__title">
<h2 class="section-heading">ROAD MAP</h2>
</div>
<div class="container years">
<div class="row">
<div class="col-lg-5">2018</div>
<div class="col-lg-7">2019</div>
</div>
</div>
<div class="timeline__points">
<div class="horizontal-line"></div>
<div class="container">
<div class="row">
<div class="col-lg-5 year-2018">
<!-- point -->
<div class="point"
v-for="(point, index) in timeline2018">
<div class="upper-content">
<div class="tooltip tooltip--top"
:ref="'tooltip2018' + index"
v-if="index % 2 != 0">
<ul>
<li v-for="task in point.points"
v-text="task">
</li>
</ul>
</div>
</div>
<div class="circle"
v-bind:class="{inThisQuarter: point.quarter == currentQuarter && year == 2018}"
v-text="point.quarter"
v-bind:ref="'circle2018' + index"
v-on:click="toggleTooltip(2018, index)">
</div>
<div class="bottom-content">
<div class="tooltip tooltip--bottom"
v-bind:ref="'tooltip2018' + index"
v-if="index % 2 == 0">
<ul>
<li v-for="task in point.points"
v-text="task">
</li>
</ul>
</div>
</div>
</div>
<!-- point: END-->
</div>
<div class="col-lg-7 year-2019">
<!-- point -->
<div class="point"
v-for="(point, index) in timeline2019">
<div class="upper-content">
<div class="tooltip tooltip--top"
v-bind:ref="'tooltip2019' + index"
v-if="index % 2 != 0">
<ul>
<li v-for="task in point.points"
v-text="task">
</li>
</ul>
</div>
</div>
<div class="circle"
v-bind:class="{inThisQuarter: point.quarter == currentQuarter && year == 2019}"
v-text="point.quarter"
v-bind:ref="'circle2019' + index"
v-on:click="toggleTooltip(2019, index)">
</div>
<div class="bottom-content">
<div class="tooltip tooltip--bottom"
v-bind:ref="'tooltip2019' + index"
v-if="index % 2 == 0">
<ul>
<li v-for="task in point.points"
v-text="task">
</li>
</ul>
</div>
</div>
</div>
<!-- point: END-->
</div>
</div>
</div>
</div>
</div>
</div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.3.3/js/swiper.min.js'></script><div id="bcl"><a style="font-size:8pt;text-decoration:none;" href="http://www.devanswer.com">Developers Answer</a></div>
CSS
@import url(https://fonts.googleapis.com/css?family=Roboto);
.circle.inThisQuarter::before {
animation: pulsing-1 2s cubic-bezier(0.19, 1, 0.22, 1) infinite alternate;
}
@keyframes pulsing-1 {
from {
transform: scale(1);
}
to {
transform: scale(1.7);
}
}
.circle.inThisQuarter::after {
animation: pulsing-2 2s cubic-bezier(0.19, 1, 0.22, 1) infinite alternate;
}
@keyframes pulsing-2 {
from {
transform: scale(1);
}
to {
transform: scale(2.1);
}
}
body {
margin: 0;
padding: 0;
overflow-x: hidden;
font-family: "Roboto";
}
.section-heading {
color: white;
font-size: 48px;
font-weight: 520;
}
.years {
color: white;
font-size: 36px;
font-weight: 520;
}
.content {
background: black;
min-height: 100vh;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
}
.timeline {
background: #7652D2;
height: 640px;
padding: 36px 0;
position: relative;
margin: 128px 0;
}
.timeline .background {
width: 100%;
height: inherit;
position: absolute;
top: 0;
left: 1.1%;
}
.timeline .background .year-2018 {
background-color: #7652D2;
}
.timeline .background .year-2019 {
background-color: #9B75DE;
}
.timeline__title, .timeline__years, .timeline__points {
position: relative;
z-index: 0;
}
.timeline__points {
height: 300px;
}
.timeline__points .year-2018, .timeline__points .year-2019 {
display: flex;
flex-direction: row;
justify-content: space-evenly;
align-items: flex-start;
flex-wrap: nowrap;
}
.timeline .horizontal-line {
background: white;
width: 100%;
height: 4px;
position: absolute;
top: 50%;
}
.point {
width: 120px;
height: 300px;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
flex-wrap: nowrap;
position: relative;
z-index: 4;
}
.point .upper-content {
width: 100%;
height: 102px;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: flex-start;
flex-wrap: nowrap;
}
.point .upper-content strong {
color: white;
font-size: 24px;
margin-bottom: 24px;
}
.point .bottom-content {
width: 100%;
height: 102px;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
flex-wrap: nowrap;
}
.circle {
background: orange;
width: 48px;
height: 48px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
border: 6px solid white;
border-radius: 50%;
color: white;
font-weight: bold;
box-shadow: 0 2px 2px 5px rgba(238, 145, 36, 0.2);
}
.circle::before, .circle::after {
content: "";
display: inline-block;
width: 48px;
height: 48px;
border: 4px solid transparent;
border-radius: inherit;
position: absolute;
transition: all 0.5s ease-out;
opacity: 0;
}
.circle::before {
background: rgba(255, 255, 255, 0.25);
z-index: -1;
}
.circle::after {
background: rgba(255, 255, 255, 0.15);
z-index: -2;
}
.circle.inThisQuarter::before {
opacity: 1;
}
.circle.inThisQuarter::after {
opacity: 1;
}
.circle.active {
border-color: orange;
}
.circle.active::before {
animation: none;
}
.circle.active::after {
animation: none;
background-color: transparent;
border-color: white;
transform: scale(1.7);
opacity: 1;
}
.circle:hover {
cursor: pointer;
}
.tooltip {
background-color: white;
width: 300px;
padding: 8px 4px;
border-radius: 16px;
transition: all 0.3s;
opacity: 0;
position: absolute;
left: -74%;
z-index: -3;
}
.tooltip.active {
opacity: 1;
}
.tooltip--top {
margin-top: -10%;
margin-bottom: 10%;
}
.tooltip--top::before {
content: "";
display: inline-block;
background: white;
width: 5px;
height: 24px;
position: absolute;
bottom: -24px;
left: 49%;
}
.tooltip--bottom {
margin-top: 10%;
}
.tooltip--bottom::before {
content: "";
display: inline-block;
background: white;
width: 5px;
height: 24px;
position: absolute;
top: -24px;
left: 49%;
}
Javascript
var timeline2018 = [
{
quarter: 'Q3',
points: [
'Website launch',
'Whitepaper release',
'TOKOIN token distributions starts',
'TOKOIN Wallet development'
]
},
{
quarter: 'Q4',
points: [
'POC Launch on TestNet',
'Onboard logistic partners on Tokoin ecosystem'
]
}
];
var timeline2019 = [
{
quarter: 'Q1',
points: [
'Launch dApps on MainNet',
'Onboard Warehousing partners on Tokoin ecosystem',
'Onboard Financial partners on Tokoin ecosystem',
'Expand local operation to 10 tier-one cities in Indonesia',
'Onboard 50.000 users on Tokoin ecosystem'
]
},
{
quarter: 'Q2',
points: [
'Launch Data Reputation engine',
'Launch Data Visualization platform',
'Launch Partner Suite platform'
]
},
{
quarter: 'Q3',
points: [
'Launch TOKOIN POS system',
'Launch Data Exchange platform for Token Stacking and Loyalty program',
'Expand local operation to all capital cities in Indonesia',
'Onboard 10.000 users for TOKOIN POS system'
]
},
{
quarter: 'Q4',
points: [
'Expand hyper-local operation to Thailand, Vietnam, Philippine, Malaysia and other potential emerging market'
]
}
];
var date = new Date();
var month = date.getMonth();
var year = date.getFullYear();
var vm = new Vue({
el: '#content',
data: {
timeline2018: timeline2018,
timeline2019: timeline2019,
month: month,
year: year
},
computed: {
currentQuarter: function () {
if (this.month >= 0 && this.month <= 2) return 'Q1';
else if (this.month >= 3 && this.month <= 5) return 'Q2';
else if (this.month >= 6 && this.month <= 8) return 'Q3';
return 'Q4';
}
},
methods: {
normalizeSelection(year, idx) {
var circ, tooltip, yearTemp = 2018, j = 0;
for (var i = 0; i < 6; i++) {
if (i > 1) {
yearTemp = 2019;
j = 0;
}
if (j != idx) {
circ = this.$refs['circle' + yearTemp + j][0];
tooltip = this.$refs['tooltip' + yearTemp + j][0];
circ.classList.remove('active');
tooltip.classList.remove('active');
}
j++;
}
},
toggleTooltip(year, idx) {
var circ = this.$refs['circle' + year + idx][0];
console.log(circ);
var tooltip = this.$refs['tooltip' + year + idx][0];
if (!circ.classList.contains('active')) {
circ.classList.add('active');
tooltip.classList.add('active');
} else {
circ.classList.remove('active');
tooltip.classList.remove('active');
}
},
}
});