Jquery 创建插件 - 如何在动画回调方法中获取对我的对象的 $(this) 引用?

Posted

技术标签:

【中文标题】Jquery 创建插件 - 如何在动画回调方法中获取对我的对象的 $(this) 引用?【英文标题】:Jquery create plugin - how get $(this) reference to my object in animation callback method? 【发布时间】:2012-06-20 23:06:17 【问题描述】:

我有以下问题: 我正在创建一个简单的插件 jquery carousel,用于为框中的图像设置动画。 但是我遇到了以下问题,使用OO构建插件,我使用this引用我的对象。

但是其中一个函数使用了 jquery 的 animate 方法。 我想以递归的方式做到这一点。因为动画是一个常数。 但是在回调函数jquery animate中,this指的是对象动画,而不是我在插件本身执行时创建的对象。 如何在动画中调用函数回调对象? 我尝试解决在动画行代码之前直接调用函数 run(),但出现错误:Uncaught RangeError: Maximum call stack size exceeded

如果我将运行函数放在动画回调中,我得到了其他错误:

this.wraper.animate(
                   top : '-='+image_height,
                        this.config.speed,
                        this.run()
                   )

 this.wraper.animate(
                   top : '-='+image_height,
                        this.config.speed,
                        function()
                            this.run();
                        
                   )

因为 this 是对 this.wraper 对象的引用,而不是对函数对象的引用 似乎有点难以解释。

代码如下:

  (function($)

carouselClass = function (el, opts) 
        this.id = $(el).attr("id");        
        this.i = 0;                     
        this.config = opts;
        this.wraper;
        this.count = 0;
        this.flag = true;
        this.init = function() 
            // variables        
            var img = $("#"+this.id).find("img");       
            this.count = img.length;


            //adiciona css necessário a div wrapper
            $("#"+this.id).css("position","relative");
            $("#"+this.id).css("overflow","hidden");


            $("#"+this.id).append("<div></div>");       
            this.wraper = $("#"+this.id).find("div");           


            //definições css da div wrapper
            this.wraper.css("position","absolute");
            this.wraper.css("right","0px");                 
            var direction = this.config.direction;
            var container = this.wraper;
            //adiciona as imagens ao novo container
            img.each(function()
                if(direction == "left" || direction == "right")
                    $(this).css("float", direction)

                $(this).css("display", "block");
                container.append($(this));                                          
            ); 

            //seta a largura e altura do wraper conforme a direção
            if(direction == "left" || direction == "right")
            
                this.wraper.width((img.width()*this.count));
                this.wraper.height(img.height());
            
            else
            
                this.wraper.height((img.height()*this.count));
                this.wraper.width(img.width());
            

            this.run(); 
                   



        this.run = function()
                           
            if(this.config.direction == "top")
            
                height = this.wraper.height();              
                image_height = height/this.count;                   
                for(j = 0; j < (this.count-1); j++)
                
                    this.wraper.animate(
                        top : '-='+image_height,
                        this.config.speed

                    )

                
                this.wraper.animate(
                    top:'0px',
                    this.config.speed   
                );
                                    //my problem is this call to function recursive
                this.run();             
            

            if(this.config.direction == "bottom")
            
                height = this.wraper.height();              
                image_height = height/this.count;                   
                for(j = 0; j < (this.count-1); j++)
                
                    this.wraper.animate(
                        bottom : '-='+image_height,
                        this.config.speed

                    )

                
                this.wraper.animate(
                    bottom:'0px',
                    this.config.speed   
                );
                                    //my problem is this call to function recursive
                this.run();         
            


            if(this.config.direction == "left")
            
                width = this.wraper.width();                
                image_width = width/this.count;                 
                for(j = 0; j < (this.count-1); j++)
                
                    this.wraper.animate(
                        left : '-='+image_width,
                        this.config.speed

                    )

                
                this.wraper.animate(
                    left:'0px',
                    this.config.speed   
                );
                                    //my problem is this call to function recursive
                this.run();         
            

            if(this.config.direction == "right")
            
                width = this.wraper.width();                
                image_width = width/this.count;                 
                for(j = 0; j < (this.count-1); j++)
                
                    this.wraper.animate(
                        right : '-='+image_width,
                        this.config.speed

                    )

                
                this.wraper.animate(
                    right:'0px',
                    this.config.speed   
                );
                                    //my problem is this call to function recursive
                this.run();         
            

                   
    ;

$.fn.carousel = function(options)


    var opts = $.extend(, $.fn.carousel.defaults, options);
    return this.each(function () 
            var instance = new carouselClass($(this), opts);
            /**********Start the carousel ***********/
            instance.init();                
        );

    $.fn.carousel.defaults = 
        'direction': "left",
        'speed': 3000
    ;      

    

 )(jQuery);

