web-dev-qa-db-fra.com

Positionner les icônes dans un cercle

Comment puis-je positionner plusieurs <img> éléments dans un cercle autour d'un autre et ces éléments sont-ils tous des liens cliquables? Je veux que cela ressemble à l'image ci-dessous, mais je ne sais pas comment atteindre cet effet.

Desired Result

Est-ce seulement possible?

89
FatalKeystroke

Oui, c'est très possible et très simple en utilisant simplement CSS. Vous devez simplement avoir à l’esprit les angles auxquels vous souhaitez associer les images (j’ai ajouté un morceau de code à la fin, uniquement pour afficher les angles lorsque vous survolez l’une d’elles).

démo

Vous avez d'abord besoin d'un emballage. Je règle son diamètre à 24em (width: 24em; height: 24em; _ fait ça), vous pouvez le régler à votre guise. Vous lui donnez position: relative;.

Vous positionnez ensuite vos liens avec les images au centre de cet emballage, horizontalement et verticalement. Vous faites cela en mettant position: absolute; et alors top: 50%; left: 50%; et margin: -2em; (où 2em est la moitié de la largeur du lien avec l’image, que j’ai définie comme étant 4em _ encore une fois, vous pouvez changer ce que vous voulez, mais n'oubliez pas de changer la marge dans ce cas).

Vous décidez ensuite des angles sous lesquels vous souhaitez avoir vos liens avec les images et vous ajoutez une classe deg{desired_angle} (par exemple deg0 ou deg45 ou peu importe). Ensuite, pour chacune de ces classes, vous appliquez des transformations CSS chaînées, comme ceci:

.deg{desired_angle} {
   transform: rotate({desired_angle}) translate(12em) rotate(-{desired_angle});
}

où vous remplacez {desired_angle} avec 0, 45, etc...

La première transformation de rotation fait pivoter l'objet et ses axes, la transformation de translation traduit l'objet le long de l'axe X pivoté et la seconde transformation de rotation ramène l'objet à la position - démo pour illustrer comment cela fonctionne .

L'avantage de cette méthode est sa flexibilité. Vous pouvez ajouter de nouvelles images sous différents angles sans modifier la structure actuelle.

CODE SNIPPET

    .circle-container {
        position: relative;
        width: 24em;
        height: 24em;
        padding: 2.8em;
        /*2.8em = 2em*1.4 (2em = half the width of a link with img, 1.4 = sqrt(2))*/
        border: dashed 1px;
        border-radius: 50%;
        margin: 1.75em auto 0;
    }
    .circle-container a {
        display: block;
        position: absolute;
        top: 50%; left: 50%;
        width: 4em; height: 4em;
        margin: -2em;
    }
    .circle-container img { display: block; width: 100%; }
    .deg0 { transform: translate(12em); } /* 12em = half the width of the wrapper */
    .deg45 { transform: rotate(45deg) translate(12em) rotate(-45deg); }
    .deg135 { transform: rotate(135deg) translate(12em) rotate(-135deg); }
    .deg180 { transform: translate(-12em); }
    .deg225 { transform: rotate(225deg) translate(12em) rotate(-225deg); }
    .deg315 { transform: rotate(315deg) translate(12em) rotate(-315deg); }
    <div class='circle-container'>
        <a href='#' class='center'><img src='image.jpg'></a>
        <a href='#' class='deg0'><img src='image.jpg'></a>
        <a href='#' class='deg45'><img src='image.jpg'></a>
        <a href='#' class='deg135'><img src='image.jpg'></a>
        <a href='#' class='deg180'><img src='image.jpg'></a>
        <a href='#' class='deg225'><img src='image.jpg'></a>
        <a href='#' class='deg315'><img src='image.jpg'></a>
    </div>

En outre, vous pouvez simplifier davantage le code HTML en utilisant des images d’arrière-plan pour les liens au lieu d’utiliser les balises img.


[~ # ~] éditer [~ # ~] : exemple avec repli pour IE8 et les versions antérieures (testé dans IE8 et IE7)

180
Ana

Voici la solution facile sans positionnement absolu:

.container .row {
  margin: 20px;
  text-align: center;
}

.container .row img {
  margin: 0 20px;
}
<div class="container">
  <div class="row">
    <img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
    <img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
  </div>
  <div class="row">
    <img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
    <img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
    <img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
  </div>
  <div class="row">
    <img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
    <img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
  </div>
</div>

http://jsfiddle.net/mD6H6/

17
gkond

En me basant sur l'excellente réponse de @ Ana, j'ai créé cette version dynamique qui vous permet d'ajouter et de supprimer des éléments du DOM et de conserver un espacement proportionnel entre les éléments - consultez mon violon: https://jsfiddle.net/skwidbreth/ q59s90oy /

var list = $("#list");

var updateLayout = function(listItems) {
  for (var i = 0; i < listItems.length; i++) {
    var offsetAngle = 360 / listItems.length;
    var rotateAngle = offsetAngle * i;
    $(listItems[i]).css("transform", "rotate(" + rotateAngle + "deg) translate(0, -200px) rotate(-" + rotateAngle + "deg)")
  };
};

$(document).on("click", "#add-item", function() {
  var listItem = $("<li class='list-item'>Things go here<button class='remove-item'>Remove</button></li>");
  list.append(listItem);
  var listItems = $(".list-item");
  updateLayout(listItems);

});

$(document).on("click", ".remove-item", function() {
  $(this).parent().remove();
  var listItems = $(".list-item");
  updateLayout(listItems);
});
#list {
  background-color: blue;
  height: 400px;
  width: 400px;
  border-radius: 50%;
  position: relative;
}

.list-item {
  list-style: none;
  background-color: red;
  height: 50px;
  width: 50px;
  position: absolute;
  top: 50%;
  left: 50%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

<ul id="list"></ul>
<button id="add-item">Add item</button>
7
skwidbreth

Il n'y a aucun moyen de placer par magie des éléments cliquables dans un cercle autour d'un autre élément avec CSS. Pour ce faire, utilisez un conteneur avec position:relative;. Ensuite, placez tous les éléments avec position:absolute; Et utilisez top et left pour cibler sa place.

Même si vous n'avez pas placé jquery dans vos balises, il peut être préférable d'utiliser jQuery/javascript pour cela.

La première étape consiste à placer parfaitement votre image centrale au centre du conteneur en utilisant position:relative;.

#centerImage {
  position:absolute;
  top:50%;
  left:50%;
  width:200px;
  height:200px;
  margin: -100px 0 0 -100px;
}

Après cela, vous pouvez placer les autres éléments autour en utilisant une offset() de la image centrale, moins la offset() du conteneur. Vous donnant l'exacte top et left de l'image.

var left = $('#centerImage').offset().left - $('#centerImage').parent().offset().left;
var top = $('#centerImage').offset().top - $('#centerImage').parent().offset().top;

$('#surroundingElement1').css({
  'left': left - 50,
  'top': top - 50 
});

$('#surroundingElement2').css({
  'left': left - 50,
  'top': top 
});

$('#surroundingElement3').css({
  'left': left - 50,
  'top': top + 50 
});

Ce que j'ai fait ici est de placer les éléments relatifs par rapport à centerImage. J'espère que cela t'aides.

5
Sem

Vous pouvez certainement le faire avec du css pur ou utiliser JavaScript. Ma suggestion:

  • Si vous savez déjà que le nombre d'images ne changera jamais, calculez simplement vos styles et utilisez des CSS simples (avantages: meilleures performances, très fiable)

  • Si le nombre peut varier de manière dynamique dans votre application ou tout simplement dans le futur, utilisez une solution Js (avantages: plus évolutif)

J'avais un travail similaire à faire. J'ai donc créé un script et ouvert le code source ici sur Github pour tous ceux qui pourraient en avoir besoin. Il accepte seulement certaines valeurs de configuration et génère simplement le code CSS dont vous avez besoin.

Si vous souhaitez opter pour la solution Js, voici un simple pointeur qui peut vous être utile. En utilisant ce code HTML comme point de départ, #box le conteneur et .dot l'image/div au milieu vous voulez toutes vos autres images:

Commencer html:

<div id="box">
  <div class="dot"></div>
  <img src="my-img.jpg">
  <!-- all the other images you need-->
</div>

Css de départ:

 #box{
  width: 400px;
  height: 400px;
  position: relative;
  border-radius: 100%;
  border: 1px solid teal;
}

