如何为CSS动画添加物理效果?

时间:2015-06-06 04:02:32

标签: javascript html css css3

我只是使用css进行一些加载,我希望他们有一个真实的行为,我尝试使用animation-timing-function: cubic-bezier(1, 0, 1, 1),看起来很好,但不像我想的那样真实,起初因为我不知道cubic-bezier参数是如何工作的,所以我找到了this网站并且只是玩弄它们直到我得到了一些不错的东西。

总结一下,如何在动画中添加真实的物理行为?

请仅使用CSS,如果不可能,欢迎使用JS解决方案。

这里有一个FIDDLE示例。

只是一个指导

的建议

像一个较小的或SCSS与定义的常量物理变量,或者你可以添加到函数的值和sumule的物理行为甚至可能已经mixin模拟某些行为,我不知道简单的东西,只有CSS

提前谢谢

1 个答案:

答案 0 :(得分:11)

可以只使用CSS,但你会花费大量时间来计算Bezier,关键帧位置,比例等的数字,最重要的是:稍微改变一下您的布局,“重力”,尺寸,距离,您必须从“全部”开始(至少在上面的部分)。

CSS动画很不错,但使用一些JavaScript代码可以获得更好的结果,更不用说如果你需要改变一些东西会有更多的灵活性 -

  • 定义球的矢量
  • 定义任意重力
  • 计算向量和反弹
  • 使用变换将结果值绑定到DOM元素(与位置相比给出更平滑的结果)。
  • 使用同步监视的requestAnimationFrame动画制作,并提供与CSS动画一样平滑的动画。

实施例

这个例子显示了基本的,不包括阴影,但这是留给读者的练习。

var div = document.querySelector("div"),
    v = {x: 2.3, y: 1},       // some vector
    pos = {x: 100, y: 20},    // some position
    g = 0.5,                  // some gravity
    absorption = 0.7,         // friction/absorption
    bottom = 150,             // floor collision
    frames = 0;               // to reset animation (for demo)

// main calculation of the animation using a particle and a vector
function calc() {
  pos.x += v.x;               // update position with vector
  pos.y += v.y;
  v.y += g;                   // update vector with gravity
  if (pos.y > bottom) {       // hit da floor, bounce
    pos.y = bottom;           // force position = max bottom
    v.y = -v.y * absorption;  // reduce power with absorption
  }
  if (pos.x < 0 || pos.x > 620) v.x = -v.x;
}

// animate
(function loop() {
  calc();
  move(div, pos);
 
  if (++frames > 220) {       // tweak, use other techniques - just to reset bounce
    frames = 0; pos.y = 20;
  }
  requestAnimationFrame(loop)
})();

function move(el, p) {
  el.style.transform = el.style.webkitTransform = "translate("+p.x+"px,"+p.y+"px)";
}
div {
  width:20px;
  height:20px;
  background:rgb(0, 135, 222);
  border-radius:50%;
  position:fixed;
}
<div></div>

如果你想要更准确地反弹地板,你可以使用实际位置的差异来反映这一点:

if (pos.y > bottom) {
    var diff = pos.y - bottom;
    pos.y = bottom - diff;
    ...

如果您需要多个元素,只需创建一个实例化对象,该对象嵌入对该元素的引用以进行动画处理,计算等。

如果你现在想要改变方向,起点,重力等等,你只需要更新各自的值,重播时一切都会顺利进行。

生成CSS关键帧的示例中间步骤

您可以修改上面的代码来处理CSS动画的数字。

使用帧数并标准化序列范围,通过计算帧来运行计算。然后提取每个值,假设每10帧以及每次反弹,最后将数字格式化为关键帧。

理想情况下,您将始终包含顶部和底部位置 - 您可以通过监视矢量的y值(符号)的方向来检测这一点,此处未显示。

这将作为生成我们稍后将使用的CSS规则的中间步骤:

var v = {x: 2.3, y: 1},       // some vector
    pos = {x: 100, y: 20},    // some position
    g = 0.5,                  // some gravity
    absorption = 0.7,         // friction/absorption
    bottom = 150,             // floor collision
    frames = 0,               // to reset animation (for demo)
    maxFrames = 220,          // so we can normalize
    step = 10,                // grab every nth + bounce
    heights = [],             // collect in an array as step 1
    css = "";                 // build CSS animation

// calc CSS-frames
for(var i = 0; i <= maxFrames; i++) {
  var t = i / maxFrames;
  pos.x += v.x;               // update position with vector
  pos.y += v.y;
  v.y += g;                   // update vector with gravity

  if (pos.y > bottom) {
    pos.y = bottom;
    v.y = -v.y * absorption;
    heights.push({pst: t * 100, y: pos.y});
  }  
  else if (!(i % step)) {heights.push({pst: t * 100, y: pos.y})}  
}

// step 2: format height-array into CSS
css += "@keyframes demo {\n";
for(i = 0; i < heights.length; i++) {
  var e = heights[i];
  css += "  " + e.pst.toFixed(3) + "% {transform: translateY(" + e.y.toFixed(3) + "px)}\n";
}
css += "}";

document.write("<pre>" + css + "</pre>");

如果我们从中获取结果并将其用作最终页面的CSS,我们会得到这个结果(对不起,此演示中只有非前缀版本):

(你当然必须调整和调整这个,但你会得到要点。)

div  {
  animation: demo 3s linear infinite;
  width:20px;
  height:20px;
  border-radius:50%;
  background:rgb(0, 148, 243);
  position:fixed;
  left:100px;
}

@keyframes demo {
  0.000% {transform: translateY(21.000px)}
  4.545% {transform: translateY(58.500px)}
  9.091% {transform: translateY(146.000px)}
  9.545% {transform: translateY(150.000px)}
  13.636% {transform: translateY(92.400px)}
  18.182% {transform: translateY(75.900px)}
  22.727% {transform: translateY(109.400px)}
  25.455% {transform: translateY(150.000px)}
  27.273% {transform: translateY(127.520px)}
  31.818% {transform: translateY(106.320px)}
  36.364% {transform: translateY(135.120px)}
  37.727% {transform: translateY(150.000px)}
  40.909% {transform: translateY(125.563px)}
  45.455% {transform: translateY(133.153px)}
  47.273% {transform: translateY(150.000px)}
  50.000% {transform: translateY(134.362px)}
  54.545% {transform: translateY(148.299px)}
  55.000% {transform: translateY(150.000px)}
  59.091% {transform: translateY(138.745px)}
  61.818% {transform: translateY(150.000px)}
  63.636% {transform: translateY(141.102px)}
  67.727% {transform: translateY(150.000px)}
  68.182% {transform: translateY(147.532px)}
  72.727% {transform: translateY(150.000px)}
  77.273% {transform: translateY(150.000px)}
  81.818% {transform: translateY(150.000px)}
  86.364% {transform: translateY(150.000px)}
  90.909% {transform: translateY(150.000px)}
  95.455% {transform: translateY(150.000px)}
  100.000% {transform: translateY(150.000px)}
}
<div></div>

我个人会推荐JavaScript支持,因为它对这些类型的动画更准确,如上所述,它可以很容易地采用新的要求。