blog.tomoyat.dev

CSSでテキストをリング状にする

2024-05-27

テキストをリング状に並べたかったのでそのためのメモ

親要素の中央に要素を配置する

親要素の中においたdivを何も考えず表示すると以下のようになる

<div class="box">
	<div class="child-box">
	</div>
</div>

<div class="box">
	<div class="child-box">
	</div>
</div>

<style>
    .box {
        width: 300px;
        height: 300px;
        margin: auto;
        background-color: #B48EAD;
    }

    .child-box {
        width: 20px;
        height: 20px;
        background-color: #88C0D0;
    }</style>

親要素の左上に、子供要素の左上が重なるように配置される。そこで親要素にposition: relative;、子供の要素にposition: absolute;をつけることで、左上の位置を調整できるようになる。 そしてそこでtop: 50%;left: 50%;を指定することで子供要素の左上が親要素の中心から始まるようになる

<div class="box" style="position: relative">
	<div class="child-box" style="position: absolute; top: 50%; left: 50%;">
	</div>
</div>

この状態で子供の要素をtransformを使って、左に50%、上に50%移動させると子供の要素の中心が親の要素の中心と同じになる

<div class="box" style="position: relative">
	<div class="child-box" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
	</div>
</div>

テキストを回転させる

上記の容量でまずはテキストをそれぞれ親要素の中央に並べる

<div class="box" style="position: relative">
	<span class="center" style="transform: translate(-50%, -50%);">a</span>
	<span class="center" style="transform: translate(-50%, -50%);">b</span>
</div>
.center {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
}
a b

そこからまず文字を移動させる

<div class="box" style="position: relative">
	<span class="center" style="transform: translateY(calc(5 * -1ch)) translate(-50%, -50%) ">a</span>
	<span class="center">b</span>
	<span class="center">g</span>
</div>
a b g

次に回転させる

この時にtransformに指定するやつの順番が大事。まず真ん中にして、角度を変えて、その変わった角度から上方向に移動させる

<div class="box" style="position: relative">
	<span class="center" style="transform: translate(-50%, -50%) translateY(calc(5 * -1ch)) ">a</span>
	<span class="center" style="transform: translate(-50%, -50%) rotate(60deg) translateY(calc(5 * -1ch)) ">b</span>
	<span class="center">g</span>
</div>
a b g
これを繰り返すと円になる
<div class="box" style="position: relative">
	<span class="center" style="transform: translate(-50%, -50%) translateY(calc(5 * -1ch)) ">a</span>
	<span class="center" style="transform: translate(-50%, -50%) rotate(60deg) translateY(calc(5 * -1ch)) ">b</span>
	<span class="center" style="transform: translate(-50%, -50%) rotate(120deg) translateY(calc(5 * -1ch)) ">c</span>
	<span class="center" style="transform: translate(-50%, -50%) rotate(180deg) translateY(calc(5 * -1ch)) ">d</span>
	<span class="center" style="transform: translate(-50%, -50%) rotate(240deg) translateY(calc(5 * -1ch)) ">e</span>
	<span class="center" style="transform: translate(-50%, -50%) rotate(300deg) translateY(calc(5 * -1ch)) ">f</span>
	<span class="center">g</span>
</div>
a b c d e f g

半径を自動で設定する

translateYで文字の位置を中心から外側に移動してるけど、文字列の長さによって移動させる長さを変えないといけない。 この長さを自動で決めたい。6Pチーズみたいに円の中に三角形を並べて、以下のように計算すると文字数と文字幅で計算できそう

計算式

これをhtmlにすると以下みたいな感じで、ぴったりの円になる

<div class="box" style="position: relative">
	<span class="center" style="transform: translate(-50%, -50%) translateY(calc(1/ ( 2 * sin(30deg)) * -1ch))">a</span>
	<span class="center"
				style="transform: translate(-50%, -50%) rotate(60deg) translateY(calc(1/ (2 * sin(30deg)) * -1ch)) ">b</span>
	<span class="center"
				style="transform: translate(-50%, -50%) rotate(120deg) translateY(calc(1/ (2 * sin(30deg)) * -1ch)) ">c</span>
	<span class="center"
				style="transform: translate(-50%, -50%) rotate(180deg) translateY(calc(1/ (2 * sin(30deg)) * -1ch))">d</span>
	<span class="center"
				style="transform: translate(-50%, -50%) rotate(240deg) translateY(calc(1/ (2 * sin(30deg)) * -1ch))">e</span>
	<span class="center"
				style="transform: translate(-50%, -50%) rotate(300deg) translateY(calc(1/ (2 * sin(30deg)) * -1ch))">f</span>
</div>
a b c d e f

パラメータ化する

svelteで上記のやつをパラメータで渡せるようにするとこんな感じ

<script lang="ts">"use strict";
let text = 'Happy Hacking !  ';
let charSize = 1.5;
function radius() {
    return charSize / (2.0 * Math.sin(Math.PI / text.length));
}
</script>

<div>
	<span class="text-ring">
		{#each text as char, index}
			<span class="text-ring-ch"
						style="--index: {index}; --total: {text.length}; --radius: {radius()}">
				{char}
			</span>
		{/each}
	</span>
</div>

<style>
    .text-ring {
        position: relative;
        font-family: monospace;
    }

    .text-ring-ch {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%) rotate(calc(360deg / var(--total) * var(--index))) translateY(calc(var(--radius) * -1ch))
    }</style>
Happy Hacking!