Browse Source

feat: 添加首页动效

pull/283/head
zhaowei 7 months ago
parent
commit
ad440d455c
21 changed files with 237 additions and 68 deletions
  1. +3
    -3
      react-ui/config/proxy.ts
  2. +2
    -0
      react-ui/package.json
  3. +1
    -1
      react-ui/public/mockServiceWorker.js
  4. BIN
      react-ui/src/assets/img/home/code-item-bg.png
  5. BIN
      react-ui/src/assets/img/home/header-bg-mini.png
  6. +1
    -1
      react-ui/src/pages/Home/components/CodeConfig/index.less
  7. +1
    -1
      react-ui/src/pages/Home/components/Dataset/index.less
  8. +22
    -9
      react-ui/src/pages/Home/components/Intro/index.less
  9. +49
    -12
      react-ui/src/pages/Home/components/Intro/index.tsx
  10. +1
    -1
      react-ui/src/pages/Home/components/Mirror/index.less
  11. +11
    -2
      react-ui/src/pages/Home/components/Model/index.less
  12. +29
    -4
      react-ui/src/pages/Home/components/Model/index.tsx
  13. +18
    -12
      react-ui/src/pages/Home/components/NavBar/index.less
  14. +21
    -16
      react-ui/src/pages/Home/components/NavBar/index.tsx
  15. +25
    -0
      react-ui/src/pages/Home/components/ScrollReveal/index.tsx
  16. +1
    -1
      react-ui/src/pages/Home/components/Service/index.less
  17. +27
    -2
      react-ui/src/pages/Home/components/Service/index.tsx
  18. +9
    -1
      react-ui/src/pages/Home/components/Statistics/index.tsx
  19. +1
    -2
      react-ui/src/pages/Home/index.less
  20. +2
    -0
      react-ui/src/styles/theme.less
  21. +13
    -0
      react-ui/src/utils/index.ts

+ 3
- 3
react-ui/config/proxy.ts View File

