Showing animations through page scrolling - javascript

So I've been trying to follow this tutorial http://www.netmagazine.com/tutorials/getting-animations-trigger-right-time and realised those effects only work for elements that contain images. I wanted to get this effect on elements that don't contain images (i.e: a block of text) but couldn't make it work. Does anyone have any idea on how to achieve this? Thank you guys!
Here's the script:
<script>
if (Modernizr.csstransitions) {
function preloadImages(imgs, callback) {
var cache = [],
imgsTotal = imgs.length,
imgsLoaded = 0;
$(imgs).each(function (i, img) {
var cacheImage = document.createElement('img');
cacheImage.onload = function () {
if (++imgsLoaded == imgsTotal) callback();
};
cacheImage.src = $(img).attr('src');
cache.push(cacheImage);
});
};
$.fn.trans = function () {
var t = arguments[0],
d = arguments[1] || '';
if (t) {
$.each(this, function (i, e) {
$(['-webkit-', '-moz-', '-o-', '-ms-', '']).each(function (i, p) {
$(e).css(p + 'transition' + d, t);
});
});
}
};
document.write('<link rel="stylesheet" href="animations.css" />');
$(function(){
//preload images contained within elements that need to animate
preloadImages($('.services img, .featured img'), function () {
$('.services, .featured').appear({
once: true,
forEachVisible: function (i, e) {
$(e).data('delay', i);
},
appear: function () {
var delay = 150,
stagger = 800,
sequential_delay = stagger * parseInt($(this).data('delay')) || 0;
$(this).children().each(function (i, e) {
$(e).trans(i * delay + sequential_delay + 'ms', '-delay');
});
$(this).removeClass('animationBegin');
}
});
});
});
}
</script>

You can find Lightweight jQuery scrolling script that handles viewport css3 animations SEE HERE
I have created one demo which show how it works, scroll the fiddle and you will see the animation when element is in viewport.
SEE THE DEMO HERE

Related

How to do line preloader with jquery

I tried this code for a line preloader but it didn't work and i don't know where is the problem.
var $preload = $("<div class='preloader'><div class='percent'></div><div class='line-parent'><div class='line'></div></div></div>");
$("body").prepend($preload);
var $imgs = $("img");
var imgsLoaded = 0;
var ratio = 0;
$imgs.each(function(){
$this = $(this);
$this.load(function(){
imgsLoaded++;
ratio = imgsLoaded / $imgs.length;
$(".percent").html(ratio / 0.01 + "%");
$(".line").animate({
width : ratio / 0.01 + "%"
});
}, function(){
if(ratio === 1){
$(".preloader").fadeOut();
}
});
});
I belive you want to show 100% wenn all images are loaded and do some action. First load event will not work if is atteched after image is already loaded.
I sugest to check for each img comlete and naturalWidth property every 100ms (with setInterval).
Loader = (function() {
var list, img_length, loaded, interval;
function _check_one(i) { o = list[i]; if (o.complete == true && o.naturalWidth > 0) { loaded++; delete list[i]; } };
function _procent(l, a) { console.log((100*loaded/img_length) + '%'); }
function _check_list() { for(var i in list) {_check_one(i)};_procent();_kill(); };
function _kill() { if(img_length <= loaded) clearInterval(interval); }
function _start() { loaded = 0; list = $('img'); img_length = list.length;
if(img_length != loaded) interval = setInterval(_check_list, 100); }
return { start: _start }
})();
Now at end of the body or in $(document).ready(..) you need to call Loader.start();
Or put all images source (src) in data-src attribite, attach load events and copy data-scr to src attribite. In body all relevant images looks like this:
<img data-src="some url">...
In Script Tag:
$('img').on('load', function() {
// Your action.
}).each(function() { var img = $(this); img.attr('src', img.attr('data-src')); });

Something is wrong with this 3-panes splitter

I'm trying to resize 3 panes inside a container , when resized, they shouldn't exceed the width of container (do not get out of it)..
But in this snippet http://jsfiddle.net/KXsrd/ , when I resize the second one, it successfully decreases the next element or increases depending on the direction. but sometimes it causes the next sibling to drop below the container and although the next sibling decreases its size when I expand one , it keeps 'shaking' ..
Here is the main function:
$(function () {
$('.col').each(function (e, u) {
$(u).data({ow: $(u).width()});
})
$(".col").resizable(
{
handles: 'e',
start: function (e) {
},
resize: function (e, ui) {
var el = ui.element;
var parent = el.parent();
var next = el.next();
var siblings = el.siblings();
var siblingsWidth = (function () {
var totalWidth = 0;
_.each(siblings, function (el) {
totalWidth += $(el).width();
});
return totalWidth;
})();
var currentSize = ui.size;
if (currentSize.width > $(el).data('ow')) {
next.css({width: '-=' + (currentSize.width - $(el).data('ow'))});
} else {
next.css({width: '+=' + ( $(el).data('ow') - currentSize.width )});
}
if (currentSize.width + siblingsWidth >= $(parent).width()) {
$(el).width($(parent).width() - siblingsWidth)
}
$(el).data({ow: currentSize.width});
},
stop: function (e, ui) {
}
});
});
Any help would be much appreciated ..
(PS. I tried many plugins for resizable panes. but they all work on 2 panes only)

