Solução 2020
Aqui está uma solução mais moderna que uso atualmente.
Eu começo gerando o HTML a partir de uma série de imagens. Se o HTML é gerado usando PHP, JS, algum pré-processador HTML, seja o que for ... isso importa menos, pois a ideia básica por trás é a mesma.
Este é o código Pug que faria isso:
//- start with an array of images, described by url and alt text
- let imgs = [
- {
- src: 'image_url.jpg',
- alt: 'image alt text'
- } /* and so on, add more images here */
- ];
- let n_imgs = imgs.length;
- let has_mid = 1; /* 0 if there's no item in the middle, 1 otherwise */
- let m = n_imgs - has_mid; /* how many are ON the circle */
- let tan = Math.tan(Math.PI/m); /* tangent of half the base angle */
.container(style=`--m: ${m}; --tan: ${+tan.toFixed(2)}`)
- for(let i = 0; i < n_imgs; i++)
a(href='#' style=i - has_mid >= 0 ? `--i: ${i}` : null)
img(src=imgs[i].src alt=imgs[i].alt)
O HTML gerado tem a seguinte aparência (e sim, você também pode escrever o HTML manualmente, mas será uma dor fazer alterações depois):
<div class="container" style="--m: 8; --tan: 0.41">
<a href='#'>
<img src="image_mid.jpg" alt="alt text"/>
</a>
<a style="--i: 1">
<img src="first_img_on_circle.jpg" alt="alt text"/>
</a>
<!-- the rest of those placed on the circle -->
</div>
No CSS, decidimos um tamanho para as imagens, digamos 8em
. Os --m
itens são posicionados em um círculo e é se eles estão no meio das arestas de um polígono de --m
arestas, todos os quais são tangentes ao círculo.
Se você tiver dificuldade em imaginar isso, pode brincar com esta demonstração interativa que constrói o incircle e o circumcircle para vários polígonos cujo número de arestas você escolhe arrastando o controle deslizante.
Isso nos diz que o tamanho do contêiner deve ser duas vezes o raio do círculo mais duas vezes a metade do tamanho das imagens.
Ainda não sabemos o raio, mas podemos calculá-lo se soubermos o número de arestas (e, portanto, a tangente da metade do ângulo base, pré-calculado e definido como uma propriedade personalizada --tan
) e a aresta do polígono. Provavelmente queremos que a borda do polígono tenha pelo menos o tamanho das imagens, mas o quanto deixamos nas laterais é arbitrário. Digamos que temos metade do tamanho da imagem em cada lado, então a borda do polígono tem o dobro do tamanho da imagem. Isso nos dá o seguinte CSS:
.container {
--d: 6.5em; /* image size */
--rel: 1; /* how much extra space we want between images, 1 = one image size */
--r: calc(.5*(1 + var(--rel))*var(--d)/var(--tan)); /* circle radius */
--s: calc(2*var(--r) + var(--d)); /* container size */
position: relative;
width: var(--s); height: var(--s);
background: silver /* to show images perfectly fit in container */
}
.container a {
position: absolute;
top: 50%; left: 50%;
margin: calc(-.5*var(--d));
width: var(--d); height: var(--d);
--az: calc(var(--i)*1turn/var(--m));
transform:
rotate(var(--az))
translate(var(--r))
rotate(calc(-1*var(--az)))
}
img { max-width: 100% }
Veja a solução antiga para uma explicação de como funciona a cadeia de transformação.
Dessa forma, adicionar ou remover uma imagem da matriz de imagens organiza automaticamente o novo número de imagens em um círculo de forma que fiquem igualmente espaçadas e também ajusta o tamanho do contêiner. Você pode testar isso nesta demonstração .
Solução ANTIGA (preservada por razões históricas)
Sim, é muito possível e muito simples usando apenas CSS. Você só precisa ter em mente os ângulos em que deseja os links com as imagens (adicionei um trecho de código no final apenas para mostrar os ângulos sempre que você passa o mouse sobre um deles).
Você primeiro precisa de um invólucro. Eu defini seu diâmetro para ser 24em
( width: 24em; height: 24em;
faz isso), você pode definir o que quiser. Você dá position: relative;
.
Em seguida, você posiciona seus links com as imagens no centro desse wrapper, tanto horizontal quanto verticalmente. Você faz isso configurando position: absolute;
e então top: 50%; left: 50%;
e margin: -2em;
(onde 2em
é a metade da largura do link com a imagem, que eu defini para ser 4em
- novamente, você pode alterá-lo para o que quiser, mas não se esqueça de alterar a margem em Aquele caso).
Você então decide os ângulos em que deseja ter seus links com as imagens e adiciona uma classe deg{desired_angle}
(por exemplo, deg0
ou deg45
ou qualquer outra coisa). Em seguida, para cada classe, você aplica transformações CSS encadeadas, como esta:
.deg{desired_angle} {
transform: rotate({desired_angle}) translate(12em) rotate(-{desired_angle});
}
onde você substitui {desired_angle}
com 0
, 45
e assim por diante ...
A primeira transformação de rotação gira o objeto e seus eixos, a transformação de translação traduz o objeto ao longo do eixo X girado e a segunda transformação de rotação traz o objeto de volta à posição.
A vantagem desse método é que ele é flexível. Você pode adicionar novas imagens em ângulos diferentes sem alterar a estrutura atual.
FRAGMENTO DE CÓDIGO
.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>
Além disso, você pode simplificar ainda mais o HTML usando imagens de fundo para os links em vez de usar img
tags.
EDIT : exemplo com fallback para IE8 e mais antigo (testado no IE8 e IE7)
Aqui está a solução fácil sem posicionamento absoluto:
http://jsfiddle.net/mD6H6/
fonte
Com base na excelente resposta de @Ana, criei esta versão dinâmica que permite adicionar e remover elementos do DOM e manter o espaçamento proporcional entre os elementos - confira meu violino: https://jsfiddle.net/skwidbreth/q59s90oy/
fonte
var rotateAngle = zero_start + (offsetAngle * i || 0);
eu também adicionei uma variável para zero_start, então se você quiser começar no ponto 270 em vez de 0, ou algo semelhante. jsfiddle.net/q59s90oy/13 . Por último, mudei o css dos itens da lista para usar margens negativas. Sério, obrigado por compartilhar o trabalho, ajudou muito.Não há como colocar itens clicáveis magicamente em um círculo ao redor de outro elemento com CSS. A maneira como eu faria isso é usando um contêiner com
position:relative;
. Em seguida, coloque todos os elementos composition:absolute;
e usandotop
eleft
para direcionar seu lugar.Mesmo que você não tenha colocado jquery em suas tags, pode ser melhor usar jQuery / javascript para isso.
O primeiro passo é colocar a imagem central perfeitamente no centro do contêiner usando
position:relative;
.Depois disso, você pode colocar os outros elementos em torno dele usando um
offset()
de centerImage menos ooffset()
do contêiner. Dando a você o exatotop
eleft
da imagem.O que fiz aqui foi colocar os elementos em relação a centerImage. Espero que isto ajude.
fonte
Você certamente pode fazer isso com CSS puro ou usar JavaScript. Minha sugestão:
Se você já sabe que o número das imagens nunca mudará, calcule seus estilos e use o CSS simples (prós: melhores performances, muito confiável)
Se o número pode variar dinamicamente em seu aplicativo ou apenas pode variar no futuro, vá com uma solução Js (prós: mais à prova de futuro)
Eu tinha um trabalho semelhante a fazer, então criei um script e abri o código aqui no Github para qualquer pessoa que pudesse precisar. Ele apenas aceita alguns valores de configuração e simplesmente exibe o código CSS de que você precisa.
Se você quiser ir para a solução Js, aqui está um ponteiro simples que pode ser útil para você. Usando este html como ponto de partida, sendo
#box
o contêiner e.dot
a imagem / div no meio em que você deseja todas as outras imagens:HTML inicial:
Css inicial:
Você pode criar uma função rápida ao longo destas linhas:
Você pode ver um exemplo ao vivo aqui
fonte
Usando a solução proposta por @Ana:
Criei o seguinte jsFiddle que coloca círculos dinamicamente usando JavaScript simples (a versão jQuery também está disponível).
A forma como funciona é bastante simples:
fonte
Aqui está uma versão que fiz no React a partir dos exemplos aqui.
Exemplo de CodeSandbox
fonte
Você poderia fazer assim: violino
Não se preocupe com o posicionamento, é um exemplo rápido
fonte