Is it possible to add a transition (like in CSS) in Javascript? - javascript

I have a Javascript code that shows a tooltip when hovering over an HTML element. Now I want to give this element a latency of about 6 milliseconds. In CSS it is very easy with the transition command. However, I did not find a transition style command in Javascript. Is there a solution or do I have to change to another programming language?
Javascript code:
var bghtooltipin = document.getElementById('bgh-tooltipin1');
var bghtooltipout = document.getElementById('bgh-tooltipout1');
bghtooltipin.addEventListener('mouseover', bghtooltipinmouseOver);
bghtooltipin.addEventListener('mouseout', bghtooltipoutmouseOut);
function bghtooltipinmouseOver() {
bghtooltipout.innerHTML = 'Go to Login';
bghtooltipout.style.color = "white";
bghtooltipout.style.top = "0";
}
function bghtooltipoutmouseOut() {
bghtooltipout.innerHTML = ' ';
bghtooltipout.style.top = "-99999px"
}

You can use something like this:
bghtooltipout.style.transition = "all 6s";

something like this it works is Vanila JS
bghtooltipout.style.transition = "all 2s";

There are 2 ways to interpret "latency". I will show you how to perform both implementations.
Delay.
6ms would pass, and then the transition would play. In JavaScript, this is done as the following:
setTimeout(function() {
// Code here
}, delay_in_ms);
Duration.
If you want your animation to last for 6ms, then you would do something as follows:
const element = document.querySelector("#testthing");
element.addEventListener("mouseover", function(){
this.style.opacity = "0";
this.style.transition = "opacity 0.6s";
});
element.addEventListener("mouseout", function(){
this.style.opacity = "1";
this.style.transition = "opacity 0.6s";
});
#testthing {
width: 100px;
height: 100px;
background: red;
}
<div id="testthing"></div>
PLEASE NOTE: In this example, the transition actually lasts for 600 milliseconds, not 6. This is because 6ms is just too quick to see. It just appears as an instant change.

Related

Is there another method to make better animation on changing an element html?

I make some code to do a changing on an element HTML with setTimeout().
Is it possible to do other way to make more beautiful animation like the element appears slowly ?
I tied with setInterval(), I tried with fadeIn() but i don't know how to do with it.
var words= [' Web', ' Fullstack', ' Mobile', ' Php'];
var i = 0;
var word = document.getElementById("word");
function newWord(){
word.innerHTML = words[i];
if(i < words.length - 1){
i++;
}
else{
i = 0;
}
setTimeout("newWord()", 2000);
}
window.onload = function(){
newWord();
}
like this it's works, the word changes every 2s but I want some effects when the word is changing.
You can add a transition to the text using CSS:
#word {
transition: transition-length linear all;
}
This creates a transition for every style element that you change with JavaScript
I find something works.
I just put this :
$('#word').fadeOut(2000, function(){ $('#word').fadeIn(2000);});
after else and it work pretty good.

Triggering CSS3 Keyframes with javascript multiple times

