アイコンを円の中に配置する


96

複数の<img>要素を別の要素の周りの円に配置し、それらの要素をすべてクリック可能なリンクにする方法を教えてください。下の図のようにしたいのですが、その効果をどのようにして実現するかわかりません。

望ましい結果

これは可能ですか?

回答:


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円形に接するすべてがエッジ。

それを描くのに苦労している場合は、スライダーをドラッグして選択したエッジの数を持つさまざまなポリゴンの内接円と外接円を作成するこのインタラクティブなデモを試してみることができます。

六角形の内接円と外接円

これは、コンテナのサイズが、円の半径の2倍と画像のサイズの半分の2倍でなければならないことを示しています。

半径はまだわかりませんが、エッジの数(したがって、事前に計算されてカスタムプロパティとして設定された底角の半分の接線--tan)とポリゴンエッジがわかっていれば、半径を計算できます。おそらくポリゴンのエッジを画像のサイズよりも小さくしたいのですが、側面にどれだけ残すかは任意です。両側に画像サイズの半分があるとしましょう。そのため、ポリゴンエッジは画像サイズの2倍になります。これにより、次の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を使用するだけで非常に簡単に実行できます。画像とのリンクが必要な角度を明確にする必要があるだけです(画像の1つにカーソルを合わせると角度が表示されるように、最後にコードを追加しました)。

最初にラッパーが必要です。私はその直径を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}045など...

最初の回転変換はオブジェクトとその軸を回転させ、移動変換は回転したX軸に沿ってオブジェクトを移動し、2番目の回転変換はオブジェクトを元の位置に戻します。

この方法の利点は、柔軟性があることです。現在の構造を変更せずに、さまざまな角度で新しい画像を追加できます。

コードスニペット

    .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 Transformのサポートがないデバイス/ブラウザからアクセスすると、何が表示されますか?
-gkond

1
CSS変換をサポートしていないデスクトップブラウザーは、IE8以前のみです。それらの場合、これはIEマトリックスフィルター変換を使用してエミュレートできます。モバイルブラウザーについては、Opera MiniがCSS変換をサポートしていない唯一のブラウザーであり、とにかく小さな画面でスペースを浪費するようなものは使用しません。
Ana

1
デモを見たとき、下にスクロールしたので、そのような質問に答えるのはあなただと思いました。@Ana、よくやった。地獄はどこにブログしますか?
Ahmad Alfy 2013年

6
@Anaは素晴らしいです。興味がある場合は、CSSを使用してn個のアイテムの一般的な例を作成しました。jsfiddle.net/sajjansarkar/zgcgq8cg
Sarkar

3
@アナ、とてもかっこいい!あなたは私に動的バージョンを作成するように促しました-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
うまくいきました。できればもっと賛成します。私が抱えていた問題の1つは、360を他の何かに変更した場合(半円が必要だった)、物事がうまくいかなくなったことです。回転角度の宣言までたどり、これを変更しvar rotateAngle = zero_start + (offsetAngle * i || 0);ました。また、zero_startの変数を追加したので、0ではなく、ポイント270から開始したい場合は、同様のものを使用します。jsfiddle.net/q59s90oy/13。最後に、負のマージンを使用するようにリスト項目のcssを変更しました。真剣に、しかし、仕事を共有してくれてありがとう、たくさん助けてくれました。
通常のJoe

必要に応じて調整できて良かったです。素敵なバリエーション!
skwidbreth 2017年

1
これはかなり壮大なスパイラル効果です😅i.imgur.com
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()コンテナのを引いたものを使用して、その周りに他の要素を配置できます。正確なあなたに与える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を基準にして要素を配置することです。お役に立てれば。


5

あなたは確かに純粋な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;
}

ここでライブの例を見ることができます


4

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


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とは関係のない答えに投稿したことです!
ブランドなしのマンチェスター

私は知っています、誰も求めていない答えですが、とにかくここにあります:)
br3ntor

私はReactで使用できる非常に有用なソリューションを探してここに来ました
Abhishek Kasireddy

1

次のようにすることができます:フィドル

ポジショニングを気にしないでください、それは簡単な例です

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.