【问题标题】:Potentially suffering from over optimization可能遭受过度优化
【发布时间】:2016-04-07 22:58:50
【问题描述】:

我正在制作的个人网站的首页有这样的设计:

您单击相应的图标,它会展开以显示信息。我经历了另外两个没有成功的设计,但包含了类似的动画。在每次迭代后修改 JS 后,我意识到我是:

  1. 遇到冗余代码位
  2. 开始考虑将来添加更多选项卡/部分的可能性(我可能需要扩展代码)

这引导我开始'通用化'我的代码。所以制作像'handleHorizo​​ntalTabs'或'handleVerticalTabs'这样的功能而不是'handleGamesTab'或'handlePhotographyTab'这样可以避免我为摄影选项卡编写一个全新的功能,它与游戏选项卡只有一个css属性不同。所以我在想,那么我需要传入一个对象而不是一个确切的 ID。然后我创建了一些对象文字来存储它们各自的属性。创建像这样的中性动画函数(至少在我的情况下)的问题是我必须定义函数中可以使用的每个属性即使它没有被使用。 在滚动到完整代码之前考虑一下这个 sn-p:

if (active){
    // Only animate horizontally
    $(elemToAnimate.ID).animate({
        width: elemToAnimate.ExpandedWidth,
        left: elemToAnimate.ExpandedLeft,
       right: elemToAnimate.ExpandedRight
    }, animateDuration)
}

我可能不需要在制作动画时更改 say,'left' 属性。但是因为这是一个通用功能,所以它不知道。所以,在我的对象文字中,我仍然必须定义它(它只是原始值)。我确实注意到(我没有改变游戏部分,所以你可以看到这一点)如果我只是把它从文字中去掉,它就会被标记为 undefined 并且对动画没有影响。我可以这样做,但我认为这不是一个好习惯。另外,任何阅读我的代码的人都会问该属性在哪里。

我的问题有两个:

  1. 在考虑应用的可扩展性时,我应该以编程方式考虑什么?
  2. 如何清理/优化此代码?

代码:

https://jsfiddle.net/rc6wnsst/ (PS 未优化浏览器;如果可以,请使用 Mozilla)

