• 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 });
    
    })));

    一切准备就绪后,我们可以在页面上调用插件的初始化代码,即可完成这个vue tab插件。

    new Vue({
      el:"#app",
    });
    
    $('.js-tilt').tilt({
    
    })

    源码下载链接:

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

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

    发送消息 jymm 获取 解压密码

  • 相关阅读:
    06软件构架实践阅读笔记之六
    05软件构架实践阅读笔记之五
    04软件构架实践阅读笔记之四
    03软件构架实践阅读笔记之三
    02软件构架实践阅读笔记之二
    01软件构架实践阅读笔记之一
    实训第四天
    实训第三天
    实训第二天
    实训第一天
  • 原文地址:https://www.cnblogs.com/frontworld/p/14490552.html
Copyright © 2020-2023  润新知