I have list that scrolls up using velocity. I want to play sound each time, first visible item of the list scrolled up.
<div class="viewport" data-winner="">
<ul class="participants-holder container" id="ph">
<li>...<li> //many li elements
</ul>
</div>
moveToEl(name) {
...
$(container).velocity({
translateY: -(offsetToScroll)+'px'
}, {
duration: 15000,
easing: [.74,0,.26,1],
complete: (el) => {
...
// complete animation callback
},
progress: (els, complete, remaining, start, tweenVal) => {
console.log(Math.floor(complete * 100) + '%')
// I think some check should do during progress animation
}
})
}
How to handle event or track changes when each element or entire list are scrolled up by certain pixels, for instance 62px. How can I detect this and call callback function on this happened.
You can find the current TranslateY using something like
+this.container.style.transform.replace(/[^0-9.]/g, '');
from https://stackoverflow.com/a/42267490/1544886, and compare it to the previous value plus an offset.
In the Roulette class add this.prevTranslatePos = 0.0; for storing the old value.
progress: (els, complete, remaining, start) => {
// from https://stackoverflow.com/a/42267490/1544886
var translatePos = +this.container.style.transform.replace(/[^0-9.]/g, '');
if (translatePos >= (this.prevTranslatePos + 62))
{
//console.log(translatePos, this.prevTranslatePos);
this.prevTranslatePos = translatePos;
this.sound.pause();
this.sound.currentTime = 0;
this.sound.play();
}
}
Demo applied to the 'Go To' button only: http://codepen.io/anon/pen/yMXwgd?editors=1010
Note that the sound cuts out when it runs too quickly, but that could be handled a few different ways.
Add a scroll eventListener to the parent element of the list (I believe it's participants-holder in your case), and within that do a check for whether the right amount of pixels have moved since the last check. Store the current position, and compare it to the last time you moved the desired amount.
Hope that helps!
Related
I'm trying to recreate this slider: https://bewegen.com/en its under the Accelerating Bike Share section.
I managed to create the count slider but having trouble getting it back to count 1 when it hits the end count of 9 - smoothly have it loop back to the start.
How to get the count to loop back to the start like in the example above?
Here is my codepen work: https://codepen.io/harp30/pen/GYEXXe
Thank you.
You can modify your next() function like this:
function next() {
count = count + 1;
if (count > num.length - 1) {
// Clone first item and add it to the end
var clone = num[0].cloneNode(true);
countItemNumberHolder.appendChild(clone);
// Let the transition happen normally
TweenMax.to(countItemNumberHolder, 0.4, { y: -289 * count });
// Wait for transition to finish and reset transform to 0 without animation
setTimeout(function() {
TweenMax.to(countItemNumberHolder, 0, { y: 0 });
countItemNumberHolder.removeChild(clone);
}, 300);
// Now reset counter
count = 0;
} else {
TweenMax.to(countItemNumberHolder, 0.4, { y: -289 * count });
}
}
Essentially, when you reach the last element, clone the first element and let the transition happen normally. Once the transition is complete, reset your transform to 0 without animation, and remove the cloned element. Similarly, you can do the same for the previous button.
However, you will probably want to remove and add the event listeners during the transition, otherwise, if the user clicks rapidly, it might lead to unexpected behavior.
A much cleaner implementation of an infinite carousel can be found here:
https://codepen.io/matthewwilliams/pen/Vayrjv
This may be a little too specific, but I have a jquery slider that I am using <p> classes instead of images to cycle through customer quotes. Basically the problem I am running into right now is when it is static and non moving (JS code is commeneted out) they are aligned how I want them to be. As soon as the JS is un commented, they stretch out of view and you just see a white box?
Any ideas?
How I want each panel to look like:
jsfiddle
So I sort of made this my Friday project. I've changed a whole lot of your code, and added a vertical-align to the quotes and authors.
Here's the fiddle http://jsfiddle.net/qLca2fz4/49/
I added a whole lot of variables to the top of the script so you could less typing throughout.
$(document).ready(function () {
//rotation speed and timer
var speed = 5000;
var run = setInterval(rotate, speed);
var slides = $('.slide');
var container = $('#slides ul');
var elm = container.find(':first-child').prop("tagName");
var item_width = container.width();
var previous = 'prev'; //id of previous button
var next = 'next'; //id of next button
Since you used a % based width I'm setting the pixel widths of the elements in case the screen is reszed
slides.width(item_width); //set the slides to the correct pixel width
container.parent().width(item_width);
container.width(slides.length * item_width); //set the slides container to the correct total width
As you had, I'm rearranging the slides in the event the back button is pressed
container.find(elm + ':first').before(container.find(elm + ':last'));
resetSlides();
I combined the prev and next click events into a single function. It checks for the ID of the element targeted in the click event, then runs the proper previous or next functions. If you reset the setInterval after the click event your browser has trouble stopping it on hover.
//if user clicked on prev button
$('#buttons a').click(function (e) {
//slide the item
if (container.is(':animated')) {
return false;
}
if (e.target.id == previous) {
container.stop().animate({
'left': 0
}, 1500, function () {
container.find(elm + ':first').before(container.find(elm + ':last'));
resetSlides();
});
}
if (e.target.id == next) {
container.stop().animate({
'left': item_width * -2
}, 1500, function () {
container.find(elm + ':last').after(container.find(elm + ':first'));
resetSlides();
});
}
//cancel the link behavior
return false;
});
I've found mouseenter and mouseleave to be a little more reliable than hover.
//if mouse hover, pause the auto rotation, otherwise rotate it
container.parent().mouseenter(function () {
clearInterval(run);
}).mouseleave(function () {
run = setInterval(rotate, speed);
});
I broke this in to its own function because it gets called in a number of different places.
function resetSlides() {
//and adjust the container so current is in the frame
container.css({
'left': -1 * item_width
});
}
});
//a simple function to click next link
//a timer will call this function, and the rotation will begin :)
And here's your rotation timer.
function rotate() {
$('#next').click();
}
It took me a little bit, but I think I figured out a few things.
http://jsfiddle.net/qLca2fz4/28/
First off, your console was throwing a few errors: first, that rotate wasn't defined and that an arrow gif didn't exist. Arrow gif was probably something you have stored locally, but I changed the 'rotate' error by changing the strings in the code here to your actual variables.
So, from:
run = setInterval('rotate()', speed);
We get:
run = setInterval(rotate, speed);
(No () based on the examples here: http://www.w3schools.com/jsref/met_win_setinterval.asp)
But I think a more important question is why your text wasn't showing up at all. It's because of the logic found here:
$('#slides ul').css({'left' : left_value});
You even say that this is setting the default placement for the code. But it isn't..."left_vaule" is the amount that you've calculated to push left during a slide. So if you inspect the element, you can see how the whole UL is basically shifted one slide's worth too far left, unable to be seen. So we get rid of 'left_value', and replace it with 0.
$('#slides ul').css({'left' : 0});
Now, there's nothing really handling how the pictures slide in, so that part's still rough, but this should be enough to start on.
Let me know if I misunderstood anything, or if you have any questions.
So, a few things:
1) I believe you are trying to get all of the lis to be side-by-side, not arranged up and down. There are a few ways to do this. I'd just make the ul have a width of 300%, and then make the lis each take up a third of that:
#slides ul {
....
width: 300%;
}
#slides li {
width: calc(100% / 3);
height:250px;
float:left;
}
2) You got this right, but JSFiddle automatically wraps all your JS inside a $(document).ready() handler, and your function, rotate needs to be outside, in the normal DOM. Just change that JSFiddle setting from 'onload' to 'no wrap - in head'
3) Grabbing the CSS value of an element doesn't always work, especially when you're dealing with animating elements. You already know the width of the li elements with your item_width variable. I'd just use that and change your code:
var left_indent = parseInt($('#slides ul').css('left')) - item_width;
$('#slides ul').animate({'left' : left_indent}, 1500, function () {
to:
$('#slides ul').stop().animate({'left' : -item_width * 2}, 1500, function () {
4) Throw in the .stop() as seen in the above line. This prevents your animations from overlapping. An alternative, and perhaps cleaner way to do this, would be to simply return false at the beginning of your 'next' and 'prev' functions if #slides ul is being animated, like so:
if ($('#slides ul').is(':animated')) return false;
And I think that's everything. Here's the JSFiddle. Cheers!
EDIT:
Oh, and you may also want to clearInterval at the beginning of the next and prev functions and then reset it in the animation callback functions:
$('#prev').click(function() {
if ($('#slides ul').is(':animated')) return false;
clearInterval(run);
$('#slides ul').stop().animate({'left' : 0}, 1500,function(){
....
run = setInterval('rotate()', speed);
});
});
I have created a newsfeed. The feed switches every 2 seconds. You can also manually switch left/right, or click the panel from the squares at the bottom. The switching between slides is down using jQuery UI Slide.
Right now, if you are in the middle of a slide, and you click left/right/squares, then another slide occurs on top of the existing, still going slide and the whole system is messed up.
How can I prevent other actions occurring if a slide/switch is already in progress?
This is my code:
$(document).ready(function(){
newsfeedTimer = setInterval(newsfeed, displayDuration);
// Manual change of feed (LEFT)
$('#newsfeeds_wrapper > .left').click(function(event){
event.stopPropagation();
feedLeft();
clearInterval(newsfeedTimer);
newsfeedTimer = setInterval(newsfeed, displayDuration);
});
// Very similar code for feed right
// Ignore the other method of switching (if it works for above, I can implement it for this one)
});
function newsfeed() {
feedRight();
}
// Feed to the Right
// jump is used to jump multiple newsfeed instead of one at a time
function feedRight(jump)
{
jump = typeof jump !== 'undefined' ? jump : 1;
var current = $('.newsfeed:first');
var next = $('.newsfeed:nth(' + jump + ')');
current.hide('slide',{duration: transitionDuration}, function(){
// Append as many needed
for( var i = 0; i < jump; i++ ) {
$('.newsfeed:first').appendTo('#newsfeeds');
}
next.show('slide',{direction : 'right' , duration: transitionDuration});
}
I don't want to stop() an animation! I want to disable changing the slides IF there is animation happening!!
without seeing the full breadth of the code, I am shooting myself in the foot here. But here is a direction I would take it. You could also have two functions, one to bind, another to unbind. When animation is initiated, you unbind the left/right controls. When stopped, you bind. Or, set a global variable... ala.
var config = {'inProgress': false};
$('#newsfeeds_wrapper > .left').click(function(event){
if(!config.inProgress){
event.stopPropagation();
feedLeft();
clearInterval(newsfeedTimer);
newsfeedTimer = setInterval(newsfeed, displayDuration);
}
});
in your animation function. Seems like when you cut/paste, some of the code is lost, so lets just assume some animation.
when you enter your animation functions, set config.inProgress = true;
function feedRight(jump)
{
config.inProgress = true;
// removed your code, but just using for simplicity sake
// added a callback
next.show('slide',{direction : 'right' , duration: transitionDuration},
function() {
// Animation complete. Set inProgress to false
config.inProgress = false;
});
)
}
So I've created the following function to fade elements in and passed in a div that I want to fade in which in this case is an image gallery popup that I want to show when a user clicks an image thumbnail on my site. I'm also passing in a speed value (iSpeed) which the timeout uses for it's time value. In this case I'm using 25 (25ms).
I've stepped through this function whilst doing so it appears to be functioning as expected. If the current opacity is less than 1, then it is incremented and it will recall itself after the timeout until the opacity reaches 1. When it reaches one it stops fading and returns.
So after stepping through it, I take off my breakpoints and try to see it in action but for some reason my gallery instantly appears without any sense of fading.
var Effects = new function () {
this.Fading = false;
this.FadeIn = function (oElement, iSpeed) {
//set opacity to zero if we haven't started fading yet.
if (this.Fading == false) {
oElement.style.opacity = 0;
}
//if we've reached or passed max opacity, stop fading
if (oElement.style.opacity >= 1) {
oElement.style.opacity = 1;
this.Fading = false;
return;
}
//otherwise, fade
else {
this.Fading = true;
var iCurrentOpacity = parseFloat(oElement.style.opacity);
oElement.style.opacity = iCurrentOpacity + 0.1;
setTimeout(Effects.FadeIn(oElement, iSpeed), iSpeed);
}
}
}
Here's where I'm setting up the gallery.
this.Show = function (sPage, iImagesToDisplay, oSelectedImage) {
//create and show overlay
var oOverlay = document.createElement('div');
oOverlay.id = 'divOverlay';
document.body.appendChild(oOverlay);
//create and show gallery box
var oGallery = document.createElement('div');
oGallery.id = 'divGallery';
oGallery.style.opacity = 0;
document.body.appendChild(oGallery);
//set position of gallery box
oGallery.style.top = (window.innerHeight / 2) - (oGallery.clientHeight / 2) + 'px';
oGallery.style.left = (window.innerWidth / 2) - (oGallery.clientWidth / 2) + 'px';
//call content function
ImageGallery.CreateContent(oGallery, sPage, iImagesToDisplay, oSelectedImage);
//fade in gallery
Effects.FadeIn(oGallery, 25);
}
Could anyone help me out?
Also, I'm using IE10 and I've also tried Chrome, same result.
Thanks,
Andy
This line:
setTimeout(Effects.FadeIn(oElement, iSpeed), iSpeed);
calls Effects.FadeIn with the given arguments, and feeds its return value into setTimeout. This is exactly like foo(bar()), which calls bar immediately, and then feeds its return value into foo.
Since your FadeIn function doesn't return a function, that would be the problem.
Perhaps you meant:
setTimeout(function() {
Effects.FadeIn(oElement, iSpeed);
}, iSpeed);
...although you'd be better off creating that function once and reusing it.
For instance, I think this does what you're looking for, but without recreating functions on each loop:
var Effects = new function () {
this.FadeIn = function (oElement, iSpeed) {
var fading = false;
var timer = setInterval(function() {
//set opacity to zero if we haven't started fading yet.
if (fading == false) { // Consider `if (!this.Fading)`
oElement.style.opacity = 0;
}
//if we've reached or passed max opacity, stop fading
if (oElement.style.opacity >= 1) {
oElement.style.opacity = 1;
clearInterval(timer);
}
//otherwise, fade
else {
fading = true;
var iCurrentOpacity = parseFloat(oElement.style.opacity);
oElement.style.opacity = iCurrentOpacity + 0.1;
}
}, iSpeed);
};
};
Your code has a lot of problems. The one culpable for the element appearing immediately is that you call setTimeout not with a function but with the result of a function, because Effects.FadeIn will be executed immediately.
setTimeout(function(){Effects.FadeIn(oElement, iSpeed)}, iSpeed);
will probably act as you intend.
But seriously, you probably should not re-invent this wheel. jQuery will allow you to fade elements in and out easily and CSS transitions allow you to achieve element fading with as much as adding or removing a CSS class.
T.J. and MoMolog are both right about the bug: you're invoking the Effects.FadeIn function immediately before passing the result to setTimeout—which means that Effects.FadeIn calls itself synchronously again and again until the condition oElement.style.opacity >= 1 is reached.
As you may or may not know, many UI updates that all take place within one turn of the event loop will be batched together on the next repaint (or something like that) so you won't see any sort of transition.
This jsFiddle includes the suggested JS solution, as well as an alternate approach that I think you may find to be better: simply adding a CSS class with the transition property. This will result in a smoother animation. Note that if you go this route, though, you may need to also include some vendor prefixes.
i have this game :http://jsfiddle.net/Qfe6L/5/
i am trying to detect when a shuriken hit an enemy so when it hit it the enemy should disappear and the score should be increased by 1 what i searched for is that i should calculate the position of the two images to check whether their is a collision but i don't seem i can do that any help from you guys ?
$(window).keypress(function (e) {
if (e.which == 32) {
CreateChuriken();
$("#Shuriken" + Shurikengid).animate({ left: '+=300px' }, 'slow');
if ($(".Shuriken").css('left') == $(".Enemy").css('left'))
{ alert("Met"); }
}
});
You need to check for collision in each animation step. Fortunately jQuery .animate() has a progress option, which you can pass a function to be called every frame.
$("#Shuriken" + Shurikengid).animate(
{ left: '+=300px' },
{ duration : 'slow',
progress: function(){
/* collision detection here */
}
}
);
Keep in mind that
if ($(".Shuriken").css('left') == $(".Enemy").css('left'))
will only compare position of first projectile and first enemy, while there are more of them on the screen. You need to iterate over every projectile and compare its powition with every enemy to find a colliding pair, like:
$('.Shuriken').each( function(){
var sOffset = $(this).offset();
$('.Enemy').each( function(){
var eOffset = $(this).offset();
if( sOffset.left == eOffset.left ){
/* boom! */
}
});
});
The above is close, but still won't work. Animation doesn't progress by 1px each frame, so you may go from Shuriken at 100px left and Enemy at 101px left at one frame to Shuriken at 102px left and Enemy at 99px left in the next one. They'll pass each other, but won't meet at the same point. So you'd need to round these values to, say, nearest 10s which will give you a bigger tolerance. You sholud also compare the vertical positions.
Updated fiddle: http://jsfiddle.net/Qfe6L/8/
(Fixed vertical posiotion of Enemies for easier testing).
Edit:
as suggested by #Kasyx it would be better to move all of this out of animation function and create a Game Loop and Scene Graph. Scene graph would keep track of elements' positions, and within Game Loop you'd check for collisions, then call a rendering function which would draw elements on screen based on the scene graph.
At the moment you’re running your hit check function once, directly after you’ve started the animation. What you need to be doing is running it every frame to see the intersect. Luckily jQuery provides a callback handler for this: $.animate’s step option. If you pass a second object into $.animate you can specify both your duration and step function like so:
$(window).keypress(function (e) {
if (e.which == 32) {
CreateChuriken();
$("#Shuriken" + Shurikengid).animate({
left: '+=300px'
}, {
duration: 'slow',
step: function(){
if ($(".Shuriken").css('left') == $(".Enemy").css('left')) {
alert("Met");
}
}
});
}
});
As you’re calling your step function once every frame you’ll want to cache your selectors inside ($('.Shuriken'), $('.Enemy')) first:
$(window).keypress(function (e) {
if (e.which == 32) {
var shuriken, enemy;
CreateChuriken();
shuriken = $('.Shuriken');
enemy = $('.Enemy');
$("#Shuriken" + Shurikengid).animate({
left: '+=300px'
}, {
duration: 'slow',
step: function(){
if (shuriken.css('left') == enemy.css('left')) {
alert("Met");
}
}
});
}
});