(function ($) {
"use strict";
$(window).on('elementor/frontend/init', () => {
elementorFrontend.hooks.addAction('frontend/element_ready/motiox-timeline.default', ($scope) => {
checkScreenSize();
window.addEventListener('resize', debounce(checkScreenSize, 200));
function checkScreenSize() {
const screenWidth = window.innerWidth;
if (screenWidth > 768) {
initTimelineLargeScreen();
} else {
initTimelineSmallScreen();
}
}
function initTimelineLargeScreen() {
setupTimeline();
setupCircles();
initializeItems(); // Đảm bảo gọi hàm này ngay sau khi load
$(window).on('scroll.largeTimeline', throttle(() => {
updateLines();
updateInViewItems();
updateCircleColors();
}, 100));
}
function initTimelineSmallScreen() {
setupTimeline();
setupCircles();
initializeItems(); // Đảm bảo gọi hàm này ngay sau khi load
$(window).on('scroll.smallTimeline', throttle(() => {
updateLines();
updateInViewItems();
updateCircleColors();
}, 100));
}
function setupTimeline() {
const wrapper = $('.elementor-timeline-wrapper');
const greyLine = wrapper.find('.default-line');
const lineToDraw = wrapper.find('.draw-line');
if (!lineToDraw.length) return;
const greyLineHeight = wrapper.height();
greyLine.css({
height: greyLineHeight + 'px',
top: 15 + 'px',
});
lineToDraw.css({
maxHeight: greyLineHeight + 'px',
top: 15 + 'px',
});
// Gọi hàm khởi tạo trạng thái ẩn cho các mục
initializeItems();
}
function initializeItems() {
const items = $('.elementor-timeline-wrapper .item');
// Ẩn tất cả các item
items.css({
opacity: 0,
}).removeClass('in-view');
// Chỉ hiển thị item đầu tiên
const firstItem = items.first();
firstItem.css({
opacity: 1,
transform: 'translateY(0)',
transition: 'opacity 0.6s ease, transform 0.6s ease',
}).addClass('in-view');
}
function setupCircles() {
const wrapper = $('.elementor-timeline-wrapper');
const items = wrapper.find('.item');
const circleContainer = $('<div class="timeline-circles"></div>');
// Remove old circles if any
wrapper.find('.timeline-circles').remove();
// Add a timeline circle for each item
items.each(function () {
const circle = $('<div class="timeline-circle"></div>');
circleContainer.append(circle);
});
// Append the container to the wrapper
wrapper.append(circleContainer);
// Position circles based on items
positionCircles(items);
}
function positionCircles(items) {
const circles = $('.timeline-circle');
items.each(function (index) {
const itemOffsetTop = $(this).position().top; // Lấy vị trí top của item
// Đặt vị trí top cho timeline-circle
$(circles[index]).css('top', itemOffsetTop + 'px');
// Tạo timeline-circle-dot nếu chưa có
const dot = $('<div class="timeline-circle-dot"></div>'); // Tạo dot mới
$(circles[index]).append(dot); // Thêm dot vào bên trong timeline-circle
});
}
function updateLines() {
const wrapper = $('.elementor-timeline-wrapper');
const lineToDraw = wrapper.find('.draw-line');
const items = wrapper.find('.item');
const windowScrollTop = $(window).scrollTop();
const windowHeightHalf = $(window).height() / 2;
const timelineTop = wrapper.offset().top;
const greyLineHeight = wrapper.height();
if (windowScrollTop >= timelineTop - windowHeightHalf) {
let newHeight = windowScrollTop - timelineTop + windowHeightHalf;
lineToDraw.css({
height: Math.min(newHeight + 10, greyLineHeight) + 'px',
});
}
}
function updateInViewItems() {
const wrapper = $('.elementor-timeline-wrapper');
const lineToDraw = wrapper.find('.draw-line');
const items = wrapper.find('.item');
const lineBottom = lineToDraw.offset().top + lineToDraw.outerHeight(); // Điểm cuối của đường draw-line
items.each(function (index) {
const itemTop = $(this).offset().top; // Vị trí top của mục
// Kiểm tra nếu đường draw-line đã đi qua mục hiện tại
if (lineBottom >= itemTop && !$(this).hasClass('in-view')) {
// Chỉ hiện mục nếu draw-line vừa chạm tới và mục chưa được đánh dấu là in-view
$(this).addClass('in-view').css({
opacity: 1,
transform: 'translateY(0)',
transition: 'opacity 0.6s ease, transform 0.6s ease',
});
} else if (lineBottom < itemTop && $(this).hasClass('in-view')) {
// Ẩn mục nếu draw-line chưa tới hoặc đã vượt qua mốc
$(this).removeClass('in-view').css({
opacity: 0,
transform: 'translateY(20px)',
transition: 'opacity 0.6s ease, transform 0.6s ease',
});
}
});
}
function updateCircleColors() {
const wrapper = $('.elementor-timeline-wrapper');
const lineToDraw = wrapper.find('.draw-line');
const circles = wrapper.find('.timeline-circle');
const lineBottom = lineToDraw.offset().top + lineToDraw.outerHeight();
circles.each(function () {
const circleTop = $(this).offset().top;
if (lineBottom >= circleTop) {
$(this).addClass('active'); // Add active class to change color
} else {
$(this).removeClass('active'); // Remove active class
}
});
}
// Utility functions: debounce and throttle
function debounce(func, wait) {
let timeout;
return function (...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function (...args) {
const context = this;
if (!lastRan) {
func.apply(context, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(function () {
if (Date.now() - lastRan >= limit) {
func.apply(context, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}
});
});
})(jQuery);