archvist
archvist

Reputation: 722

When using transform rotate stop shifting content

I'm using transform: rotate(); to rotate content in & out of view, however, each body of text is at a different position when it comes into view. So when you click the next button on my demo if you look at the border the contents are in different positions.

When the next button is clicked the wheel rotates equally 90 degrees so I expect the content to be in the same position on each rotation. Could someone explain/resolve why this doesn't happen?

I created a wheel for my content and I styled the wheel to hide options no currently visible;

// Page load transform start, setters
var degree = 0;
var itemStart = $('.wheel').find('.item-one').addClass('item-active');
var itemNext = $('.wheel').find('.item-four').addClass('item-prev');

// On click
$('.next').click(function() {
  var wheel = $('.wheel');

  // Increment rotation
  degree += 90;
  wheel.css({
    WebkitTransform: 'rotate(' + degree + 'deg)'
  });

  // Update setter
  itemStart = $('.wheel').find('.item-active');
  itemNext = $('.wheel').find('.item-prev');

  // Add Animation
  $(itemStart).addClass('fadeOut');
  $(itemNext).addClass('fadeIn');

  //If were at the end
  var getStartPrev = $(itemStart).prev();
  var getNextPrev = $(itemNext).prev();

  if (getStartPrev.length == 0) {
    $(itemStart).removeClass('item-active');
    $(itemNext).prev().addClass('item-prev');
    $('.item-four').addClass('item-active').removeClass('item-prev');
  } else {
    $(itemStart).removeClass('item-active');
    $(itemNext).removeClass('item-prev').addClass('item-active');
    $(itemNext).prev().addClass('item-prev');
  }

  if (getNextPrev.length == 0) {
    $('.item-four').addClass('item-prev');
  }

  // Remove Animation
  setTimeout(function() {
    $('.wheel').find('.item').removeClass('fadeIn fadeOut');
  }, 400);
});
.wheel-wrp {
  position: relative;
  height: 700px;
  width: 700px;
}

.wheel {
  height: 700px;
  width: 700px;
  transition: 0.75s;
  border-radius: 50%;
  position: relative;
  background: #fff;
  left: -350px;
}

.item {
  width: 250px;
  position: absolute;
  opacity: 0;
}

.item-active {
  opacity: 1;
}

.item-one {
  bottom: 300px;
  left: 450px;
}

.item-two {
  bottom: 20px;
  left: 230px;
  -webkit-transform: rotate(90deg);
  -moz-transform: rotate(90deg);
  transform: rotate(90deg);
}

.item-three {
  bottom: 320px;
  left: 0px;
  -webkit-transform: rotate(180deg);
  -moz-transform: rotate(180deg);
  transform: rotate(180deg);
}

.item-four {
  top: 20px;
  left: 230px;
  -webkit-transform: rotate(270deg);
  -moz-transform: rotate(270deg);
  transform: rotate(270deg);
}

.current-item {
  bottom: 300px;
  left: 450px;
}

