ホームページ  >  記事  >  ウェブフロントエンド  >  CSS Secret Garden: path に沿ったアニメーション_html/css_WEB-ITnose

CSS Secret Garden: path に沿ったアニメーション_html/css_WEB-ITnose

WBOY
WBOYオリジナル
2016-06-24 11:20:381353ブラウズ

『CSS Secrets』は、@Lea Verou による最新の本で、CSS に関する小さな秘密を説明しています。これは CSSers にとって読む価値のある本です。一定期間読んだ後、私、@全域と @彦子は、関連する読書感想文を W3cplus で公開し、皆さんと共有します。

質問

数年前、CSS アニメーションが新しくてとてもエキサイティングだった頃、Chris Coyier は、CSS を使用して円形のパスの周りに要素を移動させる方法はないか私に尋ねました。当時は単なる興味深いアイデアだったのですが、これの使用例をたくさん見つけました。たとえば、Google+ では、新しいメンバーを追加するためにこのようなアニメーションが使用されます。以下に示すように:

ユニークで興味深い例としては、以下に示すように、ロシアのテクノロジー Web サイトの 404 ページを参照してください:

通常、練習に適した場所は、図に示すように 404 ページです。上記のように、Web サイトの主要エリアのナビゲーション メニューが提供されます。これらのメニューは円形のパスを移動します。

しかし、各メニュー項目は地球の周りを回る惑星のようなもので、その上のいくつかのテキストには「他の星の宇宙へ飛ぶ」と書かれています。もちろん、惑星が単に円形のパスの周りを移動し、テキストが回転されなかった場合、これらのテキストはほとんど判読できなくなります。これは多数あるうちの一例にすぎません。しかし、CSS アニメーションを使用してこの効果を実現するにはどうすればよいでしょうか?

非常に単純な例を書いてみましょう。円形のパスを中心に回転する頭の動きです。これは、前述した Google+ エフェクトの簡易版に似ています。その構造は次のとおりです:

<div class="path">    <img src="lea.jpg" class="avatar" /></div>

アニメーションの作成を開始する前に、最初にいくつかの基本的なスタイル (サイズ、背景、マージンなど) を設定します。これは次の図のようになります:

これらは基本的なスタイルであるため、 , ここには書かれていませんが、問題がある場合は、例のコードを確認してください。覚えておくべき主な点は、パスの直径が 300px であるため、半径は 150px であるということです。

基本的なスタイルが完成したら、アニメーションの書き方を考え始めます。アバターを円の中に移動し、オレンジ色のパスを中心に回転させます。これを実現するには CSS アニメーションを使用できますが、そのようなアニメーションはどのように記述すればよいでしょうか?この問題に直面したとき、次のことが頭に浮かびます:

うーん、これは正しい方向への一歩ですが、以下に示すように、アバターは円形のパスの周りを回転しています:

上に示すように、アバターは円形のパスを中心に回転しますが、アバター自体も上下逆さまになっていることがわかります。テキストの場合は、文字も上下逆さまになってしまい、読むのにかなり悪影響を及ぼします。アバターが同じ方向を向いたまま円形のパスを移動するだけです。

当時、クリスも私も、この問題を解決する合理的な方法を思いつきませんでした。私たちが思いつく最善の方法は、複数のキーフレームを通るおおよその円形のパスを描くことですが、これは明らかに良いアイデアではなく、そのような円形のパスを定義する方法はありません。したがって、より良い方法を考え出す必要がありますね?

2 つの要素を使用した解決策

Chris が提供したリファレンスに基づいて、最終的に解決策を思いつきました。このソリューションの背後にある主なアイデアは、以前に紹介した「平らな四角形」と「ひし形の図」から来ており、transform のネストを解除することによって行われます。ただし、これはアニメーションであり、アニメーションのすべてのフレームで発生します。前述したように、ここでは

2 つの要素 が必要であることに注意してください。したがって、アバターをラップするには、追加の HTML 要素 dc6dce4a544fdca2df29d5ac0ea9906b を HTML に追加する必要があります。

@keyframes spin {    to { transform: rotate(1turn); }}.avatar {    animation: spin 3s infinite linear;    transform-origin: 50% 150px; /* 150px = path radius */}

前のアニメーション効果を .avatar コンテナに適用しましょう。これで、表示される効果は以前に発生したものと同じになりますが、要素自体も回転するため、これは必要なものではありません。しかし、.avatar に回転を設定し、アバター img にその逆の回転を設定し、両方とも同じ値で回転するとどうなるでしょうか?こうすることで、2 つの回転が互いに対向し、回転の原点を中心に円運動をするだけが表示されます。

しかし、問題があります。ここには表現力豊かな回転はなく、すべて一連の角度で回転されます。たとえば、角度が 60deg の場合、キャンセルされる回転角度は -60deg (または 300deg ) である必要があり、回転角度が 70deg の場合、キャンセルされる回転角度は -70deg (または 290deg ) である必要があります。それらはすべて 0 ~ 360deg (または 0 ~ 1turn) の間で発生します。では、どのようにして角度を設定すればよいのでしょうか?答えは思ったよりずっと簡単です。次のように、アニメーションの逆を 360 度から 0 度にするだけです:

<div class="path">    <div class="avatar">        <img src="lea.jpg" />    </div></div>

