こんにちは。
Githubのトレンドにvadimdemedes/inkというツールが上がっており、CLIといえばchjj/blessedやReact版のYomguithereal/react-blessedとかあったなぁ、と懐かしみつつ
そういえばPowerlineもシェルのアスキーアート的に表現されてるんだよな、
どうやってパンくずっぽいもの描画してるんだろう?
というのが気になったので調べてみたらおもったより面白そうだったので遊んでみました。
記事にも貼っていきますが、ソースはこちら(TODO)から確認できます。
上記画像のようなものを1つずつ作っていきます
その前に注意点です。
以下の文字は文字化けせずに表示されてますでしょうか。この後の内容に欠かせない重要パーツたちです。
,
, ░
, ⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏
画像として表示するとこんな感じです。
では、これらの文字をコピーしてターミナルに貼り付けても表示できていますでしょうか。
他にもこれらの文字を表示できるフォントあると思いますが、フォント設定がSource Code Pro
であればおそらく表示できていると思います。
Googleフォントとかから読み込めるWebフォント版のSource Code Proだとマルチバイトが省略されているそうで、文字化けします。
iTerm2をお使いの場合は、以下の画像のようにフォントを設定すればおそらく表示できると思います。
表示できていたら、フォントを適当に変えてみてください。だいたい文字化けすると思います。
これらの特殊文字は対応しているフォントが少ないため、ターミナルのフォント設定に強く依存するという点にご注意ください。
bash_profileとかをいじったことある人ならおそらく触れたことあると思いますが、[\e[00;34m]
とかそういうやつです。
そのままechoしてもいいのですが、コードとして分かりにくいので、npmのchalkというパッケージを使用して色名を使ってコードを書いていきます。
const chalk = require('chalk')
console.log(
chalk.red('H')
+ chalk.magenta('e')
+ chalk.yellow('l')
+ chalk.green('l')
+ chalk.blue('o')
+ ' '
+ chalk.cyan('w')
+ chalk.white('o')
+ chalk.yellowBright('r')
+ chalk.greenBright('l')
+ chalk.blueBright('d')
+ chalk.cyanBright('!')
+ chalk.whiteBright('!')
)
npm本家のスピナーが好きなので、それの簡易版を作ってみます。
npmのソースを見てみたところ、内部で使用しているのはiarna/gaugeというライブラリでした。
ソースの中を追っていったところ、先程例に出したこれらの文字(⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏
)が見つかりました。
これらの文字をくり返し表示することで、ぐるぐる回っているっぽいアニメーションを作ります。
さっそくアニメーションを実装する前に。
昔CLIでマリオが走るアニメーションを実装したことがありましたが、
そのときは複数行をアニメーションさせるためにカーソル操作もやってました。
今回は1行だけをアニメーションさせればいいので、簡単に\r
と空白を出力することで、その行の表示済みの内容をクリアする手法をとります。
// \r ≒ 行頭にカーソル移動
// スペースは消したい文字の数分だけ入れる。今回は1文字のアニメーションなので1文字で足りる。
// `process.stdout.columns`でウィンドウ幅を取得して、その分だけスペースを出力すると行の内容がすべてクリアできる
// https://stackoverflow.com/questions/30335637/get-width-of-terminal-in-node-js
process.stdout.write('\r \r')
なお、console.log
だと改行されてしまって予期したとおり動いてくれないので、process.stdout.write
を使用しています
ではアニメーションを実装してみます。
最近はinkなどのCLIに向けたライブラリが色々出てますが、今回はシンプルに原理を理解するため愚直に書きます。
コードはこちらからも確認できます
const chalk = require('chalk')
const FPS = 20
const colors = [
'red',
'magenta',
'yellow',
'green',
'blue',
'cyan',
'white',
'yellowBright',
'greenBright',
'blueBright',
'cyanBright',
'whiteBright',
]
const frames = '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'.split('')
let cursor = 0
setInterval(() => {
cursor = (cursor + 1) % frames.length
process.stdout.write(`\r \r${chalk[colors[cursor]](frames[cursor])}`)
}, 1000 / FPS)
動かしてみるとこんな感じです。
さて、本題のPowerlineです。
これもソースを追っていったところ、先程例に出した文字(
,
)や他にもアイコン相当の文字などが見つかりました。
こんな文字どこから見つけてきたんだよ。というツッコミを入れたくなるほどレパートリーがあります。
ちなみに、普通に「さんかく」と打って変換できる文字では文字サイズがテキストと同じなのでパンくずっぽい見た目になりませんでした。
これらの文字と背景色を組み合わせて、PowerlineっぽいUIを実装してみます。
まずは背景色を理解しないとそれっぽいUIにならないので、それを説明します。
先程リンクを貼ったCLIでマリオが走るアニメーションでは、半角スペースに背景色を付けてドットっぽく扱っています。
今回はそれよりもう少し難しいですが、概念は同じです。今回作りたいモノは
のように、パンくずリストっぽいものです。色の変わり目がミソです。
なぜこういう見た目になるかというと、
の文字色と背景色を設定することでレイヤーっぽく扱っているからです。
(白文字背景黒) (白文字背景赤) (白文字背景緑)...
という全体構成になってます。
例えば1つ目のつなぎ目(
)であれば、「文字色=黒、背景色=赤」で表示することでいい感じに繋がります。
を使ってしまうとどうしても図形っぽく認識してしまうと思うので、スペースを開けたりつなぎ文字を変更してみると、イメージが付きやすいかもしれません。
const chalk = require('chalk')
console.log(
chalk.white.bgRed(' Hello ')
+ ' '
+ chalk.red.bgBlack('')
+ ' '
+ chalk.bgBlack(' world!! ')
+ ' '
+ chalk.black('')
)
背景色の設定は、先ほど紹介したchalk
でできます
chalk.black.bgRed('')
と言った具合に。
概念的な部分はお話したので、コードに移ります。
とはいえ、つなぎ目の文字色と背景色をハードコードせずに計算したり、多少クラスに分けたりした程度です。
コードの全文を載せると長いのでこちらを参照して下さい。
// ... 略
const powerline = new Powerline()
const stack = new IndicatorStack()
stack.add(new Indicator({ text: 'White', color: 'red', backgroundColor: 'white', icon: '✉' }))
stack.add(new Indicator({ text: 'Cyan', color: 'white', backgroundColor: 'cyan', icon: '' }))
stack.add(new Indicator({ text: 'Blue', color: 'white', backgroundColor: 'blue' }))
stack.add(new Indicator({ text: 'Magenta', color: 'white', backgroundColor: 'magenta', icon: '♥' }))
stack.add(new Indicator({ text: 'Red', color: 'white', backgroundColor: 'red', icon: '' }))
stack.add(new Indicator({ text: 'Yellow', color: 'white', backgroundColor: 'yellow', icon: '⚡︎' }))
stack.add(new Indicator({ text: 'Green', color: 'white', backgroundColor: 'green' }))
powerline.add(stack)
console.log('\n\n\n')
console.log(powerline.render())
console.log('\n\n\n')
適当にアイコンとかも公式のソースから引っ張ってきてみました。
「なんでそんな文字あるんだよ…」という文字が思っていたより色々あって、夢が広がると思います。
HTMLの実体参照なんかもかなりの種類があるので、ユースケースによっては画像やフォントアイコンではなくそれらを使ってみるのも面白いかもしれませんね。
— 【みんなの知識 ちょっと便利帳】使いたいときの HTML特殊文字 & 機種依存文字 - 矢印 & 矢印に使える記号、使えそうな記号