I am triggering CSS3 Keyframes with javascript but its working for with first call after that any call to that function doesn't animate my div.
Here the Javascript code
function animateShare (imgSrc){
var share = document.getElementById("shareTools");
share.style.animation = "testAnimate 1s ease-in-out 0s"
//shareTools.style.animationPlayState = "running";
}
Sample of the issue (Click red box to preview)
var box = document.getElementById("box");
function animateBox(){
box.style.animation = "box 1s ease-in-out 0s";
}
#box{
background:red;
width:100px;
height:100px;
}
#keyframes box {
50%{width:300px;}
}
<div id='box' onclick='animateBox()'><div>
JSFIDDLE
I want it to animate everytime i call this function.
You can use well known hack: destroy and create element to reset animation.
var box = document.getElementById("box");
function animateBox(){
//destroy and create hack
document.body.removeChild(box);
document.body.appendChild(box);
box.style.animation = "box 1s ease-in-out 0s";
}
#box{
background:red;
width:100px;
height:100px;
}
#keyframes box {
50%{width:300px;}
}
<div id='box' onclick='animateBox()'><div>
In case someone is still interested by this there is another trick that works:
Changing the dataset value to something it has never been, and then use a css matching on that data.
I used this technique when animating the collapsing/uncollapsing of a menu, which of course can happen multiple times. Here's how I did it:
#menu[data-closed^="1"]{
animation:menu_closing;
}
#menu[data-closed^="0"]{
animation:menu_opening;
}
So the animation is based on the first character of the dataset (1 or 0).
Then in the click event that wants to close/open the menu:
var closed = menu_ele.dataset.closed // closed right now
? parseInt( menu_ele.dataset.closed.substr(0,1) )
: 0; // assuming menu is initialized open
var counter = menu_ele.dataset.closed // nb of times the menu was closed or open
? parseInt( menu_ele.dataset.closed.substr(1) )
: 0;
menu_ele.dataset.closed = ''+(1-closed)+(counter+1);
This way the "closed" dataset variable changes like this at every click:
11 (closing for the first time)
02 (reopening for the first time)
13
04
15
06
...
The first digit indicates whether it is currently closed, while all the rest is a counter to make the value new every time.
Think about your code - after first call it does nothing, becouse it already changed animation property of that element.
According to this CSS-Tricks article:
function animateShare (imgSrc){
var share = document.getElementById("shareTools");
share.style.animation = "testAnimate 1s ease-in-out 0s";
shareTools.style.animationPlayState = "paused";
shareTools.style.animationPlayState = "running";
}
add this code in the body section after the element for which the animation is being played-
<script>
document.getElementById('shareTools').addEventListener("animationend", function () {
this.removeAttribute("style");
})
</script>
or if you dont want to remove style attribute ,because you have other css than animation then create a class and add class dynimacally and remove it as above code.
You can reset the animation by just removing the animation property from the styles of the element after the animation has complete - removing the element is unnecessary. In my example, I set the duration in JS, but you could just as easily add an animationend hook to keep it simpler.
JSFiddle
var duration = 1000;
document.getElementById('box').addEventListener('click', function onClick(ev) {
var el = this;
el.style.animation = 'box ' + (duration / 1000 + 's') + ' ease-in-out';
setTimeout(function() {
el.style.animation = ''
}, duration);
});
#box {
background: red;
width: 100px;
height: 100px;
}
#keyframes box {
50% {
width: 300px;
}
}
<div id='box'><div>

Most performant way to do document wide PNG animation

