1. ホーム
  2. html

[解決済み] アイコンを円形に配置する

2022-11-21 20:39:22

質問

複数の <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 の辺の中央にあり、その全てが円の接線上にある場合です。

イメージしにくい場合は、次のように遊べます。 インタラクティブデモ で、スライダーをドラッグして辺の数を選ぶと、さまざまな多角形の近円と外接円を作成することができます。

これは、コンテナのサイズは、円の半径の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% }

トランスフォームチェーンがどのように機能するかの説明については、古い解答を参照してください。

この方法では、画像の配列から画像を追加または削除すると、自動的に新しい数の画像が等間隔になるように円周上に配置され、コンテナのサイズも調整されます。これをテストするには このデモ .


旧解答 (歴史的な理由で保存されています)

はい、それは非常に可能であり、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軸に沿ってオブジェクトを並進させ、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を簡素化できます。


EDIT : IE8 およびそれ以前のフォールバックの例 (IE8とIE7でテスト済み)