.dot{
    position: absolute;
    border-radius: 100%;
    width: 40px;
    height: 40px;
    left: 50%;
    top: 50%;
    margin-left: -20px;
    margin-top: -20px;
    background: rebeccapurple;
}
img{
  width: 40px;
  height: 40px;
  position: absolute;
}

Vous pouvez créer une fonction rapide selon ces lignes:

var circle = document.getElementById('box'),
    imgs = document.getElementsByTagName('img'),
    total = imgs.length,
    coords = {},
    diam, radius1, radius2, imgW;

// get circle diameter
// getBoundingClientRect outputs the actual px AFTER transform
//      using getComputedStyle does the job as we want
diam = parseInt( window.getComputedStyle(circle).getPropertyValue('width') ),
radius = diam/2,
imgW = imgs[0].getBoundingClientRect().width,
// get the dimensions of the inner circle we want the images to align to
radius2 = radius - imgW

var i,
    alpha = Math.PI / 2,
    len = imgs.length,
    corner = 2 * Math.PI / total;

// loop over the images and assign the correct css props
for ( i = 0 ; i < total; i++ ){

  imgs[i].style.left = parseInt( ( radius - imgW / 2 ) + ( radius2 * Math.cos( alpha ) ) ) + 'px'
  imgs[i].style.top =  parseInt( ( radius - imgW / 2 ) - ( radius2 * Math.sin( alpha ) ) ) + 'px'

  alpha = alpha - corner;
}

