前回
- 
SVGを書くのが面倒なので入力補完できるようにしてみる 1
 内部に持った状態を変更させるオブジェクト、メソッドチェイン、基本シェイプ、<path>のd要素
- 
SVGを書くのが面倒なので入力補完できるようにしてみる 2
 useに潜んでいた罠、<animate>
- SVGを書くのが面倒なので入力補完できるようにしてみる 3
- SVGを書くのが面倒なので入力補完できるようにしてみる 4
引き続き ↓ コレ作ってるときの話です。
GitHub: mafumafuultu/svg.js dev
Render 描画品質
図形、画像、テキストの描画品質を設定できるんですが、プロパティ名は長いし値も長いのでスペルミスとか怖いし、比較に時間をかけるのも嫌なので、プロパティ名とそれに紐づく値も設定できるようにしたいなー。
↑ こんなこと考えたおかげで新しい仕組を考えることになった。
そして、今回はこのRenderでおなかいっぱいになりそうな予感がする。
条件
- 図形、画像、テキストのレンダーの設定を一か所にまとめたい
- レンダーのプロパティ名は長いので補完したい
- shape-rendering
- text-rendering
- image-rendering
 
- 各レンダープロパティの値も長い。補完したい
- optimizeSpeed
- crispEdges
- geometricPrecision
- optimizeLegibility
- etc.etc...
 
- 
render().shape().optimizeSpeed().text().geometricPrecision()な感じで補完したい
 ↑ 鬼の所業
- 面倒でも可読性は確保したい
 ↑ 鬼の所業
問題点
まず、この図式
Render => Text => Value-A => Render
Render => Shape => Value-B => Render
Render => Image => Value-C => Render
 Renderを呼び出し、各レンダープロパティ名を補完、各レンダープロパティは設定可能なValueを補完し、Renderのもとに帰る。
つまりこんな書き方が成立しなければダメだということになる。
render().Text().auto().Text().auto().Text().auto()
    .Shape().auto().Shape().auto()
    .Image().auto().Text().auto().Image().auto().Text().auto();
はてさて、どうしたものか...
デカルト「困難は分割せよ」
まずは理解できる単位に、構造を分解・整理してやればいい。
Render => [Text  => Value-A] => Render
Render => [Shape => Value-B] => Render
Render => [Image => Value-C] => Render
Render => [property => Value] => Render
T = [property => Value]
Render => T => Render
他人が作ったスパゲッティを読み解くよりは楽です。
見えてくるのはTがRenderのthisを受け取り、中身を変更して返せばいい。
const __shapeRender = o => ({
    auto() {return o.render['shape-rendering'] = 'auto', o;}
});
const renderGen = () => ({
    render: {'shape-rendering': 'optimizeSpeed'},
    Shape() {return __shapeRender(this);}
});
 まぁ、これで renderGen().Shape().auto().Shape().auto() はできるようになった。
よしよし、イケるイケる。
そんなわけなかった
ダサい。
const __shapeRender = o => ({
    auto() {return o.render['shape-rendering'] = 'auto', o;},
    optimizeSpeed() {return o.render['shape-rendering'] = 'optimizeSpeed', o},
    crispEdges() {return o.render['shape-rendering'] = 'crispEdges', o;},
    geometricPrecision() {return o.render['shape-rendering'] ='geometricPrecision', o;},
});
- 
o.render['shape-rendering']何度も書いてる
- 
shape-renderingに 値を入れてoを返す構造は共通している
- そして、その構造はほかのrenderにも出てくる
というわけでその辺を解決していく
renderGen().Shape() と呼んだ時点で分かっている情報は
- 
shape-renderingに設定される値が後で選ばれる
- 
renderGenのthisも渡ってくる
なので、こんな仕組みが必要だろう。
const vmod = key => (val, obj) => (obj.render[key] = val, obj);
このままでもいいのだけれども、もう少し疎結合にしておく。
// 役割がわかればいいので、頭文字だけにする
const vmod = (k, f) => (v, o) -> (f(o, k, v), o);
const __renderMap = (o, k, v) => o.render[k] = v;
 任意の (o, k, v) を引数に取る関数を渡せるように変更した。
使うかどうかはわからないけれども、ほかの処理作るときに再利用しやすいようにはなった。
 使用する側は、変更したいプロパティと変更用の関数をvmodに渡して、適当な名前を付けて、閉じ込めておき、どの値を使用するか確定したらその値とrenderGenのthisを受け取ってrenderを変更し、renderGenのthisを returnする。
const __shapeRender = o => ({
    auto() {return this._set('auto', o)},
    _set: vmod('shape-rendering', __renderMap)
});
const renderGen = () => ({
    render: {'shape-rendering': 'optimizeSpeed'},
    Shape() {return __shapeRender(this);}
});
これで renderGen().Shape().auto().Shape().auto(); ができるようになった。
あとは残りのRenderを仕様とにらめっこしながら書いていくだけなので楽なものです。
面倒な割にはこれで80%というところでしょうか?
 次回は、「あったらいいな」「これ、ほしいな」な便利系の整理です。
使い勝手を決める重要な部分ですね。

