@egjs/flicking
Version:
Everyday 30 million people experience. It's reliable, flexible and extendable carousel.
214 lines (171 loc) • 5.99 kB
text/typescript
/*
* Copyright (c) 2015 NAVER Corp.
* egjs projects are licensed under the MIT license
*/
import { getElementSize, getStyle } from "../utils";
import Flicking from "../Flicking";
/**
* A component that detects size change and trigger resize method when the autoResize option is used
* @ko autoResize 옵션을 사용할 때 크기 변화를 감지하고 Flicking의 resize를 호출하는 컴포넌트
*/
class AutoResizer {
private _flicking: Flicking;
private _enabled: boolean;
private _resizeObserver: ResizeObserver | null;
private _resizeTimer: number;
private _maxResizeDebounceTimer: number;
public get enabled() {
return this._enabled;
}
public constructor(flicking: Flicking) {
this._flicking = flicking;
this._enabled = false;
this._resizeObserver = null;
this._resizeTimer = -1;
this._maxResizeDebounceTimer = -1;
}
public enable(): this {
const flicking = this._flicking;
const viewport = flicking.viewport;
if (this._enabled) {
this.disable();
}
if (flicking.useResizeObserver && !!window.ResizeObserver) {
const viewportSizeNot0 = viewport.width !== 0 || viewport.height !== 0;
const resizeObserver = viewportSizeNot0
? new ResizeObserver(this._skipFirstResize)
: new ResizeObserver(this._onResize);
this._resizeObserver = resizeObserver;
this.observe(flicking.viewport.element);
if (flicking.observePanelResize) {
this.observePanels();
}
} else {
window.addEventListener("resize", this._onResizeWrapper);
}
this._enabled = true;
return this;
}
public observePanels(): this {
this._flicking.panels.forEach(panel => {
this.observe(panel.element);
});
return this;
}
public unobservePanels(): this {
this._flicking.panels.forEach(panel => {
this.unobserve(panel.element);
});
return this;
}
public observe(element: HTMLElement): this {
const resizeObserver = this._resizeObserver;
if (!resizeObserver) return this;
resizeObserver.observe(element);
return this;
}
public unobserve(element: HTMLElement): this {
const resizeObserver = this._resizeObserver;
if (!resizeObserver) return this;
resizeObserver.unobserve(element);
if (this._flicking.observePanelResize) {
this.unobservePanels();
}
return this;
}
public disable(): this {
if (!this._enabled) return this;
const resizeObserver = this._resizeObserver;
if (resizeObserver) {
resizeObserver.disconnect();
this._resizeObserver = null;
} else {
window.removeEventListener("resize", this._onResizeWrapper);
}
this._enabled = false;
return this;
}
private _onResizeWrapper = () => {
this._onResize([]);
};
private _onResize = (entries: ResizeObserverEntry[]) => {
const flicking = this._flicking;
const resizeDebounce = flicking.resizeDebounce;
const maxResizeDebounce = flicking.maxResizeDebounce;
const resizedViewportElement = flicking.element;
// 현재 구현에서 리사이즈 옵저빙 대상은 패널과 뷰포트 2개만 존재.
// 아래는 뷰포트만 변경되었을 때 동작해야하는 로직이 있으므로 아래와 같이 조건문을 건다.
// 패널 쪽에서는 리사이즈 감지에 resizeObserver를 사용하지 않는 경우가 없으므로 이 조건은 곧 뷰포트만 리사이즈가 된 경우를 의미한다.
const isResizedViewportOnly = entries.find(e => e.target === flicking.element) && entries.length === 1;
// 참고: resizeObserver를 사용하지 않은 경우에는 entries.length가 0으로 오는데 이 경우에는 그냥 항상 리사이즈가 진행되도록 한다.
// (vw, vh 등을 사용하는 경우 이상 동작이 발생할 여지가 있기 때문이다)
if (isResizedViewportOnly) {
// resize 이벤트가 발생했으나 이전과 width, height의 변화가 없다면 이후 로직을 진행하지 않는다.
const beforeSize = {
width: flicking.viewport.width,
height: flicking.viewport.height
};
const afterSize = {
width: getElementSize({
el: resizedViewportElement,
horizontal: true,
useFractionalSize: this._flicking.useFractionalSize,
useOffset: false,
style: getStyle(resizedViewportElement)
}),
height: getElementSize({
el: resizedViewportElement,
horizontal: false,
useFractionalSize: this._flicking.useFractionalSize,
useOffset: false,
style: getStyle(resizedViewportElement)
})
};
if (
beforeSize.height === afterSize.height &&
beforeSize.width === afterSize.width
) {
return;
}
}
if (resizeDebounce <= 0) {
void flicking.resize();
} else {
if (this._maxResizeDebounceTimer <= 0) {
if (maxResizeDebounce > 0 && maxResizeDebounce >= resizeDebounce) {
this._maxResizeDebounceTimer = window.setTimeout(
this._doScheduledResize,
maxResizeDebounce
);
}
}
if (this._resizeTimer > 0) {
clearTimeout(this._resizeTimer);
this._resizeTimer = 0;
}
this._resizeTimer = window.setTimeout(
this._doScheduledResize,
resizeDebounce
);
}
};
private _doScheduledResize = () => {
clearTimeout(this._resizeTimer);
clearTimeout(this._maxResizeDebounceTimer);
this._maxResizeDebounceTimer = -1;
this._resizeTimer = -1;
void this._flicking.resize();
};
// eslint-disable-next-line @typescript-eslint/member-ordering
private _skipFirstResize = (() => {
let isFirstResize = true;
return (entries) => {
if (isFirstResize) {
isFirstResize = false;
return;
}
this._onResize(entries);
};
})();
}
export default AutoResizer;