I'm using Bxslider for my div slider. But I need my next slide to slide over my old slide - effectively once a slide has animated in it remains there stacked on top of the others that went before it.
When prev slide button is pressed then it needs to animate out - similar to this effect: http://storiesbylove.com (don't try it out on a tablet though).
Does anyone know how to do this with boxslider?
Bxslider itself is not designed to operate in that way. It only supports three types of transitions ("mode" in the API): fade, horizontal, and vertical. To support the behavior you want would require modifications to Bxslider.
Bxslider stores all the images in the slider in a ul element with a class "bxslider". It then uses the transform property to move the entire slider left/right (or up/down for a veritcal slider). To get the desired behavior you would have to modify bxslider to transform the individual images instead of the ul container.
Below I modified setPositionProperty from Bxslider to do this. You should be able to just modify the original jquery.bxslider.js file and change this function, beginning around line 535 (NOTE: this particular solution does not support infiniteLoop=true):
var setPositionProperty = function(value, type, duration, params){
var selectedSlide=parseInt(slider.active.index) + 1;
// Handle going backwards
if(value==0)
selectedSlide++;
//By default, slide the entire container
var selectedEl = el;
//If set to use "lockedSlide", then only slide one image rather
//than all of them
if(slider.settings.lockedSlide===true)
selectedEl = $(el.children()[selectedSlide-1]);
// use CSS transform
if(slider.usingCSS){
// determine the translate3d value
var propValue = slider.settings.mode == 'vertical'
? 'translate3d(0, ' + value + 'px, 0)'
: 'translate3d(' + value + 'px, 0, 0)';
// add the CSS transition-duration
selectedEl.css('-' + slider.cssPrefix + '-transition-duration', duration / 1000 + 's');
if(type == 'slide'){
// set the property value
selectedEl.css(slider.animProp, propValue);
// bind a callback method - executes when CSS transition completes
selectedEl.bind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function(){
// unbind the callback
el.unbind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd');
updateAfterSlideTransition();
});
}else if(type == 'reset'){
selectedEl.css(slider.animProp, propValue);
}else if(type == 'ticker'){
// make the transition use 'linear'
selectedEl.css('-' + slider.cssPrefix + '-transition-timing-function', 'linear');
selectedEl.css(slider.animProp, propValue);
// bind a callback method - executes when CSS transition completes
selectedEl.bind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function(){
// unbind the callback
selectedEl.unbind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd');
// reset the position
setPositionProperty(params['resetValue'], 'reset', 0);
// start the loop again
tickerLoop();
});
}
// use JS animate
}else{
var animateObj = {};
animateObj[slider.animProp] = value;
if(type == 'slide'){
selectedEl.animate(animateObj, duration, slider.settings.easing, function(){
updateAfterSlideTransition();
});
}else if(type == 'reset'){
selectedEl.css(slider.animProp, value)
}else if(type == 'ticker'){
selectedEl.animate(animateObj, speed, 'linear', function(){
setPositionProperty(params['resetValue'], 'reset', 0);
// run the recursive loop after animation
tickerLoop();
});
}
}
}
To use when activating bxslider set "lockedSlide" to true:
$('.bxslider').bxSlider({
infiniteLoop: false,
hideControlOnEnd: true,
mode: 'horizontal',
lockedSlide: true
});
Related
Issue right now: https://www.loom.com/share/c2567ccbd8e44ab49d1138e65ae77973
I have a section or div in the middle of the page. On every scroll, I need to detect if I entered that div after scrolling from outside that div (either up scroll or down scroll) or I am just scrolling inside that div?
I will explain what I am trying to achieve
This is the site https://dev.plusplus.co/events/
For this section https://prnt.sc/25nbxzq
What I am trying to achieve is when I start scrolling from the top of the page, and after I enter that above section, the section locks and there is a slick slider inside that div and after the section locks, I need to change slides on up and down scroll.
But What is happening right now is especially in firefox browser, If I scroll from the top and enter that div, the slider automatically changes to second. I need to lock the scroll first which works and when I enter that section and scroll and then only change slide to second on next scroll
Code I am using right now.
// debounce from underscore.js
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
// use x and y mousewheel event data to navigate flickity
function slick_handle_wheel_event(e, slick_instance, slick_is_animating) {
// do not trigger a slide change if another is being animated
if (!slick_is_animating) {
// pick the larger of the two delta magnitudes (x or y) to determine nav direction
var direction =
Math.abs(e.deltaX) > Math.abs(e.deltaY) ? e.deltaX : e.deltaY;
console.log("wheel scroll ", e.deltaX, e.deltaY, direction);
if (direction > 0) {
// next slide
slick_instance.slick("slickNext");
} else {
// prev slide
slick_instance.slick("slickPrev");
}
}
}
// debounce the wheel event handling since trackpads can have a lot of inertia
var slick_handle_wheel_event_debounced = debounce(
slick_handle_wheel_event
, 80, true
);
// slider #2
const slick_3 = $("#firstscrollsection .content-left");
slick_3.not('.slick-initialized').slick({
dots: false,
vertical: true,
speed: 400,
fade: true,
waitForAnimate: false,
verticalSwiping: true,
slidesToShow: 1,
arrows: false,
infinite: false,
});
var slick_3_is_animating = false;
slick_3.on("afterChange", function(index) {
console.log("Slide after change " + index);
slick_3_is_animating = false;
});
slick_3.on("beforeChange", function(index) {
console.log("Slide before change " + index);
slick_3_is_animating = true;
});
$("#firstscrollsection .section-wrapper-animated").on("wheel", function(e) {
slick_handle_wheel_event_debounced(e.originalEvent, slick_3, slick_3_is_animating);
});
Assuming you're trying to create a parallax effect, I believe you're going about it the wrong way.
I'm also curious about these lines of code:
// pick the larger of the two delta magnitudes (x or y) to determine nav direction
var direction = Math.abs(e.deltaX) > Math.abs(e.deltaY) ? e.deltaX : e.deltaY;
console.log("wheel scroll ", e.deltaX, e.deltaY, direction);
Is there a reason for comparing deltaY with deltaX? As I understand that you're calculating scroll direction on the vertical axis.
MDN Web Docs outlines the following — maybe this could be the source of the issue with Firefox:
Note: Don't confuse the wheel event with the scroll event. The default action of a wheel event is implementation-specific, and doesn't necessarily dispatch a scroll event. Even when it does, the delta* values in the wheel event don't necessarily reflect the content's scrolling direction. Therefore, do not rely on the wheel event's delta* properties to get the scrolling direction. Instead, detect value changes of scrollLeft and scrollTop of the target in the scroll event.
If the pointer isn't directly hovering over the section, its wheel event won't fire. Also, the wheel event won't consider touch or scrollbar events. It's a better idea, in my opinion, to instead listen for the scroll event on the documentElement.
Here is my suggestion for changing the slide on the next scroll. You can detect if the scrollTop of the document meets your target element's(section in this case) offsetTop, then you will start calculating scroll direction before navigating the slider.
I have custom buttons that replaces the browser scrollbar. The idea is so that scrolling oversize elements in a page wouldn't result to a dozen scroll bar on a page.
See: https://jsfiddle.net/bwgxs6ng/
Since I must show some code sample (according to some SO error message), see this:
$('.right').on('click', function(event) {
var target = $(".image-container");
var current_x = target.scrollLeft();
if( target.length ) {
event.preventDefault();
$(target).animate({
scrollLeft: current_x+100
}, 500);
}
});
It's very simple, basically it takes current scroll position of the parent, and add x to it based on the direction that's clicked.
However, going further, I want it to imitate the hold and continuous scroll, but I'm not sure how to do it.
1) What is the mouse hold event called? (OK, this part is answered, it's called MouseDown as someone point out of the duplicate)
2) What is the continuous scrolling called, and how can I do something that'd imitate the browser's continuous scroll?
You can just call .animate() repeatedly (with easing set to linear, for smooth movement) inside your setInterval() callback. Just arrange for the interval to be equal to the animation duration, so that the next animation starts just when the previous one ends.
Or, better yet, make the interval shorter (say, 50 ms or less) and just call .prop() instead of .animate(), effectively performing your own animation. (This is how jQuery implements animation internally, anyway.)
Anyway, here's how I'd rewrite your code to support smooth continuous scrolling:
var speed_x = 0, speed_y = 0;
var timer = null;
var target = $(".image-container");
function scroll() {
if (speed_x == 0 && speed_y == 0) return;
var current_x = target.scrollLeft();
var current_y = target.scrollTop();
target.prop({
scrollLeft: current_x - speed_x,
scrollTop: current_y - speed_y
});
}
$('.control').on('mouseover mouseout', function (event) {
var $this = $(this);
var speed = (event.type == 'mouseover' ? 10 : 0)
if ($this.hasClass('left')) speed_x = +speed;
if ($this.hasClass('right')) speed_x = -speed;
if ($this.hasClass('up')) speed_y = +speed;
if ($this.hasClass('down')) speed_y = -speed;
}).on( 'mousedown', function () {
scroll();
if (timer !== null) clearInterval(timer);
timer = setInterval(scroll, 50);
return false;
});
$(document).on('mouseup', function () {
if (timer !== null) clearInterval(timer);
timer = null;
});
Note how the animation is started and stopped in the mousedown and mouseup handlers, but the direction of movement is set on mouseover and mouseout. This allows you to change the scrolling direction while holding the mouse down, by dragging the cursor from one edge to another.
(For bonus points, add divs with e.g. class="control up left" in the corners of the scroll area, so that holding the mouse down over those corners will allow you to scroll diagonally. The JS code above already supports it.)
you need to set an interval on mousedown, and clear the interval on mouseup, as done in this fiddle for left and right.
The relevant code change is that we removed the click event and replaced it with
$('.left').on('mousedown', function(event) {
... scroll code ...
interval = setInterval(function(){
... scroll code ...
},500);
})
.on('mouseup',function(){clearInterval(interval);});
I'm using the jQuery plugin cycle2 (http://jquery.malsup.com/cycle2/) to cycle through displaying some images on my site.
I'd like to change the transition fx (fade, ScrollHorz etc) between transitions
I thought I could do this by attaching a handler to the 'cycle-after' event:
$('#myelement').on('cycle-after', function(event, optionHash, outgoingSlideEl, incomingSlideEl, forwardFlag) {
console.log('was ' + optionHash.fx); // "was fade"
optionHash.fx = 'scrollHorz';
console.log('now ' + optionHash.fx); // "now scrollHorz"
});
but changing the value in optionHash has no effect - the fx stays as "fade".
Okay I'm not sure if I'm going about this in the right way or not, but here goes...
I'm writing a custom jQuery plugin to provide drop menu functionality with animation (and as a learning exercise so please no "Why not just use superduperwonderplugin-x").
I want to be able to animate the menu in different ways depending on user options (i.e fade, slide, drop etc.). At the moment each different animation is handled by a separate function within the plugin file but I'm not sure how to handle the callback functions (passing this back)!
-- i.e. The animation is only happening on the last element in the object's stack.
Here's my code:
/**
* Grizzly's Menuifier
*
* #author Chris.Leaper
* #version a1.0
*/
(function($){
$.fn.gmenu = function(options) {
/* Transitions:
- fade >> fadeIn / fadeOut
- slide >> slideDown / slideUp
- drop >> (different to above?)
- bounce >> (custom config transition=? and easing=bounce)
- stretch >> (custom config transition=? and easing=elastic)
- fold >> (custom) drop menu # half width then 'fold' out to full menu width
*/
// Set the plugin default options:
var defaults = {
levels: '1',
fit: 'auto',
easing: 'linear',
transition: 'slide',
speed: 500
};
options = $.extend(defaults, options); // Merge the user options with the plugin defaults
return this.each(function() {
var $this = $(this);
var opt = options;
var container;
var ul;
// Setup the container elements (parent DIV/NAV and UL)!
if( $this.is('ul') ) container = $(this).parent();
else container = $(this);
console.log('Container: ' + container.get(0).tagName + ' id=#' + container.attr('id') + ' class=' + container.attr('class'));
ul = container.children('ul:first-child');
console.log('UL: ' + ul);
// Set the UL's position to relative:
if($(ul).css('position') != 'relative') $(ul).css('position', 'relative');
var offset;
var menus = ul.children('li:has(ul)');
console.log('List Item: ' + menus);
menus.each(function(index, menu) {
$menu = $(menu);
console.log('Menu: ' + $menu);
// Set the menu LI's position to relative (contains the absolutely positioned child UL!)
if($menu.css('position') != 'relative') $menu.css('position', 'relative');
// Get the menu LI's position relative to the document (it's offset)
// -- This is only needed when positioning non-child elements
// (i.e. ID linked menu>submenu relationships as may be used for a separated DIV based menu!!)
// offset = menu.offest();
// Position the submenu according to it's parent
var submenu = $menu.children('ul');
console.log('Submenu: ' + submenu.get(0).tagName + ' id=#' + submenu.attr('id') + ' class=' + submenu.attr('class'));
setPosition(submenu, $menu.height());
switch(opt.transition) {
case 'bounce':
setSMBounce(menu, opt);
break;
case 'fade':
setSMFade(menu, opt);
break;
case 'fold':
setSMFold(menu, opt);
break;
case 'stretch':
setSMStretch(menu, opt);
break;
case 'slide':
default:
menu = setSMSlide(menu, opt);
}
});
debug(this);
});
};
})(jQuery);
function setPosition(submenu, height) {
$(submenu).css({
left: 0,
position: 'absolute',
top: height
}).hide();
}
function setSMSlide(menu, opt) {
$menu = $(menu);
console.log('SM Slide: ' + $menu.get(0));
$menu.first('a').mouseenter(function() {
console.log('Start SlideDown');
$menu.stop(true, true).slideDown(opt.speed, opt.easing);
console.log('Stop SlideDown');
});
$menu.first('a').mouseleave(function() {
console.log('Start SlideUp');
$menu.stop(true, true).slideUp(opt.speed, opt.easing);
console.log('Stop SlideUp');
});
}
I think that I should be using a (same) namespace based approach to defining my separate functions (object literal or something?) but I wasn't sure what this meant or how to do it.
Can anyone help me please?
To encapsulate your functions setPosition and setSMSlide (in your example) just define them inside your plugin function (the good place would be after definition of default variable. It would look something like that:
var defaults = {
levels: '1',
fit: 'auto',
easing: 'linear',
transition: 'slide',
speed: 500
},
setPosition = function(submenu, height) {...},
setSMSlide = function(menu, opt) {...};
Because of the way the scoping in Javascript works your setPosition and setSMSlide functions will be still accessible from inside of your gmenu declaration.
Is there a way to get this behavior on this dropline menu?Need second or thrid level to stay active for one second when mouse is hover out from menu items.
To clarify, you're looking for some kind of delayed reaction to the mouse's movement? If so, check out this jQuery hoverintent plugin: http://cherne.net/brian/resources/jquery.hoverIntent.html
EDIT: I think you'll need to edit your menu script to replace any instance of .hover with .hoverIntent. I had trouble testing this in jsfiddle, but see if it works:
var droplinemenu={
arrowimage: {classname: 'downarrowclass', src:'http://www.odzaci.rs/wp-content/themes/typo/images/down.gif', leftpadding: 5}, //customize down arrow image
animateduration: {over: 200, out: 300}, //duration of slide in/ out animation, in milliseconds
buildmenu:function(menuid){
jQuery(document).ready(function($){
var $mainmenu=$("#"+menuid+">ul")
var $headers=$mainmenu.find("ul").parent()
$headers.each(function(i){
var $curobj=$(this)
var $subul=$(this).find('ul:eq(0)')
this._dimensions={h:$curobj.find('a:eq(0)').outerHeight()}
this.istopheader=$curobj.parents("ul").length==1? true : false
if (!this.istopheader)
$subul.css({left:0, top:this._dimensions.h})
var $innerheader=$curobj.children('a').eq(0)
$innerheader=($innerheader.children().eq(0).is('span'))? $innerheader.children().eq(0) : $innerheader //if header contains inner SPAN, use that
$innerheader.append(
'<img src="'+ droplinemenu.arrowimage.src
+'" class="' + droplinemenu.arrowimage.classname
+ '" style="border:0; padding-left: '+droplinemenu.arrowimage.leftpadding+'px" />'
)
// THIS IS THE PART I REPLACED
var config = {
over: function(e){
var $targetul=$(this).children("ul:eq(0)")
if ($targetul.queue().length<=1) //if 1 or less queued animations
if (this.istopheader)
$targetul.css({left: $mainmenu.offset().left, top: $mainmenu.offset().top+this._dimensions.h})
if (document.all && !window.XMLHttpRequest) //detect IE6 or less, fix issue with overflow
$mainmenu.find('ul').css({overflow: (this.istopheader)? 'hidden' : 'visible'})
$targetul.slideDown(droplinemenu.animateduration.over)
},
timeout: 1000, // number = milliseconds delay before onMouseOut
out: function(e){
var $targetul=$(this).children("ul:eq(0)")
$targetul.slideUp(droplinemenu.animateduration.out)
}
};
$curobj.hoverIntent( config );
}) //end $headers.each()
$mainmenu.find("ul").css({display:'none', visibility:'visible', width:$mainmenu.width()})
}) //end document.ready
}
}