Cannot Dynamically Set Initial Element Translation Before Transition in Same Call Stack

Dynamically set css transform before transition

If you programmatically set the style.transform property directly on your element (which you need if you want to move it to an arbitrary position through JS), it will override any transform specified in classes. Hence adding "cursorDivMoved" class later on does not transform (translate / move) it.

You have to continue moving it by specifying its style.transform property, or simply remove it: cursorDiv.style.transform = null

Demo: https://jsfiddle.net/fmt1rbsy/9/

You may also want to have the very first translate being transitioned. In that case, you have to wait for the browser to make an initial layout with your element at its start position, otherwise it will see no transition (it will see it directly after the transform is applied, i.e. at its final position). You can either:

  • Use a small (but non zero) setTimeout to give some time for the browser to do its initial layout.
  • Force a browser layout by trying to access some property that require the browser to compute the page layout (e.g. document.body.offsetWidth).
  • Use 2 nested requestAnimationFrame's before applying your transform.

Demo: https://jsfiddle.net/fmt1rbsy/8/

Transition not executing properly

Like it happens with many UI frameworks that execute all UI operations in only one thread (Windows Forms, WPF ...), most layout operations are blocked while user code is executing. Why? Well, while Javascript code is executing, the browser is tipically unresponsive, because its UI thread is occupied processing your code. So, if you change the value of the same property multiple times before relinquishing control to the browser, only the last value remains. It would be a waste of processing power to go through a layout process when you change, let's say, the left value of an element, when maybe you can change it later in the same event handler. So, the browser just creates a queue of pending layout operations an performs them when it has nothing else to do.

Applied to your case, the browser adds to the queue the operation {next.style, 'left', -50px}. When you update left again, the browser doesn't add a new operation. As there is one pending that does the same, it just updates it. So, to the browser, you aren't changing the left property from -50px to 0px, you are just setting it to 0px.

You need to relinquish control to the browser after setting left to -50px. Then, you set it back to 0px. You can do it this way:

var next = document.querySelector('.nd-slide-next');
next.style.left = '-50px';
setTimeout(function() {
next.style.transitionProperty = 'left';
next.style.transitionDuration = '5s';
next.style.left = '0';
}, 10);

It also could work if you force a synchronous layout (which is often undesirable). You can achieve this by accessing some properties of a DOM node, like the offset- or client- properties. These properties always return the most updated values, so, if there are layout operations pending, the browser stops the execution of javascript code, performs the layout, and continues executing:

var next = document.querySelector('.nd-slide-next');
next.style.left = '-50px';
var offset = next.offsetLeft; //force layout. The broswer know acknowledges that next.style.left is -50px.
next.style.transitionProperty = 'left';
next.style.transitionDuration = '5s';
next.style.left = '0';

CSS transition not working on dynamic set

As mentioned in my comment:

You have a different display value for each state. Even though you are trying to transition just the margin, the display change will affect it and nullify your margin.

You should try using translate and opacity instead, since you have fixed heights anyways (if this is supposed to be fluid based on content, this solution kind of falls apart, but that was not part of the question so I'm going to assume it's okay).

// get slide deck (returns ul)var slideDeck = document.getElementById('slider-wrapper').children[0];
/** slideIndex * @param integer * @return object*/function slideIndex(par) { for (var i = 0; i < slideDeck.children.length; i++) { if (slideDeck.children[i].getAttribute("data-active") == "true") {
var activeSlide = slideDeck.children[i]; var slideIndex;
// previous slide if (par == -1) { // if currently on first slide if (i == 0) { slideIndex = slideDeck.children.length - 1; } else { slideIndex = i - 1; } } // current slide else if (par == 0) { slideIndex = i; } // next slide else if (par == 1) { // if currently on last slide if (i == slideDeck.children.length - 1) { slideIndex = 0; } else { slideIndex = i + 1; } }
return { el: slideDeck.children[slideIndex], index: slideIndex }; } }}
// run a timeout loop to linear transgressionsetInterval(function() { // get slides var currentSlideElement = slideIndex(0).el; var previousSlideElement = slideIndex(-1).el; var nextSlideElement = slideIndex(1).el;
currentSlideElement.setAttribute("data-active", "false"); nextSlideElement.setAttribute("data-active", "true");
console.log('ok');}, 5500);
#slider-wrapper {    float: left;    width: 500px;    border: thin dotted;    /* overflow-x: hidden; */    height: 100px;}
#slider-wrapper ul { list-style-type: none; margin: 0; padding: 0; width: 100%; height: inherit; /* For consistency */ position: relative; /* To contain each slide */}
#slider-wrapper ul li { list-style-type: none; margin: 0; width: 100%; height: 100%; /* Removed all float/margin related properties */ position: absolute; left: 0; top: 0; opacity: 0; /* Hide it visually */ pointer-events: none; /* Disable interactions */ transform: translateX(100%); /* Move it away!*/ transition: all 1s; /* Feel free to make this a concise list of properties */}
#slider-wrapper ul li[data-active='true'] { opacity: 1; /* Show it */ pointer-events: auto; /* Restore interactions */ transform: translateX(0); /* Put it back in the centre */}
<div id="slider-wrapper">    <ul>      <li data-active="true">Slide one</li>      <li data-active="false">Slide two</li>      <li data-active="false">Slide three</li>    </ul></div>

