2014-06-10 3 views
42

this question과 같이 새로 추가 된 요소의 즉시 CSS 전환은 무시됩니다. 전환의 끝 상태가 즉시 렌더링됩니다.추가 된 요소에서 CSS 전환 트리거

.box { 
    opacity: 0; 
    transition: all 2s; 
    background-color: red; 
    height: 100px; 
    width: 100px; 
} 

.box.in { opacity: 1; } 

이 소자의 투명도가 바로 설정된다 (1) : I는 트리거의 여러 가지를 본

// Does not animate 
var $a = $('<div>') 
    .addClass('box a') 
    .appendTo('#wrapper'); 
$a.addClass('in'); 

이 CSS (접두사 생략) 주어진 예

, 전환 예상 동작을 얻으려면 :

// Does animate 
var $b = $('<div>') 
    .addClass('box b') 
    .appendTo('#wrapper'); 

setTimeout(function() { 
    $('.b').addClass('in'); 
},0); 

// Does animate 
var $c = $('<div>') 
    .addClass('box c') 
    .appendTo('#wrapper'); 

$c[0]. offsetWidth = $c[0].offsetWidth 
$c.addClass('in'); 

// Does animate 
var $d = $('<div>') 
    .addClass('box d') 
    .appendTo('#wrapper'); 
$d.focus().addClass('in'); 

바닐라 JS DOM 조작 - 이것은 jQuery 특정 동작이 아닙니다.

편집 - 나는

JSFiddle 크롬 35 (바닐라 JS 예 포함) 사용하고 있습니다.

  • 왜 추가 된 요소의 즉각적인 CSS 애니메이션이 무시됩니까?
  • 어떻게 그리고 왜 이러한 방법이 효과가 있습니까?
  • 다른 방법이 있습니까
  • 어느 것이 더 선호되는 솔루션입니까?
+0

좋은 질문, 그것은 때문에 코드를 최적화 스크립팅 엔진에 애니메이션이 임박 가능합니다. – Nit

답변

76

새로 추가 된 요소에 애니메이션을 적용하지 않는 원인은 브라우저에서 배치 리플 로우입니다.

요소를 추가 할 때 리플 로우가 필요합니다. 클래스를 추가하는 경우에도 마찬가지입니다. 그러나 단일 자바 스크립트 라운드에서 모두 수행 할 경우 브라우저는 첫 번째 최적화를 수행 할 수있는 기회를 갖습니다. 이 경우, 단일 (초기 및 최종 동시) 스타일 값만 있으므로 전환이 발생하지 않습니다.

setTimeout 다른 자바 스크립트 라운드에 클래스 추가를 지연시키기 때문에 트릭이 작동하므로 렌더링 엔진에는 두 가지 값이 있습니다. 즉, 첫 번째 것이 제시 될 때와 같이 계산해야합니다 사용자에게

배치 규칙의 예외가 있습니다. 브라우저가 액세스하려는 경우 즉시 값을 계산해야합니다. 이 값 중 하나는 offsetWidth입니다. 액세스 할 때 리플 로우가 트리거됩니다. 다른 하나는 실제 디스플레이 중에 별도로 수행됩니다. 다시 말하면, 우리는 두 가지 다른 스타일 값을 가지므로 시간 내에 그들을 보간 할 수 있습니다.

이것은 실제로이 동작이 바람직 할 때 아주 소수의 경우 중 하나입니다. DOM 수정 사이에서 리플 로우를 유발하는 속성에 액세스하는 대부분의 경우 심각한 속도 저하를 초래할 수 있습니다.

선호하는 해결책은 사람마다 다를 수 있지만 나를 위해 offsetWidth (또는 getComputedStyle())의 액세스가 가장 좋습니다. 경우 사이에 스타일 재 계산없이 setTimeout이 실행되는 경우가 있습니다. 드문 경우이지만 대부분로드 된 사이트에서 발생하지만 그럴 수 있습니다. 그렇다면 애니메이션을 얻지 못할 것입니다. 계산 된 스타일에 액세스하면 브라우저가 실제로 계산하도록 강제하고 있습니다.

+7

위대한 답은 +2 –

+0

* "다른 자바 스크립트 라운드에 클래스 추가를 지연시키기 때문에 setTimeout 트릭이 작동합니다."* Firefox에서 'setTimeout' 기간 문제. 나는 세부 사항을 모르지만 나를 위해 (파이어 폭스 42, 리눅스), 3ms이 작동을위한 최소입니다 : http://jsfiddle.net/ca3ee5y8/4/ –

+0

@ TJCrowder 봐 파이어 폭스는 최적화하려고하는 것 같아. 물건을 더 추가하고 타이머 콜백이 평가되기 전에 페인트와 리플 로우가 없었다면 운이 없어진 것입니다. 그렇기 때문에 동 기적으로 트리거하는 방법을 알고있는 것이 좋습니다. 원하는 경우 콜백 시작 부분에서이 작업을 수행 할 수 있습니다. – Frizi