How do I add a class of "active" to a visible slide within a slideshow?

I have a page that is mainly a large slider, I am using unslider (unslider.com) right now, but would be willing to switch to a different slider if it provides the functionality I need. The goal is to have the slide show move through my different projects. At the bottom of the page, will be an arrow button that when clicked will slide in the specific project details for the current or visible slide. I was planning to load the project details into a hidden div via ajax based on the data attribute of the "active" slide.
The problem I am having is that I don't know how to add a class of "active" to the visible slide. I am fairly new, but I believe I will need to use javascript or jquery to change the "active" class appropriately. Here is the HTML and jQuery for the slider:
<div class="banner">
<ul>
<li class="slide_1" data-link="ajaxed_content_url">
<img src="slide_1.jpg" />
</li>
<li class="slide_2" data-link="ajaxed_content_url">
<img src="slide_2.jpg" />
</li>
<li class="slide_3" data-link="ajaxed_content_url">
<img src="slide_3.jpg" />
</li>
</ul>
<a class="details_btn" href="#"></a>
</div>
<script>
$('.banner').unslider({
speed: 500,
delay: 5000,
complete: function() {},
keys: true,
dots: true,
fluid: true
});
</script>
When the slider is rendered in the browser, an "active" class is not added to the code. Any help would be greatly appreciated.
Unslider simply animates the ul, so it does not actually change the picture li's. However, it DOES add an active class to the dots. Digging through the source code, I found where it adds the active class to the dots and edited in a similar line that should apply an active class to the pictures. Here is the edited source code:
/**
* Unslider by #idiot
*/
(function($, f) {
// If there's no jQuery, Unslider can't work, so kill the operation.
if(!$) return f;
var Unslider = function() {
// Set up our elements
this.el = f;
this.items = f;
// Dimensions
this.sizes = [];
this.max = [0,0];
// Current inded
this.current = 0;
// Start/stop timer
this.interval = f;
// Set some options
this.opts = {
speed: 500,
delay: 3000, // f for no autoplay
complete: f, // when a slide's finished
keys: !f, // keyboard shortcuts - disable if it breaks things
dots: f, // display ••••o• pagination
fluid: f // is it a percentage width?,
};
// Create a deep clone for methods where context changes
var _ = this;
this.init = function(el, opts) {
this.el = el;
this.ul = el.children('ul');
this.max = [el.outerWidth(), el.outerHeight()];
this.items = this.ul.children('li').each(this.calculate);
// Check whether we're passing any options in to Unslider
this.opts = $.extend(this.opts, opts);
// Set up the Unslider
this.setup();
return this;
};
// Get the width for an element
// Pass a jQuery element as the context with .call(), and the index as a parameter: Unslider.calculate.call($('li:first'), 0)
this.calculate = function(index) {
var me = $(this),
width = me.outerWidth(), height = me.outerHeight();
// Add it to the sizes list
_.sizes[index] = [width, height];
// Set the max values
if(width > _.max[0]) _.max[0] = width;
if(height > _.max[1]) _.max[1] = height;
};
// Work out what methods need calling
this.setup = function() {
// Set the main element
this.el.css({
overflow: 'hidden',
width: _.max[0],
height: this.items.first().outerHeight()
});
// Set the relative widths
this.ul.css({width: (this.items.length * 100) + '%', position: 'relative'});
this.items.css('width', (100 / this.items.length) + '%');
if(this.opts.delay !== f) {
this.start();
this.el.hover(this.stop, this.start);
}
// Custom keyboard support
this.opts.keys && $(document).keydown(this.keys);
// Dot pagination
this.opts.dots && this.dots();
// Little patch for fluid-width sliders. Screw those guys.
if(this.opts.fluid) {
var resize = function() {
_.el.css('width', Math.min(Math.round((_.el.outerWidth() / _.el.parent().outerWidth()) * 100), 100) + '%');
};
resize();
$(window).resize(resize);
}
if(this.opts.arrows) {
this.el.parent().append('<p class="arrows"><span class="prev">â†</span><span class="next">→</span></p>')
.find('.arrows span').click(function() {
$.isFunction(_[this.className]) && _[this.className]();
});
};
// Swipe support
if($.event.swipe) {
this.el.on('swipeleft', _.prev).on('swiperight', _.next);
}
};
// Move Unslider to a slide index
this.move = function(index, cb) {
// If it's out of bounds, go to the first slide
if(!this.items.eq(index).length) index = 0;
if(index < 0) index = (this.items.length - 1);
var target = this.items.eq(index);
var obj = {height: target.outerHeight()};
var speed = cb ? 5 : this.opts.speed;
if(!this.ul.is(':animated')) {
// Handle those pesky dots
_.el.find('.dot:eq(' + index + ')').addClass('active').siblings().removeClass('active');
//HERE IS WHAT I ADDED
_.el.find('ul li:eq(' + index + ')').addClass('active').siblings().removeClass('active');
this.el.animate(obj, speed) && this.ul.animate($.extend({left: '-' + index + '00%'}, obj), speed, function(data) {
_.current = index;
$.isFunction(_.opts.complete) && !cb && _.opts.complete(_.el);
});
}
};
// Autoplay functionality
this.start = function() {
_.interval = setInterval(function() {
_.move(_.current + 1);
}, _.opts.delay);
};
// Stop autoplay
this.stop = function() {
_.interval = clearInterval(_.interval);
return _;
};
// Keypresses
this.keys = function(e) {
var key = e.which;
var map = {
// Prev/next
37: _.prev,
39: _.next,
// Esc
27: _.stop
};
if($.isFunction(map[key])) {
map[key]();
}
};
// Arrow navigation
this.next = function() { return _.stop().move(_.current + 1) };
this.prev = function() { return _.stop().move(_.current - 1) };
this.dots = function() {
// Create the HTML
var html = '<ol class="dots">';
$.each(this.items, function(index) { html += '<li class="dot' + (index < 1 ? ' active' : '') + '">' + (index + 1) + '</li>'; });
html += '</ol>';
// Add it to the Unslider
this.el.addClass('has-dots').append(html).find('.dot').click(function() {
_.move($(this).index());
});
};
};
// Create a jQuery plugin
$.fn.unslider = function(o) {
var len = this.length;
// Enable multiple-slider support
return this.each(function(index) {
// Cache a copy of $(this), so it
var me = $(this);
var instance = (new Unslider).init(me, o);
// Invoke an Unslider instance
me.data('unslider' + (len > 1 ? '-' + (index + 1) : ''), instance);
});
};
})(window.jQuery, false);
I don't think you will be able to add an active class. However, The complete function is executed after every slide animation, try something like this:
<script>
var NB_OF_SLIDES = ...;
var i = 0;
loadAjaxForSlide i;
$('.banner').unslider({
speed: 500,
delay: 5000,
complete: function() {
i++;
if(i >= NB_OF_SLIDES){
i = 0;
}
loadAjaxForSlide i;
},
keys: true,
dots: true,
fluid: true
});
</script>
But I wouldn't consider loading data via Ajax after every slide animation, it's kinda messy. Try loading everything at once at page load.