Setting source location using CSS transitions

This is both a problem and a feature with CSS transitions. The browser only sees the "state" of an object that it can use for a transition when your javascript is done executing so only when that happens can the "initial" state of an object be set and likewise, only then can the final state of an object for transitions be seen by the browser. That means that:

Widget.style.left = Src.x;
Widget.style.left = Dest.x;

won't do anything. If the object is brand new, the only state the browser will see (for the purposes of transitions is Dest.x and that will be treated as the initial state (thus no transition).

The only way I've found around it is to let my javascript finish and use a timer to kick off the transition:

// create Widget
Widget.style.left = Src.x;
Widget.style["-webkit-transition"] = "left 0.3s ease-in-out";
// user a timer to get the current location marked as the starting location
// and then set the new location
setTimeout(function() {
Widget.style.left = Dest.x; // trigger transition from Src.x to Dest.x
}, 0);

I say this is both a feature and a problem because it has its benefits along with its problems. If you move an object around a couple times in the middle of one piece of javascript, the object won't transition between all those intermediate locations, it will only transition to the final location. But, it does create problems like what you're running into.

Edit: This post Cannot dynamically set initial element translation before transition in same call stack suggests that there is a work-around that forces a browser layout by accessing any one of a number of specific properties after you set the start position before you set the final position. I have not tried it myself.

This work-around seems to work here in Chrome: http://jsfiddle.net/jfriend00/VqTHV/

programmatically changing webkit-transformation values in animation rules

Use the CSSOM

var style = document.documentElement.appendChild(document.createElement("style")),
rule = " run {\
0% {\
-webkit-transform: translate3d(0, 0, 0); }\
transform: translate3d(0, 0, 0); }\
}\
100% {\
-webkit-transform: translate3d(0, " + your_value_here + "px, 0);\
transform: translate3d(0, " + your_value_here + "px, 0);\
}\
}";
if (CSSRule.KEYFRAMES_RULE) { // W3C
style.sheet.insertRule("@keyframes" + rule, 0);
} else if (CSSRule.WEBKIT_KEYFRAMES_RULE) { // WebKit
style.sheet.insertRule("@-webkit-keyframes" + rule, 0);
}

If you want to modify a keyframe rule in a stylesheet that's already included, do the following:

var
stylesheet = document.styleSheets[0] // replace 0 with the number of the stylesheet that you want to modify
, rules = stylesheet.rules
, i = rules.length
, keyframes
, keyframe
;

while (i--) {
keyframes = rules.item(i);
if (
(
keyframes.type === keyframes.KEYFRAMES_RULE
|| keyframes.type === keyframes.WEBKIT_KEYFRAMES_RULE
)
&& keyframes.name === "run"
) {
rules = keyframes.cssRules;
i = rules.length;
while (i--) {
keyframe = rules.item(i);
if (
(
keyframe.type === keyframe.KEYFRAME_RULE
|| keyframe.type === keyframe.WEBKIT_KEYFRAME_RULE
)
&& keyframe.keyText === "100%"
) {
keyframe.style.webkitTransform =
keyframe.style.transform =
"translate3d(0, " + your_value_here + "px, 0)";
break;
}
}
break;
}
}

If you don't know the order but do know the URL of the CSS file, replace document.styleSheets[0] with document.querySelector("link[href='your-css-url.css']").sheet.

inserted DOM object dynamically and transition not applied

To maintain browser performance, browser do reflow or redraw after all the changes are made to an element or say after having a change on style of any element it waits for a fraction of time to check if any other changes occur, to minimize the number of reflow and redraw.

So in your case after the element is created and opacity is applied reflow and redraw occurs causing no transition.

To solve this you can either follow two approach .

i) Force browser to call reflow/ repaint.

self.activeSlideDom = $("<div id='module_"+self.moduleId+"-slide_"+self.activeSlide+"' class='slide'></div>").appendTo("div#module_"+self.moduleId);

var offset = self.activeSlideDom.offset();

$(self.activeSlideDom).css("opacity","1");

Any css property which need to be computed by browser calls the reflow/ repaint. like .height(), .position(), .offset() etc.

ii) Do css change after reflow/redraw is completed, by adding it on asynchronous queue.

setTimeout(function(){
$(self.activeSlideDom).css("opacity","1");
},0);