Vous pouvez voir un exemple en direct ici

4
Nobita

Vous pouvez le faire comme ceci: violon

Ne faites pas attention au positionnement, c’est un exemple rapide

1
Mark

En utilisant la solution proposée par @Ana:

transform: rotate(${angle}deg) translate(${radius}px) rotate(-${angle}deg)

J'ai créé le texte suivant jsFiddle qui place les cercles de manière dynamique à l'aide de JavaScript simple (la version jQuery est également disponible).

La façon dont cela fonctionne est assez simple:

document.querySelectorAll( '.ciclegraph' ).forEach( ( ciclegraph )=>{
  let circles = ciclegraph.querySelectorAll( '.circle' )
  let angle = 360-90, dangle = 360 / circles.length
  for( let i = 0; i < circles.length; ++i ){
    let circle = circles[i]
    angle += dangle
    circle.style.transform = `rotate(${angle}deg) translate(${ciclegraph.clientWidth / 2}px) rotate(-${angle}deg)`
  }
})
.ciclegraph {
  position: relative;
  width: 500px;
  height: 500px;
  margin: calc(100px / 2 + 0px);
}

.ciclegraph:before {
  content: "";
  position: absolute;
  top: 0; left: 0;
  border: 2px solid teal;
  width: calc( 100% - 2px * 2);
  height: calc( 100% - 2px * 2 );
  border-radius: 50%;
}

.ciclegraph .circle {
  position: absolute;
  top: 50%; left: 50%;
  width: 100px;
  height: 100px;
  margin: calc( -100px / 2 );
  background: teal;
  border-radius: 50%;
}
<div class="ciclegraph">
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
</div>
0
Itay Grudev