From c5dfff8016f7ffd14b4aa5a3d4f761cf4637d661 Mon Sep 17 00:00:00 2001
From: sight <26325820+Sight-wcg@users.noreply.github.com>
Date: Fri, 20 Jun 2025 00:29:54 +0800
Subject: [PATCH] =?UTF-8?q?feat(tabs):=20=E6=94=AF=E6=8C=81=E8=87=AA?=
=?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=88=87=E6=8D=A2=E6=A0=87=E7=AD=BE=E6=97=B6?=
=?UTF-8?q?=E7=9A=84=E8=BF=87=E6=B8=A1=E6=95=88=E6=9E=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/tabs/detail/options.md | 16 ++++++
src/modules/tabs.js | 105 +++++++++++++++++++++++++++++++++---
2 files changed, 114 insertions(+), 7 deletions(-)
diff --git a/docs/tabs/detail/options.md b/docs/tabs/detail/options.md
index 37b092b4..119b4eaa 100644
--- a/docs/tabs/detail/options.md
+++ b/docs/tabs/detail/options.md
@@ -99,6 +99,22 @@
`false`
+
+
+
+anim 2.11.4 |
+
+
+自定义切换标签时的过渡效果。例如:
+
+`['layui-anim layui-anim-fadein', 'layui-anim layui-anim-fadeout']`
+
+ |
+Array |
+
+
+undefined
+
|
diff --git a/src/modules/tabs.js b/src/modules/tabs.js
index ca188943..cab5a0a7 100644
--- a/src/modules/tabs.js
+++ b/src/modules/tabs.js
@@ -8,6 +8,8 @@ layui.define('component', function(exports) {
var $ = layui.$;
+ var isSupportsAnimationend = supportsAnimationEnd();
+
// 创建组件
var component = layui.component({
name: 'tabs', // 组件名
@@ -25,7 +27,8 @@ layui.define('component', function(exports) {
CLOSE: 'layui-tabs-close',
BODY: 'layui-tabs-body',
ITEM: 'layui-tabs-item',
- CARD: 'layui-tabs-card'
+ CARD: 'layui-tabs-card',
+ ANIM_END_EVENT_NAME: 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend'
},
// 渲染
@@ -97,7 +100,12 @@ layui.define('component', function(exports) {
if ('index' in options && data.index != options.index) {
that.change(that.findHeaderItem(options.index), true);
} else if (data.index === -1) { // 初始选中项为空时,默认选中第一个
- that.change(that.findHeaderItem(0), true);
+ var activeElem = that.findHeaderItem(0);
+ if(activeElem.length > 0){
+ activeElem.addClass(component.CONST.CLASS_THIS);
+ that.findBodyItem(activeElem.attr('lay-id') || 0).addClass(component.CONST.CLASS_SHOW);
+ that.change(activeElem, true);
+ }
}
// 初始化滚动结构
@@ -133,7 +141,9 @@ layui.define('component', function(exports) {
var trigger = options.trigger + TRIGGER_NAMESPACE;
var elemHeaderItem = that.documentElem ? that.headerElem[1] : that.headerElem.join('');
delegatedElement.off(trigger).on(trigger, elemHeaderItem, function() {
- that.change($(this));
+ if(this !== that.data().thisHeaderItem[0]){
+ that.change($(this));
+ }
});
// 窗口 resize 事件
@@ -270,7 +280,11 @@ layui.define('component', function(exports) {
}
// 移除元素
- that.findBodyItem(layid || index).remove();
+ // 延迟移除元素,否则会导致元素移除后,无法触发 animationend 事件
+ var waitRemoveEl = that.findBodyItem(layid || index)
+ setTimeout(function(){
+ waitRemoveEl.remove();
+ }, 450)
thisHeaderItem.remove();
that.roll('auto', index);
@@ -415,8 +429,16 @@ layui.define('component', function(exports) {
.removeClass(component.CONST.CLASS_THIS);
// 执行标签内容切换
- that.findBodyItem(layid || index).addClass(component.CONST.CLASS_SHOW)
- .siblings().removeClass(component.CONST.CLASS_SHOW);
+ var bodyElem = that.findBodyItem(layid || index);
+ if(
+ isSupportsAnimationend
+ && options.anim
+ && layui.type(options.anim) === 'array'
+ ){
+ that.applyAnim(data.thisBodyItem, bodyElem);
+ }else{
+ bodyElem.addClass(component.CONST.CLASS_SHOW).siblings().removeClass(component.CONST.CLASS_SHOW);
+ }
that.roll('auto', index);
@@ -432,6 +454,37 @@ layui.define('component', function(exports) {
);
};
+ Class.prototype.applyAnim = function(oldActiveContentEl, activeContentEl){
+ var that = this;
+ var options = that.config;
+
+ var onlyIn = !isConnectedElement(oldActiveContentEl[0]) || oldActiveContentEl[0] === activeContentEl[0]; // 处理初始渲染
+
+ var animInClass = options.anim[0];
+ var animOutClass = options.anim[1];
+ var transitionOut = function(cb){
+ oldActiveContentEl
+ .addClass(animOutClass)
+ .one(component.CONST.ANIM_END_EVENT_NAME, function() {
+ oldActiveContentEl.removeClass(component.CONST.CLASS_SHOW).removeClass(animOutClass);
+ cb && cb();
+ })
+ }
+ var transitionIn = function(){
+ activeContentEl
+ .addClass(component.CONST.CLASS_SHOW)
+ .addClass(animInClass)
+ .one(component.CONST.ANIM_END_EVENT_NAME, function() {
+ activeContentEl.removeClass(animInClass);
+ });
+ }
+ if(onlyIn){
+ transitionIn();
+ }else{
+ transitionOut(transitionIn);
+ }
+ };
+
/**
* 渲染标签头部项
* @param {Object} opts - 标签项配置信息
@@ -703,12 +756,13 @@ layui.define('component', function(exports) {
var container = that.getContainer();
var thisHeaderItem = container.header.items.filter('.'+ component.CONST.CLASS_THIS);
var index = thisHeaderItem.index();
+ var layid = thisHeaderItem.attr('lay-id');
return {
options: options, // 标签配置信息
container: container, // 标签容器的相关元素
thisHeaderItem: thisHeaderItem, // 当前活动标签头部项
- thisBodyItem: that.findBodyItem(index), // 当前活动标签内容项
+ thisBodyItem: that.findBodyItem(layid || index), // 当前活动标签内容项
index: index, // 当前活动标签索引
length: container.header.items.length // 标签数量
};
@@ -815,5 +869,42 @@ layui.define('component', function(exports) {
component.render();
});
+ /**
+ * 检测当前环境是否支持动画结束事件
+ * @returns {boolean} - 如果支持动画结束事件则返回 true,否则返回 false
+ */
+ function supportsAnimationEnd() {
+ if(typeof AnimationEvent !== 'undefined'){
+ return true;
+ }
+
+ var el = document.createElement('div');
+ var prefixes = ['', 'webkit', 'moz', 'MS', 'o'];
+ var isSupport = prefixes.some(function(prefix){
+ var eventName = 'on' + prefix.toLowerCase() + 'animationend'
+ return eventName in el;
+ });
+ el = null;
+
+ return isSupport;
+ };
+
+ /**
+ * 检查指定元素是否已连接到文档的 DOM 树中
+ * @param {HTMLElement} element - 需要检查的 HTML 元素
+ * @returns {boolean} - 如果元素已连接到 DOM 树则返回 true,否则返回 false
+ */
+ var isConnectedElement = function(){
+ if('isConnected' in Node.prototype){
+ return function(el){
+ return el.isConnected;
+ }
+ }else{
+ return function(el){
+ return document.body.contains(el);
+ }
+ }
+ }();
+
exports(component.CONST.MOD_NAME, component);
});