.next-item {
  top: 20px;
  left: 230px;
  -webkit-transform: rotate(0deg);
  -moz-transform: rotate(0deg);
  transform: rotate(-90deg);
  opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="page">
  <div class="wheel-wrp">
    <div class="wheel">
      <div class="item item-one">
        <h4>Project 1 - beautifully crafted digital brand</h4>
        <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum..</p>
      </div>
      <div class="item item-two">
        <h4>Project 2 - redefining technological boundaries</h4>
        <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum..</p>
      </div>
      <div class="item item-three">
        <h4>Project 3 - Beauty in Design</h4>
        <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum..</p>
      </div>
      <div class="item item-four">
        <h4>Project 4 - simply stunning </h4>
        <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum..</p>
      </div>
    </div>
  </div>
</div>
<div class="next">Next</div>

Example demo - https://codepen.io/SamXronn/pen/opqWbq

Upvotes: 6

Views: 707

Answers (3)

Jeremy Thille
Jeremy Thille

Reputation: 26370

Here's my go at it.

  • I have set the items's transform origin to top left (the same point used for positioning)

  • Using transform translateY and translateX, each item can then be placed against the wheel, regardless of their width and height

  • You don't need jQuery for animation. Just use it to toggle the active class. The same result is achieved much more smoothly and with much less code with CSS3 animation and an "active" class.

Codepen here

let currentItem = 0
const $wheel = $('.wheel'),
      $items = $wheel.find(".item")

$('.next').click(() => rotate(1));
$('.prev').click(() => rotate(-1));

const rotate = direction => {
    currentItem -= direction
    $wheel.css("transform",`rotate(${-currentItem*90}deg)`)
    $items.removeClass("active")
          .eq(currentItem%$items.length)
          .addClass("active")

}
body {
  background-color: #2c3e50;
}
button {
  background: #2980b9;
  padding: 15px 35px;
  color: #fff;
  float: left;
  position: relative;
  z-index: 9999;
  cursor: pointer;
}
.wheel {
  height: 700px;
  width: 700px;
  transition: transform 0.75s;
  border-radius: 50%;
  position: relative;
  background: #fff;
}
.item {
  width: 250px;
  position: absolute;
  opacity: 0.2;
  transition: opacity 0.75s;
  border: 1px solid #f00;
  transform-origin: top left;
}
.item:nth-child(1) {
  top: 50%;
  left: 100%;
  transform: translateY(-50%) translateX(-100%);
}
.item:nth-child(2) {
  top: 100%;
  left: 50%;
  transform: rotate(90deg) translateY(-50%) translateX(-100%);
}
.item:nth-child(3) {
  left: 0;
  top: 50%;
  transform: rotate(180deg) translateY(-50%) translateX(-100%);
}
.item:nth-child(4) {
  top: 0;
  left: 50%;
  transform: rotate(270deg) translateY(-50%) translateX(-100%);
}
.item.active {
  opacity: 1;
}
h4 {
  margin: 0px 0px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

<div id="page">
    <div class="wheel">
      <div class="item active">
        <h4>Project 1 - beautifully crafted digital brand</h4>
        <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum..</p>
      </div>
      <div class="item">
        <h4>Project 2 - redefining technological boundaries</h4>
        <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum..</p>
      </div> 
      <div class="item">
        <h4>Project 3 - Beauty in Design</h4>
        <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum..</p>
      </div> 
      <div class="item">
        <h4>Project 4 - simply stunning </h4>
        <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum..</p>
      </div>
    </div>
</div>
<button class="prev">Prev</button>
<button class="next">Next</button>

Upvotes: 3

Taha Paksu
Taha Paksu

Reputation: 15616

And this is my tryout :) I removed the item-one two.. classes and used css nth-child selectors, and prepared the initial wheel layout first with CSS using transform-origin to center of the circle, and then rotated the wheel itself. The returning to initial position was tricky because it animated şn reverse direction when rotating from 270deg's to 0deg. So I added an extra class to instantly move to initial position without animating.

$(".next").on("click", function () {
        var wheel = $(".wheel");
        if (wheel.hasClass("rotate-1")) {
            wheel.removeClass("rotate-1").addClass("rotate-2");
        }
        else if (wheel.hasClass("rotate-2")) {
            wheel.removeClass("rotate-2").addClass("rotate-3");
        }
        else if (wheel.hasClass("rotate-3")) {
            wheel.removeClass("rotate-3").addClass("rotate-4");
        }
        else if (wheel.hasClass("rotate-4")) {
            wheel.removeClass("rotate-4").addClass("rotate-5").delay(1000).queue(function (next) {
                $(this).addClass("rotate-1");
                $(this).removeClass("rotate-5");
                next();
            });
        }

    });
.wheel {
            border-radius: 50%;
            background: rebeccapurple;
            width: 700px;
            height: 700px;
            display: flex;
            align-items: center;
            justify-content: center;
            position: relative;
            transition: all 1s ease;
            margin-left : -350px;
        }

        .item {
            background:transparent;
            color: white;
            width: 250px;
            margin-right: 10px;
            margin-left: calc(50% - 135px);
            position: absolute;
            transform-origin: calc(-50% + 35px) 50%;
            transition: all 1s ease;
        }

        .wheel.rotate-1 {
            transform: rotateZ(0deg);
            transition: all 0s ease;
        }

        .wheel.rotate-2 {
            transform: rotateZ(90deg);
        }

        .wheel.rotate-3 {
            transform: rotateZ(180deg);
        }

        .wheel.rotate-4 {
            transform: rotateZ(270deg);
        }

        .wheel.rotate-5 {
            transform: rotateZ(360deg);
        }

        .item:nth-child(1) {
            transform: rotateZ(0deg);
        }

        .item:nth-child(2) {
            transform: rotateZ(-90deg);
        }

        .item:nth-child(3) {
            transform: rotateZ(-180deg);
        }

        .item:nth-child(4) {
            transform: rotateZ(-270deg);
        }

        .wheel.rotate-1 .item {
            opacity: 0;
        }
        .wheel.rotate-1 .item:nth-child(1) {
            opacity: 1;
        }
        .wheel.rotate-2 .item {
            opacity: 0;
        }
        .wheel.rotate-2 .item:nth-child(2) {
            opacity: 1;
        }
        .wheel.rotate-3 .item {
            opacity: 0;
        }
        .wheel.rotate-3 .item:nth-child(3) {
            opacity: 1;
        }
        .wheel.rotate-4 .item {
            opacity: 0;
        }
        .wheel.rotate-4 .item:nth-child(4) {
            opacity: 1;
        }
        .wheel.rotate-5 .item {
            opacity: 0;
        }
        .wheel.rotate-5 .item:nth-child(1) {
            opacity: 1;
        }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="page">
        <div class="wheel-wrp">
            <div class="wheel rotate-1">
                <div class="item">
                    <h4>Project 1 - beautifully crafted digital brand</h4>
                    <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
                        laborum..
                    </p>
                </div>
                <div class="item">
                    <h4>Project 2 - redefining technological boundaries</h4>
                    <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
                        laborum..
                    </p>
                </div>
                <div class="item">
                    <h4>Project 3 - Beauty in Design</h4>
                    <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
                        laborum..
                    </p>
                </div>
                <div class="item">
                    <h4>Project 4 - simply stunning </h4>
                    <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
                        laborum..
                    </p>
                </div>
            </div>
        </div>
    </div>
    <div class="next">Next</div>

Upvotes: 1

Facundo Corradini
Facundo Corradini

Reputation: 3913

Quite a challenging one. The issue is a combination of vertical centering for your divs have different heights, and your absolute positioning with px. And the transform rotates, of course

I've came out with a solution using the typical top:50; transform:-50% vertical centering hack, adapted to the different div rotations.

That takes care of the vertical alignment by totally predictible and calculated values. Horizontal alignment on the other hand is proving to be trickier, maybe I'm missing something here, but I've come up with a solution for those by means of magical numbers (% that work, although I'm not sure why)... Anyway, here's the snippet

// Page load transform start, setters
var degree = 0;
var itemStart = $('.wheel').find('.item-one').addClass('item-active');
var itemNext = $('.wheel').find('.item-four').addClass('item-prev');

// On click
$('.next').click( function() {
  var wheel = $('.wheel');
  
  // Increment rotation
  degree += 90;
  wheel.css({ WebkitTransform: 'rotate(' + degree + 'deg)'});
    
  // Update setter
  itemStart = $('.wheel').find('.item-active');
  itemNext = $('.wheel').find('.item-prev');

  // Add Animation
  $(itemStart).addClass('fadeOut');
  $(itemNext).addClass('fadeIn');
  
  //If were at the end
  var getStartPrev = $(itemStart).prev();
  var getNextPrev = $(itemNext).prev();
  
  if( getStartPrev.length == 0 ) {
    $(itemStart).removeClass('item-active');
    $(itemNext).prev().addClass('item-prev');
    $('.item-four').addClass('item-active').removeClass('item-prev');
  } else {
    $(itemStart).removeClass('item-active');
    $(itemNext).removeClass('item-prev').addClass('item-active');
    $(itemNext).prev().addClass('item-prev');   
  }
  
  if( getNextPrev.length == 0 ) {
    $('.item-four').addClass('item-prev');
  }
  
   // Remove Animation
   setTimeout(function(){ 
    $('.wheel').find('.item').removeClass('fadeIn fadeOut');
   }, 400);
});
body{
 background-color:  #2c3e50; 
}

.next{
  background: #2980b9;
  padding: 15px 35px;
  color: #fff;
  float: left;
  position: relative;
  z-index: 9999;
  cursor: pointer;
}

.wheel-wrp{
  position: relative;
  height: 700px;
  width: 700px;
}

.wheel{
  height: 700px;
  width: 700px;
  transition: 0.75s;
  border-radius: 50%;
  position: relative;
  background: #fff;
  left: -350px;
}
.item{
  width: 250px;
  position: absolute;
  opacity: 0;
  border: 1px solid red;
}
.item-active{
  opacity: 1;
}
.item-one {
    top: 50%;
    transform: translateY(-50%);
    left: 75%;
}

.item-two {
    right: 50%;
    top: 83%;
    transform: translateX(50%) rotate(90deg);
}

.item-three {
    top: 50%;
    right: 75%;
    transform: rotate(180deg) translateY(50%);
}

.item-four {
    left: 50%;
    bottom: 84%;
    transform: translateX(-50%) rotate(270deg);
}

.current-item{
  bottom: 300px;
  left: 450px;
}

.next-item{
  top: 20px;
  left: 230px;
  -webkit-transform:rotate(0deg);
  -moz-transform:rotate(0deg);
  transform:rotate(-90deg);
  opacity: 0;
}

h4{
  margin:0px 0px;
}


.fadeOut{
  animation: menuFadeOut 350ms;
  webkit-animation-fill-mode: forwards;
  animation-fill-mode: forwards;  
}

.fadeIn{
  animation: menuFadeIn 1500ms;
  webkit-animation-fill-mode: forwards;
  animation-fill-mode: forwards;
}

@keyframes menuFadeOut {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

@keyframes menuFadeIn {
  0% {
    opacity: 0;
  } 
  100% {
    opacity: 1;
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

<div id="page">
  <div class="wheel-wrp">
    <div class="wheel">
      <div class="item item-one">
        <h4>Project 1 - beautifully crafted digital brand</h4>
        <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum..</p>
      </div>
      <div class="item item-two">
        <h4>Project 2 - redefining technological boundaries</h4>
        <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum..</p>
      </div> 
      <div class="item item-three">
        <h4>Project 3 - Beauty in Design</h4>
        <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum..</p>
      </div> 
      <div class="item item-four">
        <h4>Project 4 - simply stunning </h4>
        <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum..</p>
      </div>
    </div>
  </div>
</div>
<div class="next">Next</div>

The only changes are in the particular items definition, so if you want to check on your pen for better visibility, this is the part you should replace:

.item-one {
    top: 50%;
    transform: translateY(-50%);
    left: 75%;
}

.item-two {
    right: 50%;
    top: 83%;
    transform: translateX(50%) rotate(90deg);
}

.item-three {
    top: 50%;
    right: 75%;
    transform: rotate(180deg) translateY(50%);
}

.item-four {
    left: 50%;
    bottom: 84%;
    transform: translateX(-50%) rotate(270deg);
}

Hope that helps. And if by any means someone feels like explaining + fixing those magic numbers, that'd be much welcomed ;)

Upvotes: 1

Related Questions