diff --git a/modules/home/Number.tsx b/modules/home/Number.tsx index cceee753..cbae4aea 100644 --- a/modules/home/Number.tsx +++ b/modules/home/Number.tsx @@ -1,6 +1,6 @@ 'use client'; import React, { useEffect, useRef } from 'react'; -import { animate, inView } from 'motion'; +import { animate, motion, useInView } from 'motion/react'; import { Roboto_Mono } from 'next/font/google'; const MonospaceFont = Roboto_Mono({ @@ -16,30 +16,32 @@ const counter = (elementRef: HTMLDivElement, start: number, end: number) => { (progress: number) => { elementRef.textContent = `${Math.floor(start + progress * range)}`; }, - { duration, easing: 'ease-out' }, + { duration, ease: 'easeOut' }, ); }; export const Number = ({ value, text }: { value: number; text: string }) => { - const elementRef = useRef(null); + const elementRef = useRef(null); const counterRef = useRef(null); + const isInView = useInView(elementRef, { once: true }); useEffect(() => { - if (elementRef.current) { - inView(elementRef.current, ({ target }) => { - animate(target, { opacity: 1 }, { delay: 0.2, easing: 'ease-out' }); - if (counterRef.current) { - counter(counterRef.current, Math.floor(value * 0.6), value); - } - }); + if (isInView && counterRef.current) { + counter(counterRef.current, Math.floor(value * 0.6), value); } // eslint-disable-next-line react-hooks/exhaustive-deps -- animation should only run on mount - }, []); + }, [isInView]); return ( -
  • +
    {value}
    {text}
    -
  • + ); }; diff --git a/package.json b/package.json index fb0c7961..eb8455d1 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@next/mdx": "16.1.6", - "motion": "^10.18.0", + "motion": "^12.0.0", "next": "16.1.6", "normalize.css": "8.0.1", "react": "19.2.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9293538c..807f2665 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: 16.1.6 version: 16.1.6 motion: - specifier: ^10.18.0 - version: 10.18.0 + specifier: ^12.0.0 + version: 12.33.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next: specifier: 16.1.6 version: 16.1.6(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -236,24 +236,6 @@ packages: cpu: [x64] os: [win32] - '@motionone/animation@10.18.0': - resolution: {integrity: sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==} - - '@motionone/dom@10.18.0': - resolution: {integrity: sha512-bKLP7E0eyO4B2UaHBBN55tnppwRnaE3KFfh3Ps9HhnAkar3Cb69kUCJY9as8LrccVYKgHA+JY5dOQqJLOPhF5A==} - - '@motionone/easing@10.18.0': - resolution: {integrity: sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==} - - '@motionone/generators@10.18.0': - resolution: {integrity: sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==} - - '@motionone/types@10.17.1': - resolution: {integrity: sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==} - - '@motionone/utils@10.18.0': - resolution: {integrity: sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==} - '@next/env@16.1.6': resolution: {integrity: sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==} @@ -520,6 +502,20 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + framer-motion@12.33.0: + resolution: {integrity: sha512-ca8d+rRPcDP5iIF+MoT3WNc0KHJMjIyFAbtVLvM9eA7joGSpeqDfiNH/kCs1t4CHi04njYvWyj0jS4QlEK/rJQ==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -540,9 +536,6 @@ packages: hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - hey-listen@1.0.8: - resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} - html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} @@ -660,8 +653,25 @@ packages: micromark@4.0.2: resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} - motion@10.18.0: - resolution: {integrity: sha512-MVAZZmwM/cp77BrNe1TxTMldxRPjwBNHheU5aPToqT4rJdZxLiADk58H+a0al5jKLxkB0OdgNq6DiVn11cjvIQ==} + motion-dom@12.33.0: + resolution: {integrity: sha512-XRPebVypsl0UM+7v0Hr8o9UAj0S2djsQWRdHBd5iVouVpMrQqAI0C/rDAT3QaYnXnHuC5hMcwDHCboNeyYjPoQ==} + + motion-utils@12.29.2: + resolution: {integrity: sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==} + + motion@12.33.0: + resolution: {integrity: sha512-TcND7PijsrTeIA9SRVUB8TOJQ+6mJnJ5K4a9oAJZvyI0Zy47Gq5oofU+VkTxbLcvDoKXnHspQcII2mnk3TbFsQ==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -974,41 +984,6 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@motionone/animation@10.18.0': - dependencies: - '@motionone/easing': 10.18.0 - '@motionone/types': 10.17.1 - '@motionone/utils': 10.18.0 - tslib: 2.8.1 - - '@motionone/dom@10.18.0': - dependencies: - '@motionone/animation': 10.18.0 - '@motionone/generators': 10.18.0 - '@motionone/types': 10.17.1 - '@motionone/utils': 10.18.0 - hey-listen: 1.0.8 - tslib: 2.8.1 - - '@motionone/easing@10.18.0': - dependencies: - '@motionone/utils': 10.18.0 - tslib: 2.8.1 - - '@motionone/generators@10.18.0': - dependencies: - '@motionone/types': 10.17.1 - '@motionone/utils': 10.18.0 - tslib: 2.8.1 - - '@motionone/types@10.17.1': {} - - '@motionone/utils@10.18.0': - dependencies: - '@motionone/types': 10.17.1 - hey-listen: 1.0.8 - tslib: 2.8.1 - '@next/env@16.1.6': {} '@next/mdx@16.1.6': @@ -1177,6 +1152,15 @@ snapshots: extend@3.0.2: {} + framer-motion@12.33.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + motion-dom: 12.33.0 + motion-utils: 12.29.2 + tslib: 2.8.1 + optionalDependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + fsevents@2.3.2: optional: true @@ -1211,8 +1195,6 @@ snapshots: dependencies: '@types/hast': 3.0.4 - hey-listen@1.0.8: {} - html-url-attributes@3.0.1: {} husky@9.1.7: {} @@ -1456,12 +1438,19 @@ snapshots: transitivePeerDependencies: - supports-color - motion@10.18.0: + motion-dom@12.33.0: dependencies: - '@motionone/animation': 10.18.0 - '@motionone/dom': 10.18.0 - '@motionone/types': 10.17.1 - '@motionone/utils': 10.18.0 + motion-utils: 12.29.2 + + motion-utils@12.29.2: {} + + motion@12.33.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + framer-motion: 12.33.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + tslib: 2.8.1 + optionalDependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) ms@2.1.3: {}