Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a circular ScrollBox using JavaScript/CSS?

I want to recreate the circular scrollbox as depicted in the GIF below,

enter image description here

I don't think there is some why if making a circular scrollbox using css is possible so I thought of adding padding-left to each child of the ul to make the scrollbox appear circular.

To achieve this,
add padding of 0px to li-1
add padding of 2px to li-2
add padding of 4px to li-3
add padding of 6px to li-4
add padding of 8px to li-5
add padding of 6px to li-6
add padding of 4px to li-7
add padding of 2px to li-8
add padding of 0px to li-9

on scroll padding can be changed to add padding of 0px to li-2
add padding of 2px to li-3
add padding of 4px to li-4
add padding of 6px to li-5
add padding of 8px to li-6
add padding of 6px to li-7
add padding of 4px to li-8
add padding of 2px to li-9
add padding of 0px to li-10    and so on...

Problem with my code is, when the scrollbox is scrolled via mouse scroll-wheel, it acts correctly but when the user uses the scroll-tab/scroll buttons in the scrollbar, padding increases drastically.

My code so far is:

var scrollBox = $(".circularScrollbox"),
  num = $(".scrollboxList li").length,
  vjListItem = $(".vjListItem"),
  max = num * 3,
  padding = 0,
  currentPadding = padding,
  scrollPos = scrollBox.scrollTop();

scrollBox.scroll(function() {
  if (scrollPos < scrollBox.scrollTop() && currentPadding < max) {
    currentPadding += 2;
    vjListItem.css("padding", "0 0 0 " + currentPadding + "px");
  } else if (scrollPos > scrollBox.scrollTop() && currentPadding > padding) {
    currentPadding -= 2;
    vjListItem.css("padding", "0 0 0 " + currentPadding + "px");
  }
  if (scrollBox.scrollTop() == 0) vjListItem.css("padding", padding + "px");
  scrollPos = scrollBox.scrollTop();
});
body {
  display: flex;
  flex-direction: column;
  height: 95vh;
  justify-content: center;
  align-items: center;
  background: #222;
  color: #eee;
  font-family: 'Ubuntu Mono', monospace;
}

.circularScrollbox {
  width: 200px;
  height: 10.6em;
  padding: 0 2em;
  overflow-Y: scroll;
  background: #161616;
  border: 2px solid aqua;
}

.circularScrollbox>ol {
  list-style-type: none;
  padding-left: 0;
}
<head>
  <link href="https://fonts.googleapis.com/css?family=Ubuntu+Mono" rel="stylesheet">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>

<body>
  <div class='circularScrollbox'>
    <ol class='scrollboxList'>
      <li class="vjListItem">Item 01</li>
      <li class="vjListItem">Item 02</li>
      <li class="vjListItem">Item 03</li>
      <li class="vjListItem">Item 04</li>
      <li class="vjListItem">Item 05</li>
      <li class="vjListItem">Item 06</li>
      <li class="vjListItem">Item 07</li>
      <li class="vjListItem">Item 08</li>
      <li class="vjListItem">Item 09</li>
      <li class="vjListItem">Item 10</li>
      <li class="vjListItem">Item 11</li>
      <li class="vjListItem">Item 12</li>
      <li class="vjListItem">Item 13</li>
      <li class="vjListItem">Item 14</li>
      <li class="vjListItem">Item 15</li>
      <li class="vjListItem">Item 16</li>
      <li class="vjListItem">Item 17</li>
      <li class="vjListItem">Item 18</li>
      <li class="vjListItem">Item 19</li>
      <li class="vjListItem">Item 20</li>
      <li class="vjListItem">Item 21</li>
      <li class="vjListItem">Item 22</li>
      <li class="vjListItem">Item 23</li>
      <li class="vjListItem">Item 24</li>
      <li class="vjListItem">Item 25</li>
      <li class="vjListItem">Item 26</li>
      <li class="vjListItem">Item 27</li>
      <li class="vjListItem">Item 28</li>
      <li class="vjListItem">Item 29</li>
      <li class="vjListItem">Item 30</li>
      <li class="vjListItem">Item 31</li>
      <li class="vjListItem">Item 32</li>
      <li class="vjListItem">Item 33</li>
      <li class="vjListItem">Item 34</li>
      <li class="vjListItem">Item 35</li>
      <li class="vjListItem">Item 36</li>
      <li class="vjListItem">Item 37</li>
      <li class="vjListItem">Item 38</li>
      <li class="vjListItem">Item 39</li>
      <li class="vjListItem">Item 40</li>
    </ol>
  </div>
