Help us understand the problem. What is going on with this article?

CSSだけで幻想的な光を放つニキシー管を作れるのか?

はじめに

htmlとCSSでニキシー管を作ってみました。(デモのページ
動的な数字の切り替えに対応しているので、いろいろな用途で使えると思います。

ニキシー管とは

ニキシー管は文字や数字を表示させる発光装置です。ガラス管の中にネオンガスが入っていて、電圧を印加するとワイヤーが幻想的なオレンジ色の光を放ちます。今は数字を表示するデバイスは6セグのLEDにとって代わられてしまっていますが、そのスチームパンクな外観に加えて、デジタルな制御なのに表示される数字がアナログという良さから愛好家が多いデバイスです。STEINS;GATEのダイバージェンスメーターに使われているのはあまりにも有名です。
NixieClock

なぜ作ろうと思ったのか

ニキシー管は今は量産されておらず、手に入れようと思うと基本的にはソ連製のデッドストックを探してきて買うしかありません。供給がほとんどないので、1個あたりの値段は結構高いです。例えばこれとかは6個で1万円します。

勢いあまって購入したはいいのですが、ちゃんと制御しようとすると、140Vの電圧を作る昇圧回路、印加するピンを切り替える回路、複数本使う場合にはダイナミック点灯するための同期回路、そしてなんといっても1本あたり10本のピンが出ているのでそれの配線などなどが必要になりとても大変です。そこまでの覚悟がなかったので、手動で数字を点灯するところまでやって投げることになってしまいました...

リアルでは挫折してしまったのですが、web上なら回路を作る必要もないし制御もjavascriptとかで簡単にできるし、いいことづくめなのではと思って作り始めました。

できたもの

nixie_sample.gif

いろいろと試行錯誤を繰り返した結果、こんな感じになりました。
もっといい感じのニキシー管を作り上げてくれる人が現れることを願って、これ以降は制作工程を1から書いていきます。

ニキシー管の作り方

フォントを選ぶ

ニキシー管の独特な数字を再現するために、適切なフォントを探す必要があります。
ニキシー管で表示される数字は、ワイヤーで作られているものなので、次のような特徴があります。

  • 一筆書きで数字が表されている
    • 4の上がくっついている
    • 1の下の線はない
  • 線が細い
  • 構成している線が少ない

これに加えて、管を使っているので少し縦長という特徴もありますが、フォントの縦横比は後で変えられるので、フォント選定の際は考えなくても良さそうです。

この辺の特徴を満たすいい感じのフォントをGoogleFontsで頑張って探しました。線の細さでフィルターすると数が減って探しやすかったです。最終的には下記のフォントを使うことにしました。

Quicksand
font.PNG

光るワイヤーを作る

上で選んだ文字を光らせます。
CSSでネオンっぽく文字を表示する方法は下記のcodepenを参考にさせてもらいました。
CSS animated neon sign

ニキシー管の光は、

  1. ワイヤー部分の黄色く光る部分
  2. その周囲の蛍光物質が放電してできたオレンジ色に光る部分
  3. 光で照らされて淡くオレンジ色に光る部分

の3つの光があるとして、それぞれCSSに書いていきました。
1はテキストのcolorで設定し、2、3はtext-shadowを使って設定しています。text-shadowの色が薄い部分は複数回かけることでいい感じの濃さにしています。

また、元のフォントよりも横長にしたかったので
CSSは以下の通りです。

@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300&display=swap');

.nixie_tube {
  font-size: 200px;
  font-family: 'Quicksand', sans-serif;
  color: #fff256;
  text-shadow: 0 0 0.05em #d45424, 0 0 0.05em #d45424, 0 0 0.05em #d45524,  /* 導線の輪郭 */
               0 0 0.1em #d45424, 0 0 0.1em #d45424, 0 0 0.1em #d45524,  /* 導線の輪郭 */
               0 0 0.5em #d45424, 0 0 0.5em #d45424, 0 0 0.5em #d45524,  /* 導線の輪郭 */
               0 0 0.5em #d45424, 0 0 0.5em #d45424, 0 0 0.5em #d45524,  /* 導線の輪郭 */
               0 0.05em 0.01em #000;
  position: relative;
}

.light_up_wire{
  position: absolute;
  top: 0.2em;
  left: -0.3em;
  top: -0.15em;
  transform: scale(0.8, 1.0);
}
  <span class="nixie_tube">
    <span class="light_up_wire">{{show_num}}</span>
  </span>

ここまでやると↓のような感じになります。
font_color.PNG

光っていないワイヤーを作る

ニキシー管の中を見ると、数字の形をしたワイヤーが中に入っていることがわかります。
このごちゃっとした感じもニキシー管の良さですよね。というわけで、光っていない部分の数字も作ります。

先ほど作ったCSSのcolorを暗くして、text-shadowを消します。
また、positionをabsoluteにすることで、何個表示しても同じ場所に重なるように表示されるようにします。

.nixie_wire {
  color: #34241c;
  text-shadow: none;
  position: absolute;
  transform   : scale(0.8, 1.0);
}

htmlの方は0~9まで全ての数字を書いてしまうと、あまりに重なりすぎてなんだかわからなくなってしまうので、特徴的な数字だけ間引いて入れています。フォントの線も細く出来たら良かったのですが...

  <span class="nixie_tube">
    <span class="nixie_wire">0</span>
    <span class="nixie_wire">2</span>
    <span class="nixie_wire">7</span>
    <span class="nixie_wire">8</span>
    <span class="light_up_wire">{{show_num}}</span>
  </span>

できあがったワイヤーは↓のような感じです。
wire.PNG

ガラス管を作る

ニキシー管は試験管みたいな容器の中に入っているところがかっこいいです。
なので、いい感じの管もCSSで作ります。

ニキシー管のガラスの部分と上下のキャップを、指定した範囲の背景にグラデーションを設定し、枠を丸めることで作っています。ガラスの方は基本透明ながらも、オレンジの光が当たって側面が光っているようにしています。キャップの方は金属っぽくなるように上の方から光が当たって白くなるような感じで斜めのグラデーションをかけています。

.nixie_glass {
  width : 0.55em;
  height : 1.2em;
  position: relative;
  top: 0.18em;
  opacity: 0.5;
  background: radial-gradient(transparent 60%, #aa3315);
  border-radius: 0.15em;
  display: inline-block;
}

.nixie_cap {
  width : 0.6em;
  height : 0.2em;
  position: absolute;
  top: -0.08em;
  left: -0.025em;
    background: linear-gradient( transparent 40%, #aa5d31 70%,#111 80%),
                    linear-gradient(to top left, #111 60%, #444 80%);
  border-radius: 0.08em 0.08em 0.02em 0.02em;
  display: inline-block;
}

.nixie_buttom {
  width : 0.6em;
  height : 0.2em;
  position: absolute;
  top: 0.98em;
  left: -0.025em;
    background: linear-gradient(to top, transparent 40%, #aa5d31 70%,#111 80%),
                    linear-gradient(to top left, #111 60%, #444 80%);
  border-radius: 0.02em 0.02em 0.08em 0.08em;
  display: inline-block;
}

ここまで設定すると以下のようになります。

nixie.PNG

切り替え時の残像を再現する

ここまでで一応数字を表示するところまでできるようになったのですが、まだニキシー管のかっこいいところを再現しきれているとは言えません。ニキシー管はただ数字を表示しているだけでも素敵なのですが、時計などで数字が切り替わるとき、前に表示していた数字がすぐには消えずに残像となって残り、その間に新しい数字が出てくるような挙動をします。
参考動画
このふわっと感をCSSで再現したい...と思ったのですが、CSSで切り替わりのタイミングを制御する方法がわからなかったので、諦めてjavascriptを使いました。

フェードインとフェードアウトのアニメーションはCSSのアニメーション機能を使いました。text-shdowとcolorを徐々に変化させるようにしています。これを表示させる数値が切り替わるたびに実行するようにします。
また、フェードインさせる数字とフェードアウトさせる数字の二種類を作り、それぞれ同時にアニメーションさせることで切り替わりを再現しています。

.fadein {
  animation: fadein linear 0.2s;
}

.fadeout {
  animation: fadeout linear 0.2s forwards;
}

@keyframes fadein {
  0% {
    color: #34241c;
    text-shadow: none;
  }
  100% {
    color: inherit;
    text-shadow: inherit;
  }
}

@keyframes fadeout {
  0% {
    color: inherit;
    text-shadow: inherit;
  }
  100% {
    color: #000;
    text-shadow: none;
  }
}

これでふわっと数字が切り替わる様子が再現できました!
↓ではわかりやすいように実際よりゆっくりと切り替えています。
nixie_sample2.gif

完成

Vueのコンポーネントとして一つのファイルにまとめました。
これで好きな数字をpropから渡してあげることで、時計やカウンターのような使い方をすることができます。
デモのページでは、このコンポーネントを使ったデモを表示しています。

<template>
  <span class="nixie_tube">
    <span class="nixie_wire">0</span>
    <span class="nixie_wire">2</span>
    <span class="nixie_wire">7</span>
    <span class="nixie_wire">8</span>
    <span v-bind:class="{ fadeout: on_change }" class="nixie_wire">{{previous_num}}</span>
    <span v-bind:class="{ fadein: on_change }" class="light_up_wire">{{show_num}}</span>
    <span class="nixie_glass"></span>
    <span class="nixie_cap"></span>
    <span class="nixie_buttom"></span>
  </span>
</template>

<script>
export default {
  props: {
    show_num: [String, Number]  // ニキシー管に表示する数字
  },
  data: () => {
    return{
      on_change: false,
      previous_num: 8
    }
  },
  methods:{
    change_num: function() {
      if(this.show_num == this.previous_num){
        return
      }
      this.on_change = true
      setTimeout(()=>{
        this.on_change = false
        this.previous_num = this.show_num
      }, 200)
    }
  },
  watch:{
    show_num: function() {
      this.change_num()
    }
  },
  mounted() {
    this.change_num()
  }
}
</script>

<style>
@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300&display=swap');

.nixie_tube {
  font-size: 200px;
  font-family: 'Quicksand', sans-serif;
  color: #fff256;
  text-shadow: 0 0 0.05em #d45424, 0 0 0.05em #d45424, 0 0 0.05em #d45524,  /* 導線の輪郭 */
               0 0 0.1em #d45424, 0 0 0.1em #d45424, 0 0 0.1em #d45524,  /* 導線の輪郭 */
               0 0 0.5em #d45424, 0 0 0.5em #d45424, 0 0 0.5em #d45524,  /* 導線の輪郭 */
               0 0 0.5em #d45424, 0 0 0.5em #d45424, 0 0 0.5em #d45524,  /* 導線の輪郭 */
               0 0.05em 0.01em #000;
  position: relative;
}

.light_up_wire{
  position: absolute;
  transform: scale(0.8, 1.0);
}

.nixie_wire {
  color: #34241c;
  text-shadow: none;
  position: absolute;
  transform   : scale(0.8, 1.0);
}

.nixie_glass {
  width : 0.55em;
  height : 1.2em;
  position: relative;
  top: 0.18em;
  opacity: 0.5;
  background: radial-gradient(transparent 60%, #aa3315);
  border-radius: 0.15em;
  display: inline-block;
}

.nixie_cap {
  width : 0.6em;
  height : 0.2em;
  position: absolute;
  top: -0.08em;
  left: -0.025em;
    background: linear-gradient( transparent 40%, #aa5d31 70%,#111 80%),
                    linear-gradient(to top left, #111 60%, #444 80%);
  border-radius: 0.08em 0.08em 0.02em 0.02em;
  display: inline-block;
}

.nixie_buttom {
  width : 0.6em;
  height : 0.2em;
  position: absolute;
  top: 0.98em;
  left: -0.025em;
    background: linear-gradient(to top, transparent 40%, #aa5d31 70%,#111 80%),
                    linear-gradient(to top left, #111 60%, #444 80%);
  border-radius: 0.02em 0.02em 0.08em 0.08em;
  display: inline-block;
}

.fadein {
  animation: fadein linear 0.2s;
}

.fadeout {
  animation: fadeout linear 0.2s forwards;
}

@keyframes fadein {
  0% {
    color: #34241c;
    text-shadow: none;
  }
  100% {
    color: inherit;
    text-shadow: inherit;
  }
}

@keyframes fadeout {
  0% {
    color: inherit;
    text-shadow: inherit;
  }
  100% {
    color: #000;
    text-shadow: none;
  }
}
</style>

おまけ1

ニキシー管で好きな数字を表示できるようにして何をするかって?
それはもちろん...
raihousya.PNG

おまけ2

propには数値だけではなく文字を指定することもできます。
試しにやってみたところ、フォント側からしたらガラス管の幅なんて知ったことではないので容赦なくはみ出してきました。
hello.gif

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした