Jumpy behavior on Transition Property - javascript

I am having trouble transitioning the width and height of an image. The image is supposed to appear small and then grow in size. However, when I add the second class, to transition the height and width, the image jumps to the next width and height without transitioning the properties. Any help is appreciated. Thanks in advance for any help!
I delayed the addition of the second class containing the adjusted CSS rules as I thought that adding the classes back to back was causing an error. After setting the delay with setTimeout() the function seems to appear slightly better as the image appears to move some but not in the intended way.
<div><img src="Some String"></div>
.lightbox-img { width: 100%; }
.light-box-small {
width: 20vw;
height: 10vh;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: width 1s ease-in;
transition: height 1s ease-in; }
.lightbox-container { width: 60vw; height: 30vh; }
const images = document.querySelectorAll('.img');
const backdrop = document.querySelector('.backdrop');
const lightboxImg = document.createElement('img');
const container = document.createElement('div');
images.forEach(image => {
image.addEventListener('click', () => {
lightboxImg.src = image.src;
container.appendChild(lightboxImg);
container.classList.add('light-box-small');
lightboxImg.classList.add('lightbox-img');
backdrop.classList.add('dim');
document.body.appendChild(container);
setTimeout(() => {
container.classList.add('lightbox-container');
}, 500);
});
});
The expected result should be as follows:
1. The image appears in the center of the viewport
2. The image grows from the smaller size to the larger size.
The error is occurring on step 2 and jumps to the next width instead of transitioning.

Thank you C14L for offering help. Unfortunately applying the transition rules to the lightbox-container didn't work.
The problem turned out to be that when I declared the 2 CSS transition rules the second rule would overwrite the first rule. By declaring the rule in one declaration they wont override each other. So the solution is:
.light-box-small {
transition: width 1s ease-in, height 1s ease-in;
}

Related

How to animate CSS transition effect via a scroll?

