[JS/CSS3] JavaScriptによるCSS3の実装判定とトランジション制御

CSSアニメーションで画像を切り替えるjQueryプラグインをちくちくと作り進めていて、現在はとりあえずの試作版が出来たところです。

DEMO

このプラグインの特徴のひとつに「CSS3に対応しない環境では、シンプル(=クロスブラウザ)なトランジションにフォールバックする」というのがあるのですが、これが少しやっかいで、試作版では姑息的な実装でお茶を濁しています。重要な部分なのでしっかりと作っておかないとならないのですが、いかんせんまだ知識に乏しいため、とりあえず現状で分かっている事などについて以下にまとめておこうと思います。

CSS3プロパティ実装の判定方法

前述の特徴を達成するには、まずtransitionやtransformなどといったCSS3プロパティの実装を確かめなければなりません。
そこで、環境に応じてベンダープレフィックスを付与したプロパティ名から機能テスト的に実装を判定するのですが、問題はブラウザやそのバージョンによっては、同じプロパティでも実装の範囲や仕様が異なるということです。(例: transformは2D変形のみ対応 など)

そのため、たとえばtransformの実装を調べる場合、

var supported = 'transform' in document.createElement( 'div' ).style;

といったような、プロパティを持つかどうかのみを判定する方法では、前述の例のような2D変形のみ対応している環境において、’rotateY(100deg)’など3D変形の値が使用された場合に正しく動作しなくなってきます。
なのでそれを回避しようとするなら

var style = document.createElement( 'div' ).style,
    supported = false;
if( 'transform' in style ) {
    style.transform = 'rotateY(100deg)';
    supported = !(style.transform === '');
}

みたいにして実際に値を入れてみる必要があります。
しかしこれだと、2D変形しか使わない場合でも(3D変形に対応していなければ)非対応と見なす処理になるため、それを考慮する場合、

var style = document.createElement( 'div' ).style,
    supported = 0;
if( 'transform' in style ) {
    style.transform = 'rotateY(100deg)';
    supported = style.transform === '' ? 1 : 2;
}

if( supported === 0 ) {
    // 非対応
} else if ( supported === 1 ) {
    // 2Dのみ対応??
} else {
    // 完全対応??
}

みたいにするなどして、プロパティごとにさらに切り分ける必要があります。
しかしこれでもまだやはり100%確実なものではないですし、あくまで「対応していると見なす」という程度でしかありません。

判定するプロパティとその値を限定すればもう少し信頼性を高くできそうですが、制作中のプラグインには「トランジションの種類を選択できる(バージョンアップで追加していく)」という仕様もあり汎用性を必要とするため、それも厳しいところです。

実用性を維持した上で確実に判定することは難しそうなので、色々な環境で調べたりテストしたりしながら、少しずつ理想に近づけていく必要がありそうです。

CSSトランジションの適用

試作版の実装判定とプロパティ名の取得は、前回の記事で作った関数を改修したものを使用しています。これに関しては、最近リリースされたjQuery v1.8.0ではcss()やanimate()にベンダープレフィックスが自動で付与されるようになったのでそれを利用すれば楽そうですが、最新版のjQueryが前提になるのもどうかと思うので、やはり拙くても自力でなんとかしておきたいところです。(といっても試作版はon()とかoff()とか使っているのでv1.7.0以上ありきなんですが)

以上を踏まえて、だいぶ端折ってますがCSSトランジションの適用は現状、以下のようになっています。

~
// 引数に取ったプロパティ名から実装を判定、
// 必要に応じてベンダープレフィックスを付与し文字列を返す
var cssProp = function( name, camelcase ) {
    ...
};

// 引数に取ったプロパティ名から実装を判定、真偽値を返す
// cssProp()のラッパー関数
var checkCSSSupport = function( name ) {
    ...
}
~
// 実装を判定
if ( !checkCSSSupport( ['transform-3d', 'transform-style', 'transition', 'opacity'] ) ) {
    // 非対応ブラウザ用トランジションへフォールバック
    return;
}

// CSSトランジションのコールバックと適用
$( elm ).on( cssProp( 'transitionEnd' ), function(){
    console.log( 'transition finished.' );
});
elm.style[cssProp( 'transform-style' )] = 'preserve-3d';
elm.style[cssProp( 'transition' )] = 'all 1s linear 0s';
// 状況によってうまく動かないのを回避するため遅延して適用
setTimeout(function(){
    elm.style[cssProp( 'transform' )] = 'rotateX(90deg)';
}, 1);
~

かなり泥臭く、そして面倒ですね…。
非対応ブラウザのフォールバックがある以上、CSS3の実装判定はどうしても必要ですが、その適用に関しては判定とは別に考えた方が良さそうです。たとえば以下のように適用できるようjQueryを拡張しておくとか。

~
// 実装の判定
~
// CSSトランジションを適用
$( elm ).nbAnimate({
    transform: 'rotateX(90deg)'
    }, 1000, 'ease-in', function(){
        console.log( 'transition finished' );
    }
});
~

jQuery.cssAnimateとかjQuery.transitとかくらい高い完成度で実現しようとすると大変そうだし荷が重いですが、コードの良し悪しは別として、制作中プラグインの用途に事足りる程度であればなんとかなりそうな気がします。

まとめ

やはりポイントはカオスな実装を如何にスマートかつ確実に判定するか、というところですね。CSS3アニメーションの扱いにくさに関しては、JS側で吸収できる部分が多いので大した問題ではない印象です。面倒臭いですけど。

とりあえずは好奇心が先行しているので気にしていませんが、実際のところこの手のコンテンツって、あくまで一般的な商用サイトにおける使用では、やはり時期尚早だと思いました。
最近の小難しい用語で言う、プログレッシブ・エンハンスメントやポリフィルといった考え方を前提に構成されている物であれば問題無いと思いますが、グレイスフル・デグラデーションみたいな「非対応環境には最低限の演出」という考え方では、クライアントの理解を得るのは中々難しいような気がしますし、第一多くの場合そこまでする必要が無いっていう。

そもそもCSS3自体まだ完成しておらず、実験的に実装されているに過ぎないわけですから、あまり完全を求め過ぎないようにしながら、今後もより良いアプローチを模索していきたいと思います。

この記事の続きはこちら→


コメントを残す

メールアドレスが公開されることはありません。


+ 2 = 六