@@ -20,9 +20,9 @@ export default {
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
'/api/': {
// 要代理的地址
target: 'http://172.20.32.197:31213', // 开发环境
// target: 'http://172.20.32.235:31213', // 测试环境
// target: 'http://172.20.32.44:8082',
// target: 'http://172.20.32.197:31213', // 开发环境
target: 'http://172.20.32.235:31213', // 测试环境
// target: 'http://36.103.199.74:31213/', // 公网环境
// target: 'http://172.20.32.164:8082',
// 配置了这个可以从 http 代理到 https
// 依赖 origin 的功能可能需要这个,比如 cookie


+ 2
- 0
react-ui/package.json View File

@@ -75,6 +75,7 @@
"fabric": "^5.3.0",
"highlight.js": "^11.7.0",
"lodash": "^4.17.21",
"motion": "~12.23.12",
"omit.js": "^2.0.2",
"pnpm": "^8.9.0",
"query-string": "^8.1.0",
@@ -82,6 +83,7 @@
"rc-util": "^5.30.0",
"react": "^18.2.0",
"react-activation": "^0.12.4",
"react-countup": "~6.5.3",
"react-cropper": "^2.3.3",
"react-dev-inspector": "^1.8.1",
"react-dom": "^18.2.0",


+ 1
- 1
react-ui/public/mockServiceWorker.js View File

@@ -8,7 +8,7 @@
* - Please do NOT serve this file on production.
*/

const PACKAGE_VERSION = '2.7.0'
const PACKAGE_VERSION = '2.7.1'
const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f'
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
const activeClientIds = new Set()


BIN
react-ui/src/assets/img/home/code-item-bg.png View File

Before After
Width: 3840  |  Height: 1786  |  Size: 5.3 MB Width: 1359  |  Height: 516  |  Size: 114 kB

BIN
react-ui/src/assets/img/home/header-bg-mini.png View File

Before After
Width: 2800  |  Height: 116  |  Size: 172 kB

+ 1
- 1
react-ui/src/pages/Home/components/CodeConfig/index.less View File

@@ -3,7 +3,7 @@
display: flex;
flex-direction: column;
align-items: center;
padding: 4.375rem 16.25rem 9.375rem;
padding: 4.375rem @home-padding-x 9.375rem;
.backgroundFullImage(url(@/assets/img/home/code-bg.png));

&__item {


+ 1
- 1
react-ui/src/pages/Home/components/Dataset/index.less View File

@@ -3,7 +3,7 @@
display: flex;
flex-direction: column;
align-items: center;
padding: 0 16.25rem 11.625rem;
padding: 0 @home-padding-x 11.125rem;

&__item {
position: relative;


+ 22
- 9
react-ui/src/pages/Home/components/Intro/index.less View File

@@ -1,15 +1,28 @@
.intro {
position: relative;
z-index: 10;
display: flex;
flex-direction: column;
align-items: center;
background-image: url(@/assets/img/home/header-bg.png);
background-repeat: no-repeat;
background-position: left top;
background-size: 100% 100%;
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 100;
height: @home-info-height;
overflow: hidden;
background-color: red;

&__content {
position: relative;
z-index: 10;
display: flex;
flex-direction: column;
align-items: center;
padding: 1.25rem @home-padding-x 0;
background-image: url(@/assets/img/home/header-bg.png);
background-repeat: no-repeat;
background-position: left top;
background-size: 100% 100%;
}

&__title {
margin-top: 1.25rem;
margin-bottom: 1.125rem;
color: #ffffff;
font-weight: 400;


+ 49
- 12
react-ui/src/pages/Home/components/Intro/index.tsx View File

@@ -1,23 +1,60 @@
import { convertRemToPx } from '@/utils';
import { useNavigate } from '@umijs/max';
import { motion, useMotionTemplate, useScroll, useSpring, useTransform } from 'motion/react';
import NavBar from '../NavBar';
import StatisticsBlock from '../Statistics';
import styles from './index.less';

function IntroBlock() {
const navigate = useNavigate();
const { scrollY } = useScroll();
const springValue = useSpring(scrollY, {
stiffness: 100,
damping: 30,
restDelta: 0.001,
});

const initialHeight = convertRemToPx(35);
console.log(initialHeight);

const height = useTransform(() => `max(calc(35rem - ${springValue.get()}px), 5rem)`);
// const left = useTransform(() => `min(${springValue.get()}px, 16.25rem)`);
const left = useTransform(springValue, [0, initialHeight], [0.0, 16.25]);
const leftRem = useMotionTemplate`${left}rem`;
const radius = useTransform(springValue, [0, initialHeight], [0.0, 2.5]);
const radiusRem = useMotionTemplate`${radius}rem`;
const top = useTransform(springValue, [0, initialHeight], [0, 10]);
const paddingX = useTransform(springValue, [0, initialHeight], [16.25, 10]);
const paddingXRem = useMotionTemplate`1.25rem ${paddingX}rem 0`;

return (
<div className={styles.intro}>
<NavBar></NavBar>
<div className={styles['intro__title']}>智能材料科研平台</div>
<div className={styles['intro__desc']}>
智能材料科研平台是用于材料研究和开发的技术平台,它旨在提供实验数据收集、分析和可视化等功能,
以支持材料工程师、科学家和研究人员在材料设计、性能评估和工艺优化方面的工作。
</div>
<div className={styles['intro__button']} onClick={() => navigate('/workspace')}>
开始使用
</div>
<StatisticsBlock></StatisticsBlock>
</div>
<motion.div
className={styles.intro}
style={{
height: height,
left: leftRem,
right: leftRem,
borderRadius: radiusRem,
top: top,
}}
transition={{
type: 'spring',
duration: 0.3,
}}
>
<motion.div className={styles['intro__content']} style={{ padding: paddingXRem }}>
<NavBar></NavBar>
<div className={styles['intro__title']}>智能材料科研平台</div>
<div className={styles['intro__desc']}>
智能材料科研平台是用于材料研究和开发的技术平台,它旨在提供实验数据收集、分析和可视化等功能,
以支持材料工程师、科学家和研究人员在材料设计、性能评估和工艺优化方面的工作。
</div>
<div className={styles['intro__button']} onClick={() => navigate('/workspace')}>
开始使用
</div>
<StatisticsBlock></StatisticsBlock>
</motion.div>
</motion.div>
);
}



+ 1
- 1
react-ui/src/pages/Home/components/Mirror/index.less View File

@@ -3,7 +3,7 @@
display: flex;
flex-direction: column;
align-items: center;
padding: 0 16.25rem 11.625rem;
padding: 0 @home-padding-x 11.125rem;

&__item {
position: relative;


+ 11
- 2
react-ui/src/pages/Home/components/Model/index.less View File

@@ -3,12 +3,21 @@
display: flex;
flex-direction: column;
align-items: center;
margin-top: 1.25rem;
padding: 4.125rem 16.25rem 14.75rem;
padding: 4.125rem @home-padding-x 14.75rem;
.backgroundFullImage(url(@/assets/img/home/model-bg.png));

&__content {
display: flex;
flex-wrap: wrap;
gap: 1.625rem 1.25rem;
align-items: center;
width: 100%;
}

&__item {
position: relative;
display: flex;
align-items: center;
width: calc((100% - 2 * 1.25rem) / 3);
padding: 1.875rem 1.25rem;
color: @home-text-color-tertiary;


+ 29
- 4
react-ui/src/pages/Home/components/Model/index.tsx View File

@@ -3,10 +3,31 @@ import { getPublicModelsReq } from '@/services/home';
import { to } from '@/utils/promise';
import { useNavigate } from '@umijs/max';
import { Divider, Flex } from 'antd';
import { motion, type Variants } from 'motion/react';
import { useEffect, useState } from 'react';
import BlockTitle from '../BlockTitle';
import styles from './index.less';

const modelVariants: Variants = {
offscreen: (index: number) => ({
y: 0,
opacity: 1,
transition: {
ease: 'linear',
duration: 0.1,
},
}),
onscreen: {
y: [0, 200, 0],
opacity: [0, 0, 1],
transition: {
ease: 'easeInOut',
duration: 0.5,
times: [0, 0, 1],
},
},
};

function ModelBlock() {
const navigate = useNavigate();
const [modelData, setModelData] = useState<ModelData[]>([]);
@@ -30,10 +51,14 @@ function ModelBlock() {
style={{ marginBottom: '5.25rem' }}
onClick={() => navigate('/dataset/model')}
></BlockTitle>
<Flex align="center" style={{ width: '100%' }} wrap gap="1.625rem 1.25rem">
<div className={styles['model__content']}>
{modelData.map((item, index) => {
return (
<Flex
<motion.div
variants={modelVariants}
initial={'offscreen'}
whileInView={'onscreen'}
custom={index}
className={styles['model__item']}
key={item.id}
onClick={() => {
@@ -64,10 +89,10 @@ function ModelBlock() {
<div className={styles['model__item__category']}>材料研发</div>
</Flex> */}
</div>
</Flex>
</motion.div>
);
})}
</Flex>
</div>
</div>
);
}


+ 18
- 12
react-ui/src/pages/Home/components/NavBar/index.less View File

@@ -1,9 +1,9 @@
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
margin-bottom: 4.375rem;
padding: 1.25rem 15.625rem;
color: white;
font-size: 0.9375rem;

@@ -20,24 +20,30 @@

&__menu-item {
margin-right: 3.125rem;
padding: 0.25rem 0.5rem;
border-radius: 0.375rem;
cursor: pointer;

&:hover {
background-color: rgba(0, 0, 0, 0.06);
}

&:last-of-type {
margin-right: 0;
}
}

&__avatar {
width: 2rem;
height: 2rem;
margin-right: 0;
margin-left: auto;
cursor: pointer;
}
:global {
.ant-dropdown-trigger {
height: 2.15rem;

&__login {
margin-right: 0;
margin-left: auto;
cursor: pointer;
.ant-avatar {
margin-right: 0 !important;
}
}

.ant-dropdown-trigger > .anticon {
display: none;
}
}
}

+ 21
- 16
react-ui/src/pages/Home/components/NavBar/index.tsx View File

@@ -2,6 +2,7 @@ import { getAccessToken } from '@/access';
import Avatar from '@/components/RightContent/AvatarDropdown';
import { useNavigate } from '@umijs/max';
import { Flex } from 'antd';
import classNames from 'classnames';
import styles from './index.less';

function NavBar() {
@@ -47,26 +48,30 @@ function NavBar() {
className={styles['nav-bar__app-logo']}
></img>
<span className={styles['nav-bar__app-name']}>智能材料科研平台</span>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('service')}>
服务
</div>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('model')}>
模型
</div>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('dataset')}>
数据集
</div>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('mirror')}>
镜像
</div>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('codeConfig')}>
代码配置
</div>
</Flex>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('service')}>
服务
</div>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('model')}>
模型
</div>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('dataset')}>
数据集
</div>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('mirror')}>
镜像
</div>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('codeConfig')}>
代码配置
</div>

{token ? (
<Avatar menu isHome />
) : (
<div className={styles['nav-bar__login']} onClick={() => gotoPage('login')}>
<div
className={classNames(styles['nav-bar__menu-item'], styles['nav-bar__login'])}
onClick={() => gotoPage('login')}
>
登录
</div>
)}


+ 25
- 0
react-ui/src/pages/Home/components/ScrollReveal/index.tsx View File

@@ -0,0 +1,25 @@
import { motion, useInView, Variants } from 'motion/react';
import { ReactNode, useRef } from 'react';

type ScrollRevealProps = {
children: ReactNode;
variants: Variants;
};

function ScrollReveal({ children, variants }: ScrollRevealProps) {
const ref = useRef<HTMLDivElement>(null);
const isInView = useInView(ref, { amount: 'all' });

return (
<motion.div
variants={variants}
ref={ref}
initial="offscreen"
animate={isInView ? 'onscreen' : 'offscreen'}
>
{children}
</motion.div>
);
}

export default ScrollReveal;

+ 1
- 1
react-ui/src/pages/Home/components/Service/index.less View File

@@ -4,7 +4,7 @@
flex-direction: column;
align-items: center;
width: 100%;
padding: 5.625rem 16.25rem 6.25rem;
padding: 5.625rem @home-padding-x 6.25rem;
.backgroundFullImage(url(@/assets/img/home/service-bg.png));

&__item {


+ 27
- 2
react-ui/src/pages/Home/components/Service/index.tsx View File

@@ -8,10 +8,31 @@ import { formatDate } from '@/utils/date';
import { to } from '@/utils/promise';
import { useNavigate } from '@umijs/max';
import { Flex } from 'antd';
import { motion, type Variants } from 'motion/react';
import { useEffect, useState } from 'react';
import BlockTitle from '../BlockTitle';
import styles from './index.less';

const serviceVariants: Variants = {
offscreen: {
y: -100,
opacity: 0,
transition: {
ease: 'linear',
duration: 0,
},
},
onscreen: (index: number) => ({
y: 0,
opacity: 1,
transition: {
type: 'spring',
duration: 1,
delay: index * 0.3,
},
}),
};

function ServiceBlock() {
const navigate = useNavigate();
const [serviceData, setServiceData] = useState<ServiceData[]>([]);
@@ -38,10 +59,14 @@ function ServiceBlock() {
<Flex align="center" style={{ width: '100%' }} gap={'0 1.125rem'}>
{serviceData.map((item, index) => {
return (
<div
<motion.div
variants={serviceVariants}
className={styles['service__item']}
key={item.id}
onClick={() => navigate(`/dataset/modelDeployment/serviceInfo/${item.id}`)}
initial="offscreen"
whileInView="onscreen"
custom={index}
>
<div className={styles['service__item__image-container']}>
<img
@@ -59,7 +84,7 @@ function ServiceBlock() {
<div className={styles['service__item__user']}>{item.create_by}</div>
<div className={styles['service__item__date']}>{formatDate(item.create_time)}</div>
</Flex>
</div>
</motion.div>
);
})}
</Flex>


+ 9
- 1
react-ui/src/pages/Home/components/Statistics/index.tsx View File

@@ -6,6 +6,7 @@ import ServiceIcon from '@/assets/img/home/service.png';
import { getAssetPublicCountReq } from '@/services/home';
import { to } from '@/utils/promise';
import { useEffect, useState } from 'react';
import CountUp from 'react-countup';
import styles from './index.less';

function StatisticsBlock() {
@@ -84,7 +85,14 @@ function StatisticsBlock() {
<div key={item.title} className={styles['statistics__item']}>
<img src={item.icon} draggable={false} className={styles['statistics__item__icon']} />
<div>
<div className={styles['statistics__item__count']}>{item.value ?? '--'}</div>
<div className={styles['statistics__item__count']}>
{item.value ? (
<CountUp end={item.value} duration={1} enableScrollSpy></CountUp>
) : (
'--'
)}
</div>

<div className={styles['statistics__item__name']}>{item.title}</div>
</div>
</div>


+ 1
- 2
react-ui/src/pages/Home/index.less View File

@@ -1,6 +1,5 @@
.home {
height: 100%;
overflow-y: auto;
padding-top: @home-info-height;
font-family: Alibaba;

&__separator {


+ 2
- 0
react-ui/src/styles/theme.less View File

@@ -31,6 +31,8 @@
@home-text-color-secondary: @text-color-secondary;
@home-text-color-tertiary: #8284a4;
@home-divider-color: #d8d8d8;
@home-padding-x: 16.25rem;
@home-info-height: 35rem;

@workspace-background: linear-gradient(
179.03deg,


+ 13
- 0
react-ui/src/utils/index.ts View File

@@ -365,3 +365,16 @@ export const trimCharacter = (str: string, ch: string): string => {
export const convertEmptyStringToUndefined = (value?: string): string | undefined => {
return value === '' ? undefined : value;
};

/**
* Converts rem to px.
*
* @param {number} rem - The value of rem.
* @return {number} The value of px
*/

export const convertRemToPx = (rem: number): number => {
const fontSize = document.documentElement.style.fontSize;
const font = parseFloat(fontSize);
return font * rem;
};

Loading…
Cancel
Save