如何使这个JavaScript / CSS随机动画代码更简单?

时间:2013-03-10 23:43:18

标签: javascript css refactoring document.write

下面的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>");

1 个答案:

答案 0 :(得分: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>"
);

两个项目立即跳出:看起来在生成的代码的开头有一个丢失的<style>标记(最后有一个</style>。)并且缺少{{} 1 {} var语句。

接下来很明显的是,这两种模式在代码中反复出现:

axis = ...

因此,让我们编写一些函数来使这些更简单,并进行简单的搜索和替换以更改现有代码以使用这些函数:

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

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

正如您所看到的,代码已经很多更简单。

现在让我们看看这五个相似代码块中的相同和不同之处。将这些块加载到可以逐个字符(intraline)差异的程序中是有帮助的。我为此使用了Araxis MergeBeyond Compare是另一个不错的选择。这些都是商业产品;毫无疑问也有很好的免费替代品。

以下是我们将第一个块与最后一个块进行比较时Araxis Merge显示的内容:

Araxis Merge diff of two similar code blocks

(如果你不喜欢这种字体,请不要责怪Araxis;这只是我的个人设置。而使用自动换行的窄宽度只是为了让它适合这里的列。)

我们可以看到只有两个区别:第一行中的百分比数字,第三行中的// 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>" ); "(-"。实际上,这些是所有区块中唯一的两个区别。

所以,我们现在可以做的是编写一个返回这段代码的函数,让我们插入这两个值。

"( "

现在看一下这个功能,还有一些重复的内容。但在这一点上它真的很简单;重复是相当小的。但是,既然我们正在使用它,那么让我们看看我们如何重构这些代码:

// 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()几乎不比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)";
}