$(document).ready(function() {
// Definitions 
//Define object literals
var aboutmeSection = {id: '#aboutme-section', get ID() {return this.id;},
                      selector: '#person-icon', get Selector() {return this.selector;},
                      origWidth: $('#aboutme-section').css('width'), get OrigWidth() {return this.origWidth;},
                      origLeft: $('#aboutme-section').css('left'), get OrigLeft() {return this.origLeft;},
                      origRight: 'auto', get OrigRight() {return this.origRight;},
                      origHeight: $('#aboutme-section').css('height'), get OrigHeight() {return this.origHeight;},
                      origTop: $('#aboutme-section').css('top'), get OrigTop() {return this.origTop;},
                      origBottom: $('#aboutme-section').css('bottom'), get OrigBottom() {return this.origBottom;},
                      expandedWidth: '65%', get ExpandedWidth() {return this.expandedWidth;},
                      expandedLeft: $('#aboutme-section').css('left'), get ExpandedLeft() {return this.expandedLef;},
                      expandedRight: $('#aboutme-section').css('right'), get ExpandedRight() {return this.expandedRight;},
                      expandedHeight: '450px', get ExpandedHeight() {return this.expandedHeight;},
                      expandedTop: '65%', get ExpandedTop() {return this.expandedTop;},
                      expandedBottom: $('#aboutme-section').css('bottom'), get ExpandedBottom() {return this.expandedBottom;}};

var photographySection = {id: '#photography-tab', get ID() {return this.id;},
                           selector: '#camera-icon', get Selector() {return this.selector;},
                           origWidth: $('#photography-tab').css('width'), get OrigWidth() {return this.origWidth;},
                           origLeft: 'auto', get OrigLeft() {return this.origLeft;},
                           origRight: $('#photography-tab').css('right'), get OrigRight() {return this.origRight;},
                           expandedWidth: '40%', get ExpandedWidth() {return this.expandedWidth;},
                           expandedLeft: 'auto', get ExpandedLeft() {return this.expandedLeft;},
                           expandedRight: $('#photography-tab').css('right'), get ExpandedRight() {return this.expandedRight;}};

var gamesSection = {id: '#games-tab', get ID() {return this.id;},
                          selector: '#gamepad-icon', get Selector() {return this.selector;},
                          origWidth: $('#games-tab').css('width'), get OrigWidth() {return this.origWidth;},
                          origLeft: $('#games-tab').css('left'), get OrigLeft() {return this.origLeft;},
                          expandedWidth: '40%', get ExpandedWidth() {return this.expandedWidth;}};

处理程序

    // Handles aboutme section functionality
    function handleAboutMeSection(elemToAnimate, selectedElem, active, animateDuration=500, fadeInDuration=500, fadeOutDuration=250){
        // First click
        if (active){
            // Animate vertically first
            $(elemToAnimate.ID).animate({height: elemToAnimate.ExpandedHeight,
                                     top: elemToAnimate.ExpandedTop,
                                     bottom: elemToAnimate.OrigBottom}, animateDuration);
            // Animate horizontally second
            $(elemToAnimate.ID).animate({width: elemToAnimate.ExpandedWidth,
                                 left: elemToAnimate.ExpandedLeft,
                                 right: elemToAnimate.ExpandedRight}, animateDuration)


            // Fade in content and remove active class
            $(elemToAnimate.ID).find(".content").fadeIn(fadeInDuration);
            $(selectedElem).removeClass('active');
        // Second click
        } else {
            // Fade out content
            $(elemToAnimate.ID).find(".content").fadeOut(fadeOutDuration, function(){
                // Animate horizontally first
                $(elemToAnimate.ID).animate({width: elemToAnimate.OrigWidth,
                                         left: elemToAnimate.OrigLeft,
                                         right: elemToAnimate.OrigRight}, animateDuration);
                // Animate vertically second
                $(elemToAnimate.ID).animate({height: elemToAnimate.OrigHeight,
                                          top: elemToAnimate.OrigTop,
                                          bottom: elemToAnimate.OrigBottom}, animateDuration)
            });
            // Add active class back in
            $(selectedElem).addClass('active');
        }
    }

    //Handles photography tab functionality
    function handleTabs(elemToAnimate, selectedElem, active, animateDuration=500, fadeInDuration=500, fadeOutDuration=250){
        // First click
        if (active){
            // Only animate horizontally
            $(elemToAnimate.ID).animate({width: elemToAnimate.ExpandedWidth,
                                  left: elemToAnimate.ExpandedLeft,
                                  right: elemToAnimate.ExpandedRight}, animateDuration)

            // Fade in content and remove active class
            $(elemToAnimate.ID).find(".content").fadeIn(fadeInDuration);
            $(selectedElem).removeClass('active');
        // Second click
        } else {
            // Fade out content and only animate horizontally
            $(elemToAnimate.ID).find(".content").fadeOut(fadeOutDuration, function(){
                $(elemToAnimate.ID).animate({width: elemToAnimate.OrigWidth,
                                         left: elemToAnimate.OrigLeft,
                                         right: elemToAnimate.OrigRight}, animateDuration);
            });
            // Add active class back in
            $(selectedElem).addClass('active');
        }
    }

主要

    //Hide content initially
    $(".content").hide();
    //Handle click events
    $(".image").click(function() {
        //On first click
        if ($(this).hasClass("active")) {
            switch($(this).attr('id')) {
                case 'person-icon':
                handleAboutMeSection(aboutmeSection, aboutmeSection.Selector, true);
                break;

                case 'gamepad-icon':
                handleTabs(gamesSection, gamesSection.Selector, true);
                break;

                case 'camera-icon':
                handleTabs(photographySection, photographySection.Selector, true);
                break;

                default:
                break;
            }
        // On second click
        } else {
            switch($(this).attr('id')) {
                case 'person-icon':
                handleAboutMeSection(aboutmeSection, aboutmeSection.Selector, false);
                break;

                case 'gamepad-icon':
                handleTabs(gamesSection, gamesSection.Selector, false);
                break;

                case 'camera-icon':
                handleTabs(photographySection, photographySection.Selector, false);
                break;

                default:
                break;
            }
        }
    });
});

【问题讨论】:

标签: javascript jquery performance animation optimization


【解决方案1】:

你是对的。它有一种 TL:DR 的感觉。

但是我想我知道你正在经历什么,所以让我来解决你的问题并浏览一下代码:

1) 在考虑应用的可扩展性时,我应该以编程方式考虑什么?如何清理/改进此代码?

这取决于您打算用它做什么。您是否需要实现更多块,也许将选项卡和块一起使用以获得多个不同的选项?比这更重要的是,你是否希望几年后回来,不要打自己的额头,然后是的,你可能可以重构一下。

在我开始之前,请注意这一点。每个程序员都是不同的,所以我的重构示例可能不会与其他人有共鸣。如果代码有效,那就是#right#。只有有错误的代码是#wrong#。再说一次,我的另一种看法。

所以,我要做的是创建一个设置文字对象来处理包含您需要的部分的部分。让我们以我为例。以下是我将如何处理 js 代码:

