LoginSignup
3
2

More than 3 years have passed since last update.

SVGを書くのが面倒なので入力補完できるようにしてみる 3

Last updated at Posted at 2020-07-14

前回

  1. SVGを書くのが面倒なので入力補完できるようにしてみる 1
    内部に持った状態を変更させるオブジェクト、メソッドチェイン、基本シェイプ、<path>のd要素
  2. SVGを書くのが面倒なので入力補完できるようにしてみる 2
    useに潜んでいた罠、<animate>
  3. SVGを書くのが面倒なので入力補完できるようにしてみる 3
  4. 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

image.png

 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

他人が作ったスパゲッティを読み解くよりは楽です。
見えてくるのはTRenderthisを受け取り、中身を変更して返せばいい。

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 に設定される値が後で選ばれる
  • renderGenthis も渡ってくる

なので、こんな仕組みが必要だろう。

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に渡して、適当な名前を付けて、閉じ込めておき、どの値を使用するか確定したらその値とrenderGenthisを受け取ってrenderを変更し、renderGenthisを 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%というところでしょうか?

 次回は、「あったらいいな」「これ、ほしいな」な便利系の整理です。
使い勝手を決める重要な部分ですね。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2