前回の記事で、アナログ時計を作成し数字を円形に配置しました。
その時は、事前に数字をそれぞれ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>
空白の特殊記号が既に変換されて表示されていると思いますが、もともと空白は「 」と記述しています。
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θ等が出てきます。
テキストを半円状に
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>