さて、いつでも、最初のアニメーションが x 度回転すると、2 つ目のアニメーションは 360 - x 度回転し、そのうちの 1 つがどのくらい増加するか、もう一つはどれだけ減りますか。たとえば、次の図に示す効果が期待されるものです。

代码我们需要改进一些。首先,我们动画的所有参数重复两次。如果我们要调整时间,我们就需要调整两次,这样做较为麻烦。其实可以通过 inherit 属性继承父元素的 animation :

@keyframes spin {    to { transform: rotate(1turn); }}@keyframes spin-reverse {    from { transform: rotate(1turn); }}.avatar {    animation: spin 3s infinite linear;    transform-origin: 50% 150px; /* 150px = path radius */}.avatar > img {    animation: inherit;    animation-name: spin-reverse;}

然而,我们不需要一个全新的动画,只需要最初的一个动画。记得我们在介绍“闪烁”动画一节中,有介绍过 animation-direction 属性,其中有一个 alternate 值是非常有用的。在这里我们将使用 reverse 值,得到一个反向的原动画,因此不需要创建第二个动画:

@keyframes spin {    to { transform: rotate(1turn); }}.avatar {    animation: spin 3s infinite linear;    transform-origin: 50% 150px; /* 150px = path radius */}.avatar > img {    animation: inherit;    animation-direction: reverse;}

我们继续吧!这可能不是最理想的解决方案,因为他需要添加额外的元素,但只使用了不到十行的CSS代码实现了相当复杂的动画效果。

使用单个元素的解决方案

在前一节中,我们解决了问题,但这不是最优的方案,因为它需要修改HTML。当初我给CSS工作组提了一个建议,建议可以在相同的元素指定多个转换源。这应该能够在一个元素上实现,也似乎是一个合理的要求。

在讨论过程中,Aryeh Gregor给CSS的 transform 规范中提了这样一个声明,似乎令人困惑不解:

transform-origin 就是一个语法糖。你可以使用 translate() 替代。—— @Aryeh Gregor

有关于相关的讨论可以点击这里阅读。

事实证明,每个 transform-origin 可以模拟两次 translate() 。例如下面的两个代码段是等价的:

transform: rotate(30deg);transform-origin: 200px 300px;transform: translate(200px, 300px)           rotate(30deg)           translate(-200px, -300px);transform-origin: 0 0;

这看起来很奇怪,让我们对 transform 了解的更清楚, transform 函数不是独立的。每个 transform 属性不仅应用在元素上,而且整个坐标系统运用在同一个元素上,也将影响所有的 transform 。这也说明 为什么不同的 transfrom 顺序很重要,不同顺序的相同转换可能前生的结果会完全不同 。如果你还不了解这一点,下图可以帮助你更好的理解:

因此,我们可以利用这个方法来处理我们的动画:

@keyframes spin {  from {    transform: translate(50%, 150px) rotate(0turn) translate(-50%, -150px);  }  to {    transform: translate(50%, 150px) rotate(1turn) translate(-50%, -150px);  }}@keyframes spin-reverse {   from {     transform: translate(50%,50%) rotate(1turn) translate(-50%, -50%);  }  to {    transform: translate(50%, 150px) rotate(0turn) translate(-50%, -50%);  }}.avatar {    animation: spin 3s infinite linear;}.avatar > img {    animation: inherit;    animation-name: spin-reverse;}

这样看起来很笨拙,但不要担心,接下来我们会改善。请注意,我们现在不再有不同的 transform-origin ,但我们要记住,我们要需要两个元素和两个动画。现在所有都使用相同的 transform-origin ,可以在 .avatar 上结合这两个动画:

@keyframes spin {  from {    transform: translate(50%, 150px)      rotate(0turn)      translate(-50%, -150px)      translate(50%,50%)      rotate(1turn)      translate(-50%,-50%)  }  to {    transform: translate(50%, 150px)      rotate(1turn)      translate(-50%, -150px)      translate(50%,50%)      rotate(0turn)      translate(-50%, -50%);  }}.avatar {     animation: spin 3s infinite linear; }

这代码是得到了改善,但仍然让人感到困惑。我们还能让它变得更简洁吗?我们进一步来改进它:

我们连续做了多次 translate() 特别是 translate(-50%,-150px) 和 translate(50%,50%) 。不幸的是,百分比和绝对长度不能结合在一起(除非我们使用 calc() )。然而,水平的 translate 相互取消了,但还有两个次 translateY ( translateY(-150px) 和 translateY(50%) )。因为为了取消翻转,我们可以对关键帧这样做:

@keyframes spin{  from{    transform: translateY(150px) translateY(-50%) rotate(0turn) translateY(-150px) translateY(50%) rotate(1turn);  }  to {    transform: translateY(150px) translateY(-150%) rotate(1turn) translateY(-150px) translateY(50%) rotate(0turn);  }}.avatar {  animation: spin 3s infinite linear;}

代码变得更少了,重复的也变得更少,但仍然不是很好。我们可以做到更好吗?如果我们的头像在圆的中心,如下图所示:

我们可以继续减少两个 translate ,然后动画就变成:

@keyframes spin{  from{    transform: rotate(0turn) translateY(-150px) translateY(50%) rotate(1turn);  }  to {    transform: rotate(1turn) translateY(-150px) translateY(50%) rotate(0turn);  }}.avatar {    animation: spin 3s infinite linear;}
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。