下面的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>");
答案 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 Merge。 Beyond Compare是另一个不错的选择。这些都是商业产品;毫无疑问也有很好的免费替代品。
以下是我们将第一个块与最后一个块进行比较时Araxis Merge显示的内容:
(如果你不喜欢这种字体,请不要责怪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)";
}