如果我以这种方式调用,插件工作,但只有我设置为 i var 的次数,我希望轮播不断变化:

this.prepare = function()
        
            this.i++;

            if(this.i < 3)
                               

                    this.run();
                                       
        

this.run = function()
                           
            if(this.config.direction == "top")
            
                height = this.wraper.height();              
                image_height = height/this.count;                   
                for(j = 0; j < (this.count-1); j++)
                
                    this.wraper.animate(
                        top : '-='+image_height,
                        this.config.speed

                    )

                
                this.wraper.animate(
                    top:'0px',
                    this.config.speed   
                );
                this.prepare();             
            
                 

【问题讨论】:

【参考方案1】:
this.setAnimation=function(state)
        var obj=this;
        if(state)obj.animationID=setInterval(function()  
            obj.run();
            ,obj.config.speed);
        else 
            clearInterval(obj.animationID);
            
        ;

你在寻找这样的东西吗?

setAnimation(true)//start Animation

【讨论】:

【参考方案2】:

尝试使用$.proxy()

this.wraper.animate(
                   top : '-='+image_height,
                        this.config.speed,
                        $.proxy(function()
                            this.run();
                        , this)
                   )

【讨论】:

【参考方案3】:

一般来说,在 jQuery 插件中,使用“this”会让人感到困惑。原因正是您所指出的 - “this”完全依赖于上下文,并且因为 jQuery 通常依赖于链接 - 关键字的值经常变化。

我的建议是——而不是使用“this”——利用闭包的力量。一个例子:

// Instead of...
carouselClass = function (el, opts) 
    this.id = $(el).attr("id");        
    this.i = 0;                     
    this.config = opts;
    this.wraper;
    this.count = 0;
    this.flag = true;
    this.init = function() 
    
;

// Try...
var Carousel = function (el, options) 
    var $el = (el instanceOf jQuery) ? el : $(el);

    var defaults = 
        id: $(el).attr('id'),
        currentIndex: 0,
        count: 0,
        flag: true,
        config: 
            // Defaults go here.
        
    ;
    var settings = $.extend(true, , defaults, options);

    var fx = 
        init: function (newOptions) 
            settings = $.extend(true, , defaults, settings, newOptions);
            // Now you can do other init stuff based on settings.Option,
            // rather than caring what "this" is equal to.
        ,
        run: function () 
            // Note that you can use settings.Option here, too - and again,
            // you don't care what "this" is, because settings is within
            // an outer closure.
        ,
        prepare: function () 
            // Do your prepare stuff here.
        
    ;
;

使用这种意识形态 - 你的功能可能会变成这样:

run = function() 
    if (settings.config.direction == "top") 
        var height = settings.wrapper.height();
        var image_height = height / settings.count;

        for(j = 0; j < (settings.count - 1); j++) 
            settings.wrapper.animate(
                 top: '-=' + image_height ,
                settings.config.speed
            )
        

        settings.wrapper.animate( top: '0px' , settings.config.speed);
        fx.prepare();
    

我还应该注意 - 此代码不打算用作直接的、有效的替代您的代码 - 只是一个示例,说明如何通过构造代码以利用闭包来避免“这个”问题,以及基于闭包的设置机制。

【讨论】:

感谢您的出色解释。但是我现在需要的任务很简单,我使用第三个答案并且它有效,未来将改进插件以用于其他项目,这个使用你的技巧和clousure

以上是关于Jquery 创建插件 - 如何在动画回调方法中获取对我的对象的 $(this) 引用?的主要内容,如果未能解决你的问题,请参考以下文章

带有方法和全局回调的 jQuery 插件

如何编写一个接受回调函数的 jQuery 插件方法?

jquery动画回调 - 如何将参数传递给回调

前端与移动开发项目二

如何设置回调以运行 *before* jquery 动画?

jquery插件-fullpage.js