这是一款Vue.js实现的3D悬停Tab菜单,设计新颖创新,鼠标悬停展示3d卡片效果,三个Tab标签可切换,非常适合用于展示您的产品,作品等,喜欢不要犹豫。它的核心是基于vue和bootstrap实现,因此扩展起来非常方便,你可以任意添加或者减少tab页面数量,同时只要更新对应tab页面的CSS代码即可,无须修改js代码。
需要引入Vue.js库与bootstrap.js库
<script src='js/vue.min.js'></script>
<script src='js/bootstrap.min.js'></script>
HTML代码:
<div id="app-container" data-tilt >
<div id="app">
<vue-tabs id="tabs">
<v-tab title="First Tab" class="tab" :selected="true">
<div class="tab-content">
<div class="tab-image first-image"></div>
<div class="tab-content-text">
<h1>First Header</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt nu aliqua. Sollicit udin purus faucibus ornare aliquam ultrices sagittis orci a scelerisque a consectetur atna purus.</p>
</div>
</div>
</v-tab>
<v-tab title="Second Tab" class="tab">
<div class="tab-content">
<div class="tab-image second-image"></div>
<div class="tab-content-text">
<h1>Second Header</h1>
<p>Ac tortor vitae purus faucibus ornare suspendisse uis tristique sed nisi. Consectetur libero id ax faucibus in ornare faucibus nislt udin purus fi faucibus ac ornare aliquam ultrices in purus faucibu.</p>
</div>
</div>
</v-tab>
<v-tab title="Third Tab" class="tab">
<div class="tab-content">
<div class="tab-image third-image"></div>
<div class="tab-content-text">
<h1>Third Header</h1>
<p>Scelerisque fermentum dui faucibus in ornare id. Amet consectetur adipiscing elit duis tristique sollicitudin purus faucibus ornare aliquam ultrices sagittis nibh elit duis nubro tristique itae purus faucibus.</p>
</div>
</div>
</v-tab>
</vue-tabs>
</div>
</div>
核心代码插件:
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.vueTabs = {})));
}(this, (function (exports) { 'use strict';
var nestRE = /^(attrs|props|on|nativeOn|class|style|hook)$/;
var babelHelperVueJsxMergeProps = function mergeJSXProps(objs) {
return objs.reduce(function (a, b) {
var aa, bb, key, nestedKey, temp;
for (key in b) {
aa = a[key];
bb = b[key];
if (aa && nestRE.test(key)) {
// normalize class
if (key === 'class') {
if (typeof aa === 'string') {
temp = aa;
a[key] = aa = {};
aa[temp] = true;
}
if (typeof bb === 'string') {
temp = bb;
b[key] = bb = {};
bb[temp] = true;
}
}
if (key === 'on' || key === 'nativeOn' || key === 'hook') {
// merge functions
for (nestedKey in bb) {
aa[nestedKey] = mergeFn(aa[nestedKey], bb[nestedKey]);
}
} else if (Array.isArray(aa)) {
a[key] = aa.concat(bb);
} else if (Array.isArray(bb)) {
a[key] = [aa].concat(bb);
} else {
for (nestedKey in bb) {
aa[nestedKey] = bb[nestedKey];
}
}
} else {
a[key] = b[key];
}
}
return a;
}, {});
};
function mergeFn(a, b) {
return function () {
a.apply(this, arguments);
b.apply(this, arguments);
};
}
var VueTabs = {
name: 'vue-tabs',
props: {
activeTabColor: String,
activeTextColor: String,
disabledColor: String,
disabledTextColor: String,
/**
* Tab title position: center | bottom | top
*/
textPosition: {
type: String,
default: 'center'
},
/**
* Tab type: tabs | pills
*/
type: {
type: String,
default: 'tabs'
},
direction: {
type: String,
default: 'horizontal'
},
/**
* Centers the tabs and makes the container div full width
*/
centered: Boolean,
value: [String, Number, Object]
},
data: function data() {
return {
activeTabIndex: 0,
tabs: []
};
},
computed: {
isTabShape: function isTabShape() {
return this.type === 'tabs';
},
isStacked: function isStacked() {
return this.direction === 'vertical';
},
classList: function classList() {
var navType = this.isTabShape ? 'nav-tabs' : 'nav-pills';
var centerClass = this.centered ? 'nav-justified' : '';
var isStacked = this.isStacked ? 'nav-stacked' : '';
return 'nav ' + navType + ' ' + centerClass + ' ' + isStacked;
},
stackedClass: function stackedClass() {
return this.isStacked ? 'stacked' : '';
},
activeTabStyle: function activeTabStyle() {
return {
backgroundColor: this.activeTabColor,
color: this.activeTextColor
};
}
},
methods: {
navigateToTab: function navigateToTab(index, route) {
this.changeTab(this.activeTabIndex, index, route);
},
activateTab: function activateTab(index) {
this.activeTabIndex = index;
var tab = this.tabs[index];
tab.active = true;
this.$emit('input', tab.title);
},
changeTab: function changeTab(oldIndex, newIndex, route) {
var oldTab = this.tabs[oldIndex] || {};
var newTab = this.tabs[newIndex];
if (newTab.disabled) return;
this.activeTabIndex = newIndex;
oldTab.active = false;
newTab.active = true;
this.$emit('input', this.tabs[newIndex].title);
this.$emit('tab-change', newIndex, newTab, oldTab);
this.tryChangeRoute(route);
},
tryChangeRoute: function tryChangeRoute(route) {
if (this.$router && route) {
this.$router.push(route);
}
},
addTab: function addTab(item) {
var index = this.$slots.default.indexOf(item.$vnode);
this.tabs.splice(index, 0, item);
},
removeTab: function removeTab(item) {
var tabs = this.tabs;
var index = tabs.indexOf(item);
if (index > -1) {
tabs.splice(index, 1);
}
},
getTabs: function getTabs() {
if (this.$slots.default) {
return this.$slots.default.filter(function (comp) {
return comp.componentOptions;
});
}
return [];
},
findTabAndActivate: function findTabAndActivate(tabNameOrIndex) {
var indexToActivate = this.tabs.findIndex(function (tab, index) {
return tab.title === tabNameOrIndex || index === tabNameOrIndex;
});
if (indexToActivate === this.activeTabIndex) return;
if (indexToActivate !== -1) {
this.changeTab(this.activeTabIndex, indexToActivate);
} else {
this.changeTab(this.activeTabIndex, 0);
}
},
renderTabTitle: function renderTabTitle(index) {
var position = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top';
var h = this.$createElement;
if (this.tabs.length === 0) return;
var tab = this.tabs[index];
var active = tab.active,
title = tab.title;
var titleStyles = { color: this.activeTabColor };
if (position === 'center') titleStyles.color = this.activeTextColor;
var simpleTitle = h(
'span',
{ 'class': 'title title_' + position, style: active ? titleStyles : {} },
[position === 'center' && this.renderIcon(index), title]
);
if (tab.$slots.title) return tab.$slots.title;
if (tab.$scopedSlots.title) return tab.$scopedSlots.title({
active: active,
title: title,
position: position,
icon: tab.icon,
data: tab.tabData
});
return simpleTitle;
},
renderIcon: function renderIcon(index) {
var h = this.$createElement;
if (this.tabs.length === 0) return;
var tab = this.tabs[index];
var icon = tab.icon;
var simpleIcon = h(
'i',
{ 'class': icon },
['\xA0']
);
if (!tab.$slots.title && icon) return simpleIcon;
},
tabStyles: function tabStyles(tab) {
if (tab.disabled) {
return {
backgroundColor: this.disabledColor,
color: this.disabledTextColor
};
}
return {};
},
renderTabs: function renderTabs() {
var _this = this;
var h = this.$createElement;
return this.tabs.map(function (tab, index) {
if (!tab) return;
var route = tab.route,
id = tab.id,
title = tab.title,
icon = tab.icon,
tabId = tab.tabId;
var active = _this.activeTabIndex === index;
return h(
'li',
babelHelperVueJsxMergeProps([{
attrs: { name: 'tab',
id: 't-' + tabId,
'aria-selected': active,
'aria-controls': 'p-' + tabId,
role: 'tab' },
'class': ['tab', { active: active }, { disabled: tab.disabled }],
key: title }, {
on: {
'click': function click($event) {
for (var _len = arguments.length, attrs = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
attrs[_key - 1] = arguments[_key];
}
(function () {
return !tab.disabled && _this.navigateToTab(index, route);
}).apply(undefined, [$event].concat(attrs));
}
}
}]),
[_this.textPosition === 'top' && _this.renderTabTitle(index, _this.textPosition), h(
'a',
babelHelperVueJsxMergeProps([{
attrs: { href: '#',
role: 'tab' },
style: active ? _this.activeTabStyle : _this.tabStyles(tab),
'class': [{ 'active_tab': active }, 'tabs__link'] }, {
on: {
'click': function click($event) {
for (var _len2 = arguments.length, attrs = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
attrs[_key2 - 1] = arguments[_key2];
}
(function (e) {
e.preventDefault();
return false;
}).apply(undefined, [$event].concat(attrs));
}
}
}]),
[_this.textPosition !== 'center' && !tab.$slots.title && _this.renderIcon(index), _this.textPosition === 'center' && _this.renderTabTitle(index, _this.textPosition)]
), _this.textPosition === 'bottom' && _this.renderTabTitle(index, _this.textPosition)]
);
});
}
},
render: function render() {
var h = arguments[0];
var tabList = this.renderTabs();
return h(
'div',
{ 'class': ['vue-tabs', this.stackedClass] },
[h(
'div',
{ 'class': [{ 'nav-tabs-navigation': !this.isStacked }, { 'left-vertical-tabs': this.isStacked }] },
[h(
'div',
{ 'class': ['nav-tabs-wrapper', this.stackedClass] },
[h(
'ul',
{ 'class': this.classList, attrs: { role: 'tablist' }
},
[tabList]
)]
)]
), h(
'div',
{ 'class': ['tab-content', { 'right-text-tabs': this.isStacked }] },
[this.$slots.default]
)]
);
},
watch: {
tabs: function tabs(newList) {
if (newList.length > 0 && !this.value) {
if (newList.length <= this.activeTabIndex) {
this.activateTab(this.activeTabIndex - 1);
} else {
this.activateTab(this.activeTabIndex);
}
}
if (newList.length > 0 && this.value) {
this.findTabAndActivate(this.value);
}
},
value: function value(newVal) {
this.findTabAndActivate(newVal);
}
}
};
var VTab = {
name: 'v-tab',
props: {
title: {
type: String,
default: ''
},
icon: {
type: String,
default: ''
},
tabData: {
default: null
},
/***
* Function to execute before tab switch. Return value must be boolean
* If the return result is false, tab switch is restricted
*/
beforeChange: {
type: Function
},
id: String,
route: {
type: [String, Object]
},
disabled: Boolean,
transitionName: String,
transitionMode: String
},
computed: {
isValidParent: function isValidParent() {
return this.$parent.$options.name === 'vue-tabs';
},
hash: function hash() {
return '#' + this.id;
},
tabId: function tabId() {
return this.id ? this.id : this.title;
}
},
data: function data() {
return {
active: false,
validationError: null
};
},
mounted: function mounted() {
this.$parent.addTab(this);
},
destroyed: function destroyed() {
if (this.$el && this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el);
}
this.$parent.removeTab(this);
},
render: function render() {
var h = arguments[0];
return h(
'section',
{ 'class': 'tab-container',
attrs: { id: 'p-' + this.tabId,
'aria-labelledby': 't-' + this.tabId,
role: 'tabpanel' },
directives: [{
name: 'show',
value: this.active
}]
},
[this.$slots.default]
);
}
};
var VueTabsPlugin = {
install: function install(Vue) {
Vue.component('vue-tabs', VueTabs);
Vue.component('v-tab', VTab);
}
};
// Automatic installation if Vue has been added to the global scope.
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(VueTabsPlugin);
window.VueTabs = VueTabsPlugin;
}
exports['default'] = VueTabsPlugin;
exports.VueTabs = VueTabs;
exports.VTab = VTab;
Object.defineProperty(exports, '__esModule', { value: true });
})));
我们可以在页面上调用插件的初始化代码,即可完成这个vue tab插件。
new Vue({
el:"#app",
});
$('.js-tilt').tilt({
})
评论前必须登录!
注册