CSS | 3Dな円柱の作り方

2021-06-12CSS 3D デザイン,CSS

CSS | 3Dな円柱の作り方

CSSのpreserve-3dを使って、3Dな円柱を作成する方法を紹介しています。

See the Pen CSS | Cylinder by yochans (@yochans) on CodePen.

円柱の作り方(3D)

この記事のサンプルではCSSの「transform-style」に「preserve-3d」を指定した立体的な円柱の作成となります。

コンテナの作成

最初にコンテナとなる本体を作成します。

<div class="cylinder">
</div>
.cylinder {
	margin: 30px;
	width: 200px; /* コンテナサイズ */
	height: 200px; /* コンテナサイズ */
	position: relative;
	transform-style: preserve-3d;
	transform: rotateX(-25deg) rotateY(0); /* 全体の角度 */
}

説明用に本体に背景色を付けています。

コンテナとする本体のサイズは、ここでは正方形にしておきます。

追加する子要素を絶対値で指定するため、position: relativeを指定。

平面だと把握しにくいですので、作業用に角度を調節します。
rotateX(-25deg)にすると、全体が25度下に向きますので斜め上から見下ろした状態になります。

底面の作成

円柱の底になる部分を作成します。
上の部分(天面?)も作成するのですが、説明するのに視覚的に邪魔になるので後にします。

3Dな円柱の作り方
<div class="cylinder">
	<div class="bottom"></div>
</div>
.cylinder .bottom {
	width: 100px; /* 円の直径 */
	height: 100px; /* 円の直径 */
	position: absolute;
	top: 150px; /* 目的の場所 - (直径 / 2) */
	left: 50px; /* (コンテナのサイズ / 2) - (直径 / 2) */
	background: #2e9963;
	border-radius: 50%;
	transform: rotateX(-90deg); /* 要素の角度 */
}

慣れるまではtopの配置に違和感がありますが、これはrotateで角度を変更すると、基本は要素の中心で回転しますので、図にある赤い部分のスペースができますので、その分も考慮する必要があります。
Y軸で横に回転した際も同様です。

3Dな円柱の作り方2

側面の作成

側面を作成しますが、このサンプルでは側面の壁を作る訳ではなく、中心から連続した正方形を360度配置しています。

最初に一枚配置したイメージとサンプルコードです。

数が多いですので、nth-childでの連番を指定しやすいように入れ子にしています。

<div class="cylinder">
	<div class="center"></div>
	<div class="bottom"></div>
	<div class="side-group">
		<div></div>
	</div>
</div>
.cylinder .side-group div{
	width: 50px; /* 半径 */
	height: 200px; /* 円柱の高さ */
	position: absolute;
	top: 0;
	left: 50px; /* 中央に移動 */
	background: #7fffbf;
	transform-origin: right; /* 回転の基準点を右端にする */
}

.side-group div:nth-child(1) {
	transform: rotateY(0); /* 中心からの角度 */
}

赤いラインが「transform-origin: right」で右端に変更した回転の基準点です。
「rotateY(0)」で0度の状態は、コンテナに対して並行に配置されます。

あとは、「side-group」の中に一枚一枚になるdivタグを追加していくだけです。

追加した要素の角度をずらしながら最終的には360度一周させますが、とりあえず広めの角度で3枚追加した時の状態を色変えてみるとわかりやすいです。

少し粗目で10px置きに360度作った感じは以下のようになります。
上面を付けますのでこの間隔でも隙間があるようには見えなくなります。

上面と底面の裏側を作成

上面と底面が側面と同色であれば問題ありませんが、サンプルではそれぞれを2重にして裏側は側面と同じ色にして、側面の隙間から覗いた時の色のずれをなくしています。

アニメーションを加えたサンプルでの全コードになります。

<div class="cylinder">
	<div class="top"></div>
	<div class="top-back"></div>
	<div class="bottom"></div>
	<div class="bottom-back"></div>
		
	<div class="side-group">
		<div></div><div></div><div></div><div></div><div></div>
		<div></div><div></div><div></div><div></div><div></div>
		<div></div><div></div><div></div><div></div><div></div>
		<div></div><div></div><div></div><div></div><div></div>
		<div></div><div></div><div></div><div></div><div></div>
		<div></div><div></div><div></div><div></div><div></div>
		<div></div><div></div><div></div><div></div><div></div>
		<div></div>
	</div>