How can I make this transition smooth instead of it jumping up

Your notif container has justify-content: flex-end. This means that whenever you add a new one, the previous ones will be pushed up with the height of the new one.

The "fix" is to give each element a negative margin-top equal to its height and integrate in your current transition getting margin-top back to 0.

Example:

let btn = document.querySelector('button'),  container = document.querySelector('.notifications-container'),  notif_contents = [  "<p>1</p><p>1</p><p>1</p><p>1</p>",  "<p>test</p>",  "<div><h1>testtesttest</h1><p>yoloalsdfasdf</p></div>",  "<code>another.test()</code>",  "<strong>another</strong> <i>test</i>"]
btn.onclick = () => { let notif = document.createElement('div'), index = Math.floor(Math.random() * notif_contents.length) notif.classList.add('notif') notif.innerHTML = notif_contents[index] notif.addEventListener('animationend', () => { notif.parentElement.removeChild(notif) })
container.append(notif) notif.style.marginTop = '-' + notif.offsetHeight + 'px'}
* {  box-sizing: border-box;}
.container { height: 300px; width: 400px; border: 1px solid #888; position: absolute;}
.notifications-container { position: absolute; top: 0; right: 0; height: 100%; width: 200px; background: rgba( 0, 0, 0, 0.2); display: flex; flex-flow: column nowrap; justify-content: flex-end; overflow: hidden;}
.notif { position: relative; width: 100%; padding: 10px; border: 1px solid #ddd; border-bottom: none; animation: notifAnim 5s forwards; background: white;}
button { z-index: 100; background: lightcoral; color: white; border: none; padding: 10px; font-size: 20px; margin: 5px;}
@keyframes notifAnim { 0%, 100% { transform: translateY( 100%) } 20%, 80% { transform: translateY( 0); margin-top: 0 }}
<div class="container">  <button>New Notification</button>  <div class="notifications-container"></div></div>

Apply two different CSS transforms simultanesouly

transform is a single property, so you can't target from CSS for only part of it, e.g, you cannot set different CSS transition-duration for two transform-functions applied in the same transform property.

You could write everything through js, updating yourself both transform-functions values over time, but that would be a lot of work, for a potentially non-hardware accelerated result, while there is a simple workaround.

The easiest solution is to change a little bit your structure so that you apply the scale on a wrapper element, and the translate on the inner-one.

This way, both transforms are applied on your inner <img>, but they don't conflict each other.

// JavaScript source codevar catchX = 0,  catchY = 0,  x = 0,  y = 0,  burn = 1 / 28;
function imageWatch() { x += (catchX - x) * burn;
translate = 'translate(' + x + 'px, ' + y + 'px)';
$('.image-area img').css({ '-webit-transform': translate, '-moz-transform': translate, 'transform': translate });
window.requestAnimationFrame(imageWatch);}
$(window).on('mousemove click', function(e) {
var mouseX = Math.max(-100, Math.min(100, $(window).width() / 2 - e.clientX)); catchX = (26 * mouseX) / 100;
});
imageWatch();
html,body {  height: 100%}
body { margin: 0; padding: 0;}
*,*::before,*::after { content: "\0020"; box-sizing: border-box;}
.poster { display: inline-block; width: 32vw; height: 100vh; position: relative; overflow: hidden !important}
.image-area { position: absolute; width: 100%; height: 100vh; opacity: .24; transition: 2.5s ease;}
.image-area { opacity: 1;}
.image-area img { margin-top: -312px; margin-left: -913px; width: auto; /* height: auto */ height: 1000px; /* here we can remove the transition */}

/* scaling on hover is only applied on the parent elmt */
.image-area>.scaler { transform: scale(1, 1); transition: 8s transform;}
.poster:hover .image-area>.scaler { transform: scale(1.3, 1.3);}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><div class="poster">  <div class="image-area">    <!-- the scaling wrapper-->    <div class="scaler">      <img class="poster" src="https://vignette1.wikia.nocookie.net/logopedia/images/f/fc/Sky_Believe_in_better_logo.jpg/revision/latest?cb=20110910152552" alt="Sky" />    </div>  </div></div>
<div class="poster"> <div class="image-area"> <!-- the scaling wrapper--> <div class="scaler"> <img class="poster" src="https://vignette1.wikia.nocookie.net/logopedia/images/f/fc/Sky_Believe_in_better_logo.jpg/revision/latest?cb=20110910152552" alt="Sky" /> </div> </div></div>
<div class="poster"> <div class="image-area"> <!-- the scaling wrapper--> <div class="scaler"> <img class="poster" src="https://vignette1.wikia.nocookie.net/logopedia/images/f/fc/Sky_Believe_in_better_logo.jpg/revision/latest?cb=20110910152552" alt="Sky" /> </div> </div></div>


Related Topics



Leave a reply



Submit