文字を円形や半円状に自動で並べる方法 CSSとjQueryで文字数による計算不要

前回の記事で、アナログ時計を作成し数字を円形に配置しました。
その時は、事前に数字をそれぞれspanタグで囲っていたのですが、その作業も自動で行うことにし、文字数に応じて円、または、今回は半円状にも自動配置してみました。

ここでは、テキストは文字の集まり(文章または単語)、文字はそれを構成する1文字ずつのこととして区別しています(もし混同している箇所がありましたらすみません)。

テキストを円形に

CODEPEN

「Run Pen」をクリックしてください。

See the Pen circle_text_auto by blue moon (@blue-moon) on CodePen.

HTMLファイル

<body>
  <div class="circle">
    <div class="inner"> <!-- 文字配置の基準となる親要素 -->
      <p class="text">ONCE IN A BLUE MOON </p>
    </div>
  </div>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  <script src="main.js"></script> <!-- ご自身のパスに -->
</body>

空白の特殊記号が既に変換されて表示されていると思いますが、もともと空白は「&nbsp;」と記述しています。

CSSファイル

body {
  font-size: 20px;
}

.circle {
  position: relative;
  margin: 20px auto;
  width: 200px;
  height: 200px;
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 50%;
}

.inner {  /* 文字を配置する起点となる親要素 */
  position: relative;
  width: 20px;  /* この親要素自体は1文字が収まる程度の小さいサイズ */
  height: 20px;
  border-radius: 50%;
  animation: rotateAnim 7s linear infinite;  /* 円形テキストを回転 */
}

.inner:hover {
  animation-play-state: paused;  /* アニメーション一時停止 */
}

@keyframes rotateAnim {
  0% { transform: rotate(0deg); }
  50%{ transform: rotate(180deg); }
  100% { transform: rotate(360deg); }  /* ここを0degにしてしまうと、180度の地点で逆方向の回転になるので注意 */
}

span {
  position: absolute;
  inset: 0;  /* topとleftとbottomとrightをまとめて指定 */
  font-weight: 700;
}

序に回転させてみました。こういうのよく見かけませんか?
回転の対象セレクターは、テキストの親要素である(配置の起点となる)円の中心(.inner)です。

JSファイル

const before = $('.text');
const text = before.text();  // spanタグで囲む前のテキストを取得
const textArray = text.split('');  // 取得したテキストを1文字ずつに分割し配列に

let after = '';
$.each(textArray,function(index,val){  // 配列のそれぞれ(1文字)をspanタグで囲み、繋げる
  after += "<span>" + val + "</span>";
});  

before.html(after);  // 元のテキストに生成したタグごと置き換え

const textcnt = textArray.length; // これは、const textcnt = $('span').length; と同じ
const circleR = ($('.circle').height()) / 2;  // 円の半径
const fontH = ($('.inner').height());
const dist = circleR - fontH;

$('span').each(function(index) {
  const num = index + 1;
  const radX = Math.sin(360 / textcnt * num * (Math.PI / 180));  // 文字の中心からのX軸方向の移動距離を計算
  const radY = Math.sin((90 - (360 / textcnt * num)) * (Math.PI / 180));  // 文字の中心からのY軸方向の移動距離(実際にはマイナス移動)を計算
  $(this).css('transform', 'translate(' + dist * radX + 'px, ' + -(dist * radY) + 'px) rotate(' + 360 / textcnt * num + 'deg)');  // 文字を水平維持ならば、rotateプロパティは不要
});

テキストを分割し1文字ずつタグで囲む方法

まず、円に配置したいテキストをtext()メソッドで取得します。

取得したテキストをsplit()メソッドで、分割し配列にします。
この引数に空白を指定した場合は、個々の文字の配列になります。
MDN 標準組込みオブジェクト String.prototype.split()

そして、作成された文字の配列に対して、each() メソッドで1文字ずつspanタグで囲み繋げます。

最後にそれらを、html()メソッドで元のタグへ挿入します。

これと円形配置の関数により、出力されるHTMLは下記のようになります。

<div class="inner">
  <p class="text">
    <span style="transform: translate(24.7214px, -76.0845px) rotate(18deg);">O</span>
    <span style="transform: translate(47.0228px, -64.7214px) rotate(36deg);">N</span>
    <span style="transform: translate(64.7214px, -47.0228px) rotate(54deg);">C</span>
    <span style="transform: translate(76.0845px, -24.7214px) rotate(72deg);">E</span>
    <span style="transform: translate(80px, 0px) rotate(90deg);"> </span>
    <span style="transform: translate(76.0845px, 24.7214px) rotate(108deg);">I</span>
    <span style="transform: translate(64.7214px, 47.0228px) rotate(126deg);">N</span>
    <span style="transform: translate(47.0228px, 64.7214px) rotate(144deg);"> </span>
    <span style="transform: translate(24.7214px, 76.0845px) rotate(162deg);">A</span>
    <span style="transform: translate(9.79717e-15px, 80px) rotate(180deg);"> </span>
    <span style="transform: translate(-24.7214px, 76.0845px) rotate(198deg);">B</span>
    <span style="transform: translate(-47.0228px, 64.7214px) rotate(216deg);">L</span>
    <span style="transform: translate(-64.7214px, 47.0228px) rotate(234deg);">U</span>
    <span style="transform: translate(-76.0845px, 24.7214px) rotate(252deg);">E</span>
    <span style="transform: translate(-80px, 9.79717e-15px) rotate(270deg);"> </span>
    <span style="transform: translate(-76.0845px, -24.7214px) rotate(288deg);">M</span>
    <span style="transform: translate(-64.7214px, -47.0228px) rotate(306deg);">O</span>
    <span style="transform: translate(-47.0228px, -64.7214px) rotate(324deg);">O</span>
    <span style="transform: translate(-24.7214px, -76.0845px) rotate(342deg);">N</span>
    <span style="transform: translate(-1.95943e-14px, -80px) rotate(360deg);"> </span>
  </p>