I have an element I want to "expand" and change the background color for a page background. As the user scrolls, a dot in the center will expand to fill the page with a new background color. I see examples of how to change the background but not how to "expand" it. I have attached a jsfiddle of the CSS animation effect I'm looking for. This example shows how it will look but only works on the hover. You can see what it's supposed to look like if you scroll the example and hover the white dot.1
Preferably I'd like to accomplish this with css animation but I'm not opposed to trying it out with javascript. I've been fiddling around with that here.
Second, I've been using a fake element to get the example but is there a way I can do this effect without needing the element and just using the container's background-color?
Here's the HTML of the example of the effect I'm trying to achieve.
<div class="container">
<span class="white"></span>
</div>
And here's the CSS:
.container {height:500px;width:100%;background:#ed565d;position:relative;}
.container span {display:block;}
.white {background:#ffffff;height:10px;width:10px;margin:auto;border-radius:100%;position:absolute;top:50%;left:50%;}
.container:hover .white {
width:300%;
height:300%;
-moz-transition: all 0.5s ease-out;
-o-transition: all 0.5s ease-out;
-webkit-transition: all 0.5s ease-out;
transition:all 0.5s ease-out;
top:-100%;
left:-100%;
}
If you want the animation to correlate directly to the percentage that the user has scrolled on the page, JavaScript will be needed.
First, get the scroll percentage. Here's a great answer on how to do that: https://stackoverflow.com/a/8028584/2957677
const scrollTop = $(window).scrollTop();
const documentHeight = $(document).height();
const windowHeight = $(window).height();
const scrollPercent = (scrollTop / (documentHeight - windowHeight)) * 100;
Then you can define an animation function that takes in the percent the user has scrolled, and will set the style on the circle to be a percentage between the CSS values at the start of the animation, and the CSS values at the end of the animation.
function growAnimation($element, animationPercentage) {
const animationDecimal = animationPercentage / 100;
// Your existing .grow CSS values
const startPositionPercent = 50; // top/left at start of animation
const finishSizePercent = 300; // width/height at end of animation
const finishPositionPercent = -100; // top/left at end of animation
// The current CSS values, based on how far the user has scrolled
const currentSizePercent = getProgressFromTo(0, finishSizePercent, animationDecimal);
const currentPositionPercent = getProgressFromTo(startPositionPercent, finishPositionPercent, animationDecimal);
$element.css({
width: `${currentSizePercent}%`,
height: `${currentSizePercent}%`,
top: `${currentPositionPercent}%`,
left: `${currentPositionPercent}%`
});
}
// A util function to get the progress between two values
// e.g. 50% between 0 and 10 is 5
function getProgressFromTo(from, to, animationDecimal) {
return from + (to - from) * animationDecimal;
}
Here's a fiddle: https://jsfiddle.net/owazk8y1
Animation Curves
You can look into animation curves to make the animation look a lot smoother. Surround animationDecimal in a bezier curve function. Here's some example functions:
https://gist.github.com/gre/1650294
https://jsfiddle.net/owazk8y1/1
It's a mix of different ideas that I have sinned here and there ...
with a small part JS, to be piloted in CSS
PS :transition command must be set on element
const storeScroll=()=>{
document.documentElement.dataset.scroll = window.scrollY;
}
window.onscroll=e=>{ // called when the window is scrolled.
storeScroll()
}
storeScroll() // first attempt
.container {
position : relative;
height : 500px;
width : 100%;
background : #ed565d;
overflow : hidden; /* added */
}
.white {
display : block;
position : absolute;
background : #fff;
height : 10px;
width : 10px;
margin : auto;
border-radius : 100%;
top : 50%;
left : 50%;
-moz-transition : all 0.5s ease-out;
-o-transition : all 0.5s ease-out;
-webkit-transition : all 0.5s ease-out;
transition : all 0.5s ease-out;
}
html:not([data-scroll='0']) .white {
width : 300%;
height : 300%;
top : -100%;
left : -100%;
}
<div class="container">
<span class="white"></span>
</div>

Crossfade images in Safari not working

I'm working on a project where I need to make a dynamically loaded full-screen image background that crossfades new images every so many seconds.
The script works now fine in most browsers, but on Safari the transition effects don't apply. In stead of nicely transitioning to the next image, the image disappears for 1.5s (the time it should do over the transition).
Does anyone know how I can fix this? I've tried to solve it using webkits on the transition of the opacity, but it didn't work out.
Here some of the code
Script.js
const images = {
init(imageArray) {
elements.image_holders.forEach(function(image, index) {
image.src = `${config.imageUrl}${imageArray[index]}`;
config.atImage ++;
});
elements.image_groups[0].style.opacity = 1;
config.imageArray = imageArray;
this.startInterval();
},
startInterval() {
imageInterval = setInterval(images.changeImage, config.interval);
},
}
In the interval I change opacity every time again and load the new picture dynamically from the image Array.
_image_groups.scss
.__image_wrapper {
opacity: 0;
transition: opacity 1.5s linear;
-webkit-transition: opacity 1.5s linear;
-webkit-backface-visibility: hidden;
display: block;
width: 100%;
height: 100%;
img {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
}
That is the styling I use for the image to do it's transition effect. I work from opacity: 0 to opacity: 1.
Has anyone a idea why the transition doesn't work on Safari? I've looked around on the internet but couldn't find the answer. Thanks!

CSS position transition not working at all when set with JavaScript

I want to do the following things:
show a div element;
move it to a an initial position;
set transition properties;
move it to the target position using CSS transition.
A minimal example:
function bla() {
/*
var obj = $('#box');
obj.css("left", "200px");
obj.css("display", "initial");
obj.addClass("trans");
obj.css("left", "500px");
*/
var elem = document.getElementById('box');
elem.style.left = "200px";
elem.style.display = "initial";
elem.className = "box trans";
elem.style.left = "500px";
}
#btn {
position: fixed;
top: 60px;
left: 0px;
width: 50px;
height: 50px;
background-color: #FEDCBA;
}
.box {
display: none;
position: fixed;
top: 0px;
left: 0px;
width: 50px;
height: 50px;
background-color: #ABCDEF;
}
.box.trans {
-webkit-transition: left 2s;
-moz-transition: left 2s;
transition: left 2s;
}
<div id="box" class="box"></div>
<div id="btn" onclick="bla()">click here</div>
JSFiddle.
It does not work at all. What is wrong?
If I set the element initially visible, I get a smooth transition starting from the origin left:0 which is totally strange because I assign elem.style.left = "200px"; before I actually add the transition properties...
You should avoid using style in javascript, just switch class years put all your animation in your css file.
You can't put transition together with display: none;, you have to use opacity: 0; instead.
function bla()
{
var obj = $('#box');
obj.toggleClass("trans");
}
#btn
{
position:fixed;
top:60px;
left:0px;
width:50px;
height:50px;
background-color:#FEDCBA;
}
.box
{
opacity: 0;
position:fixed;
top:0px;
left:0px;
width:50px;
height:50px;
background-color:#ABCDEF;
-webkit-transition: transform 2s,opacity 2s;
transition: transform 2s,opacity 2s;
}
.box.trans
{
opacity: 1;
-ms-transform: translate(500px,0); /* IE 9 */
-webkit-transform: translate(500px,0); /* Safari */
transform: translate(500px,0);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="box" class="box">
</div>
<div id="btn" onclick="bla()">
click here
</div>
DOM changes don't take effect until they can be rendered. Javascript is single-threaded (meaning you cannot run two pieces of code simultaneously), and run on the same thread as the render cycle.
Because of this, the renderer cannot fire unless you give it time to look at the new state of the DOM by deferring execution of your JS code (using setTimeout or requestAnimationFrame). So unless you give the browser time to render, only the final value before the renderer gets to look at the DOM is what matters.
This answer to a previous question goes over the exceptions to the rule.
Here's an updated version of your jsfidde that uses requestAnimationFrame to get around the issue.
I can not explain why. Maybe someone could, I'd be curious too, but with a time-out works.
setTimeout(function(){
elem.style.left = "500px";
},1);
It is probably too fast assigning properties left 500 and the transition to record the old location 200?
https://jsfiddle.net/StepBaro/s82rj48q/2/
It's because you have the div hidden, so it first need to display it and then add the transition, that is why it needs the delay.

How to make my CSS transition smoother/work on different browsers?

I'm trying to make a page that changes background image when you click a button. Code pen: http://codepen.io/meek/pen/EPLZpW
body {
background: url('https://s3.eu-central-1.amazonaws.com/meek-img/1.jpg') no-repeat center center fixed;
background-size: cover;
-webkit-transition: background .5s ease-in-out;
-moz-transition: background .5s ease-in-out;
-o-transition: background .5s ease-in-out;
transition: background .5s ease-in-out;
}
The two big problems with the way I'm doing this currently are:
It flat out doesn't work in Mozilla Firefox.
The animation isn't smooth at all. It switches to a blank background for an instant, then the animation runs. Is there a way to prevent this, or is it unavoidable due to loading times? If so are there other ways to implement this function that would circumvent this? At first, I thought of animating with jQuery, but was told that this wasn't optimal.
Doing a transition on the full property background could possibly create some wonky effects. Especially when images are involved. Directly doing these transitions on background images is very demanding for your browser. And, as mentioned by you, does not behave consistently in different browsers.
A better idea is to do a transition or animation on opacity of an element (like a div) having that background property. This way the browser would not have to worry about transitioning the image, only about transitioning the div, which would result in a much easier task for your browser. If you want to change the background of your page, you would then simply add a div with that background image.
I've included a snippet, base on your code, to demonstrate the general idea. You'll also notice, I only preload the image when the image is requested as background image. This way the user only downloads the images he gets to see. My demo uses jQuery, but it could easily be integrated using vanilla JS.
function preload_img (src, callback) {
var img = new Image();
img.onload = function() {
if( callback && typeof callback === 'function' ) {
callback( img );
}
};
img.src = src;
}
var imgs = [
"https://s3.eu-central-1.amazonaws.com/meek-img/1.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/2.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/3.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/4.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/5.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/6.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/7.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/8.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/9.jpg"
];
function change_bg() {
var random_index = Math.floor(Math.random() * imgs.length);
preload_img( imgs[random_index], function( img ) {
$('<div>')
.addClass('bg-image')
.css('background-image', 'url(' + img.src + ')')
.appendTo('body')
;
} );
}
$( 'button' ).click( function() {
change_bg();
} );
change_bg();
#keyframes fadein
{
from { opacity: 0; }
to { opacity: 1; }
}
.bg-image
{
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
animation: fadein 1s;
background-size: cover;
}
.mimi
{
z-index: 1;
bottom: 0;
height: 100px;
left: 0;
margin: auto;
position: absolute;
top: 0;
right: 0;
width: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="mimi">
<button class="btn btn-default btn-lg">click me</button>
</div>
</body>
I'd consider loading the new image on top of the current image with 0 opacity and then transition the opacity to 1. After that you can remove the image below.
Also make sure that the image is fully loaded before starting the transition.
Adding the new image on change can be done with append() and then when the new image has loaded you can add a class to it with opacity 1.

CSS3 transitions seem to need a "warmup" time before they kick off? Do I have to add a wait?

I have a CSS3 transition where I shrink the height of an element. I'm doing this by:
CSS3 transition and destination height specified on class (e.g. height:5px in class)
Assign height=$el.height() to element in style attribute (e.g. height:100px on style)
Add class to element
Remove the height defined in style attribute
Computed CSS value changes from style's 100px to class's 5px, which triggers a transition
What I'm discovering is that there seems to be a delay on setting the CSS3 transition trigger, and if step #4 is done too soon after #3, the trigger will see that height is initially auto instead of 100px, and no transition happens (due to another limitation of CSS3, you can't transition from height:auto to height:x).
Here's the actual code:
$(document).ready(function() {
$a = $('#box'); // height:auto, not specified
$a.css('height',$a.height()); // height:100px on style;
$a.addClass('shrink'); // height:100px on style, height:5px on class
$a.css('height',''); // height removed from style, falls back to 5px on class
});
Here's a JSFiddle demonstrating the issue: http://jsfiddle.net/1escyLqf/2/
In the fiddle, if I add a delay of 20ms before removing the height value, the transition works. If you comment that line out and call css('height','') immediately after adding the class, it's too fast and no transition happens.
Is this a bug in browser implementation of CSS3 transitions? Or do I really need to add in a timeout?
Create this with CSS3 animation appended in the jQuery with a dynamic starting height.
The jQuery
Get the boxes height:
var boxHeight = $('#box').height();
Create your animation and apply it to the shrink class. Get the starting height from the variable.
var animation = '
<style type="text/css">
.shrink {
-webkit-animation: shrink 12s forwards;
animation: shrink 12s forwards;
}
#-webkit-keyframes shrink {
0% {
height:' + boxHeight + 'px;
}
100% {
height: 5px;
}
}
#keyframes shrink {
0% {
height:' + boxHeight + 'px;
}
100% {
height: 5px;
}
}
</style>';
Append the style to the <head>
$('head').append(animation);
Complete Example
$(document).ready(function() {
var boxHeight = $('#box').height();
var animation = '<style type="text/css"> .shrink { -webkit-animation: shrink 12s forwards; animation: shrink 12s forwards; } #-webkit-keyframes shrink { 0% { height:' + boxHeight + 'px; } 100% { height: 5px; } } #keyframes shrink { 0% { height:' + boxHeight + 'px; } 100% { height: 5px; } }</style>';
$('head').append(animation);
});
#box,
#box2 {
background: red;
margin: 10px;
}
.shrink {
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<div id="box" class="shrink">
<p>make height</p>
<p>make height</p>
<p>make height</p>
<p>make height</p>
</div>
A colleague helped me out with the following solution, which calls an offset between the addClass() and the css() height-reset:
$a.addClass('shrink');
$a.offset();
$a.css('height','');
This forces a redraw after addClass(), and ensures that the class addition (and transition set-up) occurs before the height change.
http://jsfiddle.net/1escyLqf/3/

Categories

Resources