3

I have to show Array values in circle kind of shape and it has to be run in a loop forever. Things are working fine when you rotate circle counter clockwise. But it has a problem in when we move circle in opposite direction.

I have an active element on the wheel. So when you user clicks on any other slide then active it calculates the difference between clicked slide and active slide then add and remove items in wheel accordingly.

So basically it picks the value from Array. if you move circle clockwise it picks values from the back of the Array and if you move it counterclockwise it starts picking up values from next available. If 11 items are rendered in first-page load then it will start taking values from 12 no index.

The problem occurs when you click the item which has above position from the active element and then you again rotates it counterclockwise.

Let say you click item no. 8 then you click item no.7. In this case item, no.2 should have been added into the wheel.

Here is fiddle.

var numberOfElement = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30];
var initialRender = numberOfElement.slice(0, 11);
var startPoint = initialRender.length;
var endPoint = numberOfElement.length;

function generateHtml() {
  var html = '';
  initialRender.forEach(function(item, index) {
    var angle = 18 * (index);
    var className = angle === 90 ? 'active' : '';
    html += '<div class="shapes ' + className + '" data-deg="' + angle + '" style="--deg:' + angle + 'deg;"> <span class="set-pos">' + (item) + '</span> <span> ' + angle + ' deg </span></div>';
  })
  document.querySelector('#dynamic-html').innerHTML = html;
}

generateHtml();

$('#dynamic-html').on('click', '.shapes', function() {
  var deg = 90;
  var activeDeg = $('.active').data('deg');
  var needToremoveElement = activeDeg;
  var selectedElement = $(this).data('deg');
  var degrees = deg - selectedElement;
  var diff = Math.abs((activeDeg - selectedElement) / 18);
  $('.shapes').removeClass('active');
  $(this).addClass('active');
  var movementCloseWise = degrees > ($('.circle').data('deg') || 0);
  $('.circle').removeData('deg');
  $('.circle').css({
    'transform': 'rotate(' + degrees + 'deg)'
  }).attr('data-deg', degrees);
  if (movementCloseWise) {
    var itemLength = $('.shapes').length;
    $('.shapes:gt(' + ((itemLength - 1) - diff) + ')').remove()

    var newItems = generateItem(getItemsFromBack(diff), true);
    newItems = $(newItems).get().reverse();
    $('#dynamic-html').prepend(newItems)
    startPoint -= diff;
  } else {
    var newItems = generateItem(getItemFromStart(diff), false)
    $('#dynamic-html').append(newItems)
    $('.shapes:lt(' + (diff) + ')').remove()
    endPoint += diff;
  }

})



function getItemsFromBack(length) {
  var values = [];
  endPoint = endPoint - length;
  if (endPoint < 0) {
    endPoint = numberOfElement.length - Math.abs(endPoint)
    var otherVal = 0;
    if (endPoint + length >= numberOfElement.length) {
      otherVal = (endPoint + length) - numberOfElement.length;
      values = numberOfElement.slice(endPoint, numberOfElement.length)
    }
    if (otherVal > 0) {
      values = values.concat(numberOfElement.slice(0, otherVal))
    }
  } else {
    values = numberOfElement.slice(endPoint, endPoint + length)
  }
  var valuesCount = values.length;
  return values.reverse();
}

function getItemFromStart(length) {
  var values = numberOfElement.slice(startPoint, startPoint + length);
  var valueCount = values.length;
  startPoint += valueCount;
  if (valueCount < length) {
    startPoint = 0;
    return values.concat(getItemFromStart(length - valueCount));
  } else if (startPoint >= numberOfElement.length) {
    startPoint = 0;
  }

  return values;
}

function generateItem(items, isClockWise) {
  var html = "",
    lastItemAngle;
  if (isClockWise) {
    lastItemAngle = $('#dynamic-html .shapes:first').data('deg');
  } else {
    lastItemAngle = $('#dynamic-html .shapes:last').data('deg');
  }

  items.forEach(function(item, index) {
    if (isClockWise) {
      var angles = lastItemAngle - (18 * (index + 1))
    } else {
      var angles = lastItemAngle + (18 * (index + 1))
    }
    html += '<div class="shapes" data-deg="' + (angles) + '" style="--deg:' + angles + 'deg;"> <span class="set-pos">' + (item) + '</span> <span>   ' + angles + ' deg </span></div>';
  });

  return html;
}
4
  • There is some logic issue in getItemsFromBack function. I was debugging the code, but finding difficulties because of complex logic. I am still checking. meanwhile, can you also have a look into this function. Commented May 28, 2018 at 12:45
  • In fiddle Javascript, line 36 if you make startPoint += diff; and in line 41 endPoint -= diff;, then the no. of hands getting added properly, but the content is adding wrong, because of wrong endPoint calculation in that function. Commented May 28, 2018 at 12:48
  • 1
    Your explanation is not easy to understand of what exactly you want. Can you please add para explaining what should be there? Commented May 31, 2018 at 16:21
  • @TarunLalwani: I understood the explanation after playing with the fiddle a bit. Items are added/removed to render always the same number of items in a spinning wheel fashion. I hope my solution will help OP see clearer in a simple way of achieving this. Commented Jun 3, 2018 at 19:22

