1.1 初始化react项目

一、创建react-ts项目

yarn create vite myreact --template react-ts

二、安装依赖

yarn

三、启动项目

npm run dev

四、新建目录

五、pages目录新建个index.tsx,代码来源AntDesgin,然后改造为TS

https://pro.ant.design/

https://motion.ant.design/

https://ant.design/

import TweenOne from 'rc-tween-one';
import React from 'react';
import '../style/index.css'
import Login from "../components/login";
 class GridLayout {
     private cellWidth: number;
     private cellHeight: number;
     private gridY: number;
     private gridX: number;
     private grid: Array<any>;
    constructor(rect: number, width: number, height: number) {
        this.gridX = Math.floor(width / rect);
        this.gridY = Math.floor(height / rect);
        this.cellWidth = width / this.gridX;
        this.cellHeight = height / this.gridY;
        this.grid = [];
        for (let i = 0; i < this.gridY; i += 1) {
            this.grid[i] = [];
            for (let s = 0; s < this.gridX; s += 1) {
                this.grid[i][s] = [];
            }
        }
    }

    getCells = (e: { x: number; radius: number; y: number; }) => {
        const gridArray = [];
        const w1 = Math.floor((e.x - e.radius) / this.cellWidth);
        const w2 = Math.ceil((e.x + e.radius) / this.cellWidth);
        const h1 = Math.floor((e.y - e.radius) / this.cellHeight);
        const h2 = Math.ceil((e.y + e.radius) / this.cellHeight);
        for (let c = h1; c < h2; c += 1) {
            for (let l = w1; l < w2; l += 1) {
                gridArray.push(this.grid[c][l]);
            }
        }
        return gridArray;
    }

    hasCollisions = (t: { x: number; y: number; radius: number; }) => (
        this.getCells(t).some(e => e.some((v: any) => this.collides(t, v)))
    )

    collides = (t: { x: any; y: any; radius: any; }, a: { x: number; y: number; radius: any; }) => {
        if (t === a) {
            return false;
        }
        const n = t.x - a.x;
        const i = t.y - a.y;
        const r = t.radius + a.radius;
        return n * n + i * i < r * r;
    }

    add = (value:any) => {
        this.getCells(value).forEach((item) => {
            item.push(value);
        });
    }
}

const getPointPos = (width: number, height: number, length: number) => {
    const grid = new GridLayout(150, width, height);
    const posArray = [];
    const num = 500;
    const radiusArray = [20, 35, 60];
    for (let i = 0; i < length; i += 1) {
        let radius;
        let pos;
        let j = 0;
        for(let j =0; j< num; j+=1) {
            radius = radiusArray[Math.floor(Math.random() * radiusArray.length)];
            pos = { x: Math.random() * (width - radius * 2) + radius, y: Math.random() * (height - radius * 2) + radius, radius };
            if (!grid.hasCollisions(pos)) {
                break;
            }
        }
        posArray.push(pos);
        grid.add(pos);
    }
    return posArray;
};

