【问题标题】:Ball bounces along a parabolic trajectory球沿抛物线轨迹反弹
【发布时间】:2021-02-25 17:49:05
【问题描述】:

问题由topic发起

在那个问题中,反弹是垂直的

<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
       width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="xMinYMin meet" style="border:1px solid" >  
 
<image xlink:href="https://i.stack.imgur.com/hXyA5.png" x="82" width="25px" height="25px" >
   <animateTransform id="anT"
     attributeName="transform"
     type="translate"
     dur="3s"
     begin="svg1.click+0.5s;anT.end+1s"
     values="
        0,0;
        0,168;
        0,84;
        0,168;
        0,126;
        0,168;
        0,148;
        0,168;
        0,158;
        0,168;
        0,163;
        0,168;
        0,166;
        0,168;
        "
        keyTimes="0;0.066;0.13;0.198;0.264;0.33;0.396;0.462;0.528;0.594;0.66;0.726;0.792;1"
        repeatCount="1"
        fill="freeze"
        restart="whenNotActive" />
</image>
   <polyline points="5,193 194,193" stroke="silver" stroke-width="4" />
 </svg>  

这个问题涉及高度和长度偏移量不同的反弹

但是很难实现运动和速度的不均匀性。

如何实现逼真的抛物线球运动?

任何想法和解决方案将不胜感激。

【问题讨论】:

  • 你还没有问过问题。你为什么不选择其他解决方案?它可能是最容易修改的,因为您可以进行计算。问题出在哪里?
  • @Emiel Zuurbier 谢谢调整问题
  • 我也不确定你的问题是什么。您在寻找计算水平位置的公式吗?
  • 在我对链接问题的回答中,只需将 dx 设置为非零值。

标签: javascript html css animation svg


【解决方案1】:

