Crop icons around the expandable circle

I have a disk that can be resized and some icons are located around that disk. The icons should always be on the edge of the disk, and the spacing between each icon should always remain the same.

Is it possible to do this in pure css without having to calculate each icon position whenever the disk grows or shrinks?

Expected Result:

.container:nth-child(1)
{
    position: absolute;
    top: 20px;
    left: 30px;
    height: 280px;
    width: 280px;
}

.container:nth-child(2)
{
    position: absolute;
    top: 20px;
    right: 30px;
    height: 200px;
    width: 200px;
}

.container > *
{
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

.circle
{
    height: 90%;
    width: 90%;
    border-radius: 50%;
    border: 1px solid blue;
    z-index: 20;
}

.icons
{
    height: 100%;
    width: 100%;
    border: 1px dashed gray;
    box-sizing: border-box;
    z-index: 10;
}

.icons > *
{
    position: absolute;
    top: 50%;
    left: 50%;
    height: 20px;
    width: 20px;
    margin-top: -10px;
    margin-left: -10px;
    border: 1px solid red;
}


.container:nth-child(1) span:nth-child(1)
{
    transform: rotate(230deg) translate(150px) rotate(-230deg);
}

.container:nth-child(1) span:nth-child(2)
{
    transform: rotate(217deg) translate(150px) rotate(-217deg);
}

.container:nth-child(1) span:nth-child(3)
{
    transform: rotate(204deg) translate(150px) rotate(-204deg);
}

.container:nth-child(1) span:nth-child(4)
{
    transform: rotate(191deg) translate(150px) rotate(-191deg);
}


.container:nth-child(2) span:nth-child(1)
{
    transform: rotate(230deg) translate(110px) rotate(-230deg);
}

.container:nth-child(2) span:nth-child(2)
{
    transform: rotate(212deg) translate(110px) rotate(-212deg);
}

.container:nth-child(2) span:nth-child(3)
{
    transform: rotate(194deg) translate(110px) rotate(-194deg);
}

.container:nth-child(2) span:nth-child(4)
{
    transform: rotate(176deg) translate(110px) rotate(-176deg);
}
      

<div class="container">
    <div class="circle"></div>
    <div class="icons">
        <span>A</span>
        <span>B</span>
        <span>C</span>
        <span>D</span>
    </div>
</div>

<div class="container">
    <div class="circle"></div>
    <div class="icons">
        <span>A</span>
        <span>B</span>
        <span>C</span>
        <span>D</span>
    </div>
</div>
      

Run codeHide result


+3


source to share


2 answers


There is a way to do this programmatically, but only with CSS3. What you need to do is have an element inside it that takes the full width and can rotate, and then counterrotate the inner containers of those rotating elements.

So, you will need to define circles with :nth-child

. To get the right effect and not apply the style twice, we'll have to count from our last element just to attract elements in a specific position. This has to do with what :nth-child(2n)

and :nth-child(4n)

what can create overlapping styles. Skip down to the snippet if it seems too complicated - it's actually quite simple.

So your basic HTML:

<wrapper> <!-- This is your disc -->
    <rotation> <!-- A rotation element. -->
       <counter> <!-- A counter-rotation element. This contains your content. -->

      

The first item must have an included position. The second element needs to intersect with all other secondary elements, so it needs the position of the absolute and the third element, well, up to you. He's just there to meet the spin again.

Here's the implementation:



/*The following animation shows that the outer frame is resizeable.*/
@-webkit-keyframes sizeBounce { 0% {width: 50vw; height: 50vw; }  50% {width: 20vw; height: 20vw; } 100% {width: 50vw; height: 50vw; }}
@keyframes sizeBounce { 0% {width: 50vw; height: 50vw; }  50% {width: 20vw; height: 20vw; } 100% {width: 50vw; height: 50vw; }}

div {
  position: relative;
  /* Any height you want here */
  width: 20vw;
  height: 20vw;
  border-radius: 50%;
  border: 1px solid #000;

  -webkit-animation: sizeBounce 2s infinite;
  animation: sizeBounce 2s infinite;
  
  /* You can do anything you want with this circle! */
  position: absolute;
  top: 50%;
  left: 50%;
  -webkit-transform: translate(-50%,-50%);
  transform: translate(-50%,-50%);
}

div > span {
  position: absolute;
  display: block;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  transform: rotateZ(0deg);
  transform-origin: 50% 50%;
}

div > span > em {
  display: block;
  position: absolute;
  left: 0;
  top: 0;
  width: 20%;
  height: 20%;
  border-radius: 50%;
  border: 1px solid #dd0300;
  text-align: center;
}

div > span:nth-child(6n-6) {
  -webkit-transform: rotateZ(0deg);
  transform: rotateZ(0deg);
}
div > span:nth-child(6n-6) > em {
  -webkit-transform: rotateZ(0deg);
  transform: rotateZ(0deg);
}

div > span:nth-child(6n-4) {
  -webkit-transform: rotateZ(60deg);
  transform: rotateZ(60deg);
}
div > span:nth-child(6n-4) > em {
  -webkit-transform: rotateZ(-60deg);
  transform: rotateZ(-60deg);
}

div > span:nth-child(6n-3) {
  -webkit-transform: rotateZ(120deg);
  transform: rotateZ(120deg);
}
div > span:nth-child(6n-3) > em {
  -webkit-transform: rotateZ(-120deg);
  transform: rotateZ(-120deg);
}

div > span:nth-child(6n-2) {
  -webkit-transform: rotateZ(180deg);
  transform: rotateZ(180deg);
}
div > span:nth-child(6n-2) > em {
  -webkit-transform: rotateZ(-180deg);
  transform: rotateZ(-180deg);
}

div > span:nth-child(6n-1) {
  -webkit-transform: rotateZ(240deg);
  transform: rotateZ(240deg);
}
div > span:nth-child(6n-1) > em {
  -webkit-transform: rotateZ(-240deg);
  transform: rotateZ(-240deg);
}
  
div > span:nth-child(6n) {
  -webkit-transform: rotateZ(300deg);
  transform: rotateZ(300deg);
}
div > span:nth-child(6n) > em {
  -webkit-transform: rotateZ(-300deg);
  transform: rotateZ(-300deg);
}
      

<div>
  <span><em>1</em></span>
  <span><em>2</em></span>
  <span><em>3</em></span>
  <span><em>4</em></span>
  <span><em>5</em></span>
  <span><em>6</em></span>
</div>
      

Run codeHide result


There is a way to automate this too SASS, and it looks something like this:

@mixin rotatePiecemeally($pieces:6,$wrapper:span,$container:em){
    /* First calculate the angle between each piece */
    $degrees : 360 / $pieces;
    /* We want to count back from the amount of pieces to 0
     * The counting is because simple counting 2n, 3n, 4n, ... 
     * will result in 4n inheriting from 2n - to keep things clean,
     * we want to avoid that.  */
    @for $i from 0 through ($pieces - 1){
        & #{$wrapper}:nth-child(#{$pieces}n-#{$i}){
            transform: rotateZ(#{$i * $degrees}deg);
        }
        & #{$wrapper}:nth-child(#{$pieces}n-#{$i}) > #{$container}{
            transform: rotateZ(#{-$i * $degrees}deg);
        }
    }
}

      

You can invoke it by following these steps:

#mycircle {
    @include rotatePiecemeally(6);
}

      