3 Answers 3

1
+500

I think your problem is just your endPoint initialization value. In your code, you initialize it as var endPoint = numberOfElement.length; which is wrong( I think) and it should be initialized by 0; I changed it and it worked:

var numberOfElement = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30];
    	var initialRender = numberOfElement.slice(0,11);
    	var startPoint = initialRender.length;
    	var endPoint = 0; /* here is the change */
    	function generateHtml(){
    		var html ='';
    		initialRender.forEach(function(item,index){
    			var angle = 18 * (index);
    			var className = angle === 90? 'active':'';
    			html+='<div class="shapes '+className+'" data-deg="'+angle+'" style="--deg:'+angle+'deg;"> <span class="set-pos">'+(item)+'</span> <span>	'+angle+' deg </span></div>';
    		})
    		document.querySelector('#dynamic-html').innerHTML= html;
    	}

		generateHtml();
		
    	$('#dynamic-html').on('click','.shapes',function(){
    		var deg = 90;
    		var activeDeg = $('.active').data('deg');
    		var needToremoveElement = activeDeg;
    		var selectedElement = $(this).data('deg');
    		var degrees = deg - selectedElement;
    		var diff = Math.abs((activeDeg - selectedElement) / 18);
    		$('.shapes').removeClass('active');
    		$(this).addClass('active');
    		var movementCloseWise = degrees > ($('.circle').data('deg') || 0);
    		$('.circle').removeData('deg');
    		$('.circle').css({'transform' : 'rotate('+ degrees +'deg)'}).attr('data-deg',degrees);
    		if(movementCloseWise){
    			var itemLength = $('.shapes').length;
    			$('.shapes:gt('+((itemLength-1)-diff)+')').remove()

    			var newItems = generateItem(getItemsFromBack(diff), true);
    			newItems =  $(newItems).get().reverse();
    			$('#dynamic-html').prepend(newItems)
    			startPoint -= diff;
    		}else{
    			var newItems = generateItem(getItemFromStart(diff), false)
    			$('#dynamic-html').append(newItems)
    			$('.shapes:lt('+(diff)+')').remove()
    			endPoint += diff;
    		}
    			
    	})


    	
		function getItemsFromBack(length) {
		   var values = [];
		  endPoint = endPoint - length;
		  if (endPoint < 0) {
		    endPoint = numberOfElement.length - Math.abs(endPoint)
		    var otherVal = 0;
		    if (endPoint + length >= numberOfElement.length) {
		      otherVal = (endPoint + length) - numberOfElement.length;
		      values = numberOfElement.slice(endPoint, numberOfElement.length)
		    }
		    if (otherVal > 0) {
		      values = values.concat(numberOfElement.slice(0, otherVal))
		    }
		  } else {
		    values = numberOfElement.slice(endPoint, endPoint + length)
		  }
		  var valuesCount = values.length;
		  return values.reverse();
		}

    	function getItemFromStart(length) {
		    var values = numberOfElement.slice(startPoint, startPoint + length);
		    var valueCount = values.length;
    		startPoint += valueCount;
		    if (valueCount < length) {
		        startPoint = 0;
		        return values.concat( getItemFromStart(length - valueCount) );
		    } else if (startPoint >= numberOfElement.length) {
		        startPoint = 0;
		    }

		    	return values;
			}

		function generateItem (items, isClockWise){
			var html = "", lastItemAngle;	
			if(isClockWise){
				lastItemAngle = $('#dynamic-html .shapes:first').data('deg');	
			}
			else{
			    lastItemAngle = $('#dynamic-html .shapes:last').data('deg');		
			}
			
			items.forEach(function(item,index){
				if(isClockWise){
					var angles = lastItemAngle - (18 * (index +1))
				}
				else{
					var angles = lastItemAngle + (18 * (index +1))	
				}
    			html+='<div class="shapes" data-deg="'+(angles)+'" style="--deg:'+angles+'deg;"> <span class="set-pos">'+(item)+'</span> <span>	'+angles+' deg </span></div>';
    		});

    		return html;
		}
.main{
    	display: flex;
    	justify-content: center;
    	align-items: center;
    	height: 500px;
    }
    .pos{
		height:150px;
		width:150px;
		position: relative;
    }
	.circle{
		background: red;
		height:150px;
		width:150px;
		border-radius: 50%;
		transition: transform 0.3s ease-in-out;
	}
	.shapes{
		position: absolute;
		top:calc(50% - 75px);
		left:calc(50% - 10px);
		width: 20px;
	    height: 150px;
	    transform: rotate(var(--deg)) translate(0, 160px); 
	    background: green;
	    text-align: center;
	}

	.fake-overlay{
    position: absolute;
    width: 203%;
    height: 320%;
    background: #fff;
    top: -160px;
    right: -148%;
    display: none
    
	}
	.active{
		background: red
	}
	.set-pos{
		position: absolute;
		bottom: 0
	}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
  		<div class="pos">
	  		<div class="circle">
	  			<div id="dynamic-html"></div>
	  		</div>
	  		<div class="fake-overlay"></div>
  		</div>
  	</div>

