Javascript animation too fast - javascript

I am trying to write a javascript app which sorts dynamically created divs on a webpage, everything works and it sorts it all as it should, however I want it to animate the divs as they change position due to being sorted, I have code to do this but it moves way too fast, the only way I can see the divs move is if I put an alert in the animation code which looks like this :
function moveAnimation(to,from){
mover1.style.backgroundColor = "rgba(255,100,175,0.8)";
mover2.style.backgroundColor = "rgba(255,100,175,0.8)";
mover1.style.top = parseInt(mover1.style.top) + 10 + 'px';
mover2.style.top = parseInt(mover2.style.top) - 10 + 'px';
from = parseInt(mover1.style.top);
if(from != to && from < to)
{
//alert("TO : " + to + " From : " + from); //This makes it pause so that I can see the divs move
animate = setTimeout(moveAnimation(to,from),1000)
}
else
{
//alert("RET");
return;
}
}
And i call it simply like this :
mover1 = document.getElementById(moving1.id);
mover2 = document.getElementById(moving2.id);
var from = parseInt(in1.style.top);
var to = parseInt(in2.style.top);
moveAnimation(to,from);
When the alert is in place I can see them move frame by frame and it's doing exactly what I want, however it all happens in the blink of an eye with the divs suddenly being sorted, I would like to see them slowly move, any ideas on why my code isn't doing that?

Related

transitionend method to animate a infinite slider with vanilla Javascript

I'm new to this community and first of all, I take this opportunity to thank all of you for the wonderful work you do every day.
I'm trying to create an infinite manual carousel, in the Netflix style, this is the link to the codepen of everything I have done so far:
https://codepen.io/A12584r/pen/OjvWYp?fref=gc
Here is the relevant javascript:
let prendiContenitoreGalleria = document.querySelector('.contenitore-galleria'),
prendiArticle = Array.prototype.slice.apply(document.querySelectorAll('.contenitore-galleria__article')),
contaArticle = prendiArticle.length,
prendiImmagini = Array.prototype.slice.apply(document.querySelectorAll('.contenitori__img')),
prendiFrecciaSinistra = document.querySelector('.freccia-sinistra'),
prendiFrecciaDestra = document.querySelector('.freccia-destra');
prendiContenitoreGalleria.style.width = 100 * contaArticle + '%';
for (let numeroImmagini = 0; numeroImmagini < prendiImmagini.length; numeroImmagini++) {
prendiImmagini[numeroImmagini].style.width = 100 / contaArticle + '%';
}
prendiContenitoreGalleria.insertBefore(prendiArticle[contaArticle - 1], prendiArticle[0]);
prendiContenitoreGalleria.style.marginLeft = '-' + 100 + '%';
function andareADestra () {
prendiContenitoreGalleria.style.marginLeft = '-' + 200 + '%';
prendiContenitoreGalleria.style.transitionDuration = '.7s';
prendiContenitoreGalleria.addEventListener('transitionend', function(e) {
prendiContenitoreGalleria.appendChild(prendiArticle[0]);
prendiContenitoreGalleria.style.marginLeft = '-' + 100 + '%';
}, false);
}
function andareASinistra () {
prendiContenitoreGalleria.style.marginLeft = 0;
prendiContenitoreGalleria.style.transitionDuration = '.7s';
prendiContenitoreGalleria.addEventListener('transitionend', function(e) {
prendiContenitoreGalleria.insertBefore(prendiArticle[contaArticle - 1], prendiArticle[0]);
prendiContenitoreGalleria.style.marginLeft = '-' + 100 + '%';
}, false);
}
prendiFrecciaSinistra.addEventListener('click', function () {
andareASinistra();
});
prendiFrecciaDestra.addEventListener('click', function () {
andareADestra();
});
I have tried to use the vanilla Javascript transitionend events and what I want to achieve is that when clicking on the right arrow of the carousel the first article is put in place of the third and vice versa, when clicking on the left arrow of the carousel the last article is put in place of the first one.
For this purpose I use marginLeft to move between the articles in my carousel which are 3 and the divs that contains them (these are 3 too) has a width of 300% set via JavaScript.
My problem is that when I click on the carousel arrows, the transition is done but it does a strange effect coming back to its original location immediately.
Any one of you could help me to figure out where I'm wrong and how can I fix it?
In your two functions to move left and right, andareASinistra and andareADestra you are adding an event listener for the transitionend event.
The handler for this is removing the margin-left offset by resetting it to -100% after each move left or right.
You can confirm this is the problem by editing the inline style of the id="contenitore-galleria" element, if you set style="margin-left: 0" then the carousel is moved, then returned to the -100% position.
So the reason it is happening is because that is what you are explicitly telling it to! Delete the lines from both andareASinistra and andareADestra
rendiContenitoreGalleria.style.marginLeft = '-' + 100 + '%';

