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 yourtransform
.
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 themargin
, thedisplay
change will affect it and nullify yourmargin
.
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
Make the on Scroll Growing <Path> to Dashed Line
Capture/Save/Export an Image with CSS Filter Effects Applied
How to Get the Opacity of an Element Using JavaScript
Dynamically Changing Stylesheet Path Not Working in Ie and Firefox
Highcharts: Make the Legend Symbol a Square or Rectangle
How to Properly Escape Attribute Values in CSS/Js Attribute Selector [Attr=Value]
Customize Ng-Repeat in Angularjs for Every Nth Element
How to Show the Child Div on Mouse Hover of Parent Div
Hiding Div If Using Mobile Browser
Modify Pseudo-Element :After's Width Using JavaScript
How to Style Canvas Elements with CSS
How to Animate the Opacity of the Background of a Div
Ie Not Allowing Onclick Event on Dynamically Created Dom 'A' Element
Setting Multiple Style.Background Values
Update Three.Js Raycaster After CSS Tranformation