CSS 动画可以更好地接近这一点。您需要:

  1. 为计时函数找到合适的贝塞尔曲线 (https://cubic-bezier.com/#.17,.76,.58,1)
  2. 将持续时间因子更新为您想要的值
  3. 通过添加更多动画来添加任意数量的步骤

那么你需要根据时长正确计算延迟(the previous animation delay + 2*previous aniamation duration)。

主要技巧是使用altenate 并运行每个动画两次会给您带来镜像效果和完美的抛物线(其中一半由贝塞尔曲线定义)

.box {
  width: 200px;
  height: 190px;
  border: 1px solid;
  display: flex;
  justify-content: center;
  align-items: flex-end;
}

img {
   --d:0.8s; /*duration factor*/

   width:30px;
   animation-timing-function:cubic-bezier(.17,.76,.58,1); /* control this: https://cubic-bezier.com/#.17,.76,.58,1*/
   animation-iteration-count:2; /* don't change*/
   animation-direction: alternate; /* don't change */
   animation-name:t1,t2,t3,t4,t5; /* don't change unless you need more steps*/

   animation-duration:
     var(--d),
     calc(var(--d)/2),
     calc(var(--d)/3),
     calc(var(--d)/4),
     calc(var(--d)/5);
   animation-delay: 
     calc(var(--d)*-1), 
     calc(var(--d)), 
     calc(var(--d)   + 2*var(--d)/2), 
     calc(var(--d)*2 + 2*var(--d)/3), 
     calc(var(--d)*2 + 2*var(--d)/3 + 2*var(--d)/4); 
}

@keyframes t1{to {transform:translateY(-160px)}}
@keyframes t2{to {transform:translateY(-110px)}}
@keyframes t3{to {transform:translateY(-60px)}}
@keyframes t4{to {transform:translateY(-30px)}}
@keyframes t5{to {transform:translateY(-10px)}}
<div class="box">
  <img src="https://i.stack.imgur.com/hXyA5.png">
<div>

添加水平移动:

.box {
   --d:0.8s; /*duration factor*/
   
  width: 200px;
  height: 190px;
  border: 1px solid;
  display: flex;
  align-items: flex-end;
  overflow:hidden;
}

img {
   width:30px;
   animation-timing-function:cubic-bezier(.17,.76,.58,1); /* control this: https://cubic-bezier.com/#.17,.76,.58,1*/
   animation-iteration-count:2; /* don't change*/
   animation-direction: alternate; /* don't change */
   animation-name:t1,t2,t3,t4,t5; /* don't change unless you need more steps*/

   animation-duration:
     var(--d),
     calc(var(--d)/2),
     calc(var(--d)/3),
     calc(var(--d)/4),
     calc(var(--d)/5);
   animation-delay: 
     calc(var(--d)*-1), 
     calc(var(--d)), 
     calc(var(--d)   + 2*var(--d)/2), 
     calc(var(--d)*2 + 2*var(--d)/3), 
     calc(var(--d)*2 + 2*var(--d)/3 + 2*var(--d)/4); 
}

@keyframes t1{to {transform:translateY(-160px)}}
@keyframes t2{to {transform:translateY(-110px)}}
@keyframes t3{to {transform:translateY(-60px)}}
@keyframes t4{to {transform:translateY(-30px)}}
@keyframes t5{to {transform:translateY(-10px)}}

.box > span {
  animation:m calc(var(--d)*2 + 2*var(--d)/3 + 2*var(--d)/4 + 2*var(--d)/5) linear forwards;
}

@keyframes m{to {transform:translateX(150px)}}
<div class="box">
  <span><img src="https://i.stack.imgur.com/hXyA5.png"></span>
<div>

【讨论】:

    【解决方案2】:

    我在之前的回答中(在链接的问题中)允许这种行为。只需将 dx 设置为非零值即可。

    ball = {x: 82, y: 0, dx: 1, dy: 0};
    
    • xy 是球的起始位置
    • dxdy 是球的初速度

    let ballElem = document.getElementById("ball");
    
    let GRAVITY = 40;        // Acceleration due to gravity (pixels / sec /sec)
    let FLOOR_Y = 200 - 25;  // Y coord of floor. The 25 here is because ball.y is the top of the ball.
    let BOUNCINESS = 0.8;    // Velocity retained after a bounce
    let LIMIT = 0.1;         // Minimum velocity required to keep animation running
    let ball = {};
    let lastFrameTime = null;
    
    ballElem.addEventListener("click", startAnim);
    
    
    function startAnim()
    {
      ball = {x: 82, y: 0, dx: 1, dy: 0};
      lastFrameTime = null;
      requestAnimationFrame(animStep);
    }
    
    
    function animStep(timestamp)
    {
      if (lastFrameTime === null)
        lastFrameTime = timestamp;
      // Milliseconds elapsed since last step
      const elapsed = timestamp - lastFrameTime;
      lastFrameTime = timestamp;
      
      ball.dy += GRAVITY * elapsed / 1000;
      ball.y += ball.dy;
      ball.x += ball.dx;   // not really used in this example
    
      if (ball.y > FLOOR_Y) {
        // Step has taken us below the floor, so we need to rebound the ball.
        ball.y -= (ball.y - FLOOR_Y);
        ball.dy = -ball.dy * BOUNCINESS;
      }
      
      // Update the <image> element x and y
      ballElem.x.baseVal.value = ball.x;
      ballElem.y.baseVal.value = ball.y;
      
      // Request another animation step
      if (Math.abs(ball.y - FLOOR_Y) > LIMIT ||  // Not on ground
          Math.abs(ball.dy) > LIMIT ||           // or still moving
          Math.abs(ball.dx) > LIMIT) {
        requestAnimationFrame(animStep);
      }
    }
    <svg id="svg1" 
         width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="xMinYMin meet" style="border:1px solid" >  
     
      <image id="ball" xlink:href="https://i.stack.imgur.com/hXyA5.png" x="82" width="25px" height="25px"/>
     
    </svg>

    【讨论】:

      【解决方案3】:

      Svg smil 解决方案

      球的轨迹是在矢量编辑器 Inkscape 中根据问题中的图片创建的

      对于沿我使用的轨迹的运动动画 - animateMotion

      应用反应灵敏,全屏显示更有趣

      .container {
      width:60%;
      height:60%;
      }
      <div class="container">
      <svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
          xmlns:xlink="http://www.w3.org/1999/xlink"
             viewBox="0 0 488 232" preserveAspectRatio="xMinYMin meet" style="border:1px solid" >  
      <defs>  
      <!--Parabolic trajectory of movement and bounce of the ball -->
       <path id="trace" d="m 25.652812,16.016049 v 181.392771 0 0 l -0.596577,25.31062 c 0,0 12.411076,-62.72171 21.476772,-93.33069 5.109455,-17.25132 11.107049,-34.303277 18.493888,-50.709043 6.68532,-14.84773 11.981393,-31.214941 23.266504,-42.953546 5.128983,-5.335092 11.703068,-10.892125 19.090461,-11.334963 9.33174,-0.559391 18.66956,5.119788 25.65281,11.334963 12.5853,11.201058 18.46109,28.314233 25.65282,43.550123 7.68011,16.270538 13.21524,33.508736 18.49388,50.709046 9.35008,30.46698 17.6561,61.30229 23.26651,92.73411 0,0 7.63247,-46.2686 13.72127,-68.87103 4.15486,-15.42339 7.4192,-31.5679 15.511,-45.33985 5.42122,-9.226722 10.78091,-23.515729 21.47677,-23.863083 11.38465,-0.369722 18.27651,14.296709 24.45966,23.863083 8.5031,13.15573 11.7205,29.10909 16.10758,44.1467 6.69922,22.96293 9.48984,49.49348 15.511,70.06418 5.7106,-34.92901 6.57028,-45.00528 16.10758,-65.29157 5.26265,-11.19392 9.73289,-28.98491 22.07335,-29.82885 11.92521,-0.81554 20.99188,14.41196 25.05624,25.65281 4.04949,11.19974 6.57393,20.61892 9.54523,31.02201 3.6399,12.74398 5.25389,18.16556 10.14181,38.4456 2.98041,-18.27579 1.7071,-16.73913 4.17604,-24.72433 1.6821,-5.44034 3.72502,-10.96841 7.15892,-15.511 3.31674,-4.3876 7.05439,-11.27709 12.52812,-10.73839 7.04712,0.69355 9.92525,10.387 13.12469,16.70416 5.35774,10.57863 5.4736,18.42066 9.54524,34.26956 0.57788,-7.32318 1.59738,-10.47263 1.78973,-13.98594 0.5125,-9.36095 6.36879,-18.32474 10.84695,-17.16023 9.45261,2.45807 13.01612,16.09152 13.01612,16.09152" style="fill:none;stroke-opacity:0.9;stroke-width:2;stroke:#e8204f"/>
      </defs>
       
            <!-- Tennis ball -->
      <image xlink:href="https://i.stack.imgur.com/hXyA5.png" x="0" y="0" width="25px" height="25px" >
         <animateMotion id="anT"
             dur="1.5s"
           begin="svg1.click+0.5s;anT.end+1s"
              repeatCount="1"
              fill="freeze"
              restart="whenNotActive" > 
              <mpath xlink:href="#trace" />
          </animateMotion>    
      </image>
         </svg>
       </div>

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-10-28
        • 1970-01-01
        • 2018-04-15
        • 1970-01-01
        • 2011-12-28
        • 1970-01-01
        • 2015-07-18
        • 1970-01-01
        相关资源
        最近更新 更多