Как анимировать чистые данные, такие как числа от 0 до 100, в кадре-движении?

<motion.div>
{num}
</motion.div>

Во фреймере анимировать стили довольно просто, но как я могу анимировать чистое число, я хочу, чтобы 0 было анимировано до 100 с конфигурацией перехода, как я могу это сделать?

3 ответа

Решение

В настоящее время Framer Motion может только анимировать атрибуты CSS узлов DOM. В его системе отслеживания проблем есть предлагаемая функция, которая упоминает произвольную "анимацию без головы".

А пока я думаю, вам нужно использовать библиотеку более низкого уровня, например Popmotion:

import { spring } from "popmotion"; 

const node = document.getElementById("counter");

spring({
  from: 0,
  to: 100,
  stiffness: 200,
  damping: 40
}).start({
  update(value) {
    node.textContent = value.toFixed(2);
  }
});

CodeSandbox

Обратите внимание, что Framer Motion основан на Popmotion, поэтому вы можете использовать эту библиотеку, не увеличивая размер пакета.

Предыдущий ответ мне не помог, поэтому я реализовал его здесь, используя часть анимированного контента этой страницы https://www.framer.com/motion/animation/

      import {motion, useMotionValue, useTransform, animate}  from 'framer-motionn';   

const Counter = ({ from, to, duration }) => {
 const count = useMotionValue(from);
 const rounded = useTransform(count, (latest) => Math.round(latest));

  useEffect(() => {
    const controls = animate(count, to, { duration: duration });
    return controls.stop;
  }, []);

 return <motion.p>{rounded}</motion.p>;
};

Для тех, кто ищет подход на основе TypeScript, включая выполнение в представлении.

      import { animate } from "framer-motion";
import React, { useEffect, useRef, useState } from "react";
import { motion } from "framer-motion";

interface CounterProps {
  from: number;
  to: number;
}

const Counter: React.FC<CounterProps> = ({ from, to }) => {
  const nodeRef = useRef<HTMLParagraphElement | null>(null);
  const [isInView, setIsInView] = useState(false);

  useEffect(() => {
    const node = nodeRef.current;
    if (!node) return;

    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            setIsInView(true);
          }
        });
      },
      { threshold: 0.1 }
    );

    observer.observe(node);

    return () => {
      observer.unobserve(node);
    };
  }, []);

  useEffect(() => {
    if (!isInView) return;

    const node = nodeRef.current;
    if (!node) return;

    const controls = animate(from, to, {
      duration: 1,
      onUpdate(value) {
        node.textContent = Math.round(value).toString();
      },
    });

    return () => controls.stop();
  }, [from, to, isInView]);

  return (
    <motion.p
      ref={nodeRef}
      initial={{ opacity: 0, scale: 0.1 }}
      animate={isInView ? { opacity: 1, scale: 1 } : {}}
      transition={{ duration: 0.4 }}
    />
  );
};

export default Counter;
Другие вопросы по тегам