</div>
.cylinder {
	margin: 30px;
	width: 200px;
	height: 200px;
	position: relative;
	transform-style: preserve-3d;
	transform: rotateX(-25deg) rotateY(0);
	animation: rotate-anim 8s linear infinite;
	/* 	background: rgba(0, 0, 0, 0.4); */
}

@keyframes rotate-anim {
	0% {
		transform: rotateX(0) rotateY(0) rotateZ(-120deg);
	}
	100% {
		transform: rotateX(360deg) rotateY(360deg) rotateZ(240deg);
	}
}

.cylinder .top {
	width: 100px;
	height: 100px;
	position: absolute;
	top: -50px;
	left: 50px;
	background: #3dcc85;
	border-radius: 50%;
	transform: rotateX(90deg);
}

.cylinder .top-back {
	width: 100px;
	height: 100px;
	position: absolute;
	top: -49px;
	left: 50px;
	background: #7fffbf;
	border-radius: 50%;
	transform: rotateX(90deg);
}

.cylinder .bottom {
	width: 100px;
	height: 100px;
	position: absolute;
	top: 150px;
	left: 50px;
	background: #2e9963;
	border-radius: 50%;
	transform: rotateX(90deg);
}

.cylinder .bottom-back {
	width: 100px;
	height: 100px;
	position: absolute;
	top: 149px;
	left: 50px;
	background: #7fffbf;
	border-radius: 50%;
	transform: rotateX(90deg);
}

.cylinder .side-group div {
	width: 50px;
	height: 200px;
	position: absolute;
	top: 0;
	left: 50px;
	background: #7fffbf;
	transform-origin: right;
}

.side-group div:nth-child(1) {
	transform: rotateY(0deg);
}

.side-group div:nth-child(2) {
	transform: rotateY(10deg);
}

.side-group div:nth-child(3) {
	transform: rotateY(20deg);
}

.side-group div:nth-child(4) {
	transform: rotateY(30deg);
}

.side-group div:nth-child(5) {
	transform: rotateY(40deg);
}

.side-group div:nth-child(6) {
	transform: rotateY(50deg);
}

.side-group div:nth-child(7) {
	transform: rotateY(60deg);
}

.side-group div:nth-child(8) {
	transform: rotateY(70deg);
}

.side-group div:nth-child(9) {
	transform: rotateY(80deg);
}

.side-group div:nth-child(10) {
	transform: rotateY(90deg);
}

.side-group div:nth-child(11) {
	transform: rotateY(100deg);
}

.side-group div:nth-child(12) {
	transform: rotateY(110deg);
}

.side-group div:nth-child(13) {
	transform: rotateY(120deg);
}

.side-group div:nth-child(14) {
	transform: rotateY(130deg);
}

.side-group div:nth-child(15) {
	transform: rotateY(140deg);
}

.side-group div:nth-child(16) {
	transform: rotateY(150deg);
}

.side-group div:nth-child(17) {
	transform: rotateY(160deg);
}

.side-group div:nth-child(18) {
	transform: rotateY(170deg);
}

.side-group div:nth-child(19) {
	transform: rotateY(180deg);
}

.side-group div:nth-child(20) {
	transform: rotateY(190deg);
}

.side-group div:nth-child(21) {
	transform: rotateY(200deg);
}

.side-group div:nth-child(22) {
	transform: rotateY(210deg);
}

.side-group div:nth-child(23) {
	transform: rotateY(220deg);
}

.side-group div:nth-child(24) {
	transform: rotateY(230deg);
}

.side-group div:nth-child(25) {
	transform: rotateY(240deg);
}

.side-group div:nth-child(26) {
	transform: rotateY(250deg);
}

.side-group div:nth-child(27) {
	transform: rotateY(260deg);
}

.side-group div:nth-child(28) {
	transform: rotateY(270deg);
}

.side-group div:nth-child(29) {
	transform: rotateY(280deg);
}

.side-group div:nth-child(30) {
	transform: rotateY(290deg);
}

.side-group div:nth-child(31) {
	transform: rotateY(300deg);
}

.side-group div:nth-child(32) {
	transform: rotateY(310deg);
}

.side-group div:nth-child(33) {
	transform: rotateY(320deg);
}

.side-group div:nth-child(34) {
	transform: rotateY(330deg);
}

.side-group div:nth-child(35) {
	transform: rotateY(340deg);
}

.side-group div:nth-child(36) {
	transform: rotateY(350deg);
}