$.each animation running separately

I'm trying to run each animation function one after the other instead of all at once.
This is what I've got so far:
$(document).ready(function(){
var bars = $('.bar');
bars.each(function(){
var widthpercent = $(this).attr("data-percent");
$(this).fadeIn();
$(this).animate({width:widthpercent},500);
});
});
I've tried using .delay() and setTimeout() in various combinations to no avail.
Could anyone point me in the right direction? Thank you!
It sounds to me like you're looking for animate's complete function. You can write a recursive function to keep calling the function in the complete function until all the items have been animated. To simplify: every time one element is animated, a callback is fired that animates the next element. That is the purpose of the complete parameter, so I'm certain that is what you're looking for.
Here's an example you can adapt to your specific needs.
Live demo here (click).
var $divs = $('div');
function animate(element) {
$(element).animate({height: '30px'}, {
complete: function() {
if (current < $divs.length-1) {
++current;
animate($divs[current]);
}
}
});
}
var current = 0;
animate($divs[current]);
Further, this same logic can be applied to your fadeIn. Just wrap fadeIn's callback around that logic, like this:
Live demo here (click).
var $divs = $('div');
function animate(element) {
$(element).fadeIn(function() { //now the animation is a callback to the fadeIn
$(element).animate({height: '70px'}, {
complete: function() {
if (current < $divs.length-1) {
++current;
animate($divs[current]);
}
}
});
});
}
var current = 0;
animate($divs[current]);
And here's your code: live demo here (click).
$(document).ready(function(){
var $divs = $('.bar');
function animate(element) {
$(element).fadeIn(function() { //you could unwrap this depending on what you're looking for
var widthpercent = $(element).attr("data-percent");
$(element).animate({
width:widthpercent,
duration: '500ms'
}, {
complete: function() {
if (current < $divs.length-1) {
++current;
animate($divs[current]);
}
}
});
}); //end fadeIn callback
}
var current = 0;
animate($divs[current]);
});
Try this:
var animate = function (el) {
return function () {
var widthpercent = el.data('percent');
el.fadeIn();
el.animate({
width: widthpercent
}, 500);
}
}
var bars = $('.bar');
bars.each(function (index) {
var $this = $(this);
setTimeout(animate($this), index * 500);
});
Fiddle
$(document).ready(function(){
var bars = $('.bar');
bars.each(function(i){
var widthpercent = $(this).attr("data-percent");
$(this).delay(i*800).animate({width:widthpercent,opacity:1,},500);
});
});
This will animate after delaying 800 * i milliseconds.
See this JSFiddle example.

