次のお題はこちらです

WEBデザインとWEBディレクションのあれこれ@ビズオーシャン

Sassを使ってmmやptなどの単位をpxに変換する

弊社のメインサービス「書式の王様」では、主にMS Officeの書式テンプレートを取り扱っている。もちろん使い慣れているソフトでつくれるのは便利なんだけれど「ブラウザ上でささっと項目を入力して、PDFで書類を出力するサービス」もあったらいいのになーとも(いちユーザーとして)思う。そのためにはブラウザ上でのプレビュー表示/PDF出力の両方に対応できる書類レイアウトが必要なので、実際に試したメモをまとめておく。

WEB上でmmとptで単位指定する

PDF出力時に既存の書類と相違ないものをつくりたかったので、実際の書類でサイズを測りながら、「mm」と「pt」をCSSの単位指定に使ってみた。いつもはpxやremで指定している値の単位を置き換えるだけなので、あまり考えなくてもExcelやWordの元データを再現しやすい。

CSS3からは、長さ(length)に指定できる単位のバリエーションが増えた。すごい。

- CSS: カスケーディングスタイルシート | MDN

要素によって小数点の処理が揃わない問題

ところが、Chromeで見たときに同じ幅を指定しているはずのdivタグとtableタグの端が微妙に揃わない。ぱっと見は気づかないものの、よく見ると右端がちょっとだけずれている。

See the Pen Decimal point when unit of length is set to mm by shellme (@shellme-the-lessful) on CodePen.

mmで指定するとブラウザではpxに換算されるので(1mmはだいたい3.78px)小数点が発生しやすいのだけれど、

  • divタグ: 小数点以下も計算して表示
  • tableタグ: 小数点以下は切り捨て表示

という挙動があるらしい。知らなかった...。

小数点はブラウザ毎に丸め方が違うのでなるべく避けたいし、PDF出力にはHeadlessChromeを使う予定なのでスルーは難しい。かといって都度pxに再計算するのは時間がかかるので、Sass(Scss)のfunctionでpx変換用のオリジナル関数を作成することにした。

Sassのリファレンスを見ながら、px変換用の簡単な関数を試す

@function pixel-mm($var) {
    @return round( $var * 1.33 ) + px;
}

という関数を用意して、

.doc {
  padding: pixel-mm(18);
}

コンパイルすると、

.doc {
  padding: 68px;
}

CSSに出力できる。round()を使うと小数点以下が四捨五入されて整数になるので、前述の小数点問題は解消される。

微妙な点と改善ポイント

上記の方法で一応目的は達成されるけれど、いくつか気になる点が。

  • 単位別に関数を使い分けるのは少々手間だし、期間が空くと忘れそう
  • 単位が増える毎に関数も増殖するのは避けたい
  • 変数に単位(mmやpt)が入っていないので、他のメンバーが見たときに何の関数か分かりづらいかも

そのため、下記の条件でもうちょっといい書き方ができないか調査。

  1. 変数に単位を含めることができる
  2. 単位によって計算を分岐させることができる
  3. 単位が増えたときも簡単に追加できる

方法を調べる

分岐はif関数、変数から単位を取り出すのはunit関数でできるものの、「数値だけ」を取り出す関数は無いらしい。

http://book.scss.jp/code/c8/07.html

でも、「同じ単位同士で除算(割り算)すると単位が出ない」という仕様を利用して数値だけを取り出すハックを見つけたので、これを参考にしてつくれそう。

https://www.nxworld.net/tips/sass-number-operations-and-functions.html#anchor03-01

新しく単位をつけるには末尾に+ pxをつければいけるっぽい。

書き直したSass関数

最終的にこうなった。

@function pixel($var) {
  @if unit($var) == "pt" {
    @return round($var * 1.33 / ($var * 0 + 1)) + px;
  }
  @else if unit($var) == "cm" {
    @return round($var * 37.8 / ($var * 0 + 1)) + px;
  }
  @else if unit($var) == "mm" {
    @return round($var * 3.78 / ($var * 0 + 1)) + px;
  }
  @else if unit($var) == "in" {
    @return round($var * 96 / ($var * 0 + 1)) + px;
  }
}

この関数の場合は

.doc {
  padding: pixel(18mm);
}

.doc {
  font-size: pixel(18pt);
}

みたいな指定で書けるので、最初の関数と比べると初見でも何をしているのか理解しやすい。種類を増やしたときも、@else if...以降をコピペして書き換えればOK。今回はmmとptしか使わないけれど、せっかくなのでcmやinなども変換候補に入れてある。

[PR]ブラウザで書類がつくれるサービスつくりました

そんな試行錯誤を繰り返して開発したサービスが本日リリースされました👏👏👏

rirekisho.yagish.jp

WEBブラウザだけあれば、WordやExcelを持っていなくても、パソコンでも、スマホでも、いつもと同じスタイルの書類が作成できる、無料のWEBサービスです。 今回は、社会にでるときに必ず1度は機会がある『履歴書』をテーマに書式を用意しています。今後のアップデートでつくれる書類の種類や機能も増やしていこうとがんばっているので、転職やアルバイトに応募する際はぜひ使ってみてください。