Viết lại fadin sử dụng trong theme Astra (ok)
// Global exposure (similar to the original IIFE)
interface Window {
fadin: typeof fadin;
}
// interfaces.ts
interface AnimationQueueOptions {
items: HTMLElement[];
func: (item: HTMLElement) => void;
delay: number;
}
interface FadinOptions {
delay?: number;
selector?: string;
noInitalScrollEvent?: boolean;
animationFunction?: (item: HTMLElement) => void;
}
interface VisibleHiddenItems {
visible: HTMLElement[];
hidden: HTMLElement[];
}
// animation-queue.ts
class AnimationQueue {
private stack: HTMLElement[];
private userFunction: (item: HTMLElement) => void;
private delay: number;
private _interval: number | undefined;
constructor(options: AnimationQueueOptions) {
this.stack = options.items;
this.userFunction = options.func;
this.delay = options.delay;
}
pause(): void {
if (this._interval) {
clearInterval(this._interval);
}
}
start(): void {
if (!this.isDone()) {
this._interval = window.setInterval(() => this.fire(), this.delay);
}
}
reset(): void {
this.stack = [];
this.pause();
}
fire(): void {
this.check();
const item = this.stack.shift();
if (item) {
this.userFunction(item);
}
this.check();
}
check(): void {
if (this.stack.length === 0) {
if (this._interval) {
clearInterval(this._interval);
}
}
}
isDone(): boolean {
return this.stack.length <= 0;
}
}
// animation-service.ts
class AnimationService {
private query: string;
private toAnimate: HTMLElement[];
constructor(query: string) {
this.query = query;
this.toAnimate = Array.from(document.querySelectorAll<HTMLElement>(query));
}
animateItems(delay: number, customAnimationFunction?: (item: HTMLElement) => void): void {
const itemsToAnimate = this.getItemsToAnimate();
if (itemsToAnimate && itemsToAnimate.length > 0) {
new AnimationQueue({
items: itemsToAnimate,
delay: delay,
func: (item: HTMLElement) => {
return customAnimationFunction ? customAnimationFunction(item) : this.assignDelayAndOpacity(item);
}
}).start();
}
}
getItemsToAnimate(): HTMLElement[] | undefined {
if (!this.isDone()) {
const { visible, hidden } = this.getVisibleAndHiddenItems();
this.toAnimate = hidden; // Only keep hidden items for the next check
return visible;
}
return undefined;
}
reset(): void {
this.toAnimate = Array.from(document.querySelectorAll<HTMLElement>(this.query));
this.getVisibleAndHiddenItems().hidden.forEach((item: HTMLElement) => {
item.style.opacity = "0";
item.style.transform = "translate3d(0px, 100px, 0px)";
});
}
isDone(): boolean {
return this.toAnimate.length <= 0;
}
assignDelayAndOpacity(element: HTMLElement): void {
if (element) {
const delay = this.getDelay(element);
Object.assign(element.style, {
opacity: 1,
transform: "translate3d(0,0,0)"
});
if (delay) {
Object.assign(element.style, {
transitionDelay: delay
});
}
}
}
private getVisibleAndHiddenItems(): VisibleHiddenItems {
return this.toAnimate.reduce((acc: VisibleHiddenItems, element: HTMLElement) => {
const status = this.shouldBeVisible(element) ? "visible" : "hidden";
acc[status].push(element);
return acc;
}, { visible: [], hidden: [] });
}
private getDelay(element: HTMLElement): string | undefined {
return element?.dataset?.delay;
}
private shouldBeVisible(element: HTMLElement): boolean {
const rect = element.getBoundingClientRect();
const viewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);
return !(rect.bottom < 0 || rect.top - viewportHeight >= 0) && element.style.opacity !== "1";
}
}
// fadin.ts
const defaultOptions: FadinOptions = {
delay: 200,
selector: ".fadin",
noInitalScrollEvent: false,
animationFunction: undefined
};
class Fadin {
private animationService: AnimationService;
private _detach: () => void;
constructor(options: FadinOptions = defaultOptions) {
const mergedOptions = { ...defaultOptions, ...options };
this.animationService = new AnimationService(mergedOptions.selector!);
this._detach = this.setUpEventListener("scroll", () => {
this.animationService.animateItems(mergedOptions.delay!, mergedOptions.animationFunction);
});
if (!mergedOptions.noInitalScrollEvent) {
this.sendScroll();
}
}
isDone(): boolean {
return this.animationService.isDone();
}
detach(): () => void {
return this._detach;
}
reset(): void {
this.animationService.reset();
this.sendScroll();
}
sendScroll(): void {
window.dispatchEvent(new Event("scroll"));
}
private setUpEventListener(eventType: string, callback: () => void): () => void {
const listener = () => {
if (this.isDone()) {
window.removeEventListener(eventType, listener, true);
}
callback();
};
window.addEventListener(eventType, listener);
return () => window.removeEventListener(eventType, listener, true);
}
}
// Exported function for convenience
function fadin(selector: string, options?: FadinOptions): Fadin {
return new Fadin({ selector, ...options });
}
window.fadin = fadin;Áp dụng

Hiểu về mã JavaScript
1. n(Object.assign polyfill)
n(Object.assign polyfill)2. o(Hàng đợi hoạt hình / Trình quản lý khoảng thời gian)
o(Hàng đợi hoạt hình / Trình quản lý khoảng thời gian)3. i(Dịch vụ hoạt hình)
i(Dịch vụ hoạt hình)4. r(Tùy chọn mặc định)
r(Tùy chọn mặc định)5. s(Lớp chính Fadin)
s(Lớp chính Fadin)6. Chức năng xuất khẩu
Giải thích đơn giản (Khái niệm)
Chuyển đổi TypeScript (Ví dụ minh họa cho lớp khóa)
PreviousNghiên cứu class scrollingEffect 👌NextKiểu NonNullable<T> được sử dụng để loại bỏ các kiểu null và undefined khỏi kiểu T (ok)
Last updated