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>"
);

<style>两个项目立即跳出:生成代码的开头似乎缺少一个标签(</style>末尾有一个。)并且语句中缺少var一个。axis = ...

接下来显而易见的是,这两种模式在代码中一遍又一遍地出现:

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>"
);

如您所见,代码已经简单多了

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

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

Araxis Merge 两个相似代码块的差异

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

我们可以看到只有两个区别:第一行是百分比数字,第三行是"(-"vs "( ".。事实上,这是所有区块之间仅有的两个区别。

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

// 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()andrand180()函数并不是真正需要的,因为它们现在每个只使用一个地方并且根本不需要是单独的函数。

事实上,回顾代码,这两个函数即使在多个地方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 回答