1

以下の JavaScript コードは、ランダムな値を使用してアニメーションを設定する CSS を生成します。コードは複雑で反復的です。このコードをよりエレガントに書くにはどうすればよいでしょうか?

axis=["X","Y","Z"];
document.write("@keyframes tumble { "+
"12% {transform:rotate"+axis[Math.floor(Math.random()*3)]+"(-"+Math.floor(Math.random()*180)+"deg) rotate"+axis[Math.floor(Math.random()*3)]+
"("+Math.floor(Math.random()*180)+"deg) rotate"+axis[Math.floor(Math.random()*3)]+"("+Math.floor(Math.random()*180)+"deg)}"+
"32% {transform:rotate"+axis[Math.floor(Math.random()*3)]+"(-"+Math.floor(Math.random()*180)+"deg) rotate"+axis[Math.floor(Math.random()*3)]+
"("+Math.floor(Math.random()*180)+"deg) rotate"+axis[Math.floor(Math.random()*3)]+"("+Math.floor(Math.random()*180)+"deg)}"+
"50% {transform:rotate"+axis[Math.floor(Math.random()*3)]+"( "+Math.floor(Math.random()*180)+"deg) rotate"+axis[Math.floor(Math.random()*3)]+
"("+Math.floor(Math.random()*180)+"deg) rotate"+axis[Math.floor(Math.random()*3)]+"("+Math.floor(Math.random()*180)+"deg)}"+
"66% {transform:rotate"+axis[Math.floor(Math.random()*3)]+"( "+Math.floor(Math.random()*180)+"deg) rotate"+axis[Math.floor(Math.random()*3)]+
"("+Math.floor(Math.random()*180)+"deg) rotate"+axis[Math.floor(Math.random()*3)]+"("+Math.floor(Math.random()*180)+"deg)}"+
"84% {transform:rotate"+axis[Math.floor(Math.random()*3)]+"( "+Math.floor(Math.random()*180)+"deg) rotate"+axis[Math.floor(Math.random()*3)]+
"("+Math.floor(Math.random()*180)+"deg) rotate"+axis[Math.floor(Math.random()*3)]+"("+Math.floor(Math.random()*180)+"deg)}"+"}</style>");
4

1 に答える 1

3

私は一度に一歩ずつ答えに取り組み、あなたと共有するつもりです.

最初の簡単なステップは、行の長さを短くしてコードを見やすくするためだけに、非常にマイナーな再フォーマットです。実際には、この時点ではおそらく行の長さを気にする必要はありませんが、ここでは短い行の方が適切に表示されます。

axis = [ "X","Y","Z" ];

document.write(
    "@keyframes tumble { "+

    "12% {transform:rotate" +
    axis[Math.floor(Math.random()*3)] +
    "(-" + Math.floor(Math.random()*180) + "deg) rotate" +
    axis[Math.floor(Math.random()*3)] + 
    "(" + Math.floor(Math.random()*180) + "deg) rotate" +
    axis[Math.floor(Math.random()*3)] +
    "(" + Math.floor(Math.random()*180) + "deg)}" +

    "32% {transform:rotate" +
    axis[Math.floor(Math.random()*3)] +
    "(-" + Math.floor(Math.random()*180) + "deg) rotate" +
    axis[Math.floor(Math.random()*3)] + 
    "(" + Math.floor(Math.random()*180) + "deg) rotate" +
    axis[Math.floor(Math.random()*3)] +
    "(" + Math.floor(Math.random()*180) + "deg)}" +

    "50% {transform:rotate" +
    axis[Math.floor(Math.random()*3)] +
    "( " + Math.floor(Math.random()*180) + "deg) rotate" +
    axis[Math.floor(Math.random()*3)] + 
    "(" + Math.floor(Math.random()*180) + "deg) rotate" +
    axis[Math.floor(Math.random()*3)] +
    "(" + Math.floor(Math.random()*180) + "deg)}" +

    "66% {transform:rotate" +
    axis[Math.floor(Math.random()*3)] +
    "( " + Math.floor(Math.random()*180) + "deg) rotate" +
    axis[Math.floor(Math.random()*3)] + 
    "(" + Math.floor(Math.random()*180) + "deg) rotate" +
    axis[Math.floor(Math.random()*3)] +
    "(" + Math.floor(Math.random()*180) + "deg)}" +

    "84% {transform:rotate" +
    axis[Math.floor(Math.random()*3)] +
    "( " + Math.floor(Math.random()*180) + "deg) rotate" +
    axis[Math.floor(Math.random()*3)] + 
    "(" + Math.floor(Math.random()*180) + "deg) rotate" +
    axis[Math.floor(Math.random()*3)] +
    "(" + Math.floor(Math.random()*180) + "deg)}" +

    "}</style>"
);

2 つの項目がすぐに飛び出します:<style>生成されたコードの先頭にタグが欠落しているように見えます (</style>最後に a があります)。そしてvaraxis = ...ステートメントに欠落があります。

次に明らかなことは、これら 2 つのパターンがコード内で何度も発生していることです。

Math.floor(Math.random()*3)

Math.floor(Math.random()*180)

それでは、いくつかの関数を記述してそれらを簡単にし、単純な検索と置換を行って、これらの関数を使用するように既存のコードを変更してみましょう。

// Return a random integer n in the range 0 <= n < limit
function randInt( limit ) {
    return Math.floor( Math.random() * limit );
}

// Return a random integer n in the range 0 <= n < 3
function rand3() {
    return randInt( 3 );
}

// Return a random integer n in the range 0 <= n < 180
function rand180() {
    return randInt( 180 );
}

