|
|
/**
|
|
|
* 业务流程设计区块交互脚本
|
|
|
* Business Process Block Interactive Script
|
|
|
*/
|
|
|
|
|
|
(function($) {
|
|
|
'use strict';
|
|
|
|
|
|
/**
|
|
|
* 业务流程Tab切换类
|
|
|
*/
|
|
|
class BusinessProcessTabs {
|
|
|
constructor(container) {
|
|
|
this.container = $(container);
|
|
|
this.tabs = this.container.find('.process-tab');
|
|
|
this.images = this.container.find('.process-image');
|
|
|
this.currentIndex = 0;
|
|
|
this.isAnimating = false;
|
|
|
|
|
|
this.init();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 初始化
|
|
|
*/
|
|
|
init() {
|
|
|
this.bindEvents();
|
|
|
this.setupIntersectionObserver();
|
|
|
this.preloadImages();
|
|
|
|
|
|
// 设置初始状态
|
|
|
this.setActiveTab(0, false);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 绑定事件
|
|
|
*/
|
|
|
bindEvents() {
|
|
|
// Tab点击事件
|
|
|
this.tabs.on('click', (e) => {
|
|
|
e.preventDefault();
|
|
|
const index = this.tabs.index($(e.currentTarget));
|
|
|
this.switchTab(index);
|
|
|
});
|
|
|
|
|
|
// 键盘导航支持
|
|
|
this.tabs.on('keydown', (e) => {
|
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
|
e.preventDefault();
|
|
|
const index = this.tabs.index($(e.currentTarget));
|
|
|
this.switchTab(index);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 触摸滑动支持(移动端)
|
|
|
if ('ontouchstart' in window) {
|
|
|
this.setupTouchEvents();
|
|
|
}
|
|
|
|
|
|
// 窗口大小改变时重新计算
|
|
|
$(window).on('resize', this.debounce(() => {
|
|
|
this.handleResize();
|
|
|
}, 250));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置触摸事件(移动端滑动支持)
|
|
|
*/
|
|
|
setupTouchEvents() {
|
|
|
let startX = 0;
|
|
|
let startY = 0;
|
|
|
let isScrolling = false;
|
|
|
|
|
|
this.container.on('touchstart', (e) => {
|
|
|
const touch = e.originalEvent.touches[0];
|
|
|
startX = touch.clientX;
|
|
|
startY = touch.clientY;
|
|
|
isScrolling = false;
|
|
|
});
|
|
|
|
|
|
this.container.on('touchmove', (e) => {
|
|
|
if (isScrolling) return;
|
|
|
|
|
|
const touch = e.originalEvent.touches[0];
|
|
|
const deltaX = touch.clientX - startX;
|
|
|
const deltaY = touch.clientY - startY;
|
|
|
|
|
|
// 判断是否为水平滑动
|
|
|
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 30) {
|
|
|
isScrolling = true;
|
|
|
e.preventDefault();
|
|
|
|
|
|
if (deltaX > 0 && this.currentIndex > 0) {
|
|
|
// 向右滑动,切换到上一个tab
|
|
|
this.switchTab(this.currentIndex - 1);
|
|
|
} else if (deltaX < 0 && this.currentIndex < this.tabs.length - 1) {
|
|
|
// 向左滑动,切换到下一个tab
|
|
|
this.switchTab(this.currentIndex + 1);
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 切换Tab
|
|
|
*/
|
|
|
switchTab(index) {
|
|
|
if (index === this.currentIndex || this.isAnimating || index < 0 || index >= this.tabs.length) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
this.setActiveTab(index, true);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置活动Tab
|
|
|
*/
|
|
|
setActiveTab(index, animate = true) {
|
|
|
if (this.isAnimating && animate) return;
|
|
|
|
|
|
this.isAnimating = animate;
|
|
|
this.currentIndex = index;
|
|
|
|
|
|
// 更新Tab状态
|
|
|
this.tabs.removeClass('active').eq(index).addClass('active');
|
|
|
|
|
|
// 更新图片显示
|
|
|
if (animate) {
|
|
|
this.animateImageTransition(index);
|
|
|
} else {
|
|
|
this.images.removeClass('active').eq(index).addClass('active');
|
|
|
this.isAnimating = false;
|
|
|
}
|
|
|
|
|
|
// 更新ARIA属性
|
|
|
this.updateAccessibility(index);
|
|
|
|
|
|
// 触发自定义事件
|
|
|
this.container.trigger('tabChanged', [index, this.tabs.eq(index)]);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 图片切换动画
|
|
|
*/
|
|
|
animateImageTransition(index) {
|
|
|
const currentImage = this.images.filter('.active');
|
|
|
const nextImage = this.images.eq(index);
|
|
|
|
|
|
// 如果没有当前活动图片,直接显示新图片
|
|
|
if (currentImage.length === 0) {
|
|
|
nextImage.addClass('active');
|
|
|
this.isAnimating = false;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 淡出当前图片
|
|
|
currentImage.animate({ opacity: 0 }, 250, () => {
|
|
|
currentImage.removeClass('active');
|
|
|
|
|
|
// 淡入新图片
|
|
|
nextImage.addClass('active').css('opacity', 0).animate({ opacity: 1 }, 250, () => {
|
|
|
this.isAnimating = false;
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 更新无障碍访问属性
|
|
|
*/
|
|
|
updateAccessibility(index) {
|
|
|
this.tabs.each((i, tab) => {
|
|
|
const $tab = $(tab);
|
|
|
$tab.attr('aria-selected', i === index ? 'true' : 'false');
|
|
|
$tab.attr('tabindex', i === index ? '0' : '-1');
|
|
|
});
|
|
|
|
|
|
this.images.each((i, image) => {
|
|
|
const $image = $(image);
|
|
|
$image.attr('aria-hidden', i === index ? 'false' : 'true');
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 预加载图片
|
|
|
*/
|
|
|
preloadImages() {
|
|
|
this.images.each((index, element) => {
|
|
|
const $img = $(element).find('img');
|
|
|
if ($img.length && $img.attr('data-src')) {
|
|
|
const img = new Image();
|
|
|
img.onload = () => {
|
|
|
$img.attr('src', $img.attr('data-src')).removeAttr('data-src');
|
|
|
};
|
|
|
img.src = $img.attr('data-src');
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置交叉观察器(用于动画触发)
|
|
|
*/
|
|
|
setupIntersectionObserver() {
|
|
|
if (!window.IntersectionObserver) return;
|
|
|
|
|
|
const observer = new IntersectionObserver((entries) => {
|
|
|
entries.forEach(entry => {
|
|
|
if (entry.isIntersecting) {
|
|
|
this.container.addClass('animate-in');
|
|
|
observer.unobserve(entry.target);
|
|
|
}
|
|
|
});
|
|
|
}, {
|
|
|
threshold: 0.2,
|
|
|
rootMargin: '0px 0px -50px 0px'
|
|
|
});
|
|
|
|
|
|
observer.observe(this.container[0]);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 处理窗口大小改变
|
|
|
*/
|
|
|
handleResize() {
|
|
|
// 重新计算布局相关的尺寸
|
|
|
this.container.removeClass('animate-in');
|
|
|
|
|
|
// 延迟重新添加动画类
|
|
|
setTimeout(() => {
|
|
|
this.container.addClass('animate-in');
|
|
|
}, 100);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 防抖函数
|
|
|
*/
|
|
|
debounce(func, wait) {
|
|
|
let timeout;
|
|
|
return function executedFunction(...args) {
|
|
|
const later = () => {
|
|
|
clearTimeout(timeout);
|
|
|
func(...args);
|
|
|
};
|
|
|
clearTimeout(timeout);
|
|
|
timeout = setTimeout(later, wait);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 自动播放功能
|
|
|
*/
|
|
|
startAutoPlay(interval = 5000) {
|
|
|
this.stopAutoPlay();
|
|
|
|
|
|
this.autoPlayTimer = setInterval(() => {
|
|
|
const nextIndex = (this.currentIndex + 1) % this.tabs.length;
|
|
|
this.switchTab(nextIndex);
|
|
|
}, interval);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 停止自动播放
|
|
|
*/
|
|
|
stopAutoPlay() {
|
|
|
if (this.autoPlayTimer) {
|
|
|
clearInterval(this.autoPlayTimer);
|
|
|
this.autoPlayTimer = null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 销毁实例
|
|
|
*/
|
|
|
destroy() {
|
|
|
this.stopAutoPlay();
|
|
|
this.tabs.off('click keydown');
|
|
|
this.container.off('touchstart touchmove');
|
|
|
$(window).off('resize');
|
|
|
this.container.removeClass('animate-in');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 初始化所有业务流程区块
|
|
|
*/
|
|
|
function initBusinessProcessBlocks() {
|
|
|
$('.business-process-block').each(function() {
|
|
|
const $block = $(this);
|
|
|
|
|
|
// 避免重复初始化
|
|
|
if ($block.data('business-process-initialized')) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const instance = new BusinessProcessTabs(this);
|
|
|
$block.data('business-process-instance', instance);
|
|
|
$block.data('business-process-initialized', true);
|
|
|
|
|
|
// 如果设置了自动播放,启动自动播放
|
|
|
const autoPlay = $block.data('auto-play');
|
|
|
const autoPlayInterval = $block.data('auto-play-interval') || 5000;
|
|
|
|
|
|
if (autoPlay) {
|
|
|
instance.startAutoPlay(autoPlayInterval);
|
|
|
|
|
|
// 鼠标悬停时暂停自动播放
|
|
|
$block.on('mouseenter', () => instance.stopAutoPlay());
|
|
|
$block.on('mouseleave', () => instance.startAutoPlay(autoPlayInterval));
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 页面加载完成后初始化
|
|
|
*/
|
|
|
$(document).ready(function() {
|
|
|
initBusinessProcessBlocks();
|
|
|
});
|
|
|
|
|
|
/**
|
|
|
* 支持动态加载的内容
|
|
|
*/
|
|
|
$(document).on('DOMNodeInserted', function(e) {
|
|
|
const $target = $(e.target);
|
|
|
if ($target.hasClass('business-process-block') || $target.find('.business-process-block').length) {
|
|
|
setTimeout(initBusinessProcessBlocks, 100);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
/**
|
|
|
* 自定义器预览支持
|
|
|
*/
|
|
|
if (typeof wp !== 'undefined' && wp.customize) {
|
|
|
wp.customize.bind('ready', function() {
|
|
|
// 当自定义器设置改变时重新初始化
|
|
|
wp.customize.bind('change', function() {
|
|
|
setTimeout(initBusinessProcessBlocks, 500);
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 导出到全局作用域(用于外部调用)
|
|
|
*/
|
|
|
window.BusinessProcessTabs = BusinessProcessTabs;
|
|
|
window.initBusinessProcessBlocks = initBusinessProcessBlocks;
|
|
|
|
|
|
})(jQuery); |