一、创建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;
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= 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 (
);
}
}
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 (
{ this.box = c; }}
onMouseMove={this.onMouseMove}
onMouseLeave={this.onMouseLeave}
>
{data.map((item:any, i:any) => (
))}
);
}
}
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 {
constructor(props: any) {
super(props);
this.state = {
defaultactive : false,
}
}
render() {
const PasswordSvg = () => (
);
const PasswordIcon = (props: any) => ;
return (
Login Your Account
}/>
(visible ? : )}
className={"userInfo"}
prefix={ }
/>
)
}
}
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(
,
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 {
constructor(props: any) {
super(props);
}
render() { let a = {
width:'100%',
height:'40px'}
return (
)
}
}
export default Home
十一、启动看看效果,初学阶段,先记录一下