var axis = [ "X","Y","Z" ];

// Write a <style> tag to the document with a random animation
document.write(
    "<style>@keyframes tumble { "+

    "12% {transform:rotate" +
    axis[rand3()] +
    "(-" + rand180() + "deg) rotate" +
    axis[rand3()] + 
    "(" + rand180() + "deg) rotate" +
    axis[rand3()] +
    "(" + rand180() + "deg)}" +

    "32% {transform:rotate" +
    axis[rand3()] +
    "(-" + rand180() + "deg) rotate" +
    axis[rand3()] + 
    "(" + rand180() + "deg) rotate" +
    axis[rand3()] +
    "(" + rand180() + "deg)}" +

    "50% {transform:rotate" +
    axis[rand3()] +
    "( " + rand180() + "deg) rotate" +
    axis[rand3()] + 
    "(" + rand180() + "deg) rotate" +
    axis[rand3()] +
    "(" + rand180() + "deg)}" +

    "66% {transform:rotate" +
    axis[rand3()] +
    "( " + rand180() + "deg) rotate" +
    axis[rand3()] + 
    "(" + rand180() + "deg) rotate" +
    axis[rand3()] +
    "(" + rand180() + "deg)}" +

    "84% {transform:rotate" +
    axis[rand3()] +
    "( " + rand180() + "deg) rotate" +
    axis[rand3()] + 
    "(" + rand180() + "deg) rotate" +
    axis[rand3()] +
    "(" + rand180() + "deg)}" +

    "}</style>"
);

ご覧のとおり、コードはすでにはるかに単純になっています。

では、これら 5 つの類似したコード ブロックの中で何が同じで、何が違うのか見てみましょう。これらのブロックを、文字ごとの (行内) 差分を実行できるプログラムにロードすると便利です。これにはAraxis Mergeを使用します。Beyond Compareも良い選択です。これらは両方とも商用製品です。間違いなく優れた無料の代替品もあります。

最初のブロックと最後のブロックを比較すると、Araxis Merge は次のように表示します。

2 つの類似したコード ブロックの Araxis Merge 差分

(フォントが気に入らなくても、Araxis のせいにしないでください。これは私の個人的な設定です。ワード ラップで幅を狭くしているのは、ここの列に収まるようにするためです。)

違いは 2 つだけであることがわかります。1 行目のパーセンテージ数と3 行目の"(-"vs.です。"( "実際、これらはすべてのブロック間で唯一の 2 つの違いです。

したがって、今できることは、このコード スニペットを返す関数を記述して、これら 2 つの値をプラグインできるようにすることです。

// Return a transform:rotate string with the specified
// percent and flag
function makeTransform( percent, flag ) {
    return (
        percent + "% {transform:rotate" +
        axis[rand3()] +
        "(" + flag + rand180() + "deg) rotate" +
        axis[rand3()] + 
        "(" + rand180() + "deg) rotate" +
        axis[rand3()] +
        "(" + rand180() + "deg)}"
        );
}

その関数を見ると、まだいくつかのことが繰り返されています。しかし、この時点では非常に単純です。繰り返しはかなり少ないです。しかし、ここまで来たら、そのコードをもう少しリファクタリングする方法を見てみましょう。

// Return a random axis and degree string
function randAxisDegree( flag ) {
    return axis[rand3()] + "(" + flag + rand180() + "deg)";
}

// Return a transform:rotate string with the specified
// percent and flag
function makeTransform( percent, flag ) {
    return (
        percent + "% {transform:rotate" +
            randAxisDegree(flag) + " rotate" +
            randAxisDegree("") + " rotate" +
            randAxisDegree("") +
        "}"
    );
}

もちろん、以前に作成した 関数rand3()rand180()関数が実際には必要ではないことに気付くかもしれません。なぜなら、これらはそれぞれ 1 つの場所でしか使用されず、別個の関数である必要がまったくないからです。

実際、コードを振り返ってみると、これら 2 つの関数は、複数の場所で呼び出されたとしてあまり役に立ちません。rand3()randInt(3)rand()rand(3)rand3()

この回答を編集して、最初からこのアプローチを採用したいと思いますが、リファクタリングがたどるやや曲がった道を示すために、そのままにしておきましょう。ただし、ここでそれらを削除し、randInt()から直接呼び出しrandAxisDegree()ます。

// Return a random axis and degree string
function randAxisDegree( flag ) {
    return axis[randInt(3)] + "(" + flag + randInt(180) + "deg)";
}

これで、すべてがどのように適合するかがわかります。

// Return a random integer n in the range 0 <= n < limit
function randInt( limit ) {
    return Math.floor( Math.random() * limit );
}

var axis = [ "X", "Y", "Z" ];

// Return a random axis and degree string
function randAxisDegree( flag ) {
    return axis[randInt(3)] + "(" + flag + randInt(180) + "deg)";
}

// Return a transform:rotate string with the specified
// percent and flag
function makeTransform( percent, flag ) {
    return (
        percent + "% {transform:rotate" +
            randAxisDegree(flag) + " rotate" +
            randAxisDegree("") + " rotate" +
            randAxisDegree("") +
        "}"
    );
}

// Write a <style> tag to the document with a random animation
document.write(
    "<style>@keyframes tumble { " +
        makeTransform( 12, "-" ) +
        makeTransform( 32, "-" ) +
        makeTransform( 50, " " ) +
        makeTransform( 66, " " ) +
        makeTransform( 84, " " ) +
    "}</style>"
);
于 2013-03-11T00:25:18.347 に答える