And you can specify which elements you want to use as children. Don't forget that they all need absolute positioning for this to work. Don't forget to add any to prefixes

you (I added these to the snippet because I am using Safari)

+2


source


Without a preprocessor, I believe you need a script for this. Here I have used jQuery to calculate the spacing on hover.

Note

this will also support dynamic cardinality .circle



+ function() {
  var to;
  $(".wrap").on('mouseenter', function() {
    var circles = $(this).find(".circle");
    var degree = (2 * Math.PI) / circles.length; //calc delta angle
    var transforms = [];

    // Calculate the position for each circle
    circles.each(function(index) {
      var x = 100 * Math.cos(-0.5 * Math.PI + degree * (-1 * index - 0.5));
      var y = 100 * Math.sin(-0.5 * Math.PI + degree * (-1 * index - 0.5));

      transforms.push('translate(' + x + 'px,' + y + 'px)');
    });

    // Function to moves all the circles
    // We'll pop a circle each time and than call this function recursively
    function moveCircles() {
      var transform = transforms.shift();
      circles.css('transform', transform);

      circles.splice(0, 1);
      if (circles.length) to = setTimeout(moveCircles, 400);
    }

    moveCircles();
  });

  $(".wrap").on('mouseleave', function() {
    var circles = $(this).children().css('transform', '');
    clearTimeout(to);
  });
}();
      

