Решение 2020
Вот более современное решение, которое я использую сейчас.
Я начинаю с создания HTML, начиная с массива изображений. Генерируется ли HTML с использованием PHP, JS, какого-нибудь препроцессора HTML, что угодно ... это не так важно, поскольку основная идея остается той же.
Вот код Pug, который сделает это:
- let imgs = [
- {
- src: 'image_url.jpg',
- alt: 'image alt text'
- }
- ];
- let n_imgs = imgs.length;
- let has_mid = 1;
- let m = n_imgs - has_mid;
- let tan = Math.tan(Math.PI/m);
.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)
Сгенерированный HTML выглядит следующим образом (и да, вы также можете написать HTML вручную, но потом будет сложно вносить изменения):
<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>
</div>
Скажем, в CSS мы определяем размер изображений 8em
. Эти --m
элементы расположены по кругу , и это , если они находятся в середине ребер многоугольника --m
края, все из которых являются касательной к окружности.
Если вам сложно это представить, вы можете поиграть с этой интерактивной демонстрацией, которая строит вписанную и описанную окружность для различных многоугольников, количество ребер которых вы выбираете, перетаскивая ползунок.
Это говорит нам о том, что размер контейнера должен быть в два раза больше радиуса круга плюс вдвое меньше половины размера изображений.
Мы еще не знаем радиус, но мы можем вычислить его, если мы знаем количество ребер (и, следовательно, тангенс половины базового угла, предварительно вычисленный и установленный как настраиваемое свойство --tan
) и ребро многоугольника. Мы, вероятно, хотим, чтобы край многоугольника был как минимум размером с изображения, но то, сколько мы оставляем по бокам, произвольно. Допустим, у нас есть половина размера изображения с каждой стороны, поэтому край многоугольника вдвое больше размера изображения. Это дает нам следующий CSS:
.container {
--d: 6.5em;
--rel: 1;
--r: calc(.5*(1 + var(--rel))*var(--d)/var(--tan));
--s: calc(2*var(--r) + var(--d));
position: relative;
width: var(--s); height: var(--s);
background: silver
}
.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% }
См. Старое решение для объяснения того, как работает цепочка преобразований.
Таким образом, добавление или удаление изображения из массива изображений автоматически размещает новое количество изображений по кругу таким образом, что они разнесены на равные расстояния, а также регулирует размер контейнера. Вы можете проверить это в этой демонстрации .
СТАРОЕ решение (сохранилось по историческим причинам)
Да, это возможно и очень просто, используя только CSS. Вам просто нужно иметь четкое представление об углах, под которыми вы хотите видеть ссылки с изображениями (я добавил в конце фрагмент кода только для того, чтобы показывать углы при наведении курсора на одно из них).
Для начала вам понадобится обертка. Я установил его диаметр равным 24em
( width: 24em; height: 24em;
делает это), вы можете установить все, что захотите. Ты даешь этоposition: relative;
.
Затем вы размещаете свои ссылки с изображениями в центре этой оболочки как по горизонтали, так и по вертикали. Вы делаете это, устанавливая, position: absolute;
а затем top: 50%; left: 50%;
и margin: -2em;
(где 2em
половина ширины ссылки с изображением, которую я установил как4em
- опять же, вы можете изменить его на все, что захотите, но не забудьте изменить поле в тот случай).
Затем Вы выбираете углов , при которых вы хотите , чтобы ваши ссылки с изображениями и добавить класс deg{desired_angle}
(например , deg0
или deg45
или любой другой ). Затем для каждого такого класса вы применяете связанные преобразования CSS, например:
.deg{desired_angle} {
transform: rotate({desired_angle}) translate(12em) rotate(-{desired_angle});
}
где вы заменяете {desired_angle}
с 0
, 45
и так далее ...
Первое преобразование поворота поворачивает объект и его оси, преобразование преобразования перемещает объект вдоль повернутой оси X, а второе преобразование поворота возвращает объект в положение.
Преимущество этого метода в том, что он гибкий. Вы можете добавлять новые изображения под разными углами, не изменяя текущую структуру.
КОД СНОВА
.circle-container {
position: relative;
width: 24em;
height: 24em;
padding: 2.8em;
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); }
.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>
Кроме того, вы можете еще больше упростить HTML, используя фоновые изображения для ссылок вместо img
тегов.
РЕДАКТИРОВАТЬ : пример с резервным вариантом для IE8 и старше (проверено в IE8 и IE7)
Вот простое решение без абсолютного позиционирования:
.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/
источник
Основываясь на отличном ответе @Ana, я создал эту динамическую версию, которая позволяет вам добавлять и удалять элементы из DOM и поддерживать пропорциональное расстояние между элементами - посмотрите мою скрипку: 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);
я также добавил переменную для zero_start, поэтому, если вы хотите начать с точки 270, а не с 0, или что-то подобное. jsfiddle.net/q59s90oy/13 . Наконец, я изменил CSS для элементов списка, чтобы использовать отрицательные поля. А если серьезно, спасибо за то, что поделились работой, очень помогло.Невозможно волшебным образом разместить интерактивные элементы по кругу вокруг другого элемента с помощью CSS. Я бы сделал это с помощью контейнера с
position:relative;
. Затем разместите все элементы сposition:absolute;
помощьюtop
иleft
нацеливанием на это место.Даже если вы не разместили jquery в ваших тегах может быть лучше использовать для этого jQuery / javascript.
Первый шаг - идеально разместить ваше центральное изображение в центре контейнера, используя
position:relative;
.#centerImage { position:absolute; top:50%; left:50%; width:200px; height:200px; margin: -100px 0 0 -100px; }
После этого вы можете разместить вокруг него другие элементы, используя
offset()
центральное изображение минусoffset()
контейнер. Предоставляя вам точноеtop
иleft
изображение.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 });
Я разместил элементы относительно centerImage. Надеюсь это поможет.
источник
Конечно, вы можете сделать это с помощью чистого CSS или JavaScript. Мое предложение:
Если вы уже знаете, что количество изображений никогда не изменится, просто рассчитайте свои стили и используйте простой CSS (плюсы: лучшая производительность, очень надежная)
Если число может изменяться либо динамически в вашем приложении, либо просто может измениться в будущем, используйте решение Js (плюсы: более перспективные)
У меня была аналогичная работа, поэтому я создал сценарий и выложил его здесь, на Github, для всех, кому он может понадобиться. Он просто принимает некоторые значения конфигурации и просто выводит нужный вам код CSS.
Если вы хотите перейти на решение Js, вот простой указатель, который может быть вам полезен. Используя этот html в качестве отправной точки, являющейся
#box
контейнером, а.dot
изображение / div посередине, вы хотите, чтобы все остальные изображения были вокруг:Запуск html:
<div id="box"> <div class="dot"></div> <img src="my-img.jpg"> <!-- all the other images you need--> </div>
Запуск 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; }
Вы можете быстро создать функцию в следующих строках:
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; }
Вы можете увидеть живой пример здесь
источник
Используя решение, предложенное @Ana:
transform: rotate(${angle}deg) translate(${radius}px) rotate(-${angle}deg)
Я создал следующий jsFiddle, который динамически размещает круги, используя простой JavaScript (также доступна версия jQuery).
Принцип работы довольно простой:
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>
источник
Вот версия, которую я сделал в React из примеров здесь.
CodeSandbox Пример
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> ); }
источник
Вы можете сделать это так: скрипка
Не обращайте внимания на позиционирование, это быстрый пример
источник