</div>

文字の配置方法

文字の配置方法やそれに関わる計算方法の詳細は、冒頭で紹介した記事をご参考にしてください。
下記のように、三角関数やcosθ等が出てきます。

trigonometric_function

テキストを半円状に

CODEPEN

「Run Pen」をクリックしてください。

See the Pen semicircle_text_auto by blue moon (@blue-moon) on CodePen.

HTMLファイル

元の記述は円形配置と同じですが、出力されるHTMLはstyleのプロパティの値が異なります。
念のため、後述しています。

CSSファイル

/* これより上省略 */
.circle {
  /* 途中省略 */
  width: 300px;   /* 文字数に応じて適度なサイズに調整 */
  height: 300px;
  /* 途中省略 */
}

.inner {
  /* 途中省略、 アニメーションなし*/
  transform: rotate(-90deg);  /* 円の左端からテキストが始まるように回転 */
}

  /* @keyframesなし */

span {
  /* 途中省略 */
}

補足説明

半円にのみ文字を配置していますが、ボックスのサイズは円の下半分も含まれています。
そのため、この半円文字のすぐ下に何か要素を配置したい場合は、positionプロパティやmarginプロパティ(topの値をマイナス)等で調整してください。

JSファイル

// これより上は前述と同じため省略

$('span').each(function(index) {
  const num = index + 1;
  const radX = Math.sin(180 / textcnt * num * (Math.PI / 180));  // 文字数で除算する角度を180度に変更
  const radY = Math.sin((90 - (180 / textcnt * num)) * (Math.PI / 180));  // 上記と同様
  $(this).css('transform', 'translate(' + dist * radX + 'px, ' + -(dist * radY) + 'px) rotate(' + 180 / textcnt * num + 'deg)');  // rotateプロパティで文字数で除算する角度を180度に変更
});

補足説明

円形に配置するパターンのコードを基本としています。
ここで変更していることは、360度の部分を180度に書き換えていることくらいです。
ただし、これだけではテキストの開始地点が円の時計方向での12時の地点になってしまい、テキストは右半分の円の形状になってしまいます。
そのため、CSSの方でマイナス90度回転させています(jQueryのcssメソッドで回転させてもよいですが)。

出力されるHTMLは下記のようになります。

<div class="inner">
  <p class="text">
    <span style="transform: translate(20.3365px, -128.399px) rotate(9deg);">O</span>
    <span style="transform: translate(40.1722px, -123.637px) rotate(18deg);">N</span>
    <span style="transform: translate(59.0188px, -115.831px) rotate(27deg);">C</span>
    <span style="transform: translate(76.4121px, -105.172px) rotate(36deg);">E</span>
    <span style="transform: translate(91.9239px, -91.9239px) rotate(45deg);"> </span>
    <span style="transform: translate(105.172px, -76.4121px) rotate(54deg);">I</span>
    <span style="transform: translate(115.831px, -59.0188px) rotate(63deg);">N</span>
    <span style="transform: translate(123.637px, -40.1722px) rotate(72deg);"> </span>
    <span style="transform: translate(128.399px, -20.3365px) rotate(81deg);">A</span>
    <span style="transform: translate(130px, 0px) rotate(90deg);"> </span>
    <span style="transform: translate(128.399px, 20.3365px) rotate(99deg);">B</span>
    <span style="transform: translate(123.637px, 40.1722px) rotate(108deg);">L</span>
    <span style="transform: translate(115.831px, 59.0188px) rotate(117deg);">U</span>
    <span style="transform: translate(105.172px, 76.4121px) rotate(126deg);">E</span>
    <span style="transform: translate(91.9239px, 91.9239px) rotate(135deg);"> </span>
    <span style="transform: translate(76.4121px, 105.172px) rotate(144deg);">M</span>
    <span style="transform: translate(59.0188px, 115.831px) rotate(153deg);">O</span>
    <span style="transform: translate(40.1722px, 123.637px) rotate(162deg);">O</span>
    <span style="transform: translate(20.3365px, 128.399px) rotate(171deg);">N</span>
    <span style="transform: translate(1.59204e-14px, 130px) rotate(180deg);"> </span>
  </p>
</div>
タイトルとURLをコピーしました