[html] 원 안에 아이콘 배치

여러 <img>요소를 다른 주위의 원 안에 배치하고 해당 요소도 모두 클릭 가능한 링크가되도록하려면 어떻게해야합니까? 아래 그림처럼 보이기를 원하지만 그 효과를 얻는 방법을 모르겠습니다.

원하는 결과

이것이 가능할까요?



답변

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% }

변환 체인의 작동 방식에 대한 설명은 이전 솔루션을 참조하십시오.

이런 식으로 이미지 배열에서 이미지를 추가하거나 제거하면 원에 새로운 수의 이미지가 자동으로 정렬되어 동일한 간격으로 배치되고 컨테이너의 크기도 조정됩니다. 당신은이를 테스트 할 수 있습니다 이 데모 .


OLD 솔루션 (역사적인 이유로 보존 됨)

예, 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;
        /*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에서 테스트 됨)


답변

다음은 절대 위치 지정이없는 쉬운 솔루션입니다.

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


답변

CSS를 사용하여 다른 요소 주변의 원 안에 클릭 가능한 항목을 마법처럼 배치하는 방법은 없습니다. 이 작업을 수행하는 방법은 position:relative;. 그런 다음 모든 요소를 ​​배치 position:absolute;하고 사용 top하고 left대상을 지정합니다.

배치하지 않아도 태그에 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에 상대적인 요소를 배치 하는 것입니다. 도움이 되었기를 바랍니다.


답변

확실히 순수한 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)

일반 JavaScript를 사용하여 동적으로 원을 배치 하는 다음 jsFiddle 을 만들었습니다 (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>
  );
}