LoginSignup
3
2

More than 3 years have passed since last update.

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

Posted at

今回はゆるーく、便利系の実装。
SVGタグだけじゃなく、HTMLタグも扱えるようにしたいのでそのあたりで楽をするためのモノ作っていきます。

シリーズ

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

引き続き ↓ コレ作ってるときの話です。
GitHub: mafumafuultu/svg.js dev

とりあえず今回で一区切り

便利系の実装

  • fill, stroke は割と使う
  • viewBox で中央に表示する座標と倍率指定があるとよさそう
  • undefined 長い
  • textContent 長い
  • onloadpromise になっていた方が使い勝手がいいと思う。
  • 通常のHTMLタグも喰えるようにしたので、HTMLElementが混じっていても使えるようにする
  • すでに作っていた関数を、抱えている要素にあてたい

準備

 これらを用意する前に、まず内部的にエラーを防ぐ仕組み・製薬を用意。

const __BASE_PROTO__ = {
  type : { value (v, is) {return typeof t === is;} },
  of : { value (v, t) {return v instanceof t;} },
  has : { value (prop) {return prop in this['@'];} }
};

属性は前方互換としても存在している__BASE_PROTO__.attrs を呼ぶと、だいたい解決する。

よく使う属性とか

// 長い
const none = undefined;

const __BASE_PROTO__ = {
    fill: {
        value(color = 'transparent') {
            return this.type(color, 'string')
                ? this.attrs({'fill': color})
                : this;
        }
    },
    stroke: {
        value(color = 'transparent', width) {
            return this.type(color, 'string')
                ? Object.is(width, undefined)
                    ? this.attrs({stroke : color})
                    : this.attrs({stroke : color, 'stroke-width' : width})
                : this;
        }
    },
    fillStroke : {
        value(fill = 'transparent', stroke = 'transparent') {
            return this.type(fill, 'string') && this.type(stroke, 'string')
                ? this.attrs({fill, stroke})
                : this;
        }
  },
  txt : {
        value(txt = '') {
            if (this.has('textContent')) {
                this['@'].textContent = txt;
            }
            return this;
        }
  }
};

viewbox zoom

viewBox の指定方法は rect と同じなので計算は簡単ですね。

const __BASE_PROTO__ = {
  zoom: {
        value(center=[0,0], x) {
            if (this.has('viewBox')) {
                let {width, height} = this.attrs();
                let w = width/x, h = height/x;
                this.attrs({viewBox: `${center[0] - w/2} ${center[1] - h/2} ${w} ${h}`})
            }
            return this;
        }
    },
};

onloadをpromiseに

const onload = () => document.readyState !== 'complete'
    ? new Promise(r => document.addEventListener('readystatechange', () => {
        switch (document.readyState) {
            case 'complete': r();break;
            default:
        }
    }))
    : Promise.resolve();

普通のHTMLタグも扱いやすく

 なんだかんだ使う id とか class とか data とか

const __BASE_PROTO__ = {
  id : {
    value (id) {
      if (this.has('id') && this.type(id, 'string')) {
        this['@'].id = id;
      }
      return this;
    }
  },
    cls : {
        value(v, force = true) {
            if (this.has('classList') && this.type(v, 'string')) {
                this['@'].classList.toggle(v, force);
            }
            return this;
        }
  }, 
    data : {
        value(o) {
            if (this.has('dataset') && this.type(k, 'string')) {
                v == null
                    ? delete this['@'].dataset[k]
                    : this['@'].dataset[k] = v;
            }
            return this;
        }
    }
}

すでに作っていた関数を、抱えている要素にあてたい

全部をErrにするのもいまいちイケてないので try-catch

const __BASE_PROTO__ = {
    f: {
        value(f = el => {}) {
            try {
                f(this['@'])
            } catch (e) {
                console.error(e);
            }
            return this;
        }
    },
}
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