Onload method is calling itself more than once on successive reloads to application (var image = new Image(), javascript issue)

I don't understand why onload method(see the code below) is calling itself more than once in IE8 and IE9 browsers on consecutive reloads.
I guess, onload method is fired more than once that is causing this image to render(appear) more than once in IE8,IE9. Below js code is written by my senior Front-Ender (he is using his own written js library for all .append(), anim(), ... methods). The code is working as expected in Firefox, Chrome but the image(html code) is rendering more than once while reloading with a IE8 or IE9 browser.
Some Quick Info:
waitingFor() - Periodically scans dom for matching elements,
mdiTog - The image that is appearing more than once..
How it works:
The div attribute Class="suggestions contextSuggesions" is inserted in the body element and contains an unordered list (ul, li, ul) that toggles between opacity 0 and 1 if you click on the image (mdiTog).
Below is the image of Mditog appearing more than once and it's appearance is random:
waitingFor('.viewport .homePage', function () {
var mdiTog = new Image();
mdiTog.onload = function () {
var $mdi = M$('.mdiTabs ul'),
$contextMenu = M$($body.append('<div class="suggestions contextSuggesions" style="display:none"></div>')),
$toggle = M$($mdi.append('<li class="mdiTog" style="display:none"></li>')),
visible = false,
active = false;
var hideCtx = function () {
if (visible) {
$contextMenu.anim('', {opacity: 100}, {opacity: 0}, 250, function () {
$contextMenu.css({display: 'none'});
});
visible = false;
}
};
$toggle.css({opacity: 0, display: 'inline-block'}).anim('', {opacity: 0}, {opacity: 100}, 1000).on('click', function () {
var togOS = $toggle.offset(),
menu = '';
M$('li', $mdi.get()).each(function (el, idx) {
if (idx > 1) {
menu += '<li rel="' + idx + '">' + $mdi.text(null, el) + '</li>';
}
});
$contextMenu.css({opacity: 0, display: 'block', top: togOS.top + 'px', left: (togOS.left - 20) + 'px'}).anim('', {opacity: 0}, {opacity: 100}, 400, function () {
visible = true;
}).html('<ul>' + menu + '<li class="uiUtil hr" onclick="ApolloUI.widgets.expose.show()">' + translate('previewAll') + '</li><li class="uiUtil" onclick="ApolloUI.mdi.closeAll()">' + translate('closeAll') + '</li></ul>');
});
$contextMenu.on('click', function (e) {
var target = $contextMenu.eventTarget(e);
if (target.tagName === 'LI' && target.className.indexOf('uiUtil') === -1) {
$contextMenu.fire('click', M$('.mdiTabs li').get(parseInt(target.getAttribute('rel'), 10)));
}
hideCtx();
}).on('mouseleave', function () {
active = false;
}).on('mouseenter', function () {
active = true;
});
setInterval(function () {
if (!active && visible) {
hideCtx();
}
}, 1000);
};
mdiTog.src = 'media/img/ico_16_context.png';
});
I do have to close this issue as soon as possible, so any suggestions/modifications/addition in this code to force onload to execute only once on even on several consecutive reloads in IE8/IE9 browsers are welcome.
If the onload fires twice, you should use a guard against executing the handler more than once:
var mdiTog = new Image();
var hasFired = false;
mdiTog.onload = function () {
if (hasFired)
return;
hasFired = true;
…
};
Or, if you're using a jQuery-like library, you could try the one event binding method:
var mdiTog = new Image();
M$(mdiTog).one("load", function () {
…
});

Categories

Resources