Hobby Graphics

CSS clip-pathと三角関数で三角形を描きたい

要約CSSのclip-path: polygon(); と三角関数で二等辺三角形を描く方法をまとめました。頂点の角度と斜辺の長さが基準です。その二つを決めるとCSSのカスタムプロパティを使って二等辺三角形が描かれるようにしました。

二等辺三角形の角度を調整したい

これまでborderを使って三角形を描いていましたが、直角二等辺三角形以外の二等辺三角形を正確に描こうとすると計算が面倒になり、つい適当に済ませてしまうことが多々ありました。そこでclip-pathを使った方法をまとめ、使いまわせるようにしたいと考えました。
描く三角形の形状を決める基準は斜辺と頂点の角度にしました。当初は幅を基準にしたのですが、斜辺を基準にした方が図形の幅と高さの変化が穏やかなので使いやすいと考えました。頂点の角度を基準にしたのは個人的な好みです。底辺の角を基準にすると計算はよりシンプルになるでしょう。
なお、ここでは「頂点の角=2つの等辺に挟まれた角」、「斜辺=二等辺三角形の等辺」、「底辺=頂点の角の対向の辺」としています。数学の定義とは異なります。すみません。

clip-path: polygon(); で三角形を描く

ここでは、三角関数は図形の幅と高さを計算するために使っているだけで、実際に形状を決めているのはclip-pathプロパティのpolygon()に入れる値です。

三角形を描くには任意の三箇所の座標を指定します。座標の原点はボックスの左上です。ここでは左右対称の二等辺三角形のテンプレートとなる座標を指定しました。書式はpolygon(x1 y1, x2 y2, x3 y3)です。上図左の、二等辺三角形ではpolygon(50% 0%, 100% 100%, 0% 100%)と指定しました。順序は左上から時計回りとしています。
指定した幅と高さに応じて生成された四角形のボックスに、上の座標が対応して変形されます。そのためボックスの幅と高さの比が2:1の場合は直角二等辺三角形、2:√3の場合には正三角形になります。また、clip-path: polygon();の値を変更すれば上の右図のような不等辺三角形も描けます。

clip-pathは背景のボックスを切り抜いて表示するプロパティです。画像や背景色はボックスで生成されますが、その表示領域を指定します。私は画像作成アプリのAdobe IllustratorやPhotoshopのクリッピングパスに近いものと解釈しています。
上の2つはclip-pathの概念図として描きました。右側はIllustratorのレイヤー構造になぞらえています。z-indexがこの通りになっているわけではありません。

三角関数で幅と高さを計算する

円と斜辺が直交する点の座標が、描きたい三角形の高さと、底辺(=幅)の半分の長さに対応します。頂点の角の角度が基準なので、斜辺と底辺が交わる角の角度は(90°-θ/2)です。なので、描きたい二等辺三角形の「幅=斜辺×cos(90°-θ/2)×2」、「高さ=斜辺×sin(90°-θ/2)」となります。

これを踏まえて頂点の角度を–theta、斜辺の長さを―triangle-hypotenuseとし、それぞれカスタムプロパティにしてCSSを書きました。

See the Pen triangle / 三角形(二等辺三角形) by 岩橋直人 (@lynbvavk-the-selector) on CodePen.

index.html

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>triangle</title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/yakuhanjp@3.3.1/dist/css/yakuhanjp.min.css" />
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
        <link
            href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap"
            rel="stylesheet"
        />
        <link rel="stylesheet" href="triangle-style.css" />
    </head>
    <body>
        <main>
            <h1>斜辺の長さと、<br class="sp-only" />頂点の角θを基準とした<br class="sp-only" />二等辺三角形</h1>
            <section>
                <h2>三角形<span>triangle hypotenuse=100px</span></h2>
                <div class="triangle-container">
                    <p><span class="triangle theta-120"></span>θ=120°</p>
                    <p><span class="triangle theta-90"></span>θ=90°</p>
                    <p><span class="triangle theta-60"></span>θ=60°</p>
                    <p><span class="triangle theta-45"></span>θ=45°</p>
                    <p><span class="triangle theta-30"></span>θ=30°</p>
                </div>
            </section>
            <section>
                <h2>逆三角形・左向き・右向き<span>polygon() の値変更</span></h2>
                <div class="triangle-container">
                    <p><span class="triangle theta-60"></span>polygon(50% 0%, 100% 100%, 0% 100%)</p>
                    <p><span class="triangle inverted theta-60"></span>polygon(0% 0%, 100% 0%, 50% 100%)</p>
                    <p><span class="triangle to-left theta-60"></span>polygon(0% 50%, 100% 0%, 100% 100%)</p>
                    <p><span class="triangle to-right theta-60"></span>polygon(0% 0%, 100% 50%, 0% 100%)</p>
                </div>
            </section>
        </main>
    </body>
