111

实现前端弹簧动效

 5 years ago
source link: https://www.yinchengli.com/2018/10/08/spring-animate/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

弹簧动效是IOS系统原生自带的一个效果,如在iPhone上面的照片点开大图的展示效果就是一个弹簧动画,如下图所示:

bmuy6nm.gif

它有一个弹闪的过程,一大一小交替缩放就像一个弹簧在弹动一样,而不是以往那种简单的线性变大。

如果使用CSS的animation-timing-function只是改变运动的速度,不能改变运动的方向。

而自己手动写CSS模拟这种先变大再变小的效果:

@keyframes spring-show {
    0% {
        transform: scale(0);
    }
    90% {
        transform: scale(1);
    }
    /* 先放大一点 */
    95% {
        transform: scale(1.1);
    }
    /* 然后再缩回去 */
    100% {
        transform: scale(1);
    }
}

是没有这种弹性动感的。

因为要实现一个弹簧振动效果,需要有两个参数,一个是 阻尼系数damping ration ,另一个是 刚度stiffness ,阻尼系数决定了衰减的快慢,刚度决定了往返的周期长短。给定这两个参数和弹簧的起始位移,根据一些物理公式可以推导出任意时刻弹簧的位移,这个位移就可以当作上面的scale缩放的值,或者是translate、rotate等的值。

那怎么算呢?大漠在《 CSS如何实现弹簧动画效果 》也详细地讨论了这种效果,并写一个SASS函数实现,不过这种方式生成的CSS普遍比较大,所以我改用了JS实现,原理都是计算一个CSS的keyframes关键帧动画的在1%, 2%, 3%, …, 100%的时候属性值应该是多少,然后再动态地插入一个style标签。这里借用了一个 css spring 的库,这个库gzip后只有3KB,使用方法如下:

import spring, { toString } from 'css-spring';
const keyframes = spring(
    { scale: 0 }, // from
    { scale: 1 }, // to
    // precision表示精度有2位
    { damping: 14, stiffness: 170, precision: 2}
);
const keyframeString = toString(keyframes);
console.log(keyframeString);

生成的CSS如下图所示:

z6fmaqR.png!web

它会有一个大小的变化过程:0 -> 1 -> 1.1 -> 0.99 -> 1,把这些值画成一个图表看起来更加直观:

uimUneF.png!web

可以看到它有一个抖动且周期衰减的过程,实际的效果如下图所示:

u6fMZ33.gif

除了放大,缩小也能这样处理,还可以应用于旋转,效果如下图所示:

Ajem2ey.gif

这个是用下面的代码生成的:

const keyframes = spring(
    { rotateZ: 30 }, // from
    { rotateZ: 0 }, // to
    { damping: 14, stiffness: 170, precision: 3}
);

当我们需要借助animation-delay让3个星星逐个出现的时候,需要先visibility: hidden隐藏然后再出现,这个时候需要在keyframes里面添加visibility属性,如下代码所示:

let from = {rotateZ: '30', visibility: 'hidden' },
    to = {rotateZ: '0', visibility: 'visible' };
if (from.visibility) {
    keyframes['0%'].visibility = from.visibility;
    keyframes['1%'].visibility = to.visibility;
    // 最后结束animate-fill-mode: forwards使用
    keyframes['100%'].visibility = to.visibility;
}

最后生成一个keyframes:

@keyframes spring-rotate {
    0% {transform:rotateZ(29.1deg);visibility:hidden;}
    1% {transform:rotateZ(27.507deg);visibility:visible;}
    /* ... */
    100% {transform:rotateZ(0deg);visibility:visible;}
}

再让每个star星星的animation-delay依次增大:

.star {
    visibility: hidden;
    animation: spring-rotate .59s linear forwards;
}
.star:nth-of-type(2) {
    animation-delay: .15s;
}
.star:nth-of-type(3) {
    animation-delay: .3s;
}

这样就能实现逐个出现的效果了,如下图所示:

Mn6zai7.gif

这种弹簧动效能够增强动感,比普通的单向效果看起来更带感。

在实际的实现中我写了一个util,当页面初始化的时候就生成keyframes,然后插入一个style标签放在head里面。因为如果再加上webkit前缀,一个keyframes有4KB,10个就有40KB,直接用JS动态计算的方式,会更省空间,灵活性也更强一点。

Post Views: 5


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK