沿着使用从“react-native-svg”导入的“路径”创建的弯曲路径对图像或视图进行动画处理

问题描述 投票:0回答:1

我需要创建一个页面来根据里程碑显示用户的计划进度。为了实现曲线路径是使用从“react-native-svg”导入的“路径”创建的,现在进度图标应该沿着路径进行动画/移动。

here is the actual requirement

红色标记应根据他/她在程序中的进度沿着曲线移动到某个特定点。

注意 - 我已经创建了路径。我所需要的只是标记的动画

react-native animation mobile-development
1个回答
0
投票

这是一个有趣的问题,同时利用了几何和动态渲染技术。我们可以利用

SVG
或 React Native 的矢量绘图库的功能,并且可以根据给定的
progress percentage
动态计算进度图标沿曲线路径的位置。

实施于

React-native

import React, { useEffect, useState, useRef } from 'react';
import { Path, Svg, Circle, G } from 'react-native-svg';
import { View, Button, Text } from 'react-native';

const ProgressPath = ({ progress }) => {
  const [progressPosition, setProgressPosition] = useState({ x: 0, y: 0 });
  const ref = useRef(null);


  useEffect(() => {
    if(!ref.current) return;

    const path = ref.current;
    const pathLength = path.getTotalLength();
    const position = path.getPointAtLength(pathLength * progress);
    setProgressPosition({ x: position.x, y: position.y });
  }, [progress]);

  return (
    <Svg width="400" height="500">
      {/* Path */}
      <Path
        ref={ref}
        id="curve"
        fill="none"
        stroke="black"
        strokeWidth="6"
        d="M 200,50
           C 250,100 150,100 200,150
           C 250,200 150,200 200,250
           C 250,300 150,300 200,350
           C 250,400 150,400 200,450"
      />

      {/* Marker */}
      <Circle
        cx={progressPosition.x}
        cy={progressPosition.y}
        r="5"
        fill="red"
      />
    </Svg>
  );
};

const App = () => {
  const [progress, setProgress] = useState(0.5);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>{`Progress: ${Math.floor(progress * 100)}%`}</Text>
      <ProgressPath progress={progress} />
      <Button 
        title="Decrease Progress"
        onPress={() => setProgress((prev) => prev - 0.1)} 
      />
      <Button 
        title="Increase Progress"
        onPress={() => setProgress((prev) => prev + 0.1)} 
      />
    </View>
  );
};

export default App;

反应:

另外,我也在

React
中创作。唯一的主要变化是使用
document.getElementById
而不是
ref
。这是实现:

import React, { useEffect, useState } from "react";

const ProgressPath = ({ progress }) => {
  const [progressPosition, setProgressPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const path = document.getElementById("curve");
    const pathLength = path.getTotalLength();
    const position = path.getPointAtLength(pathLength * progress);
    setProgressPosition({ x: position.x, y: position.y });
  }, [progress]);

  return (
    <svg width="1000" height="500">
      {/* Path */}
      <path
        id="curve"
        fill="none"
        stroke="black"
        strokeWidth="6"
        d="
           M 200,50
           C 250,100 150,100 200,150
           C 250,200 150,200 200,250
           C 250,300 150,300 200,350
           C 250,400 150,400 200,450
         "
      />

      {/* Marker */}
      <circle
        cx={progressPosition.x}
        cy={progressPosition.y}
        r="5"
        fill="red"
      />
    </svg>
  );
};

const App = () => {
  const [progress, setProgress] = useState(0.5);

  return (
    <div>
      <h1>{`Progress: ${Math.floor(progress * 100)}%`}</h1>
      <ProgressPath progress={progress} />
      <button onClick={() => setProgress((prev) => prev - 0.1)}>
        Descrease Progress
      </button>
      <button onClick={() => setProgress((prev) => prev + 0.1)}>
        Increase Progress
      </button>
    </div>
  );
};

export default App;

使动画流畅:

我们可以通过在一段小时间间隔(例如50ms)内逐渐增加/减少运动0.1%来实现平滑的动画。这是相同的实现:

存储之前的进度:

const App = () => {
  const [prevProgress, setPrevProgress] = useState(0.5);
  const [progress, setProgress] = useState(0.5);

  return (
    <div>
      <h1>{`Progress: ${Math.floor(progress * 100)}%`}</h1>
      <ProgressPath prevProgress={prevProgress} progress={progress} />
      <button
        onClick={() => {
          setPrevProgress(progress);
          setProgress((prev) => prev - 0.1);
        }}
      >
        Descrease Progress
      </button>
      <button
        onClick={() => {
          setPrevProgress(progress);
          setProgress((prev) => prev + 0.1);
        }}
      >
        Increase Progress
      </button>
    </div>
  );
};

定义间隔:

const interval_in_ms = 50;

进展路径:

const ProgressPath = ({ prevProgress, progress }) => {
  const [progressPosition, setProgressPosition] = useState({ x: 0, y: 0 });
  //This will make the position change gradually depending on the value of `interval_in_ms`.
  function animatePositionChange(
    path,
    prevProgress,
    progress,
    setProgressPosition
  ) {
    const pathLength = path.getTotalLength();

    var partialProgress = prevProgress;
    const intervalId = setInterval(() => {
      if (prevProgress < progress) {
        if (partialProgress > progress) {
          clearInterval(intervalId);
        }
        partialProgress += 0.01;
      } else {
        if (partialProgress < progress) {
          clearInterval(intervalId);
        }
        partialProgress -= 0.01;
      }

      const position = path.getPointAtLength(pathLength * partialProgress);
      setProgressPosition({ x: position.x, y: position.y });
    }, interval_in_ms);
  }

  useEffect(() => {
    const path = document.getElementById("curve");
    animatePositionChange(path, prevProgress, progress, setProgressPosition);
  }, [progress]);

  return (
    <svg width="1000" height="500">
      {/* Path */}
      <path
        id="curve"
        fill="none"
        stroke="black"
        strokeWidth="6"
        d="
           M 200,50
           C 250,100 150,100 200,150
           C 250,200 150,200 200,250
           C 250,300 150,300 200,350
           C 250,400 150,400 200,450
         "
      />

      {/* Marker */}
      <circle
        style={{
          transition: `all ${interval_in_ms}ms linear`,
          willChange: "cx, cy",
        }}
        cx={progressPosition.x}
        cy={progressPosition.y}
        r="5"
        fill="red"
      />
    </svg>
  );
};

注:

  • transition: all ${interval_in_ms}ms linear
    这负责平稳过渡

  • willChange: cx, cy
    ; CSS 属性 will-change 向浏览器提示元素将如何变化。浏览器可能会在元素实际更改之前设置优化。

© www.soinside.com 2019 - 2024. All rights reserved.