img.on("load") executes after the img is loaded but before it has its size set (IE)

EDIT : Added jsfiddle
(for) https://jsfiddle.net/x60bwgw7/4/
(each) https://jsfiddle.net/v3y2v8cf/3/
open in IE, output values of 30 are wrong (IE placeholder height)
I tried to return DOM naturalHeight and clientHeight, and while naturalHeight works properly in IE, clientHeight (the one I need) does not.
Here's an obvious workaround, but it kinda sucks https://jsfiddle.net/0rhjt0wn/1/
Seems like the problem is that IE renders image after it is loaded, while other browsers render it while it is being loaded, but Im just guessing here.
I've some images, that I want to load by assigning their "data-src" attribute value to theirs "src" attribute and vertically center them after.
It works without a problem in all browsers except IE (tested from 8 to edge).
In IE some images get centered without a problem, but some wont and it is because code in the .on("load") event gets executed (probably) after the image is loaded, but before it gets its size set.
Is there any way to always execute the code after the image is loaded and its size is set (in IE)?
for (i = 0; i < 100; i++)
{
targetSrc = $divElement.eq(i).children(imgClass).attr(dataSrc) + "?v=" + datetime;
$divElement.eq(i).find(imgClass).attr("src", targetSrc).on("load", function()
{
$holder.append($(this).attr("src") + " || height = " + $(this).height() + "<br>");
});
}
Initially thought requestAnimationFrame and a timeout fallback could easily solve this but it turned out to be a lot more complicated. Apparently the exact frame at which an image is rendered after onload is not very predictable in IE. But I eventually came up with this solution that checks the naturalHeight against the current height to see if an image has been rendered, looping through to the next display frame when it is not the case yet :
https://jsfiddle.net/r8m81ajk/
$(function() {
if (window.requestAnimationFrame) var modern = true;
var element = $('.divElement'),
reference = '.imgElement',
holder = $('.holder'),
datetime = Date.now(),
path, image;
for (var i = 0; i < 100; i++) {
var target = element.eq(i).children(reference),
path = target.data('src') + '?v=' + datetime;
target.one('load', function() {
image = this;
if (modern) requestAnimationFrame(function() {
nextFrame(image);
});
else setTimeout(renderImage, 50);
}).attr('src', path);
}
function nextFrame(picture) {
var dimension = $(picture).height();
if (dimension == picture.naturalHeight) {
holder.append('height = ' + dimension + '<br>');
}
else nextFrame(picture);
}
function renderImage() {
holder.append('height = ' + $(image).height() + '<br>');
}
});
The fallback will always check on the third frame (when there's no bottleneck). Don't mind I adapted the code a bit to my usual conventions, the external references should have remained the same.
From the question I realise the loop might not even be needed and getting naturalHeight when the image loads would be enough. Interesting exercise to detect the exact point of rendering in any case.

make both editors in the same size when they autogrowing

I'm using two CKEDITOR's editors in one page, and I them to be in the same size all the time. I'm using the auto grow plugin, so I tried it:
CKEDITOR.plugins.addExternal( 'autogrow', location.href + 'ckeditor/autogrow/', 'plugin.js' );
var e1 = CKEDITOR.replace("heb_editor", {extraPlugins: 'autogrow'});
var e2 = CKEDITOR.replace("eng_editor", {extraPlugins: 'autogrow'});
e1.on("resize", r);
e2.on("resize", r);
function r(){
if($("#cke_1_contents").height() > e2.config.height)
$("#cke_2_contents").height($("#cke_1_contents").height());
else
$("#cke_1_contents").height($("#cke_2_contents").height());
}
it didn't worked. It did resized the second editor to the size of the first one, but it didn't resized the first to the size of the second when it was needed. What to do?
Here is a JSfiddle: http://jsfiddle.net/povw33x7/
Forget all I said before (I deleted it, but you can still see it in the revision history).
Using some code I found on this Web site, you can calculate the height of the box. Now, you just need to apply that to update the box heights on resize:
function getBoxHeight(boxId) {
// using a function to get the height of the box from ==>
var ckeditorFrame = $('#' + boxId + ' iframe');
var innerDoc = (ckeditorFrame.get(0).contentDocument) ? ckeditorFrame.get(0).contentDocument : ckeditorFrame.get(0).contentWindow.document;
var messageHeight = $(innerDoc.body).height();
return messageHeight ? messageHeight : 0;
}
function r() {
if (getBoxHeight("cke_1_contents") > getBoxHeight("cke_2_contents")) {
$("#cke_2_contents").height($("#cke_1_contents").height());
} else {
$("#cke_1_contents").height($("#cke_2_contents").height());
}
}
As you can see on this JSFiddle: http://jsfiddle.net/povw33x7/3/. This solution is cleaner than the other one, although it still has a glitch as it may leave an extra empty space (the height of a line) in one of the boxes.

Animating a view change in iPhone and Android

Im trying to make a "UINavigationController type animation" in a Titanium project, however currently when I do the animation it does sort of a "pop back" animation where the view "comes out" of where the other one "went to". I have managed to figure out what's the value that determines where the animation ends, that is, the left property of the animation, but how do I set where the animation starts?
code to control animation:
function hideOldWindow() {
window.animate(animateOut, function(){});
}
function showNewWindow() {
var old = views[currentView];
window.remove(old);
currentView = (currentView + 1) % views.length;
var win = views[currentView];
viewControllers[currentView].onBecomeVisible();
window.add(win);
window.animate(animateIn, function(){});
}
var animateIn = Titanium.UI.createAnimation();
animateIn.left = 0;
animateIn.duration = 250;
animateIn.curve = Ti.UI.ANIMATION_CURVE_EASE_OUT;
var animateOut = Titanium.UI.createAnimation();
animateOut.left = -screenWidth + 1;
animateOut.duration = 250;
animateOut.curve = Ti.UI.ANIMATION_CURVE_EASE_OUT;
I don't know how is UINavigationController type animation, but to control where the animation starts, you have to set the left property of the view (this works for views I didn't try it on windows).
For example, to animate a view to appear, from left of the screen to the center, you can change the showNewWindow method to this:
function showNewWindow() {
var old = views[currentView];
window.remove(old);
currentView = (currentView + 1) % views.length;
var win = views[currentView];
viewControllers[currentView].onBecomeVisible();
window.add(win);
// NEW LINE
window.left = -screenWidth + 1;
window.animate(animateIn, function(){});
}
Now, window would appear from -screenWidth + 1 to left = 0.
Maybe you have to add a timeout before you animate the window to get it works.
Hope it helps

