Using keyframes, it is possible to animate an element as soon as it is inserted into the DOM. Here's some sample CSS:
#-moz-keyframes fadeIn {
from {opacity:0;}
to {opacity:1;}
}
#-webkit-keyframes fadeIn {
from {opacity:0;}
to {opacity:1;}
}
#keyframes fadeIn {
from {opacity:0;}
to {opacity:1;}
}
#box {
-webkit-animation: fadeIn 500ms;
-moz-animation: fadeIn 500ms;
animation: fadeIn 500ms;
}
Is there some similar functionality available to apply an animation (via CSS, no JavaScript) to an element right before it is removed from the DOM? Below is a jsFiddle I made to play around with this idea; feel free to fork it if you know of a solution.
jsFiddle - http://jsfiddle.net/skoshy/dLdFZ/
Create another CSS animation called fadeOut, say. Then when you want to remove the element, change the animation property on the element to that new animation, and use the animationend event to trigger the actual removal of the element once the animation is done:
$('.hide').click(function() {
if (!$(this).hasClass('disabled')) {
$('#fill').css('-webkit-animation', 'fadeOut 500ms');
$('#fill').bind('webkitAnimationEnd',function(){
$('#fill').remove();
$('.show, .hide').toggleClass('disabled');
});
}
});
See also my updated version of your jsFiddle. That works for me in Safari, at least.
Well, you should be using a class instead of that .css().
I don't think jQuery has any "real" support for CSS animations yet, so I don't think you can get rid of that webkitAnimationEnd. In Firefox it's just called animationend.
I'm pretty sure there's no way to do this in just CSS.
I've been working on a component library for javascript and I ran into this problem myself. I have the benefit of being able to throw a ton of javascript at the problem, but since you're already using a bit, consider the following for a gracefully degrading solution:
On removal of any component/dom node, add a class called 'removing'.
Then in the css you can define your animation using that class:
.someElement.removing {
-webkit-animation: fadeOut 500ms;
-moz-animation: fadeOut 500ms;
animation: fadeOut 500ms;
}
And back in the javascript, just after you add the 'removing' class, you should be able to check for the 'animation' css property, and if it exists, then you know that you can hook on 'animationEnd' and if it doesn't, then just remove the element right away. I remember testing this a while back, it should work; I'll see if I can dig up the example code.
Update:
I've dug up this technique and started writing a really cool plugin for jQuery that allows you to use CSS3 animations for DOM elements that are being removed. No additional Javascript required: http://www.github.com/arthur5005/jquery.motionnotion
It's very experimental, use at your own risk, but would love some help and feedback. :)
ideally for something like fadeIN and fadeOUT you should be using css transitions not css animations..
Related
I'm using Angular 6 and Angular Material. I'm attempting to implement a simple drag and drop list inside of a Material tab (). Drag and drop works fine outside of a material tab, but inside the tab it's very slow (18 seconds to DnD 1 item in a list of 5 items). The onDragEvent fires many many times even when the mouse isn't moving. I've tried detaching (onDragStart) and reattaching (onDrop) the change detector. It doesn't make a difference.
Here's the html:
<div (dragover)="onDragOver($event)">
<div *ngFor="let item of listItems, let i = index"
(drop)="onDrop($event, i)"
[draggable]="true"
(dragstart)="onDragStart(i)">
{{item}}
</div>
</div>
Here are the functions:
onDragStart(i) {
}
onDragOver(event) {
console.log('1')
event.preventDefault();
}
onDrop(event, i) {
}
Thank you.
It appears that as I dragged the item around the screen- even briefly, the Angular engine was overfiring and queuing up a ridiculous number of calls of onDrop and/or onDragEnter. And that when angular hooks those events, it also assumes that something in the angular components has changed and it runs through the change detection routines.
Each... time.
What I did that fixed my problem was to turn off change detection when dragging began and turn it back on when dragging was complete.
Here is a brief hint at what I did:
import ChangeDetectorRef from '#angular/core'
inject ChangeDetector into your constructor:
constructor(private cdr: ChangeDetectorRef)
call detach from the dragStart:
this.cdr.detach();
call reattach from the drop and dragEnd:
this.cdr.reattach();
If your component is buried deep on your page, you may have to detach change detection at a higher level in order to see results.
i was experiencing a similar issue, where dragging when there were many elements on the page was unbearably slow. I found that removing the (dragover) angular binding, and replacing it with pure javascript instantly solved the problem
i switched
(dragover)="this.onDragOver($event)"
to
ondragover="onDragOver(event)"
you also need to declare the function in index.html instead of in your typescript file or html template (hence pure javascript)
I'm implementing some custom components for openui5/sapui5.
For the component, I want to have a css animation when it is initially loaded.
This is quite straight forward as I add the following css:
#-webkit-keyframes card-in-animation {
from {
-webkit-transform: translateY(50em) rotate(5deg);
opacity:0;
}
}
.card {
animation:card-in-animation 0.7s .2s ease-out both;
-webkit-animation:card-in-animation 0.7s .2s ease-out both;
}
The problem arises when the component is re-rendered for some reason by the openui5 framework.
The DOM elements are then destroyed and new ones are created.
This causes the animation to triggered once again.
To see this in practice:
Go to http://elsewhat.github.io/openui5-cards/cdn/latest/example3.html
The initial animation is triggered as wanted
Click on the menu icon in the top right corner of any card
A new unwanted animation is triggered
What's the preferred method of avoiding this in openui5?
Inside of your renderer you can set an flag that this control/component is rendered once.
second time you'll run into this renderer you'll check this flag and doesn't render this specific class.
if(!oControl._renderedOnce) {
oRm.addClass('rotate');
oControl._renderedOnce = true;
}
oRm.writeClasses();
Not sure how to overcome the unwanted re-rendering, but would it be an option to remove the animation class after the cards are rendered? (You may need to trigger it after a few seconds after onLoad for all cards to be set in place)
Something like this:
$("<element_of_your_cards>").removeClass("card")
Ok, so I don't understand why this doesn't ease out, only in: (for quick view, just paste it in http://htmledit.squarefree.com, for instance)
<style>
#over {
background: url(http://th01.deviantart.net/fs71/150/f/2013/005/0/6/dal_shabet__have__don_t_have_by_awesmatasticaly_cool-d5qkzu8.jpg);
height:150px;
width:150px;
}
#in {
background: url(http://www.mygrafico.com/images/uploads/thumbs/thumb_revidevi_CoolMonsterTruck.jpg);
height:150px;
width:150px;
}
#in:hover {
opacity: 0;
transition: opacity .3s ease-in-out;
}
</style>
<div id="over">
<div id="in"></div
</div>
I'm not sure exactly what you're asking, but I think you are saying that the transition is only occurring in on a mouse over, and not when you mouse out (it just snaps back to the way it was before, instead of transitioning). You need to add your transition property to #in, rather than #in:hover. The transition is only occurring when your #in element is being hovered over. Move the css for the transition under #in and it will work on mouse over and mouse out. You want to leave your desired css for the hover state under #in:hover, in this case, the opacity change, but the actual transition css property will go under #in.
To make it a little more clear:
#in {
background: url(http://www.mygrafico.com/images/uploads/thumbs/thumb_revidevi_CoolMonsterTruck.jpg);
height:150px;
width:150px;
transition: opacity .3s ease-in-out; /* add this here */
}
#in:hover {
opacity:0;
/* and we've removed it from here */
}
The thing to know is that ease-in-out has nothing to do with when it will transition. It may seem that way, that ease-in is on hover, and ease-out is on mouseout, but actually, ease-in-out means that it will ease in and out slowly, rather than something like ease-in, which eases into the transition slowly and then speeds up towards the end. It relates to the style of the transition itself, rather than when it will occur.
Maybe you already understood that, but based on your question it seems you were confused there.
I found #welbornio 's answer worked for me, however ease-in-out didn't work perfectly at first. When applied to the 'img' the ease effect only worked intermittently. I found that in order for the effect to work everytime, I had to apply the ease-in to the 'img' and the ease-out to the 'hover'. I'm not sure why this was happening but that's a solution if anyone runs into the same issue.
I am having a bit of a weird problem with iOS platform for a page i am developing. This is the page in question. When clicking any of the case study images, the page will first unhide the required case study then scroll to it.
This works on all desktop browsers on Windows and Mac, but on the iPhone and iPad you get a horrible flicker as it scrolls down.
Not quite sure how to debug or fix this issue.
Any ideas would be of great help!
Thanks in advance,
Shadi
UPDATE 1
The latest page can be found here. Still haven't found a fix - if anyone has any idea it would be amazing!
If you need vertical scroll only, you could use {'axis':'y'} as settings to scrollTo method.
$.scrollTo(*selector*, *time*, {'axis':'y'});
Have you tried this:
$('a[href=#target]').
click(function(){
var target = $('a[name=target]');
if (target.length)
{
var top = target.offset().top;
$('html,body').animate({scrollTop: top}, 1000);
return false;
}
});
If you're just scrolling the page vertically you can replace the entire jQuery scrollTo plugin with this simple line:
$('html,body').animate({scrollTop: $("#scrollingTo").offset().top}, 1000, 'easeOutCubic');
Personally I do something like this
$('html,body').animate({scrollTop: $("#step-1").offset().top-15}, 1000, 'easeOutCubic',function(){
//do stuff
});
I found that if I try to do other js work while it's scrolling it makes the browser crunch and the animation isn't smooth. But if you use the callback it'll scroll first, then do what you need.
I put a -15 at the end of .top because I wanted to show the top edge of the div I was scrolling do, simply for aesthetic purposes. 1000 is the duration in milliseconds of the animation.
Credit goes to the poster, animate, for the tip off.
Defining {'axis':'y'} has made it right! It helped me with slideUp/Down flickering.
I'm not sure if this applies to jquery animations. But the following seems to affect CSS animations.
http://css-infos.net/property/-webkit-backface-visibility
Syntax
-webkit-backface-visibility: visibility;
Parameters
visibility
Determines whether or not the back face of a transformed element is visible. The default value is visible.
edit
Try applying it to every element and see what happens.
*{
-webkit-backface-visibility: visible;
}
and try
*{
-webkit-backface-visibility: hidden;
}
It's just a guess really...
I will also confirm Tund Do's method works flawlessly. If you need a "left/right" variation of the same thing (as I did) here it is:
$('.pg6').click(function(){
var target = $('#page6');
if (target.length)
{
var left = target.offset().left;
$('html,body').animate({scrollLeft: left}, 1000);
return false;
}
});
I would guess you could combine the two, grab the top position and chain the animates for a "left/right/up/down" animation also.
I had the same problem.
The problem is the ScrollTo plugin. Instead of using scrollto.js just use .animate with scrollTop. No more flickering in ipad/iphone.
Here it is with no flickering http://www.sneakermatic.com
You should include {axis: 'y'} in your options object. Also be sure that you have not enabled interrupt option. You can test this with {interrupt: false}.
You need to add e.preventDefault(); to each .click() call. This prevents the browser's default action, which is to stay in the same place. Hope this helps!
i.e.
$("#quicksand li, .client-list li").click(function (e) {
e.preventDefault();
...
});
I'm having the same flickering on iPhone -- even with the preventDefault and return false options of canceling the default click event. It appears that on the device it tries to go back to the top of the page before scrolling. If you have both a scrollTop and scrollLeft animation going on it really gets buggy. It's jQuery's issue.. I've seen a scrolling method with mootools that doesn't have this issue. See this page: http://melissahie.com/
Thanks nicole for giving the example with mootools.
It really seems to be a jQuery issue when trying to do a animation on BOTH scrollTop and scrollLeft.
With mootools:
var scroll = new Fx.Scroll(window, {duration: 1000, wait: false, transition: Fx.Transitions.quadInOut});
scroll.start(y, x);
it works flawlessly on iOS5!
Clicking on an element which has a Javascript handler makes the element go have a 'grey overlay'. This is normally fine but I'm using event delegation to handle the touchdown events of many child elements. Because of the delegation the 'grey overlay' is appearing over the parent element and looks bad and confusing.
I could attach event handlers to the individual elements to avoid the problem but this would be computationally very wasteful. I'd rather have some webkit css property that I can override to turn it off. I already have visual feedback in my app so the 'grey overlay' is not needed.
Any ideas?
-webkit-tap-highlight-color
To disable tap highlighting, set the
alpha value to 0 (invisible)
$('body').bind('touchstart', function(e){
e.preventDefault()
})
For those reading this question, another CSS property which will not only remove the overlay but also prevent the touch device from interacting with the user to assess a tap intention (which delays the timing of the event trigger significantly), is to use the cursor property.
a.notTappable {
cursor:default
}
Also, in the case the asker described where you have descendants of a parent and you only want the descendants to be tappable and you wish to IGNORE ALL MOUSE EVENTS OF THE PARENT, the following solution will you give you the best performance (by completely disabling the bubbling up to the parent). Read the webkit spec here.
.parent {
pointer-events:none;
}
.parent .descendant {
pointer-events:default;
}
I only mention this because the performance difference here is significant.