可能遭受过度优化

时间:2016-04-07 22:58:50

标签: javascript jquery performance animation optimization

好的,所以我将这个设计用于我正在制作的个人网站的首页:

initial state -------------------------------------------------- -------------------------------------------------- ------------------------------- /

final state

基本上,您点击相应的图标,它会展开以显示信息。很简单。 我经历了另外两个没有成功的设计,但也加入了类似的动画。在每次迭代后修改JS后,我意识到我是: 1)遇到冗余代码 2)开始考虑将来添加更多标签/部分的可能性(我可能需要进行代码扩展)

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

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

动画制作时,我可能不需要更改说明,'左'属性。但因为这是一般功能,所以不知道。因此,在我的对象文字中,我仍然需要定义它(它只是原始值)。我注意到了(我把游戏部分保持不变,所以你可以看到这个)如果我只是将它从文字中删除,它会被标记为未定义并且对动画没有影响。我能做到这一点,但我认为这不是好的做法。再加上阅读我的代码的人都会问这个属性在哪里。

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

代码:

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

$(document).ready(function() {

//定义-------------------------------------------- -------------------------------------------------- ---------

//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');
    }
}

// Main -------------------------------------------- -------------------------------------------------- ----------------

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

});

我知道这很多,所以感谢任何愿意帮助的人。

1 个答案:

答案 0 :(得分:1)

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

但是,我想我知道你正在经历的事情,所以让我解决你的问题,只是瞥见代码:

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

这取决于您打算如何处理它。你需要实现更多的块,也许使用标签和块一起有几个不同的选项?比这更重要的是,你想在几年之后回来并且不要在额头上打自己,然后是的,你可能会稍微重构一下。

在我开始之前,请带上一粒盐。每个程序员都是不同的,所以我的重构示例可能不会与其他人混在一起。如果代码有效,那就是#right#。只有错误的代码是#error#。再次,我的另一种意见。

所以,我要做的是创建一个设置文字对象来处理包含你需要的部分。让我们以我为例。以下是我将如何处理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;
        }
    });

});

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

希望这有帮助。