I'm using jQuery.crSpline to animate a graphic along a curved path. I'm pretty happy with the result.
However, the full canvas size is intentionally pretty wide - definitely larger than most screens - so the graphic will run out of viewport space pretty quickly and animate off the screen.
Instead, I'd like browser viewport to follow or centre on the image so that it stays 'in shot'.
How would I go about this with jQuery? Is scrollTop an option?
I've created a jsFiddle demo, based on the crSpline demo source, but with a wide minX setting.
NB: I did first attempt this with YUI3 and Loktar offered a canvas based solution, however I'm not using YUI & canvas any longer.
Is this what you had in mind? http://jsfiddle.net/gNdwD/33/. It seems a little choppy but its a rough first attempt.
It doesn't seem like crSpline exposes any coordinates on the animated element so we have to periodically observe them and adjust the viewport accordingly:
setInterval(function() {
var mover = $('#mover'),
posX = mover.position().left,
posY = mover.position().top;
$(window)
.scrollLeft(posX - $(window).width() / 2)
.scrollTop(posY - $(window).height() / 2);
}, 10);
I suspect the choppiness happens because our setInterval is out of sync with the $.animate on the mover. You can fix that by running two animations: one on the mover and one on the scrollTop and scrollLeft of a wrapper div. You can simultaneously apply two $.animates like this.
There exists an option for step function in jQuery animate,which is run on every step of the animation.
See second version of function parameters here :
http://api.jquery.com/animate/
.animate( properties, options )
propertiesA map of CSS properties that the animation will move toward.
optionsA map of additional options to pass to the method. Supported keys:
duration: A string or number determining how long the animation will run.
easing: A string indicating which easing function to use for the transition.
complete: A function to call once the animation is complete.
step: A function to be called after each step of the animation.
queue: A Boolean indicating whether to place the animation in the effects queue. If false, the animation will begin immediately. As of jQuery 1.7, the queue option can also accept a string, in which case the animation is added to the queue represented by that string.
specialEasing: A map of one or more of the CSS properties defined by the properties argument and their corresponding easing functions (added 1.4).
See this fiddle based on your code, which calls step function to adjust viewport :
http://jsfiddle.net/gNdwD/35/
$('<div id="mover" />')
.appendTo($(document.body))
.animate({ crSpline: spline },{
duration: 20000,
step: function() { /* THE STEP FUNCTION TO ADJUST VIEWPORT */
var mover = $('#mover'),
posX = mover.position().left;
posY = mover.position().top;
$(window)
.scrollLeft(posX - $(window).width() / 2)
.scrollTop(posY - $(window).height() / 2);
} ,
complete:function () {
// Re-run the demo with a new spline after we're done
window.setTimeout(function() {
DEMO.run();
}, 5000);
}
});
Related
I have a scroller that animates scrolling using prev/next buttons. The duration of the animation is calculated based on the distance moved, so the duration is variable.
$c.animate({ scrollLeft: scrollLeft },
duration, 'easeInOutExpo', function () {
if (showPrev === 0) $s.attr("data-prev", showPrev);
if (showNext === 0) $s.attr("data-next", showNext);
});
After the animation is done, I want to refresh the user interface (the buttons), which you can see is done by updating two data attributes.
To date, I've tried two different options:
1. Update the data attributes before the animation
I use a CSS transition delay to make updates happen later but cannot fully co-ordinate it with the variable duration of the animation - sometimes it can be way out.
2. Update the data attributes after the animation
I attach the update function directly after the animation, which works, but there is an obvious delay after the animation is finished before the updates kick in.
Neither is perfect.
Ideally what I would like to do is kick off the function just before the scroll animation is finished - so if the scroll duration is 1s I would like to update the attributes at duration - 0.2s (0.8s).
This seems entirely possible given that I have full control of the duration.
What I don't know is how to implement this using jQuery - possibly kick off two tasks in parallel?
I'm not a jQuery expert by any stretch of the imagination so any help would be appreciated.
Please try with this code
$c.animate({ scrollLeft: scrollLeft },
duration, 'easeInOutExpo', function () {
if (showPrev === 0) $s.attr("data-prev", showPrev);
if (showNext === 0) $s.attr("data-next", showNext);
time = setTimeout(function () {
//keep your code here
}, duration-0.2s);
});
duration-0.2s => make it correct, i just mentioned it as an example.
I am creating a simple slot machine and currently using TileSprite to achieve the effects that I want - for the spinning. So far, everything works. However, after the timer stops the initial spin, I want to smoothly scroll the texture to the correct 'result' position:
R1TimerTrigger: function()
{
R1Scroll = false;
game.add.tween(SpriteReel[0].tilePosition).to( { y: R1Result }, 1000, Phaser.Easing.Bounce.Out, false);
}
There are some immediate problems, in that apparently the native tween does not recognize properties of children. Is there a way to solve this, or an alternative approach that does not use tween to achieve the result?
You code looks fine to me and the tween should work on the tile sprite as expected.
Are you starting the tween? You can start the tween automatically using 'true' as the 'autoStart' parameter
to(properties, duration, ease, autoStart, delay, repeat, yoyo)
game.add.tween(SpriteReel[0].tilePosition).to( { y: R1Result }, 1000, Phaser.Easing.Bounce.Out, true);
Working example here https://phaser.io/sandbox/edit/iTLritEj
Look in the Play and Create tabs
I tried to experiment with parallax and started from scratch to understand the core parts of this magic. To give you an example that I like to use as inspiration, you can see it at this link here at the "Photos" section.
Latest code is down the page with related information. To get an overall look of the question see the rest of the details.
Core parts I already know are the scrollTop() of the $window and the offsetTop of the element are important to apply the parallax effect on a DOM element as well as a factor for how sensitive the effect should be respond to the scroll speed. The end result should be some formule that will calculate the translateY or translate3d coordinates in pixels or percentage.
I read on the internet that the CSS property translate is faster than, for example, top from position: absolute, and my preference would be also to use translate in combination with TweenMax GSAP. So the movement of the parallax will be very smooth. But if only the css property translate is enough that's fine too. I saw some examples that where using TweenMax, so that's why I use it for now.
JS
I have code the basic things:
var win = $(window);
var el = $('#daily .entry').find('figure');
win.scroll(function() {
var scrollTop = win.scrollTop();
var parallaxFactor = 5;
el.each(function() {
var image = $(this).find('img');
var offsetTop = $(this).offset().top;
// This is the part where I am talking about.
// Here should be the magic happen...
});
});
So I've code above code, but it doesn't do anything, of course. See CodePen from above code here. It will only console log scrollTop and offsetTop. As mentioned before, I only know the core parts like scrollTop and offsetTop to apply the parallax effect. Then there should be some area created where the parallax effect will be triggered and happen, so calculations will be only done for elements within the viewport in order to keep the performance good. After that there should be some math done, but doesn't know exactly what or how to achieve this. Only after I have a final number, I could use it within for example TweenMax from Greensock like so:
TweenMax
TweenMax.to(image, 0.1, {
yPercent: offsetPercent + '%',
ease: Linear.easeNone
});
Parallax formula
If I look around to get the formula down I came to something like this (founded on the internet):
var viewportOffset = scrollTop - offsetTop + win.height();
var offsetPercent = ((viewportOffset / win.height() * 100) - 100) / parallaxFactor;
if (viewportOffset >= 0 && viewportOffset <= win.height() * 2) {
TweenMax.to(image, 0.1, {
yPercent: offsetPercent + '%',
ease: Linear.easeNone
});
}
But if I am honest, I doesn't know what this does exactly, or why it should/could be this way. I would like to know this, so I can understand the whole process of making parallax happen. The functions of scrollTop(), offsetTop and $(window).height() are clear for me, but what the trick behind the formula is, is the part that I doesn't understand.
Updates
Update 1
#Scott has notified that the inspiration site uses a plugin called scrollmagic.io, but I am very curious about how I can create a parallax by myself without the use of a plugin. How it works and how to achieve it. With emphasis on the formula, why I should it do this or that way and what exactly will be calculated, because I don't understand it and really wanna know this, so that I can use this knowledge in the future when applying a parallax effect.
Update 2
I tried to figure out what the following code snippet exactly does. I talking about this one:
var viewportOffset = scrollTop - offsetTop + win.height();
After some good debug sessions I think that I've the clue. So scrollTop is the amount of pixels that you've scrolled down the page and that are hidden from the view. offsetTop is the start position of the element within the DOM and $(window).height is the viewport height - the part that is visible in the browser -.
This is what I think that this formula does:
Set the zero point to the point where the element starts. For example, when scrollTop is equal to 0 and the element starts at 240px from the top, then the formula is: 0 minus 240 is -240. So the current scroll position is below zero point. After scrolling 240px down, the formula will output 0 because of course 240 minus 240 is 0 (zero). Am I right?
But the part that I doesn't understand yet is why + win.height.
If we go back to above formula (at Update 2) and scrollTop is zero then the $(window).height is the space from 240px till the bottom of the viewport. When scrolling down, the amount of pixel will grow on scroll, that makes no sense to me. If someone can explain what could have been the purpose of this would be fine. 'm very curious. The second part of the formula to calculate the parallax offsetPercent I still don't understand. In general the calculation of the parallax strength on scroll.
Update 3 / 4
Advised by #Edisoni, I walked the last few days by the videos of Travis Neilson and I have become a lot wiser on the basic functionalities of parallax. A must for everyone who wants to dig in parallax. I've used the new knowledge about parallax to get my above script work:
var root = this;
var win = $(window);
var offset = 0;
var elements = $('#daily .entry figure');
if (win.width() >= 768) {
win.scroll(function() {
// Get current scroll position
var scrollPos = win.scrollTop();
console.log(scrollPos);
elements.each(function(i) {
var elem = $(this);
var triggerElement = elem.offset().top;
var elemHeight = elem.height();
var animElem = elem.find('img');
if (scrollPos > triggerElement - (elemHeight / 2) && scrollPos < triggerElement + elemHeight + (elemHeight / 2)) {
// Do the magic
TweenMax.to(animElem, 0.1, {
yPercent: -(scrollPos - elemHeight / 2) / 100,
ease: Linear.easeNone
});
} else {
return false;
}
});
});
}
However, the script works only for a certain part of the elements. The problem is that it only works for the first two elements. I have a suspicion that the "error" is located in particularly after the AND && sign in the if statement, but can't get the error solved. http://codepen.io/anon/pen/XKwBAB
When the elements, that work on the trigger are animated, they will be jumping some pixels to the bottom, don't know how to fix this to.
The jumping to: 1.135%, after the trigger is fired. So it doesn't start at 0%. I already checked if I should add the CSS property translate to the CSS and set the type of number to %, but this doesn't work for me.
-webkit-transform: translateY(0%);
-moz-transform: translateY(0%);
-o-transform: translateY(0%);
transform: translateY(0%);
Should I use the TweenMax .fromTo() function instead of using the .to() function so I can set the start position as well or is my thought about this wrong and has a different cause?
Something like this:
TweenMax.fromTo(animElem, 0.1, {
yPercent: triggerElement,
z: 1
}, {
yPercent: -(scrollPos - elemHeight / 2) / 100,
ease: Linear.easeNone
});
Beside that I trying to recreate the effect of the site that I would like to use as inspiration source without the use of the scrollMagic plugin, but I don't really know how this works, with the use of two different objects that are animated.
At last, if someone thinks the code can be better formatted, don't hesitate, I would like to hear your suggestions
My actual questions are for update 2 and 3/4:
How to calculate the parallax y coordinates to get "the magic" done?
Am I right about update 2, that the zero point will be reset to offsetTop of each element?
Why my code only works for the first two elements and why they jumping some pixels down if the inline style of translate will be added to the animated element? See update 3/4 for all info.
Parallax is actually quite simple in principle. Just make the parallax element scroll slower than the rest of the content. That being said, a parallax implementation can be as simple as dividing the scroll distance by a factor:
var parallaxFactor = 3;
window.addEventListener("scroll", function(e){
el.style.top = (document.body.scrollTop / parallaxFactor) + "px";
// This is the magic. This positions the element's y-cord based off of the page scroll
}, false);
CODEPEN
This is an extremely simple demonstration of the parallax effect. Other more thorough implementations may handle values as percentages, or attempt to smooth the animation with TweenMax. This however, is the magic you're looking for.
Live long and prosper.
Update:
This example only works for elements at the top of a screen. If this were for a more general purpose, you would want to store the default y-position of the element, then something along the lines of defaultYCord + (document.body.scrollTop / parallaxFactor).
Update 2:
A very good visualization for parallax comes from Keith Clark who made a pure css parallax scroller: http://keithclark.co.uk/articles/pure-css-parallax-websites/demo3/. If you click debug in the upper left, it gives you a nice 3d-view of the magic.
This is not an answer how to build a parallax in JS. But it shows some basics, which will often be forgotten, if your too much into the code.
Basics:
Order your graphical objects in z-layers. As higher z is, as nearer
it is to observer in front of the screen.
As higher your object is in the z-axis as faster it should move on something that appears, f.e. your scrolling
Now you get a 3-D-Effect where objects nearer to you move faster to your actions as objects more far away.
Your question
How to calculate the parallax y coordinates to get "the magic" done?
The y-position depends on your z-index. If it is far away a.k.a the z-index is low, delta-y is small. If it is near too you, delta-y is big.
Please consider the z-index is not necessarily used as Style-property, it's more like it looks like.
I would add an attribute like data-z to every parallaxing layer.
function parallaxingY(el){
//el is a parallaxing element with attribute data-z
return $(el).data('z')*window.scrollTop;
}
the suggested CSS-Solution is nice and should be preferred. There the "magic" - the "z-index" - is made by the css-style "scale".
I have a JS feature on the following site that is working just fine in Firefox but not in Safari: http://rossbolger.com/kids/light-stories/
The feature slides out a grid of thumbnails called #image-thumbs when the mouse hovers over the container called #hit-area. It works (at least in Firefox) by first changing #image_thumbs height from '48px' to 'auto', the height is then measured using jQuery's height(). This height is stored in a variable and then using jQuery's css() it is given back to the #image-thumbs when the mouse is over.
The code on the site looks a little something like this:
// Thumbnails Nav Reveal and Hide Scripts
var thumbs_height = 1,
thumbs = $('#image-thumbs'),
thumbs_original_height = thumbs.css('height');
// Slide Up Thumbs
(function revealThumbs() {
// On hover let the thumbs become their full height
$('#image-thumbs #hit-area').hover(function(){ // Mouse over
// Get the unrestricted height of the thumbs
thumbs.css('height', 'auto');
thumbs_height = thumbs.height();
// then put it back to what it was so we can animate it using CSS3 transition
thumbs.css('height', 'inherit');
// delay 0.1s before triggering the change in height (time for the calculations to complete)
setTimeout( function() { thumbs.css('height', thumbs_height ) }, 100 );
}, function(){ // Mouse out
hideThumbs();
});
})();
// Hide thumbs
function hideThumbs(){
thumbs.css('height', thumbs_original_height );
};
The reason for measuring the unrestricted height and passing it back as a pixel value, rather than simply setting the height to 'auto', is to create a sliding effect via CSS3 (i.e. transition: height 0.5s). The transition only takes place if the affected attribute goes from one numeric value to another.
Thanks for any help bug testing this. I haven't even looked at other browsers yet.
All the best,
Laurence
Okay, so I've worked it out...
In the javascript document (scripts.js on the site) there was a function higher up the page calling the hideThumbs() function. So it wasn't working because the variables in hideThumbs() hadn't been declared at that point. Funny that it should still work in Firefox and IE9!
I've moved all this code to a point before that other function and the problem is now resolved. So far I've only done this locally. I'll update the site in the link above later.
Im trying to create a custom horizontal accordion style showcase. In terms of actual functionality, i have the framework (which can be seen here):
http://www.jsfiddle.net/adrianjacob/UdUus/
However my main bug bear (and the clients) is that if you look at the right hand side, there is always slight movement/flickering as the widths animate up and down.
Ideally I want it to appear smooth so only the opening/closing lists have movement.
ANy advice would be much appreciated.
A.
Use the animate function's step (it's not well documented)... I've updated the demo
var panels = $('#promo li');
panels.hoverIntent(
function() {
if (!$(this).is('.expanded') && !panels.is(':animated')) {
$(this).animate({
width: 200
}, {
// width is the calculated width, ani is the animation object
step: function(width, ani) {
var w = Math.floor(width);
// use 250 so we end up with 50 as the reduced size
$('.expanded').css('width', (250 - w) + 'px');
$(ani.elem).css('width', (200 - w) + 'px');
},
duration: 500,
complete: function() {
panels.removeClass('expanded');
$(this).addClass('expanded');
}
});
}
}, function() {});
A similar method is used in the Kwicks plugin.
You could try this plugin which may have figured out the bug. The example provided was too showy to actually tell.
Have you played around with jQuery UI easings?
You can also try to stop the event just when the div is opening.
The website with explanation is: http://api.jquery.com/stop/