</body>

I am sure I'm over-complicating the problem with padding and stuff. Is there an easier way of achieving this effect?

like image 374
YourPalNurav Avatar asked Mar 02 '19 16:03

YourPalNurav


2 Answers

For every item in the list, the amount of padding depends on:

  1. Its position in the box
  2. How much the box is scrolled
  3. And because of the "circular" thing, how big the box is

With jQuery, $item.offset().top gives you the Y position of an item on the page.
That includes scroll, so the value goes down as the box is scrolled.

Now $item.offset().top - $box.offset().top for the position relative to the top of the box.
If you have scrolled the box so that Item 23 is at the top, its value will be 0, and the value of whatever item is at the bottom (but still visible) will be the height of the box.

So divide by the height: ($item.offset().top - $box.offset().top) / $box.height()

Each (visible) item now has a value between 0 and 1 that tells you if it is currently at the top of the box, at the bottom or somewhere in between !

You can then use a trigonometry function like so: Math.sin(value*Math.PI) * maxPadding

Working example

like image 96
Jean-Alphonse Avatar answered Nov 12 '22 01:11

Jean-Alphonse


Here is a solution that rely on CSS and only few JS. The trick is to consider shape-outside to have the curve and the JS will only be used to adjust the position of the shape on scroll.

It's a very simple method but you need to pay attention to browser support (https://caniuse.com/#feat=css-shapes)

var shape = document.querySelector(".left");

document.querySelector(".circularScrollbox").onscroll=function() {
   shape.style.marginTop = this.scrollTop+"px";
};
body {
  display: flex;
  flex-direction: column;
  height: 95vh;
  justify-content: center;
  align-items: center;
  background: #222;
  color: #eee;
  font-family: 'Ubuntu Mono', monospace;
}

.circularScrollbox {
  width: 200px;
  height: 10.6em;
  padding: 0 2em;
  overflow-Y: scroll;
  background: #161616;
  border: 2px solid aqua;
}

.circularScrollbox>ol {
  list-style-type: none;
  padding-left: 0;
}

.left {
  float: left;
  shape-outside: ellipse(50px 85px at 0% calc(100% - 85px));
  width: 100px;
  height: 100%;
  margin-top:0;
}
<link href="https://fonts.googleapis.com/css?family=Ubuntu+Mono" rel="stylesheet">

<div class='circularScrollbox'>
  <div class="left"></div>
  <ol class='scrollboxList'>
    <li class="vjListItem">Item 01</li>
    <li class="vjListItem">Item 02</li>
    <li class="vjListItem">Item 03</li>
    <li class="vjListItem">Item 04</li>
    <li class="vjListItem">Item 05</li>
    <li class="vjListItem">Item 06</li>
    <li class="vjListItem">Item 07</li>
    <li class="vjListItem">Item 08</li>
    <li class="vjListItem">Item 09</li>
    <li class="vjListItem">Item 10</li>
    <li class="vjListItem">Item 11</li>
    <li class="vjListItem">Item 12</li>
    <li class="vjListItem">Item 13</li>
    <li class="vjListItem">Item 14</li>
    <li class="vjListItem">Item 15</li>
    <li class="vjListItem">Item 16</li>
    <li class="vjListItem">Item 17</li>
    <li class="vjListItem">Item 18</li>
    <li class="vjListItem">Item 19</li>
    <li class="vjListItem">Item 20</li>
    <li class="vjListItem">Item 21</li>
    <li class="vjListItem">Item 22</li>
    <li class="vjListItem">Item 23</li>
    <li class="vjListItem">Item 24</li>
    <li class="vjListItem">Item 25</li>
    <li class="vjListItem">Item 26</li>
    <li class="vjListItem">Item 27</li>
    <li class="vjListItem">Item 28</li>
    <li class="vjListItem">Item 29</li>
    <li class="vjListItem">Item 30</li>
    <li class="vjListItem">Item 31</li>
    <li class="vjListItem">Item 32</li>
    <li class="vjListItem">Item 33</li>
    <li class="vjListItem">Item 34</li>
    <li class="vjListItem">Item 35</li>
    <li class="vjListItem">Item 36</li>
    <li class="vjListItem">Item 37</li>
    <li class="vjListItem">Item 38</li>
    <li class="vjListItem">Item 39</li>
    <li class="vjListItem">Item 40</li>
  </ol>
</div>
like image 5
Temani Afif Avatar answered Nov 12 '22 02:11

Temani Afif