I've built a very basic jQuery plugin that essentially positions a sprite image left by a set amount every x milliseconds to create an animation effect. The plugin at this stage is very basic and only has a few options, and it works pretty well.
Apart from that fact that it only fires once! I have multiple instance of the animation on one page and they all fire, but only ever once each.
Now I'm not expert on Javascript and only just managed to cobble this together but here's the code anyhow:
// Animation Plugin
(function($){
$.fn.anime = function(customOptions) {
// Default Options
var defaultOptions = {
slideWidth : 100,
frames : 10,
speed : 40,
minCycles : 1,
stopFrame : 0
};
// Set options to default
var options = defaultOptions;
// Merge custom options with defaults using the setOptions func
setOptions(customOptions);
// Merge current options with the custom option
function setOptions(customOptions) {
options = $.extend(options, customOptions);
};
return this.each(function() {
// Initialize the animation object
var $elem = $('img', this);
var frameCount = 0;
var currentSlideWidth = options.slideWidth;
var intervalID = null;
var cycleCount = 1;
var animate = function() {
if (frameCount < (options.frames -1)) {
$elem.css('left', '-' + currentSlideWidth + 'px');
frameCount++;
currentSlideWidth += options.slideWidth;
}
else {
if (cycleCount < options.minCycles)
{
frameCount = 0;
currentSlideWidth = options.slideWidth;
$elem.css('left', '-' + currentSlideWidth + 'px');
cycleCount++;
}
else
{
window.clearInterval(intervalID);
$elem.css('left', '0px');
}
}
cycleCount = 1;
};
$(this).bind('mouseover', function(){
var intervalID = window.setInterval(animate, options.speed);
});
});
};
})(jQuery);
The code used to call the actual plugin on a dom element is:
$('#animeBolt').anime({
frames: 50,
slideWidth: 62,
minCycles: 1,
speed: 30,
});
This is the html it is called on:
<div class="anime" id="animeBolt">
<img src="_assets/img/anime-bolt.png" alt="Lightning Bolt" width="3100" height="114">
</div>
And finally the css:
.anime {
position: absolute;
overflow: hidden; }
.anime img {
position: absolute;
left: 0;
top: 0; }
#animeBolt {
top: 2669px;
left: 634px;
width: 62px;
height: 116px; }
How do I get the plugin to fire repeatedly?
I've created and modified jsfiddle example using your code. It's working http://jsfiddle.net/9Yz9j/16/
I've change a couple of things:
added clearInterval on mouseover to preven multiple overlapping intervals
moved intervalID variable to outside of each function, and removed var keyword from mousover handler so the script will remember intervalID set on last mouseover
reseting the frameCount, cycleCount and currentSlideWidth variables on animation end (that was actually a clue thing to get animation going more than just once)
Hope that helps
Related
I have created a JavaScript Slideshow, but I don't know how to add the fade effect. Please tell me how to do it, and please tell in JavaScript only, no jQuery!
Code:
var imgArray = [
'img/slider1.jpg',
'img/slider2.jpg',
'img/slider3.jpg'],
curIndex = 0;
imgDuration = 3000;
function slideShow() {
document.getElementById('slider').src = imgArray[curIndex];
curIndex++;
if (curIndex == imgArray.length) { curIndex = 0; }
setTimeout("slideShow()", imgDuration);
}
slideShow();
Much shorter than Ninja's solution and with hardware accelerated CSS3 animation. http://jsfiddle.net/pdb4kb1a/2/ Just make sure that the transition time (1s) is the same as the first timeout function (1000(ms)).
Plain JS
var imgArray = [
'http://placehold.it/300x200',
'http://placehold.it/200x100',
'http://placehold.it/400x300'],
curIndex = 0;
imgDuration = 3000;
function slideShow() {
document.getElementById('slider').className += "fadeOut";
setTimeout(function() {
document.getElementById('slider').src = imgArray[curIndex];
document.getElementById('slider').className = "";
},1000);
curIndex++;
if (curIndex == imgArray.length) { curIndex = 0; }
setTimeout(slideShow, imgDuration);
}
slideShow();
CSS
#slider {
opacity:1;
transition: opacity 1s;
}
#slider.fadeOut {
opacity:0;
}
As an alternative. If you are trying to make a slider.
The usual approach is to animate a frame out and animate a frame in.
This is what makes the slide effect, and the fade effect work. Your example fades in. Which is fine, but maybe not what you really want once you see it working.
If what you really want is to animate images in and ...OUT you need something a little more complex.
To animate images in and out you must use an image element for each, then flip one out and flip one in. The images need to be placed on top of each other in the case of a fade, if you want to slide you lay them beside each other.
Your slideshow function then works the magic, but before you can do that you need to add all those images in your array into the dom, this is called dynamic dom injection and it's really cool.
Make sure you check the fiddle for the full working demo and code it's linked at the bottom.
HTML
<div id="slider">
// ...we will dynamically add your images here, we need element for each image
</div>
JS
var curIndex = 0,
imgDuration = 3000,
slider = document.getElementById("slider"),
slides = slider.childNodes; //get a hook on all child elements, this is live so anything we add will get listed
imgArray = [
'http://placehold.it/300x200',
'http://placehold.it/200x100',
'http://placehold.it/400x300'];
//
// Dynamically add each image frame into the dom;
//
function buildSlideShow(arr) {
for (i = 0; i < arr.length; i++) {
var img = document.createElement('img');
img.src = arr[i];
slider.appendChild(img);
}
// note the slides reference will now contain the images so we can access them
}
//
// Our slideshow function, we can call this and it flips the image instantly, once it is called it will roll
// our images at given interval [imgDuration];
//
function slideShow() {
function fadeIn(e) {
e.className = "fadeIn";
};
function fadeOut(e) {
e.className = "";
};
// first we start the existing image fading out;
fadeOut(slides[curIndex]);
// then we start the next image fading in, making sure if we are at the end we restart!
curIndex++;
if (curIndex == slides.length) {
curIndex = 0;
}
fadeIn(slides[curIndex]);
// now we are done we recall this function with a timer, simple.
setTimeout(function () {
slideShow();
}, imgDuration);
};
// first build the slider, then start it rolling!
buildSlideShow(imgArray);
slideShow();
Fiddle:
http://jsfiddle.net/f8d1js04/2/
you can use this code
var fadeEffect=function(){
return{
init:function(id, flag, target){
this.elem = document.getElementById(id);
clearInterval(this.elem.si);
this.target = target ? target : flag ? 100 : 0;
this.flag = flag || -1;
this.alpha = this.elem.style.opacity ? parseFloat(this.elem.style.opacity) * 100 : 0;
this.elem.si = setInterval(function(){fadeEffect.tween()}, 20);
},
tween:function(){
if(this.alpha == this.target){
clearInterval(this.elem.si);
}else{
var value = Math.round(this.alpha + ((this.target - this.alpha) * .05)) + (1 * this.flag);
this.elem.style.opacity = value / 100;
this.elem.style.filter = 'alpha(opacity=' + value + ')';
this.alpha = value
}
}
}
}();
this is how to use it
fadeEffect.init('fade', 1, 50) // fade in the "fade" element to 50% transparency
fadeEffect.init('fade', 1) // fade out the "fade" element
Much shorter answer:
HTML:
<div class="js-slideshow">
<img src="[your/image/path]">
<img src="[your/image/path]" class="is-shown">
<img src="[your/image/path]">
</div>
Javascript:
setInterval(function(){
var $container = $('.js-slideshow'),
$currentImage = $container.find('.is-shown'),
currentImageIndex = $currentImage.index() + 1,
imagesLength = $container.find('img').length;
$currentImage.removeClass('is-shown');
$currentImage.next('img').addClass('is-shown');
if ( currentImageIndex == imagesLength ) {
$container.find('img').first().addClass('is-shown');
}
}, 5000)
SCSS
.promo-banner {
height: 300px;
width: 100%;
overflow: hidden;
position: relative;
img {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
z-index: -10;
transition: all 800ms;
&.is-shown {
transition: all 800ms;
opacity: 1;
z-index: 10;
}
}
}
I'm writing a jquery plugin the code below is not working (I mean the setTimeout is working but nothing is append)
var self = this;
for (var i=0; i<=10; i++) {
setTimeout(function() {
self.append(bubble);
}, 1000);
}
And the code below is working:
for (var i=0; i<=10; i++) {
this.append(bubble);
}
this is a jquery selection. I really don't get what's going on. It can't be scope issue .. can it be ? I don't get it. Thanks in advance for you help
Edit: bubble is a simple div (" ")
Below the whole plugin code:
(function($) {
'use strict';
$.fn.randomBubble = function(options) {
var self = this;
var settings = $.extend({
color: 'blue',
backgroundColor: 'white',
maxBubbleSize: 100
}, options);
var frame = {
height: this.height(),
width: this.width(),
}
var bubble = "<div class='randomBubble'> </div>";
this.getLeft = function(width) {
var left = Math.random() * frame.width;
if (left > (frame.width / 2)) {
left -= width;
} else {
left += width;
}
return left
}
this.getTop = function(height) {
var top = Math.random() * frame.height;
if (top > (frame.height / 2)) {
top -= height;
} else {
top += height;
}
return top
}
this.removeBubbles = function() {
var currentBubbles = this.find('.randomBubble');
if (currentBubbles.length) {
currentBubbles.remove();
}
}
window.oh = this;
for (var i = 0; i <= 10; i++) {
var timer = Math.random() * 1000;
setTimeout(function() {
window.uh = self;
self.append(bubble);
console.log("oh");
}, 1000);
}
this.randomize = function() {
//self.removeBubbles();
var allBubbles = this.find('.randomBubble');
allBubbles.each(function(i, el) {
var height = Math.random() * settings.maxBubbleSize;
var width = height;
$(el).css({
color: settings.color,
backgroundColor: settings.backgroundColor,
zIndex: 1000,
position: 'absolute',
borderRadius: '50%',
top: self.getTop(height),
left: self.getLeft(width),
height: height,
width: width
});
});
}
this.randomize();
//var run = setInterval(self.randomize, 4000);
return this.find('.randomBubble');
}
})(jQuery);
Because the bubbles are appended later due to the setTimeout(), this selector in your randomize() function comes up empty:
var allBubbles = this.find('.randomBubble');
That is why appending them in a simple for loop works fine.
If you really want to use the setTimout() to append your bubbles, one option is to style them when you add them:
setTimeout(function() {
var height = Math.random() * settings.maxBubbleSize;
var width = height;
var b = $(bubble).css({
color: settings.color,
backgroundColor: settings.backgroundColor,
zIndex: 1000,
position: 'absolute',
borderRadius: '50%',
top: self.getTop(height),
left: self.getLeft(width) ,
height: height,
width: width
});
self.append(b);
}, 1000);
Fiddle
Is it because you still call randomize() right away, even when you postpone the creation for one second?
You will also return an empty selection in that case, for the same reason.
Also, you probably want to use the timer variable in setTimeout() instead of hardcoding all to 1000 ms?
this is a javascript selection, the selector in jquery is $(this)
$.fn.randomBubble = function(options) {
var self = $(this);
};
I have this function
var flakeImage = new Image();
function loadImage(){
flakeImage.onload = drawFlake;
flakeImage.src = "game/snowflake.png";
}
and this one that initialises the image
function initFlake() {
flakex = Math.random()*(WIDTH-140)+70;
flakey = (Math.random()*20)+70;
flakes = Math.random()*40;
}
and this one that updates the image so it will look like it s actually falling
function updateFlake(){
flakey = flakey + 1;
}
and also the draw function
function drawFlake() {
context.drawImage(flakeImage, flakex, flakey, flakes, flakes);
}
I want to make it look like it s snowing in my canvas. I can't use a for loop because it will just modify the same picture . I tried making a big array with the same picture, but in the end I don't get that effect .. because the images have to keep falling randomly . How should I combine an array with that image stored at random positions with an interval to get that effect ?
You can use setInterval().
doSomething: function() {
clearInterval(myInterval);
myInterval = setInterval(function() {
// update flake position
updateFlake();
}, timeInMilliseconds);
},
This runs the function given as a parameter every timeInMilliseconds milliseconds. The setInterval() function returns an ID which you can pass to clearInterval() in order to stop updating.
You can also pass a function directly.
doSomething: function() {
clearInterval(myInterval);
myInterval = setInterval(updateFlake, timeInMilliseconds);
},
EDIT: OP, you can do this just fine without relying on HTML5 canvas, instead use DOM elements:
JS BIN
Javascript:
function update () {
var myInterval = null;
clearInterval(myInterval);
myInterval = setInterval(function() {
// update flake position
$("#holder > img").each(function() {
if ($(this).position().top >= $(window).height())
$(this).remove();
else
$(this).css({top: $(this).position().top+=3});
});
}, 50); //update each of the drawn children
}
function drawFlake() {
clearInterval(myInterval);
var myInterval = null;
myInterval = setInterval(function() {
var randX = (Math.floor((Math.random() * $(window).width()) + 1));
var $img = $('<img>');
$img.attr('src','flake.png');
$("#holder").append($img);
$img.css({left: randX, top: 0, position:'absolute'});
}, 2000); //draw a new flake every 2 seconds
update();
}
HTML:
<body onload="drawFlake()">
<div id="holder"></div>
</body>
CSS:
body {
background: white;
}
#holder {
position: relative;
width: 100%;
height: 100%;
}
So I had this slideshow working perfectly fine using .fade and .appear, but the people I'm doing this for weren't satisfied with that and they want it to behave like THIS ONE where the text overlay drops out and the image slides sideways and then the overlay pops back up.
script.aculo.us has a .DropOut, but not an opposite function so I am working with one I found elsewhere.
This is the code I have so far, but the slideshow isn't doing anything. It just loads the first image and overlay and that's it. No animation. I'm sure I have made some syntax mistakes somewhere, but it's valid and not giving any errors.
JSFiddle (includes html and css as well)
Note that this still doesn't accomodate the new image sliding in from the right, which I haven't found an effect for yet. For now I'd just be happy if this worked as is.
var i = 0;
var image_slide = new Array('image-1', 'image-2');
var overlay_slide = new Array('overlay-1', 'overlay-2');
var NumOfImages = image_slide.length;
var wait = 8000;
function SwapImage(x, y) {
$(overlay_slide[y]).DropOut({
duration: 0.5
});
$(overlay_slide[y]).fade({
duration: 0.1
});
$(image_slide[x]).appear({
duration: 0.5
});
$(image_slide[y]).Move({
x: -1000,
y: 0,
mode: 'relative',
duration: 0.5
});
$(overlay_slide[x]).appear({
duration: 0.1
});
$(overlay_slide[x]).Emerge({
duration: 0.5
});
}
function StartSlideShow() {
play = setInterval(Play, wait);
}
function Play() {
var imageShow, imageHide;
imageShow = i + 1;
imageHide = i;
if (imageShow == NumOfImages) {
SwapImage(0, imageHide);
i = 0;
} else {
SwapImage(imageShow, imageHide);
i++;
}
}
Effect.Emerge = function (element) {
element = $(element);
var oldStyle = {
top: element.getStyle('top'),
left: element.getStyle('left')
};
var position = element.positionedOffset();
return new Effect.Parallel(
[new Effect.Move(element, {
x: 0,
y: -100,
sync: true
}),
new Effect.Opacity(element, {
sync: true,
from: 0.0,
to: 1.0
})],
Object.extend({
duration: 0.5,
beforeSetup: function (effect) {
effect.effects[0].element.show().makePositioned().setStyle({
top: (position.top + 100) + 'px'
});
},
afterFinishInternal: function (effect) {
effect.effects[0].element.undoPositioned().setStyle(oldStyle);
}
}, arguments[1] || {}));
};
StartSlideShow();
You were on the right track just needed to play with the Effects library a little more, the Emerge function was too much and you needed to go simpler.
I've updated your jsfiddle
http://jsfiddle.net/aB79G/3/
basically you need to prepare the next slide off to the right and move both pictures at once with Effect.Morph instead of Effect.Move
Also notice how I used the afterFinish callback that allows you to string effects together.
Looking for a little help with this slider that came with a Magento Template I purchased.
I'm trying to add a pause on hover and resume on mouse out but am very new to getting my hands this dirty with the JavaScript.
Hoping for a push in the right direction
Here is the code I'm working with
function decorateSlideshow() {
var $$li = $$('#slideshow ul li');
if ($$li.length > 0) {
// reset UL's width
var ul = $$('#slideshow ul')[0];
var w = 0;
$$li.each(function(li) {
w += li.getWidth();
});
ul.setStyle({'width':w+'px'});
// private variables
var previous = $$('#slideshow a.previous')[0];
var next = $$('#slideshow a.next')[0];
var num = 1;
var width = ul.down().getWidth() * num;
var slidePeriod = 3; // seconds
var manualSliding = false;
// next slide
function nextSlide() {
new Effect.Move(ul, {
x: -width,
mode: 'relative',
queue: 'end',
duration: 1.0,
//transition: Effect.Transitions.sinoidal,
afterFinish: function() {
for (var i = 0; i < num; i++)
ul.insert({ bottom: ul.down() });
ul.setStyle('left:0');
}
});
}
// previous slide
function previousSlide() {
new Effect.Move(ul, {
x: width,
mode: 'relative',
queue: 'end',
duration: 1.0,
//transition: Effect.Transitions.sinoidal,
beforeSetup: function() {
for (var i = 0; i < num; i++)
ul.insert({ top: ul.down('li:last-child') });
ul.setStyle({'position': 'relative', 'left': -width+'px'});
}
});
}
function startSliding() {
sliding = true;
}
function stopSliding() {
sliding = false;
}
// bind next button's onlick event
next.observe('click', function(event) {
Event.stop(event);
manualSliding = true;
nextSlide();
});
// bind previous button's onclick event
previous.observe('click', function(event) {
Event.stop(event);
manualSliding = true;
previousSlide();
});
// auto run slideshow
new PeriodicalExecuter(function() {
if (!manualSliding) nextSlide();
manualSliding = false;
}, slidePeriod);
}
Now I'm guessing the best way would be to manipulate a hover or mouseover observer similar to the next and previous ones to stop and start but I'm just not sure on how to set this up.
Would appreciate a push in the right direction!
Edit ....
So I'm getting much closer but I seem to have a problem yet hopefully someone who know about prototype can help.
I got it to work by adding this variable
var stopSliding = false;
and then adding an if like so
function nextSlide() {
if (!stopSliding) {
new Effect.Move(ul, {
x: -width,
mode: 'relative',
queue: 'end',
duration: 1.0,
//transition: Effect.Transitions.sinoidal,
afterFinish: function() {
for (var i = 0; i < num; i++)
ul.insert({ bottom: ul.down() });
ul.setStyle('left:0');
}
});
}
}
// previous slide
function previousSlide() {
if (!stopSliding) {
new Effect.Move(ul, {
x: width,
mode: 'relative',
queue: 'end',
duration: 1.0,
//transition: Effect.Transitions.sinoidal,
beforeSetup: function() {
for (var i = 0; i < num; i++)
ul.insert({ top: ul.down('li:last-child') });
ul.setStyle({'position': 'relative', 'left': -width+'px'});
}
});
}
}
then
ul.observe('mouseover', function(event) {
stopSliding = true;
});
ul.observe('mouseout', function(event) {
stopSliding = false;
});
This method works but only Safari will auto start my slideshow now and firefox needs interaction to trigger a start.
However I did find that switching the var to true at the start and switching around the order of the mouseovers it then auto starts fine in Firefox and not in Safari.
Had enough for this evening.
So I managed to get it to work in both Firefox, Safari, IE etc using:
Event.observe( window, 'load', function() { stopSliding = false; });
This ensures that my variable "stopSliding" is set.