// Definitions ----------------------------------------
var section =  {
    elem: null,
    selector: null,
    content: null,
    opened: false,
    origDim: { },
    currDim: { },
    expdDim: { },
    setup: function(settings) { // gets the settings and sets up the initial position
        var self = this;
        if (    !(
                self.is_set(settings) ||
                self.is_set(settings.elem) ||
                self.is_set(settings.selector) ||
                self.is_set(settings.content)
            )
        )
        {
            console.log('Your settings must send out an element, a content and a selector');
        } else {
            self.elem = settings.elem;
            self.selector = settings.selector;
            self.content = settings.content;
            self.origDim = self.getPosition();
        }
        return self; // this allows chaining
    },
    // Sets up the range of motion the section will have
    setRange: function(expdDim) {
        var self = this;
        if ( !(self.is_set(expdDim)) ) {
            console.log('You have to provide a set of new positions.')
        } else {
            self.expdDim = {
                width: (self.is_set(expdDim.width)?expdDim.width:self.currDim.width),
                height: (self.is_set(expdDim.height)?expdDim.height:self.currDim.height),
                top: (self.is_set(expdDim.top)?expdDim.top:self.currDim.top),
                right: (self.is_set(expdDim.right)?expdDim.right:self.currDim.right),
                bottom: (self.is_set(expdDim.bottom)?expdDim.bottom:self.currDim.bottom),
                left: (self.is_set(expdDim.left)?expdDim.left:self.currDim.left)
            };
        }
        return self; // this allows chaining
    },
    // Toggles from opened to close by listening to a property opened
    toggle: function(animTime, fadeInTime, fadeOutTime) {
        var self = this;
        if (self.opened) self.close(animTime, fadeOutTime);
        else self.open(self.expdDim, animTime, fadeInTime);
        return self; // this allows chaining
    },
    // Expands the section
    open: function(newDim, animTime, fadeInTime) {
        var self = this;
        if (    !(self.is_set(newDim)) )    console.log('You must send new dimensions!');
        else {
            var elem = $(self.elem);
            elem
                .animate(self.optionsVert(newDim), animTime)
                .animate(self.optionsHorz(newDim), animTime)
                .promise().done( function() {
                  $(this).find(self.content).fadeIn(fadeInTime)
                    self.currDim = self.getPosition();
                    self.opened = true;
                    });
        }
        return self; // this allows chaining
    },
    // Closes the section
    close: function(animTime, fadeOutTime) {
        var self = this;
        var elem = $(self.elem);
        // first fade
        elem.find(self.content)
            .fadeOut(fadeOutTime)
            .promise()
            .done(function(){
                elem
                    .animate(self.optionsHorz(self.origDim), animTime)
                    .animate(self.optionsVert(self.origDim), animTime)
                    .promise()
                    .done( function() {
                        self.currDim = self.getPosition();
                        self.opened = false;
                    });
            });
        return self; // this allows chaining
    },
    // HELPER FUNCTIONS - these do not allow chaining - used as private functions
    // Sets up original dimensions based on the element 
    getPosition: function() {
        var self = this;
        var offset = $(self.elem).offset();
        var posDim = {
            width: $(self.elem).width()+'px',
            height: $(self.elem).height()+'px',
            top: offset.top+'px',
            right: parseInt(offset.left)+parseInt($(self.elem).width())+'px',
            bottom: parseInt(offset.top)+parseInt($(self.elem).height())+'px',
            left: offset.left+'px'
        };
        return posDim;
    },
    // validates if a given variable is set
    is_set: function(vary) {
        return (typeof vary != 'undefined');
    },
    // returns a subset of dimension variables belonging to the X plane
    optionsHorz: function(newDim) {
        return {
            width: newDim.width,
            left: newDim.left,
            right: newDim.right
        };
    },
    // returns a subset of dimension variables belonging to the Y plane
    optionsVert: function(newDim) {
        return {
            height: newDim.height,
            top: newDim.top,
            bottom: newDim.bottom
        };
    }
};

// Definitions ----------------------------------------

$(document).ready(function() {

    // Setting up section about me
    var aboutme = section;
    aboutme.setup({
        elem: '#aboutme-section',
        selector: '#person-icon',
        content: '.content'
    }).setRange({
        width: '65%',
        height: '450px',
        top: '65%'
    });

    //Hide content initially
    $(".content").hide();
    //Handle click events
    $(".image").click(function() {
        switch($(this).attr('id')) {
            case 'person-icon':
                aboutme.toggle(500,500,250);
                break;

            default:
                break;
        }
    });

});

这是您可以模板化您的部分的方式。您可以扩充此块以增强其功能,但是,如果您看一下,设置会变得非常简单。

希望这会有所帮助。

【讨论】:

  • 对不起,我花了这么长时间才回复这个很好的答案。真的很惊讶你特意写了这一切。这绝对是我试图实现的那种通用解决方案。虽然我最终更改了网站的设计(此处为新设计:www.domsilva.com),但我将把这段代码存储起来,因为我以后肯定会需要它。再次感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-02
相关资源
最近更新 更多