SVG动画有延迟

时间:2018-07-13 13:38:38

标签: css reactjs animation

什么?

我正在编写一个React应用程序,其中有一个使用CSS的带有延迟的顺序动画。我正在为SVG中的<path />元素设置动画。


问题

在手机上[em>(功能不如台式机) ,动画手柄是(似乎)异步附加的。因此,不能保证动画能够完全按预期执行。

/* Elements.pcss file */ 

.element {
    opacity: 0.1;
    animation-duration: 1.7s;
    animation-iteration-count: infinite;
}

.isAnimated {
    @for $i from 1 through $animatedItems {
        .element:nth-child($i) {
            animation-delay: calc(500ms - ($i * 100ms - 100ms)); /* <--- adding delay */
            animation-name: glow$i; /* <--- attaching animation */
        }
    }
}


结构

以下是我的代码结构的抽象:

Parent.tsx

<Parent>
    <Elements /> {/* Renders all animating elements */}
</Parent>

Elements.tsx

import * as styles from "./Elements.pcss";

// ...

export class Elements extends React.PureComponent<{}, { isLoaded: boolean; }> {
    public state = {
        isLoaded: false,
    };

    public componentDidMount() {
        // The following is a hack to make it work. 
        // This is not a reliable solution. Thus, I want suggestions
        // from smarter people. 

        setTimeout(() => {
            this.setState({
                isLoaded: true,
            });
        }, 2000);
    }

    public render() {
        return (
            <div className={{ [styles.isAnimated]: this.state.isLoaded }}>
                <div className={styles.element} />
                <div className={styles.element} />
                <div className={styles.element} />
                <div className={styles.element} />
                <div className={styles.element} />
                <div className={styles.element} />
            </div>
        );
    }
}


它看起来应该像这样。平滑的连续动画。相反,有时某些块同时开始动画,有时所有动画一起开始,有时还会发生其他随机组合。

enter image description here

  • 实现此目标或解决此类问题的最佳方法是什么?
  • 有人选择了mp4和gif而不是使用CSS动画吗?


附言如果有不清楚的地方,我会修复描述。

2 个答案:

答案 0 :(得分:0)

您也许可以使用一个动画和一个元素来组合它。

这是一个主意:

.box {
  height:250px;
  width:50px;
  background:
  /*Gradient that we will animate*/
  linear-gradient(to bottom,
    red 0px,red 30px,
    transparent 30px,transparent 50px,
    rgba(255,0,0,0.5) 50px, rgba(255,0,0,0.5) 80px,
    transparent 80px,transparent 100px,
    rgba(255,0,0,0.2) 100px, rgba(255,0,0,0.2) 0) 
    0 0/100% 130px no-repeat,
  /*Bottom gradient*/  
  repeating-linear-gradient(to bottom,
      rgba(255,0,0,0.3) 0px,rgba(255,0,0,0.3) 30px,
      transparent 30px,transparent 50px); 
   animation:change 1s infinite steps(10);
}
@keyframes change {
  from {
    background-position:0 250px,0 0
  }
  to {
    background-position:0 -250px,0 0;
  }
}
<div class="box">

</div>

答案 1 :(得分:0)

Ok, so if anyone stumbles upon this and has a similar problem then the solution for me was completely different from the ones mentioned:

const canvas = document.querySelector("canvas");
const cHeight = 249;
const cWidth = 72;
canvas.height = cHeight;
canvas.width = cWidth;

const ctx = canvas.getContext("2d");
const bW = 72;
const bH = 48;


const fps = 120;
const duration = 1700;
const frameTime = 1000 / fps;
const elementCount = 6;

const startTime = Date.now();
let previousFrame = startTime;

const timings = {
  0: [0.1, 0.1, 0.1, 0.1, 0.1, 1.0, 1.0, 1.0, 1.0, 0.5, 0.1],
  1: [0.1, 0.1, 0.1, 0.1, 1.0, 0.9, 0.8, 0.5, 0.5, 0.1, 0.1],
  2: [0.1, 0.1, 0.1, 1.0, 0.8, 0.7, 0.6, 0.3, 0.1, 0.1, 0.1],
  3: [0.1, 0.1, 1.0, 0.6, 0.4, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1],
  4: [0.1, 1.0, 0.5, 0.2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1],
  5: [1.0, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1],
};

const timingFunction = (animationTime, animationLength, timing) => {

   // How many steps there are in timing definition, we count spaces not steps
  const positions = timing.length - 1;

  const stepSize = animationLength / positions; // How long time is for each step
  // For which step to take values

  const animationStep = Math.floor(animationTime / stepSize);
  // How much time is elapsed in step

  const minMaxTimingDelta = timing[animationStep + 1] - timing[animationStep];
  const timingAddition = timing[animationStep];

  const timeInStep = animationTime % stepSize;
  // Coificient to multiply max value with
  const coefficient = timeInStep / stepSize;
  const timingCoefficient = coefficient * minMaxTimingDelta;

  return timingAddition + timingCoefficient;
};


function drawElement(animationTime) {
  ctx.clearRect(0, 0, cWidth, cHeight)
  const min = 0;
  const max = 1;

  const delay = 700;
  const inDelay = animationTime < delay;
  const newAnimTime = duration - delay;

  for (let i = 0; i < elementCount; i++) {
    const alpha = inDelay ? 0.1 : timingFunction(
      animationTime - delay,
      newAnimTime,
      timings[i],
      min,
      max,
    )


    ctx.globalAlpha = alpha;
    ctx.fillStyle = 'green';
    ctx.fillRect(0, i * (bH + 3), bW, bH);
    
  }
}


const animate = () => {
  if (Date.now() - previousFrame > frameTime) {
      previousFrame = Date.now();
      drawElement((previousFrame - startTime) % duration);
  }

  requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
body {
  height: 100%;
  width: 100%;
}

canvas {
  position: absolute;
  top: 0;
  left: 0;
}

.1 {
  opacity: .9;
  background-color: blue;
  width: 50px;
  height: 50px;
  border: 1px solid red;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body> 
  <canvas />
</body>
</html>


N.B. This approach was required only because of the specifics of the case. Here the issue was the mounting of the component with the animation as the app still loads. Meaning there were potentially some leftover JS processes going on that messed up DOM element mounting.

Main takeaway is this:

Adding CSS animations with or without delays don't guarantee that all animations will initialise at the same time and thus be "synchronous".