JSFiddle - React, Tailwind, and code Playground

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.min.js"></script>
<script src="https://goodsight.hk/raptor/hkida/js/jquery.panelSnap.js"></script>
<div class="indicator">
	<div class="section"><span>Section 1</span></div>
	<div class="section"><span>Section 2</span></div>
	<div class="section"><span>Section 3</span></div>
	<div class="section"><span>Section 4</span></div>
	<canvas id="line_effect" width="50" height="100%"></canvas>
</div>
<div class="contents">
	<section class="page" data-panel="page1">
		<div id="page1">Page 1</div>
	</section>
	<section class="page" data-panel="page2">
		<div id="page2">Page 2</div>
	</section>
	<section class="page" data-panel="page3">
		<div id="page3">Page 3</div>
	</section>
	<section class="page" data-panel="page4">
		<div id="page4">Page 4</div>
	</section>
</div>

CSS

html, body {
	height: 100%;
	width: 100%;
	padding: 0;
	margin: 0;
}
body {
	font-size: 1em;
	background-color: #EEE;
	overflow: hidden;
}
.indicator {
	position: relative;
	width: 50px;
	height: 100%;
	float: left;
}
.indicator .section {
	height: 25%;
	width: 99%;
	border-right: 1px #FFF solid;
	border-bottom: 2px #FFF dashed;
}
.indicator .section span {
	font-size: 0.3em;
}
.indicator .canvas-container {
	position: absolute !important;
	width: 100% !important;
	height: 100% !important;
	top: 0;
	left: 0;
}
.indicator .canvas-container #line_effect {
	width: 100% !important;
	height: 100% !important;
	top: 0;
	left: 0;

}
.contents {
	width: calc(100% - 50px);
	height: 100%;
	float: left;
	overflow: auto;
}
.contents .page {
	width: 100%;
	height: 100%;
	font-size: 4em;
}
.contents .page > div {
	height: 100%;
	line-height: 200px;
	text-align: center;
}
.contents #page1 {
	background-color: #ABCDEF;
}
.contents #page2 {
	background-color: #67890A;
}
.contents #page3 {
	background-color: #345678;
}
.contents #page4 {
	background-color: #7890AB;
}

JavaScript

var canvas;
var current_page = 1;
var is_animating = false;
var line_options = {
	stroke: 'red',
	strokeWidth: 3
};
$(document).ready(function() {
	canvas = new fabric.Canvas('line_effect');
	canvas.setHeight($('.indicator').height());
	canvas.setWidth(50);
	canvas.renderAll();
	var section_h = $('.section').height();
	
	var startPts = [
		{x: 48, y: 0},
		{x: 48, y: $('.section').height()}
	];
	var endPts = [
		{x: 48, y: $('.section').height() * 2},
		{x: 48, y: $('.section').height()}
	];
	
	var line = new fabric.Polyline(startPts, line_options);
	canvas.add(line);
	
	function animateLine(i, prop, endPts) {
		fabric.util.animate({
			startValue: line.points[i][prop],
			endValue: endPts[i][prop],
			duration: 1000,
			easing: fabric.util.ease.easeInOutCubic,
			onChange: function(value) {
				line.points[i][prop] = value;
				if(i === startPts.length - 1 && prop == 'y') {
					canvas.renderAll();
				}
			},
			onComplete: function() {
				line.setCoords();
				if(i === startPts.length - 1 && prop == 'y') {
					even = !even;
					animateLine(i, prop, endPts);
				}
			}
		});
	}
	function animate(startPage, endPage) {
		if(is_animating) {
			return;
		} else if(startPage == endPage) {
			return;
		} else if(Math.abs(startPage - endPage) > 1) {
			// Jump to that section immediately.
			console.log('Jump to Page ' + endPage);
			canvas.remove(line);
			if(endPage == 1) {
				line = new fabric.Line([48, section_h * 0, 48, section_h * 1], line_options);
			} else if(endPage == 2) {
				line = new fabric.Line([48, section_h * 1, 48, section_h * 2], line_options);
			} else if(endPage == 3) {
				line = new fabric.Line([48, section_h * 2, 48, section_h * 3], line_options);
			} else if(endPage == 4) {
				line = new fabric.Line([48, section_h * 3, 48, section_h * 4], line_options);
			} 
			canvas.add(line);
		} else {
			is_animating = true;
			if(startPage == 1 && endPage == 2) {
				console.log('Page 1 > 2');
				startPts = [
					{x: 48, y: section_h *...