html {
  height: 100%;
  background: radial-gradient(ellipse at center, rgba(79, 79, 79, 1) 0%, rgba(34, 34, 34, 1) 100%);
}
.wrap {
  height: 300px;
  width: 300px;
  position: relative;
  transform-origin: center center;
  transition: all 0.8s;
}
.circle {
  transition: all 0.8s;
  position: absolute;
  height: 5px;
  width: 5px;
  text-align: center;
  line-height: 15px;
  top: calc(50% - 2px);
  left: calc(50% - 2px);
  border-radius: 50%;
  overflow: hidden;
}
.parent {
  transition: all 0.8s;
  position: absolute;
  background: gray;
  height: 50px;
  width: 50px;
  text-align: center;
  line-height: 25px;
  top: calc(50% - 25px);
  left: calc(50% - 25px);
  border-radius: 50%;
  z-index: 8;
  box-shadow: inset 2px 2px 10px black, inset 0 0 15px black, 0 0 15px black;
}
.parent:before,
.parent:after {
  content: "";
  position: absolute;
  transition: all 0.8s;
  height: 5px;
  width: 25px;
  top: 22px;
  left: 12px;
  background: black;
  opacity: 1;
}
.parent:before {
  top: 15px;
  box-shadow: 0 14px 0 black;
}
.parent:hover:before,
.parent:hover:after {
  transform: translate(0, 20px);
  color: gray;
  opacity: 0;
  box-shadow: 0 14px 0 none;
}
.wrap:hover .parent,
.wrap:hover .parent:before,
.wrap:hover .parent:after {
  background: darkgray;
}
.wrap:hover .parent:before {
  box-shadow: none;
}
.wrap:hover .circle {
  height: 50px;
  width: 50px;
  line-height: 25px;
  top: calc(50% - 25px);
  left: calc(50% - 25px);
  box-shadow: inset 2px 2px 10px black, inset 0 0 15px black, 0 0 15px black;
}
.circle img {
  position: absolute;
  height: 100%;
  width: 100%;
  left: 0;
  top: 0;
}
.circle:before {
  border-radius: 50%;
  transition: all 0.8s;
  content: "";
  position: absolute;
  height: 100%;
  width: 100%;
  top: 0;
  left: 0;
  z-index: 8;
}
.circle:after,
button:after {
  transition: all 0.8s;
  border-radius: 50%;
  content: "";
  position: absolute;
  height: 200%;
  width: 200%;
  top: 50%;
  left: 200%;
  z-index: 8;
  background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0) 100%);
  background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(50%, rgba(255, 255, 255, 0.4)), color-stop(100%, rgba(255, 255, 255, 0)));
  background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0) 100%);
  background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0) 100%);
  background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0) 100%);
  background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0) 100%);
  filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#00ffffff', endColorstr='#00ffffff', GradientType=1);
}
.circle:hover:after,
button:hover:after {
  left: -200%;
  top: -50%;
}
.circle:hover:before {
  box-shadow: inset 2px 2px 10px black, inset 0 0 15px black, 0 0 15px black;
}
      

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrap">
  <div class="parent"></div>
  <div class="circle">
    <img src="http://lorempixel.com/300/300" />
  </div>
  <div class="circle">
    <img src="http://placekitten.com/g/200/300" />
  </div>
  <div class="circle">
    <img src="http://cdn.flaticon.com/png/256/56729.png" />
  </div>
  <div class="circle">
    <img src="http://cdn.flaticon.com/png/256/54976.png" />
  </div>
  <div class="circle">Just Text</div>
  <div class="circle">
    <img src="http://cdn.flaticon.com/png/256/56582.png" />
  </div>
</div>
      

Run codeHide result


+2


source







All Articles