I've got a GIF animation that I use thoughout my site as a saving/loading icon:
Because of edges on different background colors, I'd like to change it to a PNG animation.
When I want to show a loader at this moment I only have to make sure the following span is visible:
<span class="loader"></span>
There are several ways how this span be inserted in the document: through knockout visible binding, through JS, only by stylesheets etc.
Problem
I don't want my animation code to be aware of how this span ended up visible on the document, I just want him to animate it.
Of course scanning the whole document every frame (16 fps) for potential new spans with the 'loader' class, just to know which position properties need to be animated is not quite performant.
So what would be a good performant way to do a document wide png animation?
Note that I do need to support IE8 :(
Using a sprite in combination with CSS background-position comes immediately to mind:
(yes I know it's a pretty shitty one, but it'll do the job).
You mentioned Knockout, and as its aim is to separate logic from presentation, I'll use that. However, there's no way to completely separate it that I know of that doesn't come with a performance cost (cf your comment). Typically in Knockout bindingHandlers are used to do DOM manip independently of your viewModel.
Haven't tested, but should normally work on IE8. Run the snippet below for a demo
ko.bindingHandlers.loadIndicator = {
update: function(element, valueAccessor) {
var val = ko.unwrap(valueAccessor());
if (val == true) {
var intv = setInterval(function() {
var bgX = parseInt(element.style.backgroundPosition.split(' ')[0].replace('px',''));
if (bgX > -48) // 4 frames of 14px
element.style.backgroundPosition = (bgX - 14) + 'px';
else
element.style.backgroundPosition = '0px';
}, 150);
element.style.display = 'block';
} else {
element.style.display = 'none';
clearInterval(intv);
}
}
};
var app = { loading: ko.observable(true) };
ko.applyBindings(app);
// simulate 'loaded' after 3 secs
setTimeout(function() { app.loading(false); }, 3000);
.loader {
display: block;
width: 14px;
height: 14px;
background-image: url(http://i.imgur.com/1OZACw8.png);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<span class="loader" data-bind="loadIndicator: loading"></span>
If you think a custom binding is not worth it, you can use the visible binding in combination with a transparent animated GIF (yes, it is possible). See for example: http://blog.ciuly.com/general/internet/making-animated-gif-transparent-with-gimp/.
Any way you choose, you'll have to keep track of whether something has finished loading/ saving, for example in a KO observable property.
Prefer not using Knockout? You could do the same in vanilla JS/ jQuery. However, because you have to track the status from somewhere, you have to either interval-check DOM attributes (which achieves the same as Knockout does automatically on observables) or choose a variant of the approach below where you call an init/ stop function to hide/display the loader.
function loader(container) {
var elem = document.createElement('span');
elem.className = 'loader';
container.appendChild(elem);
var intv = setInterval(function() {
var bgX = parseInt(elem.style.backgroundPosition.split(' ')[0].replace('px',''));
if (bgX > -48) // 4 frames of 14px
elem.style.backgroundPosition = (bgX - 14) + 'px';
else
elem.style.backgroundPosition = '0px';
}, 150);
this.stop = function() {
clearInterval(intv);
container.removeChild(elem);
};
}
var x = new loader(document.body);
setTimeout(function() { x.stop() }, 10000);
Performance-wise I believe the animated transparent GIF with visible binding and the vanilla init/stop method are the 2 best candidates.

Fade in using plain javascript

Trying to do a simple fade in using the opacity property of an h1 element. I'm learning javascript, so would like to try this using plain javascript (yes, I know it is much easier using jQuery).
Pasting only relevant snippets:
<body onload="fadeIn()">
...
<div class = "container">
<div class = "row">
<div class = "col-md-3">
<img class = "img-responsive" src="icons/Website_Logo.png">
</div>
<div class = "col-md-9 page-header">
<h1 id="welcomeHeader" style="opacity:0">
Welcome to the world!
</h1>
</div>
</div>
</div>
...
<script>
function fadeIn() {
var el = document.getElementById("welcomeHeader");
var op = parseFloat(el.style.opacity);
var timer = (function () {
if(op >= 1.0)
clearInterval(timer);
op += 0.1;
el.style.opacity = op;
}, 50);
}
</script>
</body>
Help is much appreciated! Thanks!
jsFIDDLE
You need to call the setInterval function first in order to invoke a timer. Rest is fine. Here is a working fiddle
Code Snippet:
function fadeIn() {
var el = document.getElementById("welcomeHeader");
var op = parseFloat(el.style.opacity);
var timer = setInterval(function () {
console.log('here');
if(op >= 1.0)
clearInterval(timer);
op += 0.1;
el.style.opacity = op;
}, 50);
}
You need to change your function to use setInterval like so:
var timer = setInterval(function () { // notice the setInterval added.
if(op >= 1.0)
clearInterval(timer);
op += 0.1;
el.style.opacity = op;
}, 50);
Notes:
I give you this answer to help you LEARN javascript as you mentioned, otherwise,
it would be better done with pure css of course.
Also, make sure your opacity is set to 0 in your css as a starting point.
You don't need a timer for this - all you need to do is change the class. Here's an example:
the CSS:
element{
/* whatever styles you have */
}
element_faded{
transition: opacity .5s;
opacity: 50%; /* or whatever values you want */
}
the javascript
var element = document.getElementById('element');
// in order to trigger the fade, just change the class
element.className = "element_faded";
In the transition will happen between the values of the original and new class, so if you want a fade-in, have the original opacity be 0% and the new one be 100% or something higher than zero, depending on what you want the final opacity to be. Also, remember that the transition characteristics are determined by the transition attribute in the new class.
Doing this without CSS will just make things more complicated unless you need to do something more sophisticated than just plain fading in or out. If that's the case, then use setInterval or perhaps even something like requestAnimationFrame if you're feeling adventurous.
Honestly, this isn't really the kind of thing you need to learn when first learning javascript. Eventually this will be really easy once you get some confidence under your belt doing things that work more easily in javascript (setTimeout and the like can have their own weird caveats). Try to set a meaningful, practical goal and fulfill it first, using whatever mix of javscript/css/html you can and you'll soon have the basics down well enough to find things like this obvious.

How to improve image cross-fade performance?

I want to be able to do a cross fade transition on large images whose width is set to 100% of the screen. I have a working example of what I want to accomplish. However, when I test it out on various browsers and various computers I don't get a buttery-smooth transition everywhere.
See demo on jsFiddle: http://jsfiddle.net/vrD2C/
See on Amazon S3: http://imagefader.s3.amazonaws.com/index.htm
I want to know how to improve the performance. Here's the function that actually does the image swap:
function swapImage(oldImg, newImg) {
newImg.css({
"display": "block",
"z-index": 2,
"opacity": 0
})
.removeClass("shadow")
.animate({ "opacity": 1 }, 500, function () {
if (oldImg) {
oldImg.hide();
}
newImg.addClass("shadow").css("z-index", 1);
});
}
Is using jQuery animate() to change the opacity a bad way to go?
You might want to look into CSS3 Transitions, as the browser might be able to optimize that better than Javascript directly setting the attributes in a loop. This seems to be a pretty good start for it:
http://robertnyman.com/2010/04/27/using-css3-transitions-to-create-rich-effects/
I'm not sure if this will help optimize your performance as I am currently using IE9 on an amped up machine and even if I put the browser into IE7 or 8 document mode, the JavaScript doesn't falter with your current code. However, you might consider making the following optimizations to the code.
Unclutter the contents of the main photo stage by placing all your photos in a hidden container you could give an id of "queue" or something similar, making the DOM do the work of storing and ordering the images you are not currently displaying for you. This will also leave the browser only working with two visible images at any given time, giving it less to consider as far as stacking context, positioning, and so on.
Rewrite the code to use an event trigger and bind the fade-in handling to the event, calling the first image in the queue's event once the current transition is complete. I find this method is more well-behaved for cycling animation than some timeout-managed scripts. An example of how to do this follows:
// Bind a custom event to each image called "transition"
$("#queue img").bind("transition", function() {
$(this)
// Hide the image
.hide()
// Move it to the visible stage
.appendTo("#photos")
// Delay the upcoming animation by the desired value
.delay(2500)
// Slowly fade the image in
.fadeIn("slow", function() {
// Animation callback
$(this)
// Add a shadow class to this image
.addClass("shadow")
// Select the replaced image
.siblings("img")
// Remove its shadow class
.removeClass("shadow")
// Move it to the back of the image queue container
.appendTo("#queue");
// Trigger the transition event on the next image in the queue
$("#queue img:first").trigger("transition");
});
}).first().addClass("shadow").trigger("transition"); // Fire the initial event
Try this working demo in your problem browsers and let me know if the performance is still poor.
I had the same problem too. I just preloaded my images and the transitions became smooth again.
The point is that IE is not W3C compliant, but +1 with ctcherry as using css is the most efficient way for smooth transitions.
Then there are the javascript coded solutions, either using js straight (but need some efforts are needed to comply with W3C Vs browsers), or using libs like JQuery or Mootools.
Here is a good javascript coded example (See demo online) compliant to your needs :
var Fondu = function(classe_img){
this.classe_img = classe_img;
this.courant = 0;
this.coeff = 100;
this.collection = this.getImages();
this.collection[0].style.zIndex = 100;
this.total = this.collection.length - 1;
this.encours = false;
}
Fondu.prototype.getImages = function(){
var tmp = [];
if(document.getElementsByClassName){
tmp = document.getElementsByClassName(this.classe_img);
}
else{
var i=0;
while(document.getElementsByTagName('*')[i]){
if(document.getElementsByTagName('*')[i].className.indexOf(this.classe_img) > -1){
tmp.push(document.getElementsByTagName('*')[i]);
}
i++;
}
}
var j=tmp.length;
while(j--){
if(tmp[j].filters){
tmp[j].style.width = tmp[j].style.width || tmp[j].offsetWidth+'px';
tmp[j].style.filter = 'alpha(opacity=100)';
tmp[j].opaque = tmp[j].filters[0];
this.coeff = 1;
}
else{
tmp[j].opaque = tmp[j].style;
}
}
return tmp;
}
Fondu.prototype.change = function(sens){
if(this.encours){
return false;
}
var prevObj = this.collection[this.courant];
this.encours = true;
if(sens){
this.courant++;
if(this.courant>this.total){
this.courant = 0;
}
}
else{
this.courant--;
if(this.courant<0){
this.courant = this.total;
}
}
var nextObj = this.collection[this.courant];
nextObj.style.zIndex = 50;
var tmpOp = 100;
var that = this;
var timer = setInterval(function(){
if(tmpOp<0){
clearInterval(timer);
timer = null;
prevObj.opaque.opacity = 0;
nextObj.style.zIndex = 100;
prevObj.style.zIndex = 0;
prevObj.opaque.opacity = 100 / that.coeff;
that.encours = false;
}
else{
prevObj.opaque.opacity = tmpOp / that.coeff;
tmpOp -= 5;
}
}, 25);
}

Categories