将图标放置到圆圈中


96

如何将几个<img>元素放置在另一个元素周围的圆中,并使这些元素也都可单击链接?我希望它看起来像下面的图片,但是我不知道如何实现这种效果。

所需结果

这有可能吗?

Answers:


194

2020解决方案

这是我最近使用的更现代的解决方案。

我首先从图像数组生成HTML。不管HTML是使用PHP,JS,某些HTML预处理器生成的,还是……都无关紧要,因为后面的基本思想是相同的。

这是执行此操作的Pug代码:

//- 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)

生成的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>
  <!-- the rest of those placed on the circle -->
</div>

在CSS中,我们决定图片的大小,例如8em。这些--m项目位于圆上,如果它们位于多边形的--m边的中间,则所有这些边都与圆相切。

如果您很难想象这一点,则可以玩这个交互式演示,该演示为您拖动滑块选择的边数为各种多边形构造了内切圆和外切圆。

六边形的圆和圆

这告诉我们容器的大小必须是圆半径的两倍加上图像大小的一半。

我们尚不知道半径,但是如果我们知道边的数量(因此预先计算并设置为自定义属性的底角的一半的切线--tan)和多边形边,就可以计算出半径。我们可能希望多边形的边缘至少是图像的大小,但是我们在侧面上留下多少是任意的。假设我们每侧的图像尺寸只有一半,因此多边形的边缘是图像尺寸的两倍。这为我们提供了以下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% }

有关转换链如何工作的说明,请参见旧解决方案。

这样,在图像阵列中添加或删除图像会自动将新数量的图像排列在一个圆上,以使它们均匀分布,并调整容器的大小。您可以在此演示对此进行测试。


旧解决方案(出于历史原因保留)

是的,仅使用CSS很有可能而且非常简单。您只需要清楚要与图像链接的角度即可(我在最后添加了一段代码,用于在将鼠标悬停在其中时显示角度)。

您首先需要一个包装器。我将其直径设置为24emwidth: 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;
        /*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>

同样,您可以通过使用链接的背景图像而不是使用img标签来进一步简化HTML 。


编辑IE8和更早版本的回退示例(在IE8和IE7中测试)


1
很好,但是当人们从不支持CSS转换的设备/浏览器访问时会看到什么?
gkond 2012年

1
唯一不支持CSS转换的桌面浏览器是IE8和更低版本。对于这些,可以使用IE矩阵滤波器转换来模拟。对于移动浏览器,Opera Mini是唯一不支持CSS转换的浏览器,而且我真的不会使用那种会在小屏幕上浪费空间的东西。
2012年

1
当我看到演示时,我向下滚动,因为我知道是您回答这样的问题。@Ana做得好。你到底在哪里写博客?
艾哈迈德·阿尔菲

6
@Ana太棒了,如果有兴趣,可以使用CSS为n个项目制作一个通用示例。jsfiddle.net/sajjansarkar/zgcgq8cg
Sajjan Sarkar

3
@Ana很酷!您启发了我创建一个动态版本-jsfiddle.net/skwidbreth/q59s90oy
skwidbreth

18

这是没有绝对定位的简单解决方案:

.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/


10

基于@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>


1
很棒,如果可以的话,我会投票更多。我遇到的一个问题是,如果我将360更改为其他任何值(我想要一个半圆),事情就会变得一团糟。我将其追踪到旋转角度的声明并将其更改为此var rotateAngle = zero_start + (offsetAngle * i || 0);变量,我还为zero_start添加了一个变量,因此,如果要从点270开始而不是从0开始,或类似的东西。jsfiddle.net/q59s90oy/13。最后,我将列表项的css更改为使用负边距。认真地说,感谢您的分享,对我们提供了很多帮助。
常规乔

是的,很高兴您能够根据需要进行调整。不错的变化!
skwidbreth

1
哟,这是一个非常史诗般的螺旋效果😅i.imgur.com/ 1VrubKC.png
Ethan

@Ethan哈哈耶!我喜欢这样做!我认为这可以做成一件很酷的艺术品。
skwidbreth

5

无法使用CSS将可点击项神奇地放置在另一个元素周围的圆圈中。我这样做的方法是使用带有的容器position:relative;。然后,将所有元素与一起position:absolute;使用,并使用topleft以其位置为目标。

即使你没有放置 在您的标签中,最好使用jQuery / javascript。

第一步是使用将您的中心图像完美地放置在容器的中心position:relative;

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

之后,您可以使用offset()centerImage减去offset()容器的放置其他元素。给你确切topleft图像。

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 放置元素。希望这可以帮助。


5

您当然可以使用纯CSS或使用JavaScript来实现。我的建议:

  • 如果您已经知道图像数量永远不会改变,则只需计算您的样式并使用简单的CSS(优点:性能更好,非常可靠)

  • 如果数字可以在您的应用程序中动态变化,或者只是将来可能变化,请使用Js解决方案(优点:更具前瞻性)

我有类似的工作要做,所以我创建了一个脚本,并在Github上开源了该脚本,供可能需要的人使用。它只接受一些配置值,仅输出所需的CSS代码。

如果您想使用Js解决方案,这里有一个简单的指针,可能对您有用。使用此html作为起点,#box将容器和.dotimage / 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;
}

您可以在此处看到一个实时示例


4

使用@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>


2

这是我在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>
  );
}

好的答案和很棒的代码,唯一的问题是您已将其发布在与React无关的答案上!
烙印的曼彻斯特

我知道,没有人要求的答案,但是无论如何,这是:he
br3ntor

我来这里寻找的是可以在React中使用的解决方案,因此它仍然非常有用
Abhishek Kasireddy

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.