And here is Jsfiddle

Sign up to request clarification or add additional context in comments.

1 Comment

Good catch! I still think the bigger problem was in the design of the solution which is still fragile. @Carlos: If it ain't broke, don't fix it
1

I have fixed this by the following code.

function getItemsFromBack(length) {
  var values = [];
  if (endPoint > numberOfElement.length) {
    var diff = endPoint - numberOfElement.length;
    values = numberOfElement.slice(diff - length, diff)
    endPoint = endPoint - length;
    return values.reverse();
  }
  endPoint = endPoint - length;
  if (endPoint < 0) {
    endPoint = numberOfElement.length - Math.abs(endPoint)
    var otherVal = 0;
    if (endPoint + length >= numberOfElement.length) {
      otherVal = (endPoint + length) - numberOfElement.length;
      values = numberOfElement.slice(endPoint, numberOfElement.length)
    }
    if (otherVal > 0) {
      values = values.concat(numberOfElement.slice(0, otherVal))
    }
  } else {
    values = numberOfElement.slice(endPoint, endPoint + length)
  }
  var valuesCount = values.length;
  return values.reverse();
}

I am checking if endPoint is greater then total Array length. Then endPoint- Array.length and using diff to get the element

Comments

1

Your CSS is awesome, but your JavaScript still has a bug. Try this:

click 7, then click 2

To fix it, I propose we simplify your logic a bit:

  • give meaningful ids to items so we can easily select them, compare them and deduce the item angle
  • do not use hard coded arrays (if you did it because of a code quality tool like jslint, consider telling it to tolerate for loops)

The complexity of your code was coming from the management of start and end points within a range of 1-30. This is now made easy by 1st point above.

I have not changed your code too much so you can recognize your working parts easily:

// Global variables
var numberOfItems = 30,
    numberOfRenderedItems = 11,
    firstItem = 0
;

function generateHtml(){
    var html ='';
    for (var item = 0; item < numberOfRenderedItems; item++) {
        var angle = 18 * item,
            className = angle === 90? 'active':'';
        html +=
            '<div class="shapes '+className+'" data-item="'+item+'" data-deg="'+angle+'" style="--deg:'+angle+'deg;">'+
            ' <span class="set-pos">'+(item+1)+'</span>'+
            ' <span>'+angle+' deg </span>'+
            '</div>';
    }
    document.querySelector('#dynamic-html').innerHTML= html;
}

generateHtml();

$('#dynamic-html').on('click','.shapes',function(){

    // Set clicked item active
    $('.shapes').removeClass('active');
    $(this).addClass('active');

    var selectedItem = Number($(this).data('item')),
        previousActiveItem = firstItem + Math.floor(numberOfRenderedItems/2),
        diff = selectedItem - previousActiveItem,
        selectedAngle = selectedItem * 18,
        degrees = 90 - selectedAngle,
        isClockWise = diff < 0;

    // Rotate all items
    $('.circle').removeData('deg');
    $('.circle').css({'transform' : 'rotate('+ degrees +'deg)'}).attr('data-deg',degrees);

    var items;
    if (isClockWise)
        items = getItemsFromBack(diff);
    else
        items = getItemsFromFront(diff);

    // Remove items
    items.toRemove.forEach( function (item) {
        $(".shapes[data-item="+item+"]").remove();        
    });

    // Add items
    var newItems = items.toAdd.reduce( function (html, item) {
        // Get item number between 1 and max
        var itemNumber = getNumberInRange(item),
            angle = 18 * item;
        return html +
            '<div class="shapes" data-item="'+item+'" data-deg="'+angle+'" style="--deg:'+angle+'deg;">'+
            ' <span class="set-pos">'+itemNumber+'</span>'+
            ' <span>'+angle+' deg </span>'+
            '</div>';
    }, '');
    if (isClockWise)
        $('#dynamic-html').append(newItems);
    else
        $('#dynamic-html').prepend(newItems)

})

function getItemsFromBack (diff) {
    var items = {
        toAdd: [],
        toRemove: []
    };

    firstItem += diff;
    for (var i = 0; i < Math.abs(diff); i++) {
        items.toAdd.push(firstItem + i);
        items.toRemove.push(firstItem + i + numberOfRenderedItems);
    }

    return items;
}

function getItemsFromFront (diff) {
    var items = {
        toAdd: [],
        toRemove: []
    };

    for (var i = 0; i < Math.abs(diff); i++) {
        items.toAdd.push(firstItem + i + numberOfRenderedItems);
        items.toRemove.push(firstItem + i);
    }
    firstItem += diff;

    return items;
}

function getNumberInRange (item) {
    do {
        item = (item + numberOfItems) % numberOfItems;
    }
    while (item < 0);
    return item + 1;
}

Updated JSFiddle link is below

https://jsfiddle.net/dpvjtvjd/2/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.