const getDistance = (t: { x: any; y: any; }, a: { x: any; y: any; }) => (Math.sqrt((t.x - a.x) * (t.x - a.x) + (t.y - a.y) * (t.y - a.y)));

 class Point extends React.PureComponent {
    render() {
        const { tx, ty, x, y, opacity, backgroundColor, radius, ...props }:Readonly<any>= this.props;
        let transform;
        let zIndex = 0;
        let animation:any = {
            y: (Math.random() * 2 - 1) * 20 || 15,
            duration: 3000,
            delay:Math.random() * 1000,
            yoyo: true,
            repeat: -1,
        };
        if (tx && ty) {
            if (tx !== x && ty !== y) {
                const distance = getDistance({ x, y }, { x: tx, y: ty });
                const g = Math.sqrt(2000000 / (0.1 * distance * distance));
                transform = `translate(${g * (x - tx) / distance}px, ${g * (y - ty) / distance}px)`;
            } else if (tx === x && ty === y) {
                transform = `scale(${80 / radius})`;
                animation = { y: 0, yoyo: false, repeat: 0, duration: 300 };
                zIndex = 1;
            }
        }
        return (
            <div
                style={{
                    left: x - radius,
                    top: y - radius,
                    width: radius * 1.8,
                    height: radius * 1.8,
                    opacity,
                    zIndex,
                    transform,
                }}
                {...props}
            >
                <TweenOne
                    animation={animation}
                    style={{
                        backgroundColor,
                    }}
                    // @ts-ignore
                    className={`${this.props.className}-child`}
                />
            </div>

        );
    }
}

 class LinkedAnimate extends React.Component {
    static defaultProps = {
        className: 'linked-animate-demo',
    };

    num = 60;// 点的个数
     private box: any;

    constructor(props: {} | Readonly<{}>) {
        super(props);
        this.state = {
            data: getPointPos(1280, 600, this.num).map(item => ({
                ...item,
                opacity: Math.random() * 0.2 + 0.05,
                backgroundColor: `rgb(${Math.round(Math.random() * 40 + 120)},230,255)`,
            })),
            tx: 0,
            ty: 0,
        };
    }

    onMouseMove = (e: { clientX: any; clientY: any; }) => {
        const cX = e.clientX;
        const cY = e.clientY;
        const boxRect = this.box.getBoundingClientRect();
        // @ts-ignore
        const pos = this.state.data.map((item: { x: any; y: any; radius: any; }) => {
            const { x, y, radius } = item;
            return { x, y, distance: getDistance({ x: cX - boxRect.x, y: cY - boxRect.y }, { x, y }) - radius };
        }).reduce((a: { distance: number; }, b: { distance: number; }) => {
            if (!a.distance || a.distance > b.distance) {
                return b;
            }
            return a;
        });
        if (pos.distance < 60) {
            this.setState({
                tx: pos.x,
                ty: pos.y,
            });
        } else {
            this.onMouseLeave();
        }
    }

    onMouseLeave = () => {
        this.setState({
            tx: 0,
            ty: 0,
        });
    }

    render() {
        // @ts-ignore
        const { className } = this.props;
        const { data, tx, ty }:any = this.state;
        return (
            <div className={`${className}-wrapper`}>
                <Login />
                <div

                    className={`${className}-box`}
                    ref={(c) => { this.box = c; }}
                    onMouseMove={this.onMouseMove}
                    onMouseLeave={this.onMouseLeave}
                >
                    {data.map((item:any, i:any) => (
                        <Point {...item} tx={tx} ty={ty} key={i.toString()} className={`${className}-block`} />
                    ))}
                </div>
            </div>
        );
    }
}

export default LinkedAnimate

 六、style目录下建个index.css

.linked-animate-demo-wrapper {
    overflow: hidden;
    height: 100%;
    background: GhostWhite;
    position: absolute;
    width: 100%;
}

.linked-animate-demo-box {
    position: absolute;
    width: 50%;
    height: 50%;
    display: block;
    left: 0%;
    top: 0;
    bottom: 0;
    right: 70%;
    margin: auto;
}

.linked-animate-demo-block {
    position: absolute;
    transition: transform .45s ease;
}

