Viết lại fadin sử dụng trong theme Astra (ok)
src\test.ts
// 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

src\test.js
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function (t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
// animation-queue.ts
var AnimationQueue = /** @class */ (function () {
function AnimationQueue(options) {
this.stack = options.items;
this.userFunction = options.func;
this.delay = options.delay;
}
AnimationQueue.prototype.pause = function () {
if (this._interval) {
clearInterval(this._interval);
}
};
AnimationQueue.prototype.start = function () {
var _this = this;
if (!this.isDone()) {
this._interval = window.setInterval(function () { return _this.fire(); }, this.delay);
}
};
AnimationQueue.prototype.reset = function () {
this.stack = [];
this.pause();
};
AnimationQueue.prototype.fire = function () {
this.check();
var item = this.stack.shift();
if (item) {
this.userFunction(item);
}
this.check();
};
AnimationQueue.prototype.check = function () {
if (this.stack.length === 0) {
if (this._interval) {
clearInterval(this._interval);
}
}
};
AnimationQueue.prototype.isDone = function () {
return this.stack.length <= 0;
};
return AnimationQueue;
}());
// animation-service.ts
var AnimationService = /** @class */ (function () {
function AnimationService(query) {
this.query = query;
this.toAnimate = Array.from(document.querySelectorAll(query));
}
AnimationService.prototype.animateItems = function (delay, customAnimationFunction) {
var _this = this;
var itemsToAnimate = this.getItemsToAnimate();
if (itemsToAnimate && itemsToAnimate.length > 0) {
new AnimationQueue({
items: itemsToAnimate,
delay: delay,
func: function (item) {
return customAnimationFunction ? customAnimationFunction(item) : _this.assignDelayAndOpacity(item);
}
}).start();
}
};
AnimationService.prototype.getItemsToAnimate = function () {
if (!this.isDone()) {
var _a = this.getVisibleAndHiddenItems(), visible = _a.visible, hidden = _a.hidden;
this.toAnimate = hidden; // Only keep hidden items for the next check
return visible;
}
return undefined;
};
AnimationService.prototype.reset = function () {
this.toAnimate = Array.from(document.querySelectorAll(this.query));
this.getVisibleAndHiddenItems().hidden.forEach(function (item) {
item.style.opacity = "0";
item.style.transform = "translate3d(0px, 100px, 0px)";
});
};
AnimationService.prototype.isDone = function () {
return this.toAnimate.length <= 0;
};
AnimationService.prototype.assignDelayAndOpacity = function (element) {
if (element) {
var delay = this.getDelay(element);
Object.assign(element.style, {
opacity: 1,
transform: "translate3d(0,0,0)"
});
if (delay) {
Object.assign(element.style, {
transitionDelay: delay
});
}
}
};
AnimationService.prototype.getVisibleAndHiddenItems = function () {
var _this = this;
return this.toAnimate.reduce(function (acc, element) {
var status = _this.shouldBeVisible(element) ? "visible" : "hidden";
acc[status].push(element);
return acc;
}, { visible: [], hidden: [] });
};
AnimationService.prototype.getDelay = function (element) {
var _a;
return (_a = element === null || element === void 0 ? void 0 : element.dataset) === null || _a === void 0 ? void 0 : _a.delay;
};
AnimationService.prototype.shouldBeVisible = function (element) {
var rect = element.getBoundingClientRect();
var viewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);
return !(rect.bottom < 0 || rect.top - viewportHeight >= 0) && element.style.opacity !== "1";
};
return AnimationService;
}());
// fadin.ts
var defaultOptions = {
delay: 200,
selector: ".fadin",
noInitalScrollEvent: false,
animationFunction: undefined
};
var Fadin = /** @class */ (function () {
function Fadin(options) {
if (options === void 0) { options = defaultOptions; }
var _this = this;
var mergedOptions = __assign(__assign({}, defaultOptions), options);
this.animationService = new AnimationService(mergedOptions.selector);
this._detach = this.setUpEventListener("scroll", function () {
_this.animationService.animateItems(mergedOptions.delay, mergedOptions.animationFunction);
});
if (!mergedOptions.noInitalScrollEvent) {
this.sendScroll();
}
}
Fadin.prototype.isDone = function () {
return this.animationService.isDone();
};
Fadin.prototype.detach = function () {
return this._detach;
};
Fadin.prototype.reset = function () {
this.animationService.reset();
this.sendScroll();
};
Fadin.prototype.sendScroll = function () {
window.dispatchEvent(new Event("scroll"));
};
Fadin.prototype.setUpEventListener = function (eventType, callback) {
var _this = this;
var listener = function () {
if (_this.isDone()) {
window.removeEventListener(eventType, listener, true);
}
callback();
};
window.addEventListener(eventType, listener);
return function () { return window.removeEventListener(eventType, listener, true); };
};
return Fadin;
}());
// Exported function for convenience
function fadin(selector, options) {
return new Fadin(__assign({ selector: selector }, options));
}
window.fadin = fadin;
src\test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.fadin {
opacity: 0;
transition-property: opacity, transform;
transform: translate3d(0, 100px, 0);
transition-duration: 1s;
}
</style>
</head>
<body>
<p class="fadin">Lorem ipsum dolor sit amet.</p>
<p class="fadin">Lorem ipsum dolor sit amet.</p>
<p class="fadin" style="margin-top: 450px;">Lorem ipsum dolor sit amet.</p>
<p class="fadin" style="margin-top: 450px;">Lorem ipsum dolor sit amet.</p>
<p class="fadin" style="margin-top: 450px;">Lorem ipsum dolor sit amet.</p>
<script src="test.js"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
fadin(".fadin")
}
);
console.log("aaaaaa");
</script>
</body>
</html>
Nghiên cứu từ file
astra-addon/assets/js/minified/reveal-effect.min.js
!function (t, e) {
"object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = t || self).fadin = e()
}(this, function () {
"use strict";
var n = function () {
return (n = Object.assign || function (t) {
for (var e, n = 1, i = arguments.length; n < i; n++)
for (var o in e = arguments[n])
Object.prototype.hasOwnProperty.call(e, o) && (t[o] = e[o]);
return t
}
).apply(this, arguments)
}
, o = function () {
function t(t) {
this.stack = t.items,
this.userFunction = t.func,
this.delay = t.delay
}
return t.prototype.pause = function () {
clearInterval(this._interval)
}
,
t.prototype.start = function () {
this.isDone() || (this._interval = window.setInterval(this.fire.bind(this), this.delay))
}
,
t.prototype.reset = function () {
this.stack = [],
this.pause()
}
,
t.prototype.fire = function () {
this.check(),
this.userFunction(this.stack.shift()),
this.check()
}
,
t.prototype.check = function () {
if (!this.stack.length)
return clearInterval(this._interval)
}
,
t.prototype.isDone = function () {
return this.stack.length <= 0
}
,
t
}()
, i = function () {
function t(t) {
this.query = t,
this.toAnimate = Array.from(document.querySelectorAll(t))
}
return t.prototype.animateItems = function (t, e) {
var n = this
, i = this.getItemsToAnimate();
i && new o({
items: i,
delay: t,
func: function (t) {
return e ? e(t) : n.assignDelayAndOpacity(t)
}
}).start()
}
,
t.prototype.getItemsToAnimate = function () {
if (!this.isDone()) {
var t = this.getVisibleAndHiddenItems()
, e = t.visible
, n = t.hidden;
return this.toAnimate = n,
e
}
}
,
t.prototype.reset = function () {
this.toAnimate = Array.from(document.querySelectorAll(this.query)),
this.getVisibleAndHiddenItems().hidden.forEach(function (t) {
return t.style.opacity = "0",
t.style.transform = "translate3d(0px, 100px, 0px)",
t
})
}
,
t.prototype.isDone = function () {
return this.toAnimate.length <= 0
}
,
t.prototype.assignDelayAndOpacity = function (t) {
if (t) {
var e = this.getDelay(t);
Object.assign(t.style, {
opacity: 1
}),
Object.assign(t.style, {
transform: "translate3d(0,0,0)"
}),
e && Object.assign(t.style, {
transitionDelay: e
})
}
}
,
t.prototype.getVisibleAndHiddenItems = function () {
var i = this;
return this.toAnimate.reduce(function (t, e) {
var n = i.shouldBeVisible(e) ? "visible" : "hidden";
return t[n] = t[n].concat(e),
t
}, {
visible: [],
hidden: []
})
}
,
t.prototype.getDelay = function (t) {
if (t && t.dataset && t.dataset.delay)
return t.dataset.delay
}
,
t.prototype.shouldBeVisible = function (t) {
var e = t.getBoundingClientRect()
, n = Math.max(document.documentElement.clientHeight, window.innerHeight);
return !(e.bottom < 0 || 0 <= e.top - n) && "1" !== t.style.opacity
}
,
t
}()
, r = {
delay: 200,
selector: ".fadin",
noInitalScrollEvent: !1,
animationFunction: void 0
}
, s = function () {
function t(t) {
void 0 === t && (t = r);
var e = this
, n = Object.assign({}, r, t);
this.animationService = new i(n.selector),
this._detach = this.setUpEventListener("scroll", function () {
e.animationService.animateItems(n.delay, n.animationFunction)
}),
n.noInitalScrollEvent || this.sendScroll()
}
return t.prototype.isDone = function () {
return this.animationService.isDone()
}
,
t.prototype.detach = function () {
return this._detach()
}
,
t.prototype.reset = function () {
this.animationService.reset(),
this.sendScroll()
}
,
t.prototype.sendScroll = function () {
window.dispatchEvent(new Event("scroll"))
}
,
t.prototype.setUpEventListener = function (t, e) {
var n = this
, i = function () {
n.isDone() && window.removeEventListener(t, i, !0),
e()
};
return window.addEventListener(t, i.bind(this)),
function () {
return window.removeEventListener(t, i, !0)
}
}
,
t
}();
return function (t, e) {
return new s(n({
selector: t
}, e))
}
});
Nhờ Gemini giải quyết bài toán chuyển từ js sang ts
Có vẻ như bạn đã cung cấp một tệp JavaScript triển khai thư viện hoạt ảnh "fadin". Bạn đang yêu cầu chuyển đổi nó thành TypeScript hoặc làm cho nó dễ hiểu hơn.
Tôi chắc chắn có thể giúp bằng cách chia nhỏ mã JavaScript thành các phần dễ hiểu hơn và giải thích chức năng của nó. Việc chuyển đổi nó sang TypeScript sẽ liên quan đến việc thêm các định nghĩa kiểu, mà tôi cũng có thể minh họa.
Hiểu về mã JavaScript
Mã JavaScript được cung cấp thiết lập một mô-đun có tên là mô-đun fadin
này sẽ hoạt hình hóa các thành phần khi chúng hiển thị trong khung nhìn. Nó sử dụng phương pháp hoạt hình so le.
Sau đây là phân tích các thành phần chính:
1. n
(Object.assign polyfill)
n
(Object.assign polyfill)Đây là một polyfill đơn giản cho Object.assign
. Nó đảm bảo rằng các trình duyệt cũ hơn không hỗ trợ gốc Object.assign
vẫn có thể sử dụng chức năng của nó. Nó hợp nhất các thuộc tính từ một hoặc nhiều đối tượng nguồn vào một đối tượng đích.
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)Lớp này ( o
tạm gọi là AnimationQueue
) chịu trách nhiệm quản lý danh sách các mục và thực thi một chức năng nhất định trên mỗi mục với độ trễ được chỉ định.
constructor(t)
: Lấy một đối tượng vớiitems
(một mảng các phần tử để hoạt hình),func
(hàm áp dụng cho từng mục) vàdelay
(khoảng thời gian giữa các mục hoạt hình).pause()
: Dừng hoạt ảnh bằng cách xóa_interval
.start()
: Bắt đầu hoạt ảnh. Nếu còn mục nào, nó sẽ thiết lập lệnhsetInterval
gọifire()
lặp lại.reset()
: Xóastack
(các mục cần hoạt hình) và tạm dừng khoảng thời gian.fire()
: Xóa mục đầu tiên khỏistack
, gọi theuserFunction
trên mục đó, sau đó kiểm tra xem tất cả các mục đã được xử lý hay chưa.check()
: Kiểm tra xemstack
có trống không. Nếu trống, nó sẽ xóa_interval
.isDone()
: Trả vềtrue
nếu tất cả các mục đã được hoạt hình hóa.
3. i
(Dịch vụ hoạt hình)
i
(Dịch vụ hoạt hình)Lớp này ( i
tạm gọi là AnimationService
) quản lý các thành phần được hoạt hình hóa và xác định khả năng hiển thị của chúng.
constructor(t)
: Sử dụngquery
bộ chọn (ví dụ:".fadin"
) và tìm tất cả các phần tử khớp trên trang, lưu trữ chúng trongtoAnimate
.animateItems(t, e)
: Đây là trình kích hoạt hoạt ảnh cốt lõi. Nó lấy các mục hiện đang hiển thị và ẩn, sau đó tạo mộtAnimationQueue
thể hiện để hoạt ảnh hóa các mục hiển thị. Nó cho phép một hàm hoạt ảnh tùy chỉnh (e
) hoặc mặc định làassignDelayAndOpacity
.getItemsToAnimate()
: LọctoAnimate
danh sách thànhvisible
cáchidden
mục dựa trên việc chúng có trong khung nhìn hay không. Sau đó, nó cập nhậtthis.toAnimate
để chỉ chứahidden
các mục cho chu kỳ hoạt ảnh tiếp theo và trả vềvisible
các mục.reset()
: Đặt lạitoAnimate
danh sách thành tất cả các phần tử khớp với truy vấn ban đầu và đặt độ mờ ban đầu cũng như kiểu chuyển đổi của chúng (làm chúng ẩn đi và hơi lệch đi).isDone()
: Kiểm tra xem có còn thành phần nào để hoạt hình hay không.assignDelayAndOpacity(t)
: Đây là hàm hoạt ảnh mặc định. Nó áp dụngopacity: 1
,transform: translate3d(0,0,0)
cho phần tử và tùy chọn atransitionDelay
nếu được chỉ định trong thuộc tính của phần tửdata-delay
.getVisibleAndHiddenItems()
: Lặp lạitoAnimate
và phân loại các phần tử theovisible
hoặchidden
dựa trênshouldBeVisible()
.getDelay(t)
: Lấydata-delay
thuộc tính từ một phần tử.shouldBeVisible(t)
: Xác định xem một phần tử hiện có nằm trong khung nhìn hay không và độ mờ của phần tử đó có phải đã bằng 1 hay chưa.
4. r
(Tùy chọn mặc định)
r
(Tùy chọn mặc định)Đối tượng này ( r
, chúng ta hãy gọi nó là defaultOptions
) xác định cấu hình mặc định cho fadin
thư viện, chẳng hạn như bộ chọn mặc định ( .fadin
), độ trễ và liệu có kích hoạt sự kiện cuộn ban đầu hay không.
5. s
(Lớp chính Fadin)
s
(Lớp chính Fadin)Lớp này ( s
chúng ta có thể gọi là Fadin
) là điểm vào chính để sử dụng thư viện.
constructor(t)
: Khởi tạoFadin
phiên bản với các tùy chọn được cung cấp (hợp nhất chúng vớidefaultOptions
). Nó tạo raAnimationService
và thiết lập trình lắng nghe sự kiện cuộn. NếunoInitalScrollEvent
là false, nó sẽ gửi một sự kiện cuộn để kích hoạt hoạt ảnh ban đầu.isDone()
: Kiểm tra xem dịch vụ hoạt hình đã hoàn tất việc tạo hoạt ảnh cho tất cả các thành phần hay chưa.detach()
: Xóa trình lắng nghe sự kiện cuộn.reset()
: Đặt lạiAnimationService
và gửi sự kiện cuộn mới để kích hoạt lại hoạt ảnh.sendScroll()
: Gửi một sự kiện cuộn mới trênwindow
.setUpEventListener(t, e)
: Một hàm trợ giúp để thêm một trình lắng nghe sự kiện và trả về một hàm để tách nó ra. Nó cũng tự động xóa trình lắng nghe sau khi tất cả các hoạt ảnh hoàn tất.
6. Chức năng xuất khẩu
Phần cuối cùng của tập lệnh xuất ra một hàm tạo một Fadin
thể hiện mới, cho phép bạn sử dụng nó như fadin(".my-elements", { delay: 300 })
.
Giải thích đơn giản (Khái niệm)
Hãy tưởng tượng bạn có một loạt hộp trên một trang web và bạn muốn chúng xuất hiện từng cái một một cách mượt mà khi bạn cuộn xuống và chúng xuất hiện trong chế độ xem của bạn.
Mã JavaScript này thực hiện điều đó bằng cách:
Tìm các hộp : Xác định tất cả các thành phần trên trang mà bạn muốn tạo hiệu ứng động (ví dụ: các thành phần có lớp
fadin
).Kiểm tra khả năng hiển thị : Khi bạn cuộn, nó sẽ liên tục kiểm tra xem hộp nào vừa xuất hiện trên màn hình của bạn.
Hoạt hình từng cái một : Đối với các hộp hiển thị, tính năng này làm cho chúng xuất hiện mượt mà, thường có độ trễ nhỏ giữa mỗi hộp, khiến chúng "mờ dần" và có khả năng trượt lên.
Quản lý hoạt ảnh : Theo dõi các hộp đã xuất hiện và dừng kiểm tra hộp mới khi tất cả các hộp được chỉ định đã được hoạt ảnh.
Tùy chọn : Bạn có thể tùy chỉnh các yếu tố như tốc độ xuất hiện, độ trễ giữa mỗi hộp và thậm chí xác định hiệu ứng hoạt hình tùy chỉnh của riêng bạn.
Chuyển đổi TypeScript (Ví dụ minh họa cho lớp khóa)
Việc chuyển đổi toàn bộ tệp sang TypeScript sẽ liên quan đến việc thêm giao diện và kiểu cho tất cả các lớp và phương thức của chúng. Sau đây là ví dụ về cách các lớp AnimationQueue
( o
) và AnimationService
( i
) có thể trông như thế nào trong TypeScript, cùng với một số giao diện:
Kiểu chữ
// 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 });
}
// Global exposure (similar to the original IIFE)
declare global {
interface Window {
fadin: typeof fadin;
}
}
window.fadin = fadin;
Phiên bản TypeScript bổ sung tính năng an toàn kiểu bằng cách định nghĩa giao diện cho các tùy chọn và nhập rõ ràng các biến và tham số hàm. Điều này giúp mã dễ đọc, dễ hiểu và dễ bảo trì hơn vì bạn biết chính xác loại dữ liệu mà từng phần của mã mong đợi.
Bạn có muốn biết lời giải thích chi tiết hơn về một phần cụ thể nào đó của mã hoặc một số ví dụ về cách sử dụng fadin
thư viện này không?
Last updated
Was this helpful?