✏️

CSSの三角関数を使ってJSなしできれいな斜線を引きたい!

2024/12/19に公開

この記事はCSS Advent Calendar 2024の18日目の記事です。


LAWGUEでは、Wordのようにテーブルセル内で斜線を表示する機能があります。

現在は transform: rotate でDOM要素を回転させて斜線を表示していますが、他にも新しい機能や方法があるのではないかと思い、斜線の描画方法についていくつか検証してみました。

既存の斜線の例

グラデーションの斜線

おそらく斜線を引きたい場合これが一番使われているのではないかと思います。

background-image: linear-gradient(to top right, transparent calc(50% - 1px), black, transparent calc(50% + 1px));

メリット

  • 簡単に斜線が引ける

デメリット

rotate で傾ける斜線

現状のLAWGUEで用いている方法。

JSで縦幅と横幅を取得し、傾きを計算。要素のサイズが変更される場合、ResizeObserver の対応も必要です。

メリット

  • DOM を回転しているだけなので柔軟にカスタマイズができる

デメリット

  • 斜線を引きたいだけなのにJSを使わないといけない感がある

CSS の三角関数 atan2 とコンテナークエリーの長さ単位を使って計算できるか?

CSS の 三角関数 atan2 を使うと、要素の横幅と高さが分かれば角度を求めることができます。つまり、どうにかして要素の高さと横幅をCSS側で取得することができれば…

最近出た cqwcqh で要素の横幅と高さは取れるはず!

クエリーコンテナーとして設定したものに対してサイズが取得できるはずなので、それを用いて試してみました。

<div class="container">
  <!--  可変コンテンツの例  -->
  <textarea readonly style="display: block; width: calc(100vw - 16px); max-width: calc(100vw - 16px); height: calc(100vh - 16px); max-height: calc(100vh - 16px); outline: none;">
  </textarea>
  <div class="diagonal-box"></div>
</div>
.container {
  position: relative;
  width: fit-content;
}

.diagonal-box {
  container-type: size;
  position: absolute;
  height: 100%;
  background: rgba(0,0,0,0.25);
  top: 0;
  left: 0;
  width: 100%;
  overflow: hidden;
  pointer-events: none;
  
  &::before {
    content: "";
    display: block;
    position: absolute;
    width: max(200cqh, 200cqw); // どちらか2倍すればとりあえずははみ出る
    border-bottom: 1px solid #000;
    transform: rotate(atan2(100cqh, 100cqw)); // atan2 を用いて角度を取得
    transform-origin: left top;
    top: 0;
    left: 0;
  }
}

.containerposition: relative; にし、その中に斜線を描画する div を配置します。その div をクエリーコンテナーとして container-type: size; を設定した上で、擬似要素 ::beforecqhcqw を用いて角度を計算し、斜線を引くという仕組みです。

検証結果

以下は2024年12月18日時点での各ブラウザでの挙動です。

Chrome

なぜか45度固定のような挙動になり、 atan2(100cqh, 100cqw) が常に atan2(1,1) のように処理されているように見えました。値が正しく解釈されていないようです。(無効値扱いになってほしいところですが…)

Firefox

角度がつかず、ただの水平線になってしまいました。

現時点では atan2 内でコンテナークエリーの長さ単位を使用すると、Firefox では無効な値として解釈されているようです。

Safari

うまくいってる!

Safari では期待通りうまく斜線が引けました!

デモ(現状 Safari でしか動きません)

古い Safari だと

18.2 より前のものだと Chrome と同じように45度になるようでした。

18.2 のリリースノートだと calc で変更があったようなので、その対応で atan2 も調整が入ったのかもしれません。18.2 ではうまくいき、18.1 では45度になってしまうようでは現状使い所が難しい印象です。

まとめ

現状最新の Safari でしか期待通りに動きませんが、うまく斜線を引くことができました。atan2cqw/cqh を組み合わせることで、JSを用いずに要素のサイズに応じた斜線を描ける可能性があることがわかりました。他のブラウザでも対応が進むことを期待します。

それでは、よいCSSライフを!

FRAIMテックブログ

Discussion