I have a scroll event that checks for the scroll position and triggers an animation if its passed a certain position. This works nicely until I try it with different browser heights. Then my numbers are off. How can I keep my code and alter it so it works for different browser heights.
Here is the shell of my code.
runStatus is where my animations are set. p is the position point. Now when I test in taller browsers the p value comes in later then when I test in shorter browser heights. Without having to re-write this whole thing I'm trying to see if there is something I can do to compensate for all browser heights.
I also tried using a percentage but it still doesn't trigger consistently if the browser is short or tall.
$(window).scroll(function () {
topScroll = $(this).scrollTop();
run();
});
function run(){
for (var i=0; i < animData.length; i++) {
update(animData[i]);
};
}
function update(obj){
var target = obj.target;
var step = obj.runStatus.length;
if(topScroll < obj.runStatus[0].p){
step = 0;
};
switch(step){
case 0:
//if(obj.repeat) change(target,obj.runStatus[step]);
break;
case obj.runStatus.length:
change(target,obj.runStatus[1]);
break;
}
}
function change(t,a,b){
if(!t.data("complete")) animateTo(a,t);
}
function animateTo(a,t){
t.animate( { left:a.x, top:a.y, opacity:a.opacity }, { duration: 600, easing: easing});
}
var animData =[
// scene 1
{
scene:"#scene1",
name:"#compass",
runStatus:[
{p:220, y:500, opacity:0},
{y:22, opacity:1}
]
},
{
scene:"#scene1",
name:"#chess-right",
runStatus:[
{p:260, y:600, opacity:0},
{y:493, opacity:1}
]
}
]
Related
basically, what I want to do is trigger an event if the user increases the size of the browser from X to Y. Provided X = Anything less than 750 pixels, and Y is anything more than 750 pixels.
Right now, I am doing something like this:
$(window).resize(function(){
if ($(window).width() >= 750) {
console.log('750 or more');
}
});
This works, however, its clearly not efficient. For example, if I resize my window from 780px to max width (1024px), even then the event gets triggered. Or even if I decrease the size from 800px to 780px, I still obviously get the console output.
How do I get this to work right?
You will need to setTimeout to allow check to take place .
Example :
var resizeTimer;
$(window).resize(function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
var body_size = $(window).width();
// ...
// do your screen check here
// ...
}, 1);
})
Hope this helps
There's no true solution for this issue since removing the on resize event after max width has been reached results in the on resize function no longer being called even when the width is below 1024px.
Maybe in the future it's possible to have an on resize event under certain conditions only.
You can also use the on resize end event to only trigger the function after resizing the window, keep in mind this might result in visual changes happening after a user has resized a window instead of during the resizing of a window.
There are multiple methods to make the on resize event perform better: http://bencentra.com/code/2015/02/27/optimizing-window-resize.html
Here is a throttled version using script that might be a good start
Fiddle demo
(function(timeout,bigger) { // local static var - timeout,bigger
window.addEventListener("resize", function(e) {
if ( !timeout ) {
timeout = setTimeout(function() {
timeout = null;
actualResizeHandler(e);
// Set the actual fire rate
}, 66);
}
}, false);
function actualResizeHandler(e) {
// handle the resize event
if (window.innerWidth >= 750 && !bigger) {
//passed above (or equal) 750
document.querySelector('span').style.color = 'blue';
document.body.innerHTML += '<br>above 750';
} else if (window.innerWidth < 750 && bigger) {
//passed below 750
document.querySelector('span').style.color = 'red';
document.body.innerHTML += '<br>below 750';
}
bigger = (window.innerWidth >= 750);
}
// run once at load
bigger = (window.innerWidth < 750);
actualResizeHandler();
}(null,false));
<span>This text is blue on big and red on small</span>
and here is one use CSS media query
span {
color: red;
}
#media screen and (min-width: 750px) {
span {
color: blue
}
}
<span>This text is blue on big and red on small</span>
I'm playing around with pure JavaScript, so I created a small fade in/out object, to adjust images opacity onmouseover and onmouseout. Fading works fine when the mouseover and mouseout actions are precise:
Start moving the cursor from the white background
Hover over an image
Hover back over the white background
The problem is, as soon as I start to move the mouse "naturally" from one image to another, the fading (or rather the script itself) freezes.
I'm not sure whether it's a animation-speed problem, or there's something I'm missing in the implementation.
If someone has the time to take a look, I would appreciate a peer check, so I can crack the issue and learn new stuff.
Here's a fiddle: http://jsfiddle.net/6bd3xepe/
Thanks!
As I see it, you have one INTERVAL for you FADER, you need one for each IMG.
My jsfiddle fixes this. I added an ALT-attribute to each IMG with "dome" content, so as to circumvent the jsfiddle working on non-cat-images .. ignore that part - commented out below.
There are some fundamental things wrong with the design - keeping track of objects & references is key. Usage of "this" & "that" aren't helping in the current implementation (see comments to OP). Also, on another note, the usage of "toFixed(2)" is not really required IMHO and you can shorten "o = o + 0.1" to "o += 0.1".
JS:
var fader = {
target: document.getElementsByTagName('img'),
interval: [],
speed: 25,
default_opacity: 1,
init: function() {
this.bindEvents();
},
// Get element's opacity and increase it up to 1
fadeIn: function(element) {
var element_opacity = this.getOpacity(element),
that = this,
idx = element.getAttribute('data-idx');
console.log("fI: "+idx+" "+element_opacity);
this.default_opacity = element_opacity.toFixed(2);
this.interval[idx] = setInterval(function() {
if (element_opacity.toFixed(2) < 1) {
element_opacity = element_opacity + 0.1;
element.style.opacity = element_opacity.toFixed(2);
} else {
clearInterval(that.interval[idx]);
}
}, that.speed);
},
// Get current opacity and decrease it back to the default one
fadeOut: function(element) {
var element_opacity = this.getOpacity(element),
that = this,
idx = element.getAttribute('data-idx');
console.log("fO: "+idx+" "+element_opacity);
this.interval[idx] = setInterval(function() {
if (element_opacity.toFixed(2) > that.default_opacity) {
element_opacity = element_opacity - 0.1;
element.style.opacity = element_opacity.toFixed(2);
} else {
clearInterval(that.interval[idx]);
element.removeAttribute('style');
}
}, that.speed);
},
// Get opacity of an element using computed styles
getOpacity: function(element) {
var styles = window.getComputedStyle(element),
opacity = parseFloat(styles.getPropertyValue('opacity'));
return opacity;
},
bindEvents: function() {
var that = this, count = 0;
for (var i in this.target) {
// the whole "dome" is just a fsfiddle hack - otherwise it sees 7 images instead of 4!
//if( this.target[i].alt == "dome" ){
console.log("COUNT: "+count);
this.target[i].setAttribute('data-idx',count);
this.target[i].onmouseover = function() {
that.fadeIn(this);
}
this.target[i].onmouseout = function() {
that.fadeOut(this);
}
count++;
//}
}
}
};
fader.init();
I currently have html enabled tooltips that also display "sub graphs". However, it would be nice if it was possible to have all tooltips pop up in a fixed location or have an offset that adjusted their relative poition.
This is an example of the kind of tooltip that I have (blank data). I'd like to move it to the right. Any suggestions would be appreciated, including any javascript trickery.
whilst the answer is very good it is a little outdated now. Google has implemented CSS control so there is greater flexibility without the need to hack the JavaScript.
.google-visualization-tooltip { position:relative !important; top:0 !important;right:0 !important; z-index:+1;}
will provide a tooltip fixed at the bottom of the chart, live example: http://www.taxformcalculator.com/federal-budget/130000.html
alternatively you could just tweak the left margin...
.google-visualization-tooltip { margin-left: 150px !important; z-index:+1;}
Note that pulling the container forward with z-index reduces (but does not stop entirely) visibility flicker as the mouse moves. The degree of flicker will vary on chart size, call etc. Personally, I prefer to fix the tool tip and make it part of the design as per the first example. Hope this helps those who are deterred by the JS hack (which is good but really no longer necessary).
The tooltip position is set inline, so you need to listen for DOM insertion of the tooltip and change the position manually. Mutation events are deprecated, so use a MutationObserver if it is available (Chrome, Firefox, IE11) and a DOMNodeInserted event handler if not (IE 9, 10). This will not work in IE8.
google.visualization.events.addOneTimeListener(myChart, 'ready', function () {
var container = document.querySelector('#myChartDiv > div:last-child');
function setPosition () {
var tooltip = container.querySelector('div.google-visualization-tooltip');
tooltip.style.top = 0;
tooltip.style.left = 0;
}
if (typeof MutationObserver === 'function') {
var observer = new MutationObserver(function (m) {
for (var i = 0; i < m.length; i++) {
if (m[i].addedNodes.length) {
setPosition();
break; // once we find the added node, we shouldn't need to look any further
}
}
});
observer.observe(container, {
childList: true
});
}
else if (document.addEventListener) {
container.addEventListener('DOMNodeInserted', setPosition);
}
else {
container.attachEvent('onDOMNodeInserted', setPosition);
}
});
The MutationObserver should be fine, but the events may need some work; I didn't test them.
I had more or less the same question as Redshift, having been trying to move the tooltip relative to the node being hovered over. Using asgallant's fantastic answer I've implemented his code as below.
I haven't been able to test whether this works with the MutationObserver because during my testing in Firefox, Chrome and IE11 it always fails that test and uses addEventListener. The docs suggest it should work though.
I had to introduce a timeout to actually manipulate the styles as otherwise the left and top position of the element was always reported as 0. My assumption is that the event fired upon addition of the node but the DOM wasn't quite ready. This is just a guess though and I'm not 100% happy with implementing it in this way.
var chart = new google.visualization.LineChart(document.getElementById('line_chart'));
google.visualization.events.addOneTimeListener(chart, 'ready', function () {
var container = document.querySelector('#line_chart > div:last-child');
function setPosition(e) {
if (e && e.target) {
var tooltip = $(e.target);
setTimeout(function () {
var left = parseFloat(tooltip.css('left')) - 49;
var top = parseFloat(tooltip.css('top')) - 40;
tooltip.css('left', left + 'px');
tooltip.css('top', top + 'px');
$(".google-visualization-tooltip").fadeIn(200);
}, 1);
}
else {
var tooltip = container.querySelector('.google-visualization-tooltip');
var left = parseFloat(tooltip.style.left) - 49;
var top = parseFloat(tooltip.style.top) - 40;
tooltip.style.left = left + 'px';
tooltip.style.top = top + 'px';
$(".google-visualization-tooltip").fadeIn(200);
}
}
if (typeof MutationObserver === 'function') {
var observer = new MutationObserver(function (m) {
if (m.length && m[0].addedNodes.length) {
setPosition(m);
}
});
observer.observe(container, {
childList: true
});
}
else if (document.addEventListener) {
container.addEventListener('DOMNodeInserted', setPosition);
}
else {
container.attachEvent('onDOMNodeInserted', setPosition);
}
});
chart.draw(data, options);
}
EDIT: Updated to get the MutationObserver working following asgallant's comment.
I'm using a simple chunk of code (based of 'ScrollTo Posts with jQuery' which allows you to click a next/previous link and it'll jump through to the top of each post.
I have my HTML structure so it goes post > image > post > image etc.
I'm wondering if it's possible that if you click the next/previous button, it scrolls to the next post as normal, but it hangs/hovers over the images/div inbetween? So it eventually completes it's scroll, but slows down over the divs inbetween.
Here's my jQuery code:
$(function () {
function a(f) {
var b, e, c = [],
d = $(window).scrollTop(),
g = $('.section-slide');
g.each(function () {
c.push(parseInt($(this).offset()['top'], 10))
});
for (e = 0; e < c.length; e++) {
if (f == 'next' && c[e] > d) {
b = g.get(e);
break
}
if (f == 'prev' && e > 0 && c[e] >= d) {
b = g.get(e - 1);
break
}
}
if (b) {
$.scrollTo(b, {
duration: 1400
})
}
return false
}
$('#next,#prev').click(function () {
return a($(this).attr('id'))
});
$('.scrolltoanchor').click(function () {
$.scrollTo($($(this).attr('href')), {
duration: 1400
});
return false
})
});
Assuming your structure will remain static: post -> image -> post -> image etc. you can accomplish this by finding the previous / next image to the post you will be scrolling to, and scrolling to it first, then use the onAfter callback/setting from the $.scrollTo plugin to fire a secondary scroll after a predefined setTimeout like this:
$(function () {
function scroll(direction) {
var scroll, scrollImage, i,
positions = [],
here = $(window).scrollTop(),
collection = $('.post');
collection.each(function () {
positions.push(parseInt($(this).offset()['top'], 10));
});
for (i = 0; i < positions.length; i++) {
if (direction == 'next' && positions[i] > here) {
scroll = collection.get(i);
// Find Image Before Post
scrollImage = $(scroll).prev('.image').get(0);
break;
}
if (direction == 'prev' && i > 0 && positions[i] >= here) {
scroll = collection.get(i - 1);
// Find Image After Post
scrollImage = $(scroll).next('.image').get(0);
break;
}
}
if (scroll) {
// Check if Scroll Image Exists
if (scrollImage){
// Scroll with Image Delay
$.scrollTo(scrollImage, {
duration: 750,
onAfter: function(){
setTimeout(function(){
$.scrollTo(scroll, {
duration: 750
});
}, 1000); // Change the Delay to Increase / Decrease the Hover
}
});
} else {
$.scrollTo(scroll, {
duration: 750
});
}
}
return false;
}
$("#next,#prev").click(function () {
return scroll($(this).attr('id'));
});
$(".scrolltoanchor").click(function () {
$.scrollTo($($(this).attr("href")), {
duration: 750
});
return false;
});
});
You can find an updated fiddle here: http://jsfiddle.net/hfg2v/2/
I hope this helps.
This is happening because you're using a parallax scrolling library (Stellar.js), which makes different elements scroll at different speeds.
A possible fix would be to scroll at a higher speed when no element is in the current viewport until the edge of the next element is just off the screen, then immediately scroll at the original scrolling speed until there are no elements in the viewport again, and keep repeating this until you reach the desired scroll offset.
Edit:
Sorry, something came up while I was writing my answer and I didn't have time to finish the code.
However, after working on it for some time I'm starting to think that my proposed solution wouldn't work. I was thinking something along those lines:
$(window).scrollTo(640, {onAfter: function () {
var scrollRatio = 3;
var distance = 855 - 640;
$(window).scrollTo(855, {
easing: 'linear',
duration: distance * scrollRatio / speed,
onAfter: function () {
var scrollRatio = 1;
var distance = 1200 - 855;
$(window).scrollTo(1200, {
easing: 'linear',
duration: distance * scrollRatio / speed,
onAfter: function () {
var scrollRatio = 3;
var distance = 1280 - 1200;
$(window).scrollTo(1280, {
easing: 'linear',
duration: distance * scrollRatio / speed
});
}
});
}
});
}});
If you paste the previous code in the website provided in the question (http://dev.du.st/field-station/), you'll be taken to the first element, and it will attempt to scroll you to the next one using the method I described. I hardcoded the offset values because I was still experimenting with it. However, I don't think this approach would work since it still feels off. This is because changing instantly speed in the middle of the animation will always be noticeable.
Right now, I think the best way you can mitigate the slow-scrolling feel that parallax scrolling is causing is by using a different easing function. After all, making the background pictures slower, is exactly what you're using parallax scrolling for.
The following code, when ran in your website, would make all animations use 'easeOutCirc' for their easing function by default, after some experimenting, I found it to be the one that makes the scrolling feel least odd:
// Add the jQuery-easing plugin, needed for the more sophisticated easing functions.
$.getScript('//cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js');
// Set easeOutCirc as the default easing.
jQuery.easing.def = 'easeOutCirc';
You can find more easing functions at this website
Once you're done experimenting, if you do decide on using an easing (you can use different ones for scrolling up and down), then you should probably keep the default easing as is, and just change the easing in the scroll animation by adding {easing: EASING_NAME} to your options hash in the scrollTo function. So your code will look something like this:
$.scrollTo($($(this).attr("href")), {
duration: 750,
easing: 'easeOutCirc'
});
I am newbie in JS. Right now i am working on an effect in which i want when page scroll first time then the natural motion animation starts but it's creating a problem because when i scroll the element animation became fast.
Check this more you got the idea.
http://jsfiddle.net/byvLy/
i know that this is a swinging box (figured it out due to the Math.sin())
however, you have to note that scrolling event is fired every few milliseconds during scrolling. in your code, you are calling animate and creating an interval every time the scroll event is fired. that's why your animation is jumpy;
try this instead:
$(function() {
$(window).on('scroll', function() {
swing.start('.cloud1, .cloud2');
});
var swing = (function() {
var animated = false;
function startAnimation(selector) {
if (!animated) {
var banner = $(selector);
var start = 0;
animated = true;
window.setInterval(function() {
banner.css('left', 100 * Math.sin(start) + 80);
start += 0.1;
}, 30);
}
}
return {
start: startAnimation
}
}());
});