如何让狭缝滑块在幻灯片完成后停止

How can i get Slit Slider to stop after the slides are finished?

本文关键字:幻灯片      更新时间:2023-09-26

我正在为项目使用狭缝滑块 (http://tympanus.net/codrops/2012/06/05/fullscreen-slit-slider-with-jquery-and-css3/),我希望滑块在最后一张幻灯片后停止,同时保持箭头仍然处于活动状态。我对jquery编码相当陌生,所以如果有人能提供帮助,它将不胜感激。http://jsfiddle.net/totimage/y40wy5uf/

这是我目前使用的代码:

;( function( $, window, undefined ) {
    'use strict';
    var $event = $.event,
    $special,
    resizeTimeout;
    $special = $event.special.debouncedresize = {
        setup: function() {
            $( this ).on( "resize", $special.handler );
        },
        teardown: function() {
            $( this ).off( "resize", $special.handler );
        },
        handler: function( event, execAsap ) {
            // Save the context
            var context = this,
                args = arguments,
                dispatch = function() {
                    // set correct event type
                    event.type = "debouncedresize";
                    $event.dispatch.apply( context, args );
                };
            if ( resizeTimeout ) {
                clearTimeout( resizeTimeout );
            }
            execAsap ?
                dispatch() :
                resizeTimeout = setTimeout( dispatch, $special.threshold );
        },
        threshold: 20
    };
    // global
    var $window = $( window ),
        $document = $( document ),
        Modernizr = window.Modernizr;
    $.Slitslider = function( options, element ) {
        this.$elWrapper = $( element );
        this._init( options );
    };
    $.Slitslider.defaults = {
        // transitions speed
        speed : 1500,
        // if true the item's slices will also animate the opacity value
        optOpacity : true,
        // amount (%) to translate both slices - adjust as necessary
        translateFactor : 230,
        // maximum possible angle
        maxAngle : 25,
        // maximum possible scale
        maxScale : 2,
        // slideshow on / off
        autoplay : true,
        // keyboard navigation
        keyboard : false,
        // time between transitions
        interval : 500,
        // callbacks
        onBeforeChange : function( slide, idx ) { return true; },
        onAfterChange : function( slide, idx ) { return false; }
    };
    $.Slitslider.prototype = {
        _init : function( options ) {
            // options
            this.options = $.extend( true, {}, $.Slitslider.defaults, options );
            // https://github.com/twitter/bootstrap/issues/2870
            this.transEndEventNames = {
                'WebkitTransition' : 'webkitTransitionEnd',
                'MozTransition' : 'transitionend',
                'OTransition' : 'oTransitionEnd',
                'msTransition' : 'MSTransitionEnd',
                'transition' : 'transitionend'
            };
            this.transEndEventName = this.transEndEventNames[ Modernizr.prefixed( 'transition' ) ];
            // suport for css 3d transforms and css transitions
            this.support = Modernizr.csstransitions && Modernizr.csstransforms3d;
            // the slider
            this.$el = this.$elWrapper.children( '.sl-slider' );
            // the slides
            this.$slides = this.$el.children( '.sl-slide' ).hide();
            // total slides
            this.slidesCount = this.$slides.length;
            // current slide
            this.current = 0;
            // control if it's animating
            this.isAnimating = false;
            // get container size
            this._getSize();
            // layout
            this._layout();
            // load some events
            this._loadEvents();
            // slideshow
            if( this.options.autoplay ) {
                this._startSlideshow();
            }
        },
        // gets the current container width & height
        _getSize : function() {
            this.size = {
                width : this.$elWrapper.outerWidth( true ),
                height : this.$elWrapper.outerHeight( true )
            };
        },
        _layout : function() {
            this.$slideWrapper = $( '<div class="sl-slides-wrapper" />' );
            // wrap the slides
            this.$slides.wrapAll( this.$slideWrapper ).each( function( i ) {
                var $slide = $( this ),
                    // vertical || horizontal
                    orientation = $slide.data( 'orientation' );
                $slide.addClass( 'sl-slide-' + orientation )
                      .children()
                      .wrapAll( '<div class="sl-content-wrapper" />' )
                      .wrapAll( '<div class="sl-content" />' );
            } );
            // set the right size of the slider/slides for the current window size
            this._setSize();
            // show first slide
            this.$slides.eq( this.current ).show();
        },
        _navigate : function( dir, pos ) {
            if( this.isAnimating || this.slidesCount < 2 ) {
                return false;
            }
            this.isAnimating = true;
            var self = this,
                $currentSlide = this.$slides.eq( this.current );
            // if position is passed
            if( pos !== undefined ) {
                this.current = pos;
            }
            // if not check the boundaries
            else if( dir === 'next' ) {
                this.current = this.current < this.slidesCount - 1 ? ++this.current : 0;
            }
            else if( dir === 'prev' ) {
                this.current = this.current > 0 ? --this.current : this.slidesCount - 1;
            }
            this.options.onBeforeChange( $currentSlide, this.current );
            // next slide to be shown
            var $nextSlide = this.$slides.eq( this.current ),
                // the slide we want to cut and animate
                $movingSlide = ( dir === 'next' ) ? $currentSlide : $nextSlide,
                // the following are the data attrs set for each slide
                configData = $movingSlide.data(),
                config = {};
            config.orientation = configData.orientation || 'horizontal',
            config.slice1angle = configData.slice1Rotation || 0,
            config.slice1scale = configData.slice1Scale || 1,
            config.slice2angle = configData.slice2Rotation || 0,
            config.slice2scale = configData.slice2Scale || 1;
            this._validateValues( config );
            var cssStyle = config.orientation === 'horizontal' ? {
                    marginTop : -this.size.height / 2
                } : {
                    marginLeft : -this.size.width / 2
                },
                // default slide's slices style
                resetStyle = {
                    'transform' : 'translate(0%,0%) rotate(0deg) scale(1)',
                    opacity : 1 
                },
                // slice1 style
                slice1Style = config.orientation === 'horizontal' ? {
                    'transform' : 'translateY(-' + this.options.translateFactor + '%) rotate(' + config.slice1angle + 'deg) scale(' + config.slice1scale + ')'
                } : {
                    'transform' : 'translateX(-' + this.options.translateFactor + '%) rotate(' + config.slice1angle + 'deg) scale(' + config.slice1scale + ')'
                },
                // slice2 style
                slice2Style = config.orientation === 'horizontal' ? {
                    'transform' : 'translateY(' + this.options.translateFactor + '%) rotate(' + config.slice2angle + 'deg) scale(' + config.slice2scale + ')'
                } : {
                    'transform' : 'translateX(' + this.options.translateFactor + '%) rotate(' + config.slice2angle + 'deg) scale(' + config.slice2scale + ')'
                };
            if( this.options.optOpacity ) {
                slice1Style.opacity = 0;
                slice2Style.opacity = 0;
            }
            // we are adding the classes sl-trans-elems and sl-trans-back-elems to the slide that is either coming "next"
            // or going "prev" according to the direction.
            // the idea is to make it more interesting by giving some animations to the respective slide's elements
            //( dir === 'next' ) ? $nextSlide.addClass( 'sl-trans-elems' ) : $currentSlide.addClass( 'sl-trans-back-elems' );
            $currentSlide.removeClass( 'sl-trans-elems' );
            var transitionProp = {
                'transition' : 'all ' + this.options.speed + 'ms ease-in-out'
            };
            // add the 2 slices and animate them
            $movingSlide.css( 'z-index', this.slidesCount )
                        .find( 'div.sl-content-wrapper' )
                        .wrap( $( '<div class="sl-content-slice" />' ).css( transitionProp ) )
                        .parent()
                        .cond(
                            dir === 'prev', 
                            function() {
                                var slice = this;
                                this.css( slice1Style );
                                setTimeout( function() {
                                    slice.css( resetStyle );
                                }, 50 );
                            }, 
                            function() {
                                var slice = this;
                                setTimeout( function() {
                                    slice.css( slice1Style );
                                }, 50 );
                            }
                        )
                        .clone()
                        .appendTo( $movingSlide )
                        .cond(
                            dir === 'prev', 
                            function() {
                                var slice = this;
                                this.css( slice2Style );
                                setTimeout( function() {
                                    $currentSlide.addClass( 'sl-trans-back-elems' );
                                    if( self.support ) {
                                        slice.css( resetStyle ).on( self.transEndEventName, function() {
                                            self._onEndNavigate( slice, $currentSlide, dir );
                                        } );
                                    }
                                    else {
                                        self._onEndNavigate( slice, $currentSlide, dir );
                                    }
                                }, 50 );
                            },
                            function() {
                                var slice = this;
                                setTimeout( function() {
                                    $nextSlide.addClass( 'sl-trans-elems' );
                                    if( self.support ) {
                                        slice.css( slice2Style ).on( self.transEndEventName, function() {
                                            self._onEndNavigate( slice, $currentSlide, dir );
                                        } );
                                    }
                                    else {
                                        self._onEndNavigate( slice, $currentSlide, dir );
                                    }
                                }, 50 );
                            }
                        )
                        .find( 'div.sl-content-wrapper' )
                        .css( cssStyle );
            $nextSlide.show();
        },
        _validateValues : function( config ) {
            // OK, so we are restricting the angles and scale values here.
            // This is to avoid the slices wrong sides to be shown.
            // you can adjust these values as you wish but make sure you also ajust the
            // paddings of the slides and also the options.translateFactor value and scale data attrs
            if( config.slice1angle > this.options.maxAngle || config.slice1angle < -this.options.maxAngle ) {
                config.slice1angle = this.options.maxAngle;
            }
            if( config.slice2angle > this.options.maxAngle  || config.slice2angle < -this.options.maxAngle ) {
                config.slice2angle = this.options.maxAngle;
            }
            if( config.slice1scale > this.options.maxScale || config.slice1scale <= 0 ) {
                config.slice1scale = this.options.maxScale;
            }
            if( config.slice2scale > this.options.maxScale || config.slice2scale <= 0 ) {
                config.slice2scale = this.options.maxScale;
            }
            if( config.orientation !== 'vertical' && config.orientation !== 'horizontal' ) {
                config.orientation = 'horizontal'
            }
        },
        _onEndNavigate : function( $slice, $oldSlide, dir ) {
            // reset previous slide's style after next slide is shown
            var $slide = $slice.parent(),
                removeClasses = 'sl-trans-elems sl-trans-back-elems';
            // remove second slide's slice
            $slice.remove();
            // unwrap..
            $slide.css( 'z-index', 1 )
                  .find( 'div.sl-content-wrapper' )
                  .unwrap();
            // hide previous current slide
            $oldSlide.hide().removeClass( removeClasses );
            $slide.removeClass( removeClasses );
            // now we can navigate again..
            this.isAnimating = false;
            this.options.onAfterChange( $slide, this.current );
        },
        _setSize : function() {
            // the slider and content wrappers will have the window's width and height
            var cssStyle = {
                width : this.size.width,
                height : this.size.height
            };
            this.$el.css( cssStyle ).find( 'div.sl-content-wrapper' ).css( cssStyle );
        },
        _loadEvents : function() {
            var self = this;
            $window.on( 'debouncedresize.slitslider', function( event ) {
                // update size values
                self._getSize();
                // set the sizes again
                self._setSize();
            } );
            if ( this.options.keyboard ) {
                $document.on( 'keydown.slitslider', function(e) {
                    var keyCode = e.keyCode || e.which,
                        arrow = {
                            left: 37,
                            up: 38,
                            right: 39,
                            down: 40
                        };
                    switch (keyCode) {
                        case arrow.left :
                            self._stopSlideshow();
                            self._navigate( 'prev' );
                            break;
                        case arrow.right :
                            self._stopSlideshow();
                            self._navigate( 'next' );
                            break;
                    }
                } );
            }
        },
        _startSlideshow: function() {
            var self = this;
            this.slideshow = setTimeout( function() {
                self._navigate( 'next' );
                if ( self.options.autoplay ) {
                    self._startSlideshow();
                }
            }, this.options.interval );
        },
        _stopSlideshow: function() {
            if ( this.options.autoplay ) {
                clearTimeout( this.slideshow );
                this.isPlaying = false;
                this.options.autoplay = false;
            }
        },
        _destroy : function( callback ) {
            this.$el.off( '.slitslider' ).removeData( 'slitslider' );
            $window.off( '.slitslider' );
            $document.off( '.slitslider' );
            this.$slides.each( function( i ) {
                var $slide = $( this ),
                    $content = $slide.find( 'div.sl-content' ).children();
                $content.appendTo( $slide );
                $slide.children( 'div.sl-content-wrapper' ).remove();
            } );
            this.$slides.unwrap( this.$slideWrapper ).hide();
            this.$slides.eq( 0 ).show();
            if( callback ) {
                callback.call();
            }
        },
        // public methos: adds more slides to the slider
        add : function( $slides, callback ) {
            this.$slides = this.$slides.add( $slides );
            var self = this;

            $slides.each( function( i ) {
                var $slide = $( this ),
                    // vertical || horizontal
                    orientation = $slide.data( 'orientation' );
                $slide.hide().addClass( 'sl-slide-' + orientation )
                      .children()
                      .wrapAll( '<div class="sl-content-wrapper" />' )
                      .wrapAll( '<div class="sl-content" />' )
                      .end()
                      .appendTo( self.$el.find( 'div.sl-slides-wrapper' ) );
            } );
            this._setSize();
            this.slidesCount = this.$slides.length;
            if ( callback ) {
                callback.call( $items );
            }
        },
        // public method: shows next slide
        next : function() {
            this._stopSlideshow();
            this._navigate( 'next' );
        },
        // public method: shows previous slide
        previous : function() {
            this._stopSlideshow();
            this._navigate( 'prev' );
        },
        // public method: goes to a specific slide
        jump : function( pos ) {
            pos -= 1;
            if( pos === this.current || pos >= this.slidesCount || pos < 0 ) {
                return false;
            }
            this._stopSlideshow();
            this._navigate( pos > this.current ? 'next' : 'prev', pos );
        },
        // public method: starts the slideshow
        // any call to next(), previous() or jump() will stop the slideshow
        play : function() {
            if( !this.isPlaying ) {
                this.isPlaying = true;
                this._navigate( 'next' );
                this.options.autoplay = true;
                this._startSlideshow();
            }
        },
        // public method: pauses the slideshow
        pause : function() {
            if( this.isPlaying ) {
                this._stopSlideshow();
            }
        },
        // public method: check if isAnimating is true
        isActive : function() {
            return this.isAnimating;
        },
        // publicc methos: destroys the slicebox instance
        destroy : function( callback ) {
            this._destroy( callback );
        }
    };
    var logError = function( message ) {
        if ( window.console ) {
            window.console.error( message );
        }
    };
    $.fn.slitslider = function( options ) {
        var self = $.data( this, 'slitslider' );
        if ( typeof options === 'string' ) {
            var args = Array.prototype.slice.call( arguments, 1 );
            this.each(function() {
                if ( !self ) {
                    logError( "cannot call methods on slitslider prior to initialization; " +
                    "attempted to call method '" + options + "'" );
                    return;
                }
                if ( !$.isFunction( self[options] ) || options.charAt(0) === "_" ) {
                    logError( "no such method '" + options + "' for slitslider self" );
                    return;
                }
                self[ options ].apply( self, args );
            });
        } 
        else {
            this.each(function() {
                if ( self ) {
                    self._init();
                }
                else {
                    self = $.data( this, 'slitslider', new $.Slitslider( options, this ) );
                }
            });
        }
        return self;
    };
} )( jQuery, window );
您需要

更改 _navigate 中的代码,其中显示:

// if not check the boundaries
else if( dir === 'next' ) {
    this.current = this.current < this.slidesCount - 1 ? ++this.current : 0;
}
else if( dir === 'prev' ) {
    this.current = this.current > 0 ? --this.current : this.slidesCount - 1;
}

这是指定哪张幻灯片将是下一张/上一张幻灯片的地方(检查它是否是第一张/最后一张幻灯片),创建"循环效果"。尝试更改它,以便如果它是限制之一,幻灯片将保持不变,而不是更改为下一个/上一个。

像这样:

// if not check the boundaries
else if( dir === 'next' ) {
    this.current = this.current < this.slidesCount - 1 ? ++this.current : this.slidesCount - 1;
}
else if( dir === 'prev' ) {
    this.current = this.current > 0 ? --this.current : 0;
}

这将防止幻灯片中的圆形效果...但它会给动画带来问题。要解决动画问题,请执行以下操作:

  1. 在_navigate的开头添加行this.prevCurrent = this.current;
  2. 更改 this.current(上面的代码)的值后,像这样包装函数的其余部分:

    if (this.prevCurrent != this.current) { .... } else { this.isAnimating = false; }

箭头仍然有效,但它不会从最后一个跳到第一个(反之亦然)。但这有点笨拙,我本来希望插件有一个选项。

我还没有实际测试过它,但是您可以通过说以下内容来扩展插件以提供该选项:

$.Slitslider.prototype._startSlideshow = function() {
    var self = this;
    this.rotationCount = ++this.rotationCount || 1;
    if((this.rotationCount !== this.slidesCount && this.options.playOnce) || !this.options.playOnce) {
        this.slideshow = setTimeout( function() {
            self._navigate( 'next' );
            if ( self.options.autoplay ) {
                self._startSlideshow();
            }
        }, this.options.interval );
    }
};

如果该代码不起作用,您可以尝试以下方式(停止幻灯片放映,将自动播放设置为 false):

$.Slitslider.prototype._startSlideshow = function() {
    var self = this;
    this.rotationCount = ++this.rotationCount || 1;
    if(this.rotationCount === this.slidesCount && this.options.playOnce) this._stopSlideshow();
    this.slideshow = setTimeout( function() {
        self._navigate( 'next' );
        if ( self.options.autoplay ) {
            self._startSlideshow();
        }
     }, this.options.interval );
};

这样,您可以在插件调用中添加一个选项,并说:

$('div').slitslider({
    autoplay: true,
    playOnce: true
});

此代码将允许自动播放一次,如果将 playOnce 选项设置为 true,它将在旋转到最后一个选项后停止。