</html>

style.css

@charset "UTF-8";
/* reset css */
body,
main,
h1,
h2,
div,
p {
    padding: 0;
    margin: 0;
}
body {
    font-family: YakuHanJP, "Roboto", "游ゴシック", "Yu Gothic", YuGothic, "メイリオ", Meiryo, "メイリオ", Meiryo,
        "Hiragino Kaku Gothic ProN", "Hiragino Kaku Gothic Pro", sans-serif;
    padding: 2em;
    display: grid;
    grid-template-columns: minmax(5vw, 1fr) minmax(33vw, 640px) minmax(5vw, 1fr);
    row-gap: 4rem;
    background-color: #f7f7f7;
}

h1,
h2,
p {
    font-weight: normal;
    text-align: center;
}
h1 {
    font-size: 1.5rem;
}
h2 {
    font-size: 1.25rem;
    margin-bottom: 0.25em;
    span {
        font-size: 1rem;
        margin-left: 0;
        margin-bottom: 1em;
        display: inline-block;
        @media screen and (min-width: 768px) {
            margin-left: 0.5em;
            margin-bottom: 0;
            display: inline-block;
        }
    }
}
main {
    grid-column: 2;
}
p {
    font-size: 1rem;
    text-align: center;
}
section {
    margin-top: 3em;
}
section:has(.inverted) h2 {
    margin-bottom: 1em;
}
span {
    display: block;
}
/* global styles */
:root {
    --theta: 60deg;
    --triangle-hypotenuse: 100px;
    --triangle-color: #fa0505;
    --triangle-border: 1px solid #000;
}
.sp-only {
    display: block;
    @media screen and (min-width: 768px) {
        display: none;
    }
}
/* .triangle styles */
.triangle-container {
    display: grid;
    grid-auto-flow: row;
    row-gap: 1em;
    align-items: end;
    &:has(.inverted) {
        align-items: start;
    }
    &:has(.to-left),
    &:has(.to-right) {
        align-items: center;
    }
    @media screen and (min-width: 768px) {
        grid-auto-flow: column;
        column-gap: 1em;
    }
}
.triangle {
    width: calc(var(--triangle-hypotenuse) * cos(90deg - var(--theta) / 2) * 2);
    height: calc(var(--triangle-hypotenuse) * sin(90deg - var(--theta) / 2));
    background-color: var(--triangle-color);
    clip-path: polygon(50% 0%, 100% 100%, 0% 100%);
    margin: 0 auto 0.25em;
}

.theta-120 {
    --theta: 120deg;
}
.theta-90 {
    --theta: 90deg;
}
.theta-45 {
    --theta: 45deg;
}
.theta-30 {
    --theta: 30deg;
}

.inverted {
    clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
}
.to-left,
.to-right {
    width: calc(var(--triangle-hypotenuse) * sin(90deg - var(--theta) / 2));
    height: calc(var(--triangle-hypotenuse) * cos(90deg - var(--theta) / 2) * 2);
    clip-path: polygon(0% 50%, 100% 0%, 100% 100%);
}
.to-right {
    clip-path: polygon(0% 0%, 100% 50%, 0% 100%);
}

カスタムプロパティに値を入力すると三角形が描けるようになりました

あとで使いまわせるように、斜辺の長さと頂点の角度、三角形の色を決めれば二等辺三角形を描けるようにカスタムプロパティ(CSS変数)で記述しました。
逆三角形や、左向き、右向きの三角形はcliping-path: polygon(); の値を変更して描いています。また、横向きの三角形では、幅と高さの式を入れ替えました。

blog 一覧へ