web-dev-qa-db-ja.com

追加された要素でCSS遷移をトリガーする

この質問 が観察するように、新しく追加された要素の即時CSS遷移は何らかの形で無視されます-遷移の終了状態はすぐにレンダリングされます。

たとえば、次のCSSがある場合(ここではプレフィックスは省略されます):

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

.box.in { opacity: 1; }

この要素の不透明度はすぐに1に設定されます。

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

期待される動作を得るために移行をトリガーするいくつかの方法を見てきました。

// 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');

同じメソッドがVanilla JS DOM操作に適用されます-これはjQuery固有の動作ではありません。

編集-Chrome 35。

JSFiddle (Vanilla JSの例を含む)。

  • 追加された要素の即時CSSアニメーションが無視されるのはなぜですか?
  • これらの方法はどのように、またなぜ機能するのですか?
  • 他の方法がありますか
  • もしあれば、どちらが望ましい解決策ですか?
59
joews

新しく追加された要素をアニメーション化しない原因は、ブラウザーによるバッチリフローです。

要素が追加されると、リフローが必要になります。同じことがクラスの追加にも当てはまります。ただし、両方をsingle javascript roundで実行すると、ブラウザーは最初の1つを最適化するチャンスを利用します。その場合、単一の(同時に初期および最終の)スタイル値のみが存在するため、遷移は発生しません。

setTimeoutトリックは機能します。これは、別のjavascriptラウンドへのクラスの追加を遅らせるため、レンダリングエンジンに存在する2つの値が存在するためです。ユーザーに提示されます。

バッチ処理ルールには別の例外があります。あなたがそれにアクセスしようとしている場合、ブラウザは即値を計算する必要があります。これらの値の1つはoffsetWidthです。アクセスすると、リフローがトリガーされます。もう1つは、実際の表示中に個別に行われます。繰り返しますが、2つの異なるスタイル値があるため、それらを時間内に補間できます。

これは、この動作が望ましい非常にまれな機会の1つです。ほとんどの場合、DOMの変更の間にリフローを引き起こすプロパティにアクセスすると、深刻な速度低下を引き起こす可能性があります。

推奨されるソリューションは人によって異なりますが、私にとっては、offsetWidth(またはgetComputedStyle())のアクセスが最適です。間にスタイルの再計算を行わずにsetTimeoutが起動される場合があります。これはまれなケースで、主にロードされたサイトで発生しますが、発生します。その後、アニメーションを取得できません。計算されたスタイルにアクセスすることにより、ブラウザに実際に計算を強制します。

99
Frizi

jQueryを使用して、これを試してください( ここに例があります。 ):

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

Vanilla javaScriptを使用して、これを試してください。

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

簡単なアイデア:

getComputedStyle() は、保留中のすべてのスタイル変更をフラッシュし、レイアウトエンジンに要素の現在の状態を強制的に計算させます。したがって、 。 css() は同様に機能します。

css() from jQueryサイトについて:

.css()メソッドは、最初に一致した要素からスタイルプロパティを取得する便利な方法です。特に、ブラウザがこれらのプロパティのほとんどにアクセスするさまざまな方法( getComputedStyle ()標準ベースのブラウザのメソッドと currentStyle およびInternet ExplorerのruntimeStyleプロパティ)およびブラウザが特定のプロパティに使用するさまざまな用語。

setTimeoutの代わりにgetComputedStyle()/css()を使用できます。また、詳細情報と例については、 この記事 を参照してください。

27
The Alpha

以下のコードを使用してください、「focus()」を使用してください

JQuery

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

Javascript

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

@Friziのソリューションは機能しますが、エレメントの特定のプロパティを変更したときに、getComputedStyleが機能しないことが時々わかりました。それでもうまくいかない場合は、次のようにgetBoundingClientRect()を試すことができます。

要素elがあり、その上でopacityを遷移させたいが、eldisplay:none; opacity: 0

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

// reflow
el.getBoundingClientRect();

// it transitions!
el.style.opacity = 1;
6
nikk wong

すぐに再描画またはスタイルの計算を強制するのではなく、requestAnimationFrame()を使用して、ブラウザが次に利用可能なフレームにペイントできるようにしました。

Chrome + Firefoxでは、ブラウザーはレンダリングを最適化しすぎているため、これはまだ役に立ちません(Safariで動作します)。

私は手動でsetTimeout()で遅延を強制し、その後requestAnimationFrame()を使用してブラウザに責任を持ってペイントさせることに決めました。タイムアウトが終了する前に追加がペイントされていない場合、アニメーションは無視される可能性がありますが、確実に機能するようです。

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

20fpsを選択したのは、60fps(16.7ms)で1フレームよりも大きく、一部のブラウザーでは5ms未満のタイムアウトが登録されないためです。

指を交差させると、アニメーションが次のフレームに強制的に開始され、ブラウザーが再びペイントする準備ができたときに責任を持って開始します。

2

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;
}
0
James T

私はrequestAnimationFrame + setTimeoutを好みます( この投稿 を参照)。

const child = document.createElement("div");
child.style.backgroundColor = "blue";
child.style.width = "100px";
child.style.height = "100px";
child.style.transition = "1s";

parent.appendChild(child);

requestAnimationFrame(() =>
  setTimeout(() => {
    child.style.width = "200px";
  })
);

試してみてください こちら

0
pomber