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


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

### シリーズ

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

引き続き ↓ コレ作ってるときの話です。
GitHub: [mafumafuultu/svg.js dev](https://github.com/mafumafuultu/svg.js/tree/dev)

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

### 便利系の実装

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

### 準備

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

```js
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` を呼ぶと、だいたい解決する。

### よく使う属性とか


```js
// 長い
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` と同じなので計算は簡単ですね。

```js
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に

```js
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` とか

```js
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`

```js
const __BASE_PROTO__ = {
	f: {
		value(f = el => {}) {
			try {
				f(this['@'])
			} catch (e) {
				console.error(e);
			}
			return this;
		}
	},
}
```