LitElementで拡大縮小表示をやってみた
こんにちは、エンジニア@milab です。
前回Litのライフサイクルについて記事を書きましたが
今回は拡大表示用のWebCompomentを、LitElementベースで作ってみます。
やりたいこと
- 属性のzoom指定で拡大/縮小を制御する
- 拡大時は画面中央に要素を表示する
- zoomIn(),zoomOut()メソッドを実装する
- プロパティのattributeオプションも使いたい
サンプルソース
HTML側
<zoom-view>タグ内に拡大要素を定義する。
<body>
<zoom-view id="zoomView" zoom="false">
<div class="logo">
<img src="/lit.svg" />
<span>Lit</span>
</div>
</zoom-view>
<button
onclick="document.getElementById('zoomView').setAttribute('zoom', 'true')"
>
Set attribute true!
</button>
<button
onclick="document.getElementById('zoomView').setAttribute('zoom', 'false')"
>
Set attribute false!
</button>
<button onclick="document.getElementById('zoomView').zoomIn()">
Call zoomIn()!
</button>
<button onclick="document.getElementById('zoomView').zoomOut()">
Call zoomOut()!
</button>
</body>
TypeScript側のソース
import { LitElement, css, html, PropertyValues } from 'lit';
import { customElement, property } from 'lit/decorators.js';
@customElement('zoom-view')
export class MyElement extends LitElement {
static override styles = css`
:host {
}
#box {
position: relative;
}
#zoomBox{
position: absolute;
}
`;
@property({
type: Boolean,
converter: (value) => {
console.log(value);
return value && value.toLowerCase() === 'true';
},
attribute: 'zoom',
})
private _zoom?: boolean = false;
private get _box(): HTMLDivElement | null {
return this.renderRoot?.querySelector('#box') ?? null;
}
private get _zoomBox(): HTMLDivElement | null {
return this.renderRoot?.querySelector('#zoomBox') ?? null;
}
private _keyframes: any[] = [];
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
override attributeChangedCallback(
name: string,
_old: string | null,
value: string | null
): void {
super.attributeChangedCallback(name, _old, value);
this.requestUpdate(name, _old);
}
override render() {
console.log('render');
return html`<div id="box"><div id="zoomBox"><slot /></div></div>`;
}
override firstUpdated(changedProperties: PropertyValues): void {
if (this._box && this._zoomBox) {
this._box.style.height = `${this._zoomBox.offsetHeight}px`;
this._box.style.width = `${this._zoomBox.offsetWidth}px`;
}
return super.firstUpdated(changedProperties);
}
override updated(changedProperties: PropertyValues): void {
if (changedProperties.has('zoom') && this._zoomBox && this._box) {
this._zoomBox.style.transformOrigin = 'top left';
if (this._zoom) {
this._box.style.backgroundColor = '#EEE';
const scale = 1.5;
const toY =
(document.documentElement.clientHeight -
(this._box.offsetHeight as number) * 1.5) /
2;
const toX =
(document.documentElement.clientWidth -
(this._box.offsetWidth as number) * 1.5) /
2;
this._keyframes = [
{
transform: `1`,
left: `${this._zoomBox.offsetLeft}px`,
top: `${this._zoomBox.offsetTop}px`,
},
{
transform: `scale(${scale})`,
left: `${toX}px`,
top: `${toY}px`,
},
];
this._zoomBox.animate(this._keyframes, {
iterations: 1,
fill: 'forwards',
duration: 300,
});
} else {
this._zoomBox.animate(this._keyframes, {
iterations: 1,
fill: 'forwards',
duration: 300,
direction: 'reverse',
});
this._box.style.backgroundColor = '';
}
}
}
public zoomIn() {
if (this._zoom === true) return;
const _old = this._zoom;
this._zoom = true;
this.requestUpdate('zoom', _old);
}
public zoomOut() {
if (this._zoom === false) return;
const _old = this._zoom;
this._zoom = false;
this.requestUpdate('zoom', _old);
}
}
Chromeで動作確認ができます。
コンソールログで確認してみる!
ボタンを押下して表示されたログは赤い四角で囲んでいます。
最初
デフォルトでは独自に定義した<zoom-view>タグの要素zoomにfalseをセットしているため
最初は「false」がコンソールに表示されます。
Set attribute true!ボタンを押下する
ボタンを押下するとsetAttribute()が実行されて
<zoom-view>タグの要素zoomにtrueがセットされます。
属性が変わったのでattributeChangedCallback()が実行されます。
changedPropertiesは、requestUpdate()メソッドの処理が終わると以下のように
trueからfalseに変わります。
changedPropertiesには変更したものだけがセットされます。
# | Key | Value |
---|---|---|
0 | _zoom | false |
1 | zoom | false |
Set attribute false!ボタンを押下する
ボタンを押下するとsetAttribute()が実行されて
<zoom-view>タグの要素zoomにfalseがセットされます。
属性が変わったのでattributeChangedCallback()が実行されます。
changedPropertiesは、requestUpdate()メソッドの処理が終わると以下のように
今度はfalseからtrueに変わります。
# | Key | Value |
---|---|---|
0 | _zoom | true |
1 | zoom | true |
Call zoomIn()!ボタンを押下する
ボタン押下によりイベントzoomIn()が呼び出されます。
requestUpdate()メソッドを使って手動でzoomにfalseをセットします。
HTML要素のzoomは変わらないので、attributeChangedCallback()は実行されません。
Call zoomOut()!ボタンを押下する
最後に
これがReactでもAngularでも使えるのは便利ですね。
ほかにもjQueryやHTMLとも、もちろん一緒に使えるので
フレームワークを導入するのは敷居が高いけれど・・・といった場合にも使えそうです!
参考文献
Discussion