.linked-animate-demo-block-child {
    border-radius: 100%;
    width: 100%;
    height: 100%;
}
body {
    margin: 0;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

code {
    font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

七、components下建立个login.tsx

import React from "react";
import {Button, Input, Space} from 'antd';
import Icon,{UserOutlined, EyeInvisibleOutlined, EyeTwoTone } from '@ant-design/icons';
import 'antd/dist/antd.css'
import './login.css'
import {Link} from "react-router-dom"
class Login extends React.Component<any, any> {
    constructor(props: any) {
        super(props);
        this.state = {
            defaultactive : false,
        }
    }
    render() {
        const PasswordSvg = () => (
            <svg d="1629272463432" className="icon" viewBox="0 0 1024 1024" version="1.1"
                 xmlns="http://www.w3.org/2000/svg" p-id="14838" width="16" height="16">
                <path
                    d="M825.5 832.2h-627c-72.7 0-131.9-59.1-131.9-131.9V323.6c0-72.7 59.1-131.9 131.9-131.9h626.9c72.7 0 131.9 59.1 131.9 131.9v376.7c0 72.8-59.1 131.9-131.8 131.9z m-627-602.7c-51.9 0-94.2 42.2-94.2 94.2v376.7c0 51.9 42.3 94.2 94.2 94.2h626.9c51.9 0 94.2-42.2 94.2-94.2V323.6c0-51.9-42.3-94.2-94.2-94.2H198.5z"
                    fill="#2c2c2c" p-id="14839"></path>
                <path
                    d="M247.6 646.7h528.8v37.7H247.6zM830.4 388.7h-75.3V351h75.3v37.7z m-131.8 0h-75.3V351h75.3v37.7z m-131.9 0h-75.3V351h75.3v37.7z m-131.8 0h-75.3V351h75.3v37.7z m-131.9 0h-75.3V351H303v37.7zM830.4 547.9h-75.3v-37.7h75.3v37.7z m-131.8 0h-75.3v-37.7h75.3v37.7z m-131.9 0h-75.3v-37.7h75.3v37.7z m-131.8 0h-75.3v-37.7h75.3v37.7z m-131.9 0h-75.3v-37.7H303v37.7z"
                    fill="#2c2c2c" p-id="14840"></path>
            </svg>
        );
        const PasswordIcon = (props: any) => <Icon component={PasswordSvg} {...props} />;
        return (
            <div id={"login"}>
            <Space direction={"vertical"} align={"center"} size={18} >
                <h3>Login Your Account</h3>
                <Input placeholder=" Enter username" allowClear={true} className={"userInfo"} prefix={<UserOutlined />}/>
                <Input.Password
                    placeholder=" Enter password"
                    iconRender={visible => (visible ? <EyeTwoTone/> : <EyeInvisibleOutlined/>)}
                    className={"userInfo"}
                    prefix={<PasswordIcon />}
                />
                 <Link to={'/test'}> <Button type="primary" className={"userInfo"} >Sign In</Button></Link>
            </Space>
            </div>
        )
    }
}

export default Login

login.css

#login{
    background-color: GhostWhite;
    width: 20%;
    height: 40%;
    text-align: center;
    position: relative;
    left: 68%;
    top: 10%;
    z-index: 1;
    opacity: 0.8;
}
.userInfo {
    width: 260px;
    font: 16px bold;
}
h3{
    font-size: 24px;
}


八、assets目录下准备几张icon

https://www.iconfont.cn/

logo.svg\icon.svg\keyboard.svg

九、main.tsx修改

import React from 'react'
import ReactDOM from 'react-dom'
import LinkedAnimate from './pages/index'
import Home from "./pages/home";
import {BrowserRouter, Route, Switch} from 'react-router-dom';
ReactDOM.render(
        <BrowserRouter>
            <Switch>
            <Route path="/test" component={Home}/>
            <Route path="/" component={LinkedAnimate}/>
            </Switch>
        </BrowserRouter>,
  document.getElementById('root')

十、pages新建个home.tsx,代码依然是antdesign上的

import {Layout, Menu} from 'antd';
import {
    AppstoreOutlined,
    BarChartOutlined,
    CloudOutlined,
    ShopOutlined,
    TeamOutlined,
    UserOutlined,
    UploadOutlined,
    VideoCameraOutlined,
} from '@ant-design/icons';
import React from 'react';

const {Header, Content, Footer, Sider} = Layout;

class Home extends React.Component<any, any> {
    constructor(props: any) {
        super(props);
    }
    render() {  let a = {
    width:'100%',
    height:'40px'}

        return (
            <Layout>
                <Sider
                    style={{
                        overflow: 'auto',
                        height: '100vh',
                        position: 'fixed',
                        left: 0,
                    }}
                >

                    <div className="logo" />
                    <Menu theme="dark" mode="inline" defaultSelectedKeys={['4']}>
                        <div style={a}>111</div>
                        <Menu.Item key="1" icon={<UserOutlined />}>
                            Home
                        </Menu.Item>
                        <Menu.Item key="2" icon={<VideoCameraOutlined />}>
                            Period
                        </Menu.Item>
                        <Menu.Item key="3" icon={<UploadOutlined />}>
                            Stroies
                        </Menu.Item>
                        <Menu.Item key="4" icon={<BarChartOutlined />}>
                            Tasks
                        </Menu.Item>
                        <Menu.Item key="5" icon={<CloudOutlined />}>
                            Bugs
                        </Menu.Item>
                        <Menu.Item key="6" icon={<AppstoreOutlined />}>
                            Statistics
                        </Menu.Item>
                        <Menu.Item key="7" icon={<TeamOutlined />}>
                            UserInfo
                        </Menu.Item>
                        <Menu.Item key="8" icon={<ShopOutlined />}>
                            RoleInfo
                        </Menu.Item>
                    </Menu>
                </Sider>
                <Layout className="site-layout" style={{ marginLeft: 200 }}>
                    <Header className="site-layout-background" style={{ padding: 0 }} />
                    <Content style={{ margin: '24px 16px 0', overflow: 'initial' }}>
                        <div className="site-layout-background" style={{ padding: 24, textAlign: 'center' }}>

                        </div>
                    </Content>
                    <Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer>
                </Layout>
            </Layout>
        )
    }

}

export default Home


 十一、启动看看效果,初学阶段,先记录一下

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值