22

시도 jQuery 사용이 (An Example Here.) : 바닐라 자바 ​​스크립트가 이것을 시도 사용

var $a = $('<div>') 
.addClass('box a') 
.appendTo('#wrapper'); 
$a.css('opacity'); // added 
$a.addClass('in'); 

:

var e = document.createElement('div'); 
e.className = 'box e'; 
document.getElementById('wrapper').appendChild(e); 
window.getComputedStyle(e).opacity; // added 
e.className += ' in'; 

간단한 아이디어 :

getComputedStyle() 플러시에게 대기중인 모든 스타일을 변화시키다 d 은 레이아웃 엔진이 요소의 현재 상태를 계산하도록 강제하므로 .css()과 비슷한 방식으로 작동합니다.

소개 css()jQuery 사이트 :

.CSS() 메소드는 특히 다른 방법 브라우저 액세스의 빛에서 첫 번째 일치하는 요소에서 스타일 속성을 얻을 수있는 편리한 방법입니다 대부분의 해당 속성 (표준 브라우저에서는 getComputedStyle ( 메서드)과 Internet Explorer의 currentStyle 및 runtimeStyle 속성)과 브라우저 간에는 특정 속성에 대해 브라우저가 사용됩니다.

setTimeout 대신 getComputedStyle()/css()을 사용할 수 있습니다. 또한 세부 정보 및 예제는 this article을 참조하십시오.

+1

+1 –

+1

감사합니다 .-AndreaLigios –

+0

.css ('opacity')가 jquery 버전 2.2를 사용하여 IE11의 스타일 변경을 비우지 않는 것을 보았습니다. 그 대신에 .offset()은 IE11의 트릭을 수행했습니다. –

4

아래의 코드를 사용하십시오 사용 "초점()"

jQuery를

var $a = $('<div>') 
    .addClass('box a') 
    .appendTo('#wrapper'); 
$a.focus(); // focus Added 
$a.addClass('in'); 

자바 스크립트

var e = document.createElement('div'); 
e.className = 'box e'; 
document.getElementById('wrapper').appendChild(e).focus(); // focus Added 
e.className += ' in'; 
+1

질문에 이렇게 많이 쓰여 있습니다. 나는 _why_을 이해하고 싶다. – joews

2

오히려 즉시 재 페인트 또는 스타일 계산을 강제하는 것보다, 내가 시도 브라우저가 다음 사용 가능한 프레임에 페인트 할 수있게하려면 requestAnimationFrame()을 사용하십시오.

Chrome + Firefox에서 브라우저가 렌더링을 너무 많이 최적화하므로 여전히 도움이되지 않습니다 (Safari에서 작동).

setTimeout()으로 지연을 수동으로 설정 한 다음 requestAnimationFrame()을 사용하여 책임감있게 브라우저에 페인트를 보냅니다. 타임 아웃이 끝나기 전에 append가 색칠되어 있지 않으면 애니메이션이 무시 될 수 있지만 안정적으로 작동하는 것 같습니다. 이 초당 60 프레임 (16.7ms)에서 1보다 큰 프레임의 일부 브라우저가 타임 아웃을 < 5ms의 등록을하지 않기 때문에

setTimeout(function() { 
    requestAnimationFrame(function() { 
     // trigger the animation 
    }); 
}, 20); 

나는이 20ms를 선택했다.

손가락이 넘어서서 애니메이션이 다음 프레임으로 시작되고 브라우저가 다시 칠할 준비가되면 책임감있게 시작해야합니다.

4

@ Frizi의 해결책이 효과가 있지만 때로는 요소의 특정 속성을 변경할 때 getComputedStyle이 작동하지 않는다는 것을 발견했습니다.

의 우리는 우리가 opacity을 전환하려는 요소 el을 가지고 가정하자,하지만 eldisplay:none; opacity: 0입니다 : 문제가 해결되지 않으면 다음과 같이 당신은 내가 방탄 것으로 발견 한 이는 getBoundingClientRect()을 시도 할 수 있습니다

el.style.display = 'block'; 
el.style.transition = 'opacity .5s linear'; 

// reflow 
el.getBoundingClientRect(); 

// it transitions! 
el.style.opacity = 1; 
0

Brendan과 달리 requestAnimationFrame()은 Chrome 63, Firefox 57, IE11 및 Edge에서 작동합니다.

var div = document.createElement("div"); 
 
document.body.appendChild(div); 
 

 
requestAnimationFrame(function() { 
 
    div.className = "fade"; 
 
});
div { 
 
    height: 100px; 
 
    width: 100px; 
 
    background-color: red; 
 
} 
 

 
.fade { 
 
    opacity: 0; 
 
    transition: opacity 2s; 
 
}

관련 문제