ホームページ >ウェブフロントエンド >htmlチュートリアル >CSS Secret Garden: 柔軟なトランジション_html/css_WEB-ITnose
『CSS Secrets』は @Lea Verou による最新の本で、CSS に関する小さな秘密がいくつか説明されています。これは CSSers にとって読む価値のある本です。一定期間読んだ後、私、@全域と @彦子は、関連する読書感想文を W3cplus で公開し、皆さんと共有します。
柔軟なトランジションおよびアニメーション効果 (バウンストランジション効果など) は常に人気のあるエフェクトであり、人々に良い感触を与えます。物体が位置 A から位置 B に移動する場合、それが柔軟性に欠ける動きになることはほとんどありません。
技術的な観点から見ると、バウンス エフェクトはオーバーバウンス エフェクトであり、最終値に到達する前に少しずつバウンスし、最後に到達するまでの過程で 1 回以上縮小します。たとえば、以下に示すエフェクトでは、球が落下するエフェクトであると仮定し、実行するのは、translateY 値の none から translationY(350px) への遷移です。
なぜ、top や margin-top などの CSS プロパティを使用する代わりに、transform を使用するのでしょうか?これは、他の CSS プロパティが通常、オブジェクト境界の周囲で使用される一方で、transform を使用したアニメーションはスムーズであるためです。
もちろん、バウンス効果は位置を変えるだけの動きではありません。主に次のような、ほとんどすべてのタイプの変換属性をトランジションで変更できます:
バウンス アニメーション関数を提供する JavaScript ライブラリは多数あります。ただ、最近はこのアニメーションを台本を使わずにやってみようかなと考えています。では、CSS を使用してバウンス アニメーション効果を実現する最良の方法は何でしょうか?
バウンスのようなバウンス アニメーション効果を作成するには、以下に示すように CSS @keyframes を使用する必要があると常々感じていました。
@keyframes bounce { 60%, 80%, to { transform: translateY(350px); } 70% { transform: translateY(250px); } 90% { transform: translateY(300px); }}.ball { /* 省略球体其他样式。 */ animation: bounce 3s;}
指定上記のコードでは、キーフレームの同じ手順は上記のようになります。しかし、このアニメーションを実行すると、アニメーション効果が非常に偽物に見えることがすぐにわかります (ボールの跳ね返り効果とはまったく異なります)。主な理由の 1 つは、球体が毎回方向を変えて加速し続けるため、非常に不自然に見えることです。主な理由は、これらのキーフレームはすべて同じ時間がかかるためです。
「時間…これは何ですか?」と尋ねるかもしれません。実際、すべてのトランジションとアニメーションはカーブに関連付けられており、時間の経過とともにどのように展開するかを指定します (他の記事の紹介から、イージングがあることは誰もが知っています)。トランジションまたはアニメーションのタイミング関数 (transition-timing-funciton または anime-timing-function ) を明示的に指定しない場合、以下に示すような線形関数ではなくデフォルト値が取得されます。 (注: 以下の図に示すように、ピンクの点は、半分の時間で実行される方法と、80% に移行した後に実行されるルートを示します):
デフォルトの時間関数は、transition とアニメーション、または、transition-timing-function と anime-timing-function の略語で明示的に設定することもできます。デフォルトの関数値は、ease です。ただし、easy はデフォルトのタイミング関数であるため、あまり役に立ちません。実際、CSS には、以下の図に示すように、アニメーションの動きを変更するためのプリセット線形関数が他に 4 つ用意されています。
これらのキーワードは、あらかじめ決められた時間関数に使用できます。
ご覧のとおり、イーズアウトとイーズインは正反対です。これはまさに私たちが望むバウンス効果です。方向を変えるたびに単純に時間関数を逆にしたいのです。したがって、キー アニメーションにプライマリ時間関数を割り当てて、デフォルトの時間関数をオーバーライドできます。また、主方向に加速したい場合は、時間関数にイーズアウトを設定し、他の方向に減速関数のイーズインを設定できます。
@keyframes bounce { 60%, 80%, to { transform: translateY(400px); animation-timing-function: ease-out; } 70% { transform: translateY(300px); } 90% { transform: translateY(360px); }}.ball { /* 这里省略其他样式 */ animation: bounce 3s ease-in;}
このコードを実行すると、ここに単純な変更を加えただけでも、実際の跳ねるアニメーション効果に近い結果が得られることがわかります。しかし、これら 5 つの時間関数だけを使用すると、制限が多すぎます。任意の時間関数を選択できたら、アニメーション効果はより現実的になるでしょうか?たとえば、バウンス アニメーションがオブジェクトの落下のものである場合、降下中の加速を速くすると ( easy によって提供されるものなど)、オブジェクトが落下するアニメーションがより現実的に作成されます。しかし、キーワードを使用せずに、easy の逆の時間関数を作成するにはどうすればよいでしょうか?
実際、これら 5 つのキーワード時間関数はすべて 3 次ベジェ曲線 (ベジェ曲線) です。ベジェ曲線は、通常の描画ツール (例: Adobe Illustrator) の曲線パス ツールに似ています。これらには多くのパス セグメントがあり、各セグメントはカーブ レートを制御する 2 つのハンドルで構成されます (通常、これらのハンドルはコントロール ポイントと呼ばれます)。 SEG などの複雑な曲線には、次の図に示すように、曲線セグメントと制御点が含まれます:
而CSS的时间函数的Bézier曲线只有一段,因此他们有两个控制点。拿 ease 时间函数对应的Bézier曲线图来做示例:
除了五个预定的时间函数之外,接下来我们将一起讨论另一个时间函数: cubic-bezier() ,它允许我们指定一个自定义的时间函数。它接受四个参数,就是两个控制点的坐标参数,创建Bézier曲线可以根据这样的形式来创建: cubic-bezier(x1, y1, x2, y2) ,其中 (x1,y1) 是指典线的第一个控制点坐标, (x2,yx) 是第二个控制点坐标。曲线的起点是固定在 (0,0) 位置,表示计时开始(即零时间,零进展),结束点位置在 (1,1) (表示 100% 的运行时间, 100% 的进展)。
注意,限制一段曲线的端点并不仅仅是固定的一个。两个控制点的 x 值限制在 [0,1] 区间(即,我们不能把控制点移动多图的水平之外)。这种限制并不是任意的。我们不能穿越时间,不能指定过度的触发时间。这里唯一限制的是节点的数量:限制了节点的结果就限制曲线的结果,这也使用 cubic-bezier() 函数的使用变得更为简单。尽管受到这些限制, cubic-bezier() 允许我们创建一个非常多样化的时间函数。
从逻辑上而言,我们可以通过把时间函数的水平和垂直坐标的控制点对外而获取反转的时间函数。我们使用的时间函数关键字,也可以使用 cubic-bezier() 对应值。例如, ease 函数相当于 cubic-bezier(.25,.1,.25,1) ,其反转的时间函数为 cubic-bezier(.1,.25,1,.25) ,如下图所示:
这种方式,使我们的 bounce 动画效果实现更简单,效果看起来更加逼真:
@keyframes bounce { 60%, 80%, to { transform: translateY(400px); animation-timing-function: ease; } 70% { transform: translateY(300px); } 90% { transform: translateY(360px); }}.ball { /* 省略其他样式 */ animation: bounce 3s cubic-bezier(.1,.25,1,.25);}
使用在线的图形工具,比如 cubic-bezier.com ,我们可以对 cubic-bezier() 函数做进一步的试验,使 bounce 动画效果更完善。
Cubic Bézier 曲线在没有可视化之下,是出了名的难以指定和理解,特别是当它们作为 transition-timing-function 时。幸运的是,有很多在线工具可以帮助我们,比如上面提到的 cubic-bezier.com 。
友情提示:在 Dan Eden 写的 Animation.css 动画库中,时间函数用的就是 cubic-bezier(.215,.61,.355,1) 和 cubic-bezier(.755,.05,.855,.06) ,使动画效果更真实。
假设我们想要给聚焦后的文本展示一个提示信息。模板结构如下所示:
<label> Your username: <input id="username" /> <span class="callout">Only letters, numbers, underscores (_) and hyphens (-) allowed!</span></label>
提示:如果你给提示信息 .callout 使用的是 heihgt 而不是 transform ,你会注意到 .callout 从 height:0 (或其他值)过渡到 height:auto ,并不能正常工作。那是因为在动画中对关键词是不能识别的。在这种情况下,使用 max-height 来替换 height 。
使用CSS来切换显示、隐藏的效果,代码如下(我们省略了一切相关的样式或布局):
input:not(:focus) + .callout { transform: scale(0);}.callout { transition: .5s transform; transform-origin: 1.4em -.4em;}
目前,当用户获取文本焦点时,有 .5s 的过渡时间,来显示提示信息 .callout ,如下图所示:
如果显示信息超过最后一点(例如,提示信息放大到 110% ,然后在回到 100% ),这样的效果看起来更为自然。我们可以把 transition 效果换成 animation 属性,并且把前面所学到的知识运用到这里:
@keyframes elastic-grow { from { transform: scale(0); } 70% { transform: scale(1.1); /* Reverse ease */ animation-timing-function:cubic-bezier(.1,.25,1,.25); }}input:not(:focus) + .callout { transform: scale(0);}input:focus + .callout { animation: elastic-grow .5s;}.callout { transform-origin: 1.4em -.4em;}
如果我们尝试后就知道他是确实能正确的工作。可以的看看下图的效果,看看它与之前的过渡效果对比:
然而,我们需要的是一个过渡效果,但基本上使用了一个动画效果。动画的功能是非常强大,但在这种情况下,需要给过渡添加一些灵活度而使用动画,感觉有点过会,就像是杀鸡焉用牛刀。那么有没有办法改变这一切呢?
解决方案还是使用 cubic-bezier() 时间函数。到目前为止,我们只讨论了曲线的 0-1 之间的控制点。正如前面所提到的,我们不能超出这个水平范围,但是在垂直范围,我们可以超过 0-1 的范围,让我们的过渡进展范围可以小于 0% ,也可以大于 100% 。你可能猜出这是什么意思。他的意思是,如果 transform:scale(0) 过渡到 transform:scale(1) ,我们可以把最终值变得更大,比如 scale(1.1) ,或者更大的值。同时还要依赖于过渡的时间函数。
在这个示例中,我们只需要很少的弹性效果,所以我们希望在时间函数上来实现,其中进展到 110% 对应的是 scale(1.1) ,然后进展到 100% ,对应的就是 scale(1) 。开始点使用 cubic-bezier(.25,.1,.25,1) 时间函数,移动到第二个控制点,直到我们达到的时间函数 cubic- bezier(.25,.1,.3,1.5) 。
正如上图你所看到的:过渡总持续时间到达 50% 这个点时,过渡进展到 100% 。但过渡不能就到这就停止,它需要继续移动到最终值,总持续时间到达 70% 时,过渡进展到 110% ,然后剩下的 30% 的可用时间,让过渡进展到我们需要的最终值,这样就实现了使用 transition 达到 animation 制作的动画效果,而整个实现过程只需要一行代码就可以实现,现在比较一下我们实现用例效果的代码:
input:not(:focus) + .callout { transform: scale(0); }.callout { transform-origin: 1.4em -.4em; transition: .5s cubic-bezier(.25,.1,.3,1.5);}
然而,尽管我们的过渡效果看起来达到了预期的那样,但我们文本失去焦点时, .callout 信息收缩到到消失时,会发生如下图的效果:
这里发生了什么呢?这里看起来有点怪怪的,但这效果实际上就是预期会发生的:当我们在文本域中输入字段, transform 从 scale(0) 过渡到 scale(1.1) ,也就是他的最终值。因此,因为他们使用了相同的时间函数,过渡在 350ms 进展到 110% 。但这一次, 110% 处理的不是 scale(1.1) ,而是 (-0.1) 。
不要放弃,因为解决这个问题只需要增加一行代码。如果我们只想在 .callout 收缩时设置一个普能的时间函数,我们可以通过CSS的规则来覆盖当前时间函数:
input:not(:focus) + .callout { transform: scale(0); transition-timing-function: ease;}.callout { transform-origin: 1.4em -.4em; transition: .5s cubic-bezier(.25,.1,.3,1.5);}
如果你再试一次,你会发现以完全相同的方式关闭弹出的信息框。就像前面定制的 cubic-bezier() 时间函数,但他打开是有一个很好的弹性动画效果。
大家需要特别注意的是:关闭 .callout 感觉非常缓慢。那是为什么呢?思考一下,当它不断增长时,动画进展到 50% 是,尺寸达到 100% (也就是 250ms 后)。然而,当它收缩时,从 0% 到 100% 占有了所有的时间 ,我们指定的过渡时间为 500ms ,所以感觉速度慢了一半。
解决最后一个问题,我们可以覆盖时间,通过 transition-duration 或使用 transition 来覆盖。如果我们使用后者,我们不需要显式指定,它为它是初始值:
input:not(:focus) + .callout { transform: scale(0); transition: .25s;}.callout { transform-origin: 1.4em -.4em; transition: .5s cubic-bezier(.25,.1,.3,1.5);}
虽然弹性过渡可以运用在任何类型的过渡上,但这也是一个可怕的想法。典型的情况,你不希望弹性的对颜色做过渡。尽管弹性的给颜色做过渡效果很有意思,如下图所示,但这通常是不可取的UI方案。
使用 cubic-bezier(.25,.1,.2,3) 过渡函数,把颜色 rgb(100%, 0%, 40%) 弹性过渡到 gray (rgb(50%, 50%, 50%)) 。整个过程会篡改 rgb 颜色,所以我们看到一些奇怪的颜色,比如 RGB(0%、100%、60%) 。
为了防范意外的将颜色做了弹性过渡,可以尝试指定特定的属性,而不是像以前一样不指定任何属性。当我们使用简写的 transition 属性, transition-property 其默认值为 all 。这意味着任何能转过渡的属性都将过渡。因此,如果我们在 .callout 上添加一个背景颜色,那么在过渡效果中也会运用到这个属性。那么最后的代码如下所示:
input:not(:focus) + .callout { transform: scale(0); transition: .25s transform;}.callout { transform-origin: 1.4em -.4em; transition: .5s cubic-bezier(.25,.1,.3,1.5) transform;}
提示:说到限制对指定属性过渡,你甚至可以通过 transition-delay 对多个属性进行过渡,也可以使用 transition 简写。例如,如果你想对 width 和 height 两个属性做过渡,你可以这样写 transition: .5s height, .8s .5s width; ,其中 width 的延迟时间和 height 持续时间相同。