Vue.js 实现的 3D Tab 菜单

今天给大家带来一款基于VueJS的3D Tab菜单,它跟我们之前分享的许多CSS3 Tab菜单不同的是,它可以随着鼠标移动呈现出3D立体的视觉效果,每个tab页面还可以通过CSS自定义封面照片。它的核心是基于vue和bootstrap实现,因此扩展起来非常方便,你可以任意添加或者减少tab页面数量,同时只要更新对应tab页面的CSS代码即可,无须修改js代码。

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>

当然,你要引入vue脚本库和bootstrap脚本库:

<script src='js/vue.min.js'></script>
<script src='js/bootstrap.min.js'></script>

这个插件的核心代码如下:

/*!
 * vue-nav-tabs v0.5.7
 * (c) 2018-present cristij <joracristi@gmail.com>
 * Released under the MIT License.
 */
(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 });

})));

源码下载链接:

https://www.html5tricks.com/download/vuejs-3d-tab-menu.rar

扫描下方二维码关注公众号

发送消息 jymm 获取 解压密码

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,我可以为您提供一些参考。首先,使用 Vue 和 JavaScript 实现 tab 切换菜单的基本思路是:创建菜单组件,选择默认显示的选项卡,当点击选项卡时切换对应的内容。以下是一个简单的实例: HTML: ```html <div id="app"> <ul class="tabs"> <li v-for="(tab, index) in tabs" :key="index" @click="activeTab = tab">{{ tab }}</li> </ul> <div class="content"> <div v-for="(content, index) in contents" :key="index" v-show="activeTab === tab">{{ content }}</div> </div> </div> ``` JavaScript: ```javascript var app = new Vue({ el: '#app', data: { tabs: ['Tab 1', 'Tab 2', 'Tab 3'], contents: ['Content for Tab 1', 'Content for Tab 2', 'Content for Tab 3'], activeTab: 'Tab 1' } }); ``` CSS: ```css .tabs { list-style: none; margin: 0; padding: 0; display: flex; } .tabs li { padding: 12px 24px; background-color: #ddd; cursor: pointer; } .tabs li.active { background-color: #fff; } .content div { display: none; } .content div.show { display: block; } ``` 这个例子中,我们创建了一个菜单组件,包含选项卡和相应的内容。当用户点击选项卡时,我们使用 `v-show` 指令来切换显示对应的内容。我们还设置了一个 `activeTab` 变量来追踪当前活动的选项卡。 希望这个例子能帮到您。如果您有任何问题,请随时问我。 ### 回答2: 使用Vue和JS实现Tab切换菜单的方法如下: 1. 在HTML中定义菜单的容器,并为每个菜单项添加对应的索引值,例如: ```html <div id="app"> <ul class="menu"> <li class="menu-item" :class="{active: activeTab === 1}" @click="setActiveTab(1)">菜单1</li> <li class="menu-item" :class="{active: activeTab === 2}" @click="setActiveTab(2)">菜单2</li> <li class="menu-item" :class="{active: activeTab === 3}" @click="setActiveTab(3)">菜单3</li> </ul> <div v-show="activeTab === 1">内容1</div> <div v-show="activeTab === 2">内容2</div> <div v-show="activeTab === 3">内容3</div> </div> ``` 2. 在Vue的实例中定义相关的数据和方法,在data中添加activeTab变量,并在methods中添加setActiveTab方法,用于设置当前点击的菜单项对应的索引值,并切换显示相应的内容,例如: ```javascript new Vue({ el: '#app', data: { activeTab: 1 }, methods: { setActiveTab(tabIndex) { this.activeTab = tabIndex; } } }); ``` 3. 使用CSS样式来设置菜单项的样式和切换时的选中状态,例如: ```css .menu-item { display: inline-block; padding: 10px 20px; cursor: pointer; } .menu-item.active { background-color: #ccc; } ``` 这样,点击不同的菜单项时,对应的内容将会显示出来,并且菜单项也会有选中的样式。 ### 回答3: 使用Vue和JavaScript可以轻松实现Tab切换菜单。首先,在Vue的data属性中定义一个用于存储当前选中Tab的变量。 ``` data() { return { activeTab: 'tab1' } } ``` 然后,在HTML模板中添加一个包含Tab标题的菜单,并为每个Tab添加一个点击事件,用于切换选中的Tab。 ``` <div> <div class="tab-menu"> <button @click="activeTab = 'tab1'" :class="{ 'active': activeTab === 'tab1' }">Tab 1</button> <button @click="activeTab = 'tab2'" :class="{ 'active': activeTab === 'tab2' }">Tab 2</button> <button @click="activeTab = 'tab3'" :class="{ 'active': activeTab === 'tab3' }">Tab 3</button> </div> <div class="tab-content"> <div v-if="activeTab === 'tab1'">Tab 1的内容</div> <div v-if="activeTab === 'tab2'">Tab 2的内容</div> <div v-if="activeTab === 'tab3'">Tab 3的内容</div> </div> </div> ``` 在上述代码中,每个按钮都添加了一个点击事件来更新activeTab的值,关联了对应的Tab内容。同时,使用了Vue的条件渲染指令v-if,根据activeTab的值来显示对应的Tab内容。 最后,可以根据需要自定义样式,给当前选中的Tab按钮添加一个active类来突出显示: ``` <style> .tab-menu button.active { background-color: #ccc; } </style> ``` 以上就是使用Vue和JavaScript实现Tab切换菜单的基本步骤,通过修改activeTab的值来更新所显示的Tab内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值