Image Rotation using pure Javascript

PLEASE DO NOT RECOMMEND JQUERY - I AM DOING THIS EXERCISE FOR LEARNING PURPOSES.
I have implemented a JavaScript, which rotates images (_elementSlideChange) on a timer, using a set interval of 10 seconds. Also I have added a slide functionality to this, which is 7 milliseconds (_slideImage).
The image rotates automatically every 10 seconds on page load, and I have also provided next and previous buttons, which allow the user to change the images manually.
_elementSlideChange: function () {
var myString;
var myText;
for (var i = 0; i < this._imgArray.length; i++) {
var imageArr = "url(" + this._imgArray[i].src + ")";
var imageBg = this._imageHolder.style.background + "";
if (imageArr == imageBg) {
if (i == (this._imgArray.length - 1)) {
myString = "url(" + this._imgArray[0].src + ")";
myText = this._infoArray[0];
} else {
myString = "url(" + this._imgArray[(i + 1)].src + ")";
myText = this._infoArray[i + 1];
}
}
}
this._imageNextSlide.style.background = myString;
this._imageNextSlide.style.background);
this._infoElement.innerHTML = myText;
this._myTimer = setInterval(MyProject.Utils.createDelegate(this._slideImage, this), 7);
},
_slideImage: function () {
if (parseInt(this._imageHolder.style.width) >= 0 && parseInt(this._imageNextSlide.style.width) <= 450) {
this._imageHolder.style.backgroundPosition = "right";
this._imageHolder.style.width = (parseInt(this._imageHolder.style.width) - 1) + 'px';
console.log(this._imageNextSlide.style.background);
this._imageNextSlide.style.width = (parseInt(this._imageNextSlide.style.width) + 1) + 'px';
} else {
console.log("reached 0px");
if (parseInt(this._imageHolder.style.width) == 0) {
this._imageHolder.style.background = this._imageNextSlide.style.background;
this._imageHolder.style.width = 450 + 'px';
this._imageHolder === this._imageNextSlide;
this._imageHolder.className = "orginalImage";
this._imageNextSlide.style.width = 0 + "px";
this._imageNextSlide = this._dummyImageNextSlide;
this._imagesElement.appendChild(this._imageHolder);
this._imagesElement.appendChild(this._imageNextSlide);
clearInterval(this._myTimer);
}
clearInterval(this._myTimer);
clearInterval(this._elementSlideChange);
}
}
So when the user clicks on the Next arrow button, the event listener for "click" is triggered. This creates a div for the current image on display, and creates a new div, which will contain the next image. The image slide and rotation works correctly (whether it's onLoad or onClick). The issue I have is if I click the Next button, while the new div image is sliding into position, it causes it to run into an infinite loop, so the same div with the image to be displayed keeps sliding in, and the more you click the Next button, the faster the image starts to rotate.
I have tried putting a clear interval for the image rotation and slider, but I do understand my code is wrong, which causes the infinite loop of the sliding image. And I know I am close to finishing the functionality.
Can anyone please advise where I could be going wrong? Or should I try to implement the sliding DIV in another way?
Once again please don't recommend jQuery.
And thank you for your help in advance.
Kush
To solve the issue, I did re-write the entire code, where I had a next and previous button event listener.
myProject.Utils.addHandler(this._nextImageElement, "click", myProject.Utils.createDelegate(this._changeImage, this));
Both the buttons will call the same function :
_changeImage: function (e)
In this function I check to see if the function is Transition (changing images),
I declare a boolean var forward = e.target == this._nextImageElement;
Then check to see the current index if forward ? Add 1 else minus 1
this._currentImageIndex += forward ? 1 : -1;
If its at the end of the Array and forward is true, assign the this._currentImageIndex to reset to 0 or Array.length – 1 if it’s in reverse
Then call another function which gives the ‘div’ a sliding effect. In this case call it this._transitionImage(forward);
In this function, set the this._inTranstion to true. (Because the div’s are sliding in this case).
The following code solved the issue i was having.
this._slideImageElement.style.backgroundImage = "url(\"" + this._imgArray[this._currentImageIndex].src + "\")";
this._slideImageElement.style.backgroundPosition = forward ? "left" : "right";
this._slideImageElement.style.left = forward ? "auto" : "0px";
this._slideImageElement.style.right = forward ? "0px" : "auto";
The above code is very important as the object is to place the “sliding in div” Left or Right of the current Visible “div” to the user, and this is mainly dependent on if the forward variable is true or false.
var i = 0;
Then start the transition by
setInterval( function() {
this._currentImageElement.style.backgroundPosition = (forward ? -1 : 1) * (i + 1) + "px";
this._slideImageElement.style.width = (i + 1) + "px";
Notice the forward will determine if the bgPosition will go to the left if its forward as we multiple by -1 or +1,
So for example
If the user clicks NEXT BUTTON,
Forward = true
So the first thing we do is set the
this._slideImageElement.style.backgroundPosition = "left"
Then
this._slideImageElement.style.left = "auto"
this._slideImageElement.style.right = "0px"
This means when the sliding image moves in its background position is LEFT but the div is placed on the RIGHT to 0px;
then this._currentImageElement.style.backgroundPosition = -1 * (i + 1)
Which moves the position of the currentImageElement to the left by 1px,
Increase the width of the slideImage which in this case is right of the current div,
and as the current div moves to the left the sliding image starts to appear from the right. (By default set the width of slideImageElement to 0px so the div exists but isn’t visible to the user). This gives it the slide effect of moving forward new image coming from the right.
this._slideImageElement.style.width = (i + 1) + "px";
then declare it to stop when it it’s the image width. In this case it will be 500px.
if ((i = i + 2) == 500) {
In this if statement reset the currentImageElement background and the background position “right” or “left” don’t really matter as long it has been reset.
Clear the interval
Set the transition to false again
Then call a setTimeout for the function changeImage, which will continue until the slide is completed.
The following shows the reset code as this is very important to prevent repeating the same image (This solved my entire issue)
// set the current image to the "new" current image and reset it's background position
this._currentImageElement.style.backgroundImage = "url(\"" + this._imgArray[this._currentImageIndex].src + "\")";
this._currentImageElement.style.backgroundPosition = "right";
// reset the slide image width
this._slideImageElement.style.width = "0px";
// clear the transition interval and mark as not in transition
clearInterval(this._transitionInterval);
this._inTransition = false;
// setup the next image timer
this._nextImageTimeout = setTimeout(myProject.Utils.createDelegate(this._changeImage, this), 2500);
}
I have provided a thorough detail because then it easier to understand the logic of the problem, and even if your not having the same issue, this may help you fingure out any problem.
I couldn't provide a JSfiddle, as i have created my CSS using Javascript, there are different ways of doing this, but i wanted to understand the logic behind the forward and reverse, and having a timer which continuously goes forward.
It seems like you want to cancel the animation on the slide (perhaps have it fade out while the next slide animates in, cancel its animation abruptly or let it finish and ignore the button click)
What I usually do, personally, is check for the animated state (yes, I use jquery, but you should be able to test the CSS or positioning values you are using to animate in the same way) you could even add an "active" class or data type during animation to make testing easier. Global flags work, too. If there is animation, ignore the button. (For my work... Depends on your intention)
Like I said, the problem may be with button behaviour not with the animation routine. It would be useful to see how you are calling this from the button click, and what your intended results are going to be.
How about CSS3 transitions?
transition: all 1s ease 0.5s;
Simple example on JS Fiddle.
This takes care of the animation, so you just need to set the intended destination using JavaScript, i.e.
this.style.left = '100px';
Or
this.style.top = '30px';
And CSS3 transitions will smoothly slide the element.
Cross Browser Note!
The transition property may need a vendor prefix for some browsers, I am using the latest production Firefox and you don't need -moz for that. Same goes for Opera, no '-o' required. Internet Exporer 10 needs no prefix. You may need to use -webkit for Safari / Chrome, but test without first.

Categories

Resources