Jak ustawić kilka <img>elementów w kółko wokół innego i sprawić, by wszystkie te elementy były również klikalnymi linkami? Chcę, żeby wyglądało jak na poniższym obrazku, ale nie mam pojęcia, jak to osiągnąć.

Czy to w ogóle możliwe?
Jak ustawić kilka <img>elementów w kółko wokół innego i sprawić, by wszystkie te elementy były również klikalnymi linkami? Chcę, żeby wyglądało jak na poniższym obrazku, ale nie mam pojęcia, jak to osiągnąć.

Czy to w ogóle możliwe?
Odpowiedzi:
Oto bardziej nowoczesne rozwiązanie, z którego korzystam obecnie.
Zaczynam od wygenerowania kodu HTML, zaczynając od tablicy obrazów. Niezależnie od tego, czy HTML jest generowany za pomocą PHP, JS, jakiegoś preprocesora HTML, cokolwiek ... ma to mniejsze znaczenie, ponieważ podstawowa idea jest taka sama.
Oto kod mopsa, który mógłby to zrobić:
//- 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)
Wygenerowany kod HTML wygląda następująco (i tak, możesz też napisać kod HTML ręcznie, ale później wprowadzanie zmian będzie trudne):
<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>
Powiedzmy, że w CSS decydujemy o rozmiarze obrazów 8em. Te --melementy są umieszczone na kole i to, czy są one w środku krawędzi wielokąta z --mkrawędziami, z których wszystkie są styczne do okręgu.
Jeśli trudno ci to sobie wyobrazić, możesz pobawić się tym interaktywnym demo, które tworzy okrąg i okrąg dla różnych wielokątów, których liczbę krawędzi wybierasz, przeciągając suwak.
To mówi nam, że rozmiar pojemnika musi być dwa razy większy od promienia okręgu plus dwa razy mniejszy od rozmiaru obrazów.
Nie znamy jeszcze promienia, ale możemy go obliczyć, jeśli znamy liczbę krawędzi (a zatem styczną połowy kąta bazowego, wstępnie obliczoną i ustawioną jako właściwość niestandardowa --tan) i krawędź wielokąta. Prawdopodobnie chcemy, aby krawędź wielokąta była co najmniej wielkości obrazów, ale to, ile pozostawiamy po bokach, jest arbitralne. Powiedzmy, że mamy połowę rozmiaru obrazu z każdej strony, więc krawędź wielokąta jest dwukrotnie większa od rozmiaru obrazu. To daje nam następujący 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% }
Zobacz stare rozwiązanie, aby wyjaśnić, jak działa łańcuch transformacji.
W ten sposób dodanie lub usunięcie obrazu z tablicy obrazów automatycznie rozmieszcza nową liczbę obrazów w okręgu, tak aby były równo rozmieszczone, a także dostosowuje rozmiar kontenera. Możesz to sprawdzić w tym demo .
Tak, jest to bardzo możliwe i bardzo proste przy użyciu samego CSS. Musisz tylko mieć na uwadze kąty, pod którymi chcesz tworzyć linki z obrazami (dodałem na końcu fragment kodu, aby pokazać kąty za każdym razem, gdy najedziesz na jeden z nich).
Najpierw potrzebujesz opakowania. Ustawiłem jego średnicę na 24em( width: 24em; height: 24em;robi to), możesz ustawić ją na dowolną. Ty to dajesz position: relative;.
Następnie umieść linki z obrazami w środku tego opakowania, zarówno w poziomie, jak i w pionie. Robisz to, ustawiając, position: absolute;a następnie top: 50%; left: 50%;i margin: -2em;(gdzie 2emjest połowa szerokości linku z obrazem, na który ustawiłem 4em- znowu możesz to zmienić na cokolwiek chcesz, ale nie zapomnij zmienić marginesu w ta walizka).
Następnie decyduje o prostym, w którym chcesz mieć swoje powiązania z obrazami i dodać klasę deg{desired_angle}(na przykład deg0lub deg45lub cokolwiek). Następnie dla każdej takiej klasy zastosujesz łańcuchowe transformacje CSS, na przykład:
.deg{desired_angle} {
transform: rotate({desired_angle}) translate(12em) rotate(-{desired_angle});
}
gdzie można wymienić {desired_angle}się 0, 45i tak dalej ...
Pierwsza transformacja z obrotem obraca obiekt i jego osie, transformacja z obrotem powoduje przesunięcie obiektu wzdłuż obróconej osi X, a druga transformacja z obrotem przywraca obiekt na miejsce.
Zaletą tej metody jest to, że jest elastyczna. Możesz dodawać nowe obrazy pod różnymi kątami bez zmiany aktualnej struktury.
KOD 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>
Możesz także dodatkowo uprościć kod HTML, używając obrazów tła dla łączy zamiast imgtagów.
EDYCJA : przykład z rezerwą dla IE8 i starszych (testowane w IE8 i IE7)
Oto proste rozwiązanie bez pozycjonowania bezwzględnego:
.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>
Opierając się na doskonałej odpowiedzi @ Any, stworzyłem tę dynamiczną wersję, która pozwala dodawać i usuwać elementy z DOM oraz zachować proporcjonalne odstępy między elementami - sprawdź moje skrzypce: 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>
var rotateAngle = zero_start + (offsetAngle * i || 0);Dodałem również zmienną dla zero_start, więc jeśli chcesz zacząć od punktu 270, a nie 0, lub coś podobnego. jsfiddle.net/q59s90oy/13 . Na koniec zmieniłem css dla elementów listy, aby używały ujemnych marginesów. Poważnie jednak, dzięki za podzielenie się pracą, bardzo pomogło.
Nie ma możliwości magicznego umieszczenia klikalnych elementów w okręgu wokół innego elementu za pomocą CSS. Sposób, w jaki bym to zrobił, to użycie kontenera z position:relative;. A następnie umieść wszystkie elementy za position:absolute;pomocą topi używając i leftaby wycelować w to miejsce.
Nawet jeśli nie umieściłeś jquery w swoich tagach najlepiej do tego celu użyć jQuery / javascript.
Pierwszym krokiem jest idealne umieszczenie centralnego obrazu na środku pojemnika za pomocą position:relative;.
#centerImage {
position:absolute;
top:50%;
left:50%;
width:200px;
height:200px;
margin: -100px 0 0 -100px;
}
Następnie możesz umieścić inne elementy wokół niego, używając offset()of the centerImage minus offset()kontener. Co daje dokładny topi leftobrazu.
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
});
To, co tutaj zrobiłem, to umieszczenie elementów względem centerImage. Mam nadzieję że to pomoże.
Z pewnością możesz to zrobić za pomocą czystego CSS lub użyć JavaScript. Moja sugestia:
Jeśli już wiesz, że liczba zdjęć nigdy się nie zmieni, po prostu oblicz swoje style i korzystaj z prostego CSS (plusy: lepsza wydajność, bardzo niezawodna)
Jeśli liczba ta może się zmieniać dynamicznie w aplikacji lub może się zmieniać w przyszłości, wybierz rozwiązanie Js (zalety: bardziej przyszłościowe)
Miałem podobną pracę do wykonania, więc stworzyłem skrypt i otworzyłem go tutaj na Github dla każdego, kto mógł go potrzebować. Po prostu akceptuje niektóre wartości konfiguracyjne i po prostu wyświetla kod CSS, którego potrzebujesz.
Jeśli chcesz skorzystać z rozwiązania Js, oto prosty wskaźnik, który może Ci się przydać. Używając tego html jako punktu wyjścia, będąc #boxkontenerem i .dotobrazem / div w środku, wokół którego chcesz umieścić wszystkie inne obrazy:
Uruchamianie html:
<div id="box">
<div class="dot"></div>
<img src="my-img.jpg">
<!-- all the other images you need-->
</div>
Początek CSS:
#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;
}
Możesz utworzyć szybką funkcję w następujący sposób:
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;
}
Można zobaczyć na żywo przykład tutaj
Korzystając z rozwiązania zaproponowanego przez @Ana:
transform: rotate(${angle}deg) translate(${radius}px) rotate(-${angle}deg)
Stworzyłem następujący jsFiddle, który dynamicznie umieszcza okręgi przy użyciu zwykłego JavaScript (dostępna jest również wersja jQuery).
Sposób działania jest dość prosty:
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>
Oto wersja, którą stworzyłem w React na podstawie przykładów tutaj.
import React, { useRef, useEffect } from "react";
import "./styles.css";
export default function App() {
const graph = useRef(null);
useEffect(() => {
const ciclegraph = graph.current;
const circleElements = ciclegraph.childNodes;
let angle = 360 - 90;
let dangle = 360 / circleElements.length;
for (let i = 0; i < circleElements.length; i++) {
let circle = circleElements[i];
angle += dangle;
circle.style.transform = `rotate(${angle}deg) translate(${ciclegraph.clientWidth /
2}px) rotate(-${angle}deg)`;
}
}, []);
return (
<div className="App">
<div className="ciclegraph" ref={graph}>
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
</div>
</div>
);
}