fix: 修改配置
This commit is contained in:
parent
1f82717a98
commit
bfd5f0fc1f
@ -11,7 +11,7 @@ export default defineConfig({
|
||||
type: 'hash',
|
||||
},
|
||||
mode: 'site',
|
||||
publicPath: '/niceNote/',
|
||||
publicPath: '/',
|
||||
resolve: {
|
||||
includes: [
|
||||
'docs',
|
||||
|
@ -4,7 +4,7 @@ nav:
|
||||
path: /fea
|
||||
group:
|
||||
title: 💊 canvas
|
||||
order: 1
|
||||
order: 2
|
||||
---
|
||||
|
||||
## 基础入门
|
||||
|
@ -9,6 +9,14 @@ group:
|
||||
|
||||
## 动画案例
|
||||
|
||||
### 粒子背景
|
||||
|
||||
<code src="./demos/demo3/index.jsx" />
|
||||
|
||||
### 粒子图片
|
||||
|
||||
<code src="./demos/demo2/index.jsx" />
|
||||
|
||||
### 贪吃蛇
|
||||
|
||||
```jsx
|
||||
@ -800,108 +808,90 @@ export default () => {
|
||||
const clockRef = useRef()
|
||||
|
||||
function clock() {
|
||||
let now = new Date()
|
||||
let canvas = clockRef && clockRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
var theCanv = clockRef.current;
|
||||
var theCanvObject = theCanv.getContext('2d');
|
||||
var x = 200;
|
||||
var y = 200;
|
||||
|
||||
ctx.save()
|
||||
ctx.clearRect(0, 0, 150, 150)
|
||||
ctx.translate(75, 75)
|
||||
ctx.scale(0.4, 0.4)
|
||||
ctx.rotate(-Math.PI / 2)
|
||||
ctx.strokeStyle = 'black'
|
||||
ctx.fillStyle = 'white'
|
||||
ctx.lineWidth = 8
|
||||
ctx.lineCap = 'round'
|
||||
startTime();
|
||||
|
||||
ctx.save()
|
||||
for (let i = 0; i < 12; i++) {
|
||||
ctx.beginPath()
|
||||
ctx.rotate(Math.PI / 6)
|
||||
ctx.moveTo(100, 0)
|
||||
ctx.lineTo(120, 0)
|
||||
ctx.stroke()
|
||||
}
|
||||
ctx.restore()
|
||||
function startTime() {
|
||||
|
||||
ctx.save()
|
||||
ctx.lineWidth = 5
|
||||
for (let i = 0; i < 60; i++) {
|
||||
if (i % 5 != 0) {
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(117, 0)
|
||||
ctx.lineTo(120, 0)
|
||||
ctx.stroke()
|
||||
}
|
||||
ctx.rotate(Math.PI / 30)
|
||||
}
|
||||
ctx.restore()
|
||||
//分秒刻度和表盘
|
||||
theCanvObject.lineWidth = 1;
|
||||
for (var i = 0; i < 60; i++) {
|
||||
drawArc(150, i*6, (i+1)*6);
|
||||
}
|
||||
drawArc(145, 0, 360, true);
|
||||
|
||||
let sec = now.getSeconds()
|
||||
let min = now.getMinutes()
|
||||
let hr = now.getHours()
|
||||
//时刻度
|
||||
theCanvObject.lineWidth = 2;
|
||||
for (var i = 0; i < 12; i++) {
|
||||
drawArc(150, i*30, (i+1)*30);
|
||||
}
|
||||
drawArc(140, 0, 360, true);
|
||||
|
||||
ctx.fillStyle = 'black'
|
||||
//针
|
||||
drawHand(getTime().hour,5,60,'#ECFC00');
|
||||
drawHand(getTime().min,4,100,'#00BB3F');
|
||||
drawHand(getTime().sec,3,130,'#D60062');
|
||||
|
||||
// hours
|
||||
ctx.save()
|
||||
ctx.rotate( hr * (Math.PI / 6) + (Math.PI / 360) * min + (Math.PI / 21600) * sec)
|
||||
ctx.lineWidth = 14
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(-20, 0)
|
||||
ctx.lineTo(80, 0)
|
||||
ctx.stroke()
|
||||
ctx.restore()
|
||||
setInterval(function () {
|
||||
drawArc(135,0,360,true);
|
||||
drawHand(getTime().hour,5,60,'#ECFC00');
|
||||
drawHand(getTime().min,4,100,'#00BB3F');
|
||||
drawHand(getTime().sec,3,130,'#D60062');
|
||||
},1000);
|
||||
}
|
||||
|
||||
// min
|
||||
ctx.save()
|
||||
ctx.rotate((Math.PI / 30) * min + (Math.PI / 1800) * sec)
|
||||
ctx.lineWidth = 10
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(-28, 0)
|
||||
ctx.lineTo(112, 0)
|
||||
ctx.stroke()
|
||||
ctx.restore()
|
||||
function drawArc(iRadius, iBeginAngle, iEndAngle, ifClear) {
|
||||
var beginRadian = iBeginAngle*Math.PI/180;
|
||||
var endRadian = iEndAngle*Math.PI/180;
|
||||
|
||||
// sec
|
||||
ctx.save()
|
||||
ctx.rotate(sec * Math.PI / 30)
|
||||
ctx.strokeStyle = '#d40000'
|
||||
ctx.fillStyle = '#d40000'
|
||||
ctx.lineWidth = 6
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(-30, 0)
|
||||
ctx.lineTo(83, 0)
|
||||
ctx.stroke()
|
||||
ctx.beginPath()
|
||||
ctx.arc(0, 0, 10, 0, Math.PI * 2, true)
|
||||
ctx.fill()
|
||||
ctx.beginPath()
|
||||
ctx.arc(95, 0, 10, 0, Math.PI * 2, true)
|
||||
ctx.stroke()
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 0)'
|
||||
ctx.arc(0, 0, 3, 0, Math.PI * 2, true)
|
||||
ctx.fill()
|
||||
ctx.restore()
|
||||
theCanvObject.beginPath(); //创建一个路径
|
||||
theCanvObject.moveTo(x, y); //将路径移到x,y
|
||||
theCanvObject.arc(x, y, iRadius, beginRadian, endRadian, false);
|
||||
//画弧
|
||||
!ifClear && theCanvObject.stroke();
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.lineWidth = 14
|
||||
ctx.strokeStyle = '#325fa2'
|
||||
ctx.arc(0, 0, 142, 0, Math.PI * 2, true)
|
||||
ctx.stroke()
|
||||
if (ifClear) {
|
||||
theCanvObject.fillStyle = 'white';
|
||||
theCanvObject.fill();
|
||||
}
|
||||
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(clock)
|
||||
|
||||
function drawHand(iAngle, iWidth, iLength, iColor) {
|
||||
|
||||
theCanvObject.save(); //保存的是canvas的属性,不是截图
|
||||
theCanvObject.lineWidth = iWidth;
|
||||
theCanvObject.strokeStyle = iColor;
|
||||
drawArc(iLength, iAngle, iAngle);
|
||||
theCanvObject.restore(); //弹出栈中的状态
|
||||
|
||||
}
|
||||
|
||||
//根据当前时间返回各个针要指的度数
|
||||
function getTime() {
|
||||
|
||||
var jTime = {};
|
||||
var iNow = new Date();
|
||||
jTime.sec = -90 + iNow.getSeconds()*6;
|
||||
jTime.min = -90 + iNow.getMinutes()*6 + iNow.getSeconds()/20;
|
||||
jTime.hour = -90 + iNow.getHours()*30 + iNow.getMinutes()/2;
|
||||
|
||||
return jTime;
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
window.requestAnimationFrame(clock)
|
||||
clock()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={clockRef} width="200" height="200" />
|
||||
<canvas ref={clockRef} width="600" height="600" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
BIN
docs/fea/canvas/demos/demo2/imgs/1.jpg
Normal file
BIN
docs/fea/canvas/demos/demo2/imgs/1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
docs/fea/canvas/demos/demo2/imgs/2.jpg
Normal file
BIN
docs/fea/canvas/demos/demo2/imgs/2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
BIN
docs/fea/canvas/demos/demo2/imgs/3.jpg
Normal file
BIN
docs/fea/canvas/demos/demo2/imgs/3.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
BIN
docs/fea/canvas/demos/demo2/imgs/4.jpg
Normal file
BIN
docs/fea/canvas/demos/demo2/imgs/4.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
BIN
docs/fea/canvas/demos/demo2/imgs/5.jpg
Normal file
BIN
docs/fea/canvas/demos/demo2/imgs/5.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
BIN
docs/fea/canvas/demos/demo2/imgs/7.jpg
Normal file
BIN
docs/fea/canvas/demos/demo2/imgs/7.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
@ -1,51 +1,217 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
import img1 from './imgs/1.jpg'
|
||||
import img2 from './imgs/2.jpg'
|
||||
import img3 from './imgs/3.jpg'
|
||||
|
||||
const imgs = [img1, img2, img3]
|
||||
class Particale {
|
||||
constructor(opt) {
|
||||
this.warp = opt.warp; //画布
|
||||
this.ctx = opt.warp && opt.warp.getContext('2d');
|
||||
this.imgsUrl = opt.imgsUrl; //图片地址数组
|
||||
this.imgsObj = []; //图片对象数组
|
||||
this.radius = opt.radius || 10; //粒子半径
|
||||
this.index = 0; //当前图片下标
|
||||
this.initz = 300;
|
||||
this.dots = [];
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
//画布居中
|
||||
this.warp.style.marginLeft = "calc(50vw - 500px)";
|
||||
this.warp.style.marginTop = "calc(50vh - 300px)";
|
||||
|
||||
//限制小球半径
|
||||
if (this.warp.width > 500 || this.warp.height > 300)
|
||||
this.radius >= 4 ? this.radius = this.radius : this.radius = 4;
|
||||
else
|
||||
this.radius >= 2 ? this.radius = this.radius : this.radius = 2;
|
||||
|
||||
let promiseArr = this.imgsUrl.map(imgUrl => {
|
||||
return new Promise((resolve, reject) => {
|
||||
var imgObj = new Image();
|
||||
imgObj.onload = () => {
|
||||
this.imgsObj.push(imgObj);
|
||||
resolve();
|
||||
};
|
||||
imgObj.src = imgUrl;
|
||||
});
|
||||
});
|
||||
//图片全部加载完毕开始绘制
|
||||
Promise.all(promiseArr).then(() => {
|
||||
this.picLoop();
|
||||
});
|
||||
}
|
||||
|
||||
picLoop() {
|
||||
this.dots = [];
|
||||
this.drawPic(); //绘制当前图片
|
||||
this.toParticle(); //得到像素点
|
||||
this.combineAnimate(); //合成图像
|
||||
this.index === this.imgsUrl.length-1 ? this.index = 0 : this.index++; //下标移动到下一张图片
|
||||
}
|
||||
drawPic() {
|
||||
//清除画布
|
||||
this.ctx.clearRect(0, 0, this.warp.width, this.warp.height);
|
||||
let imgObj = this.imgsObj[this.index];
|
||||
|
||||
//限制图片大小
|
||||
if(imgObj.width > imgObj.height) {
|
||||
let ImgScale = imgObj.height / imgObj.width;
|
||||
imgObj.width = this.warp.width * .5;
|
||||
imgObj.height = imgObj.width * ImgScale;
|
||||
} else {
|
||||
let ImgScale = imgObj.width / imgObj.height;
|
||||
imgObj.height = this.warp.height * .7;
|
||||
imgObj.width = imgObj.height * ImgScale;
|
||||
}
|
||||
|
||||
|
||||
//绘制图片到canvas
|
||||
this.ctx.drawImage(imgObj, this.warp.width / 2 - imgObj.width / 2, this.warp.height / 2 - imgObj.height / 2, imgObj.width, imgObj.height);
|
||||
|
||||
}
|
||||
toParticle() {
|
||||
//得到像素
|
||||
let imageData = this.ctx.getImageData(0, 0, this.warp.width, this.warp.height);
|
||||
let data = imageData.data;
|
||||
|
||||
for(let x = 0; x < imageData.width; x += this.radius * 2) {
|
||||
for(let y = 0; y < imageData.height; y += this.radius * 2) {
|
||||
let i = (x + y * this.warp.width) * 4;
|
||||
if(data[i+3] !== 0 && data[i] !== 255 && data[i+1] !== 255 && data[i+2] !== 255) {
|
||||
let dot = {
|
||||
x: x, //图片x轴坐标
|
||||
y: y, // y轴坐标
|
||||
z: 0, // z轴坐标
|
||||
r: data[i], // rgba
|
||||
g: data[i+1], // rgba
|
||||
b: data[i+2], // rgba
|
||||
a: 1, // rgba
|
||||
ix: Math.random() * this.warp.width, //初始化x轴坐标
|
||||
iy: Math.random() * this.warp.height, // y轴坐标
|
||||
iz: Math.random() * this.initz * 2 - this.initz, // z轴坐标
|
||||
ir: 255, // rgba
|
||||
ig: 255, // rgba
|
||||
ib: 255, // rgba
|
||||
ia: 0, // rgba
|
||||
tx: Math.random() * this.warp.width, //目标x轴坐标
|
||||
ty: Math.random() * this.warp.height, // y轴坐标
|
||||
tz: Math.random() * this.initz * 2 - this.initz, // z轴坐标
|
||||
tr: 255, // rgba
|
||||
tg: 255, // rgba
|
||||
tb: 255, // rgba
|
||||
ta: 0, // rgba
|
||||
};
|
||||
this.dots.push(dot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
combineAnimate() {
|
||||
let combined = false;
|
||||
this.ctx.clearRect(0, 0, this.warp.width, this.warp.height);
|
||||
this.dots.map(dot => {
|
||||
if (Math.abs(dot.ix - dot.x) < 0.1 && Math.abs(dot.iy - dot.y) < 0.1 && Math.abs(dot.iz - dot.z) < 0.1) {
|
||||
dot.ix = dot.x;
|
||||
dot.iy = dot.y;
|
||||
dot.iz = dot.z;
|
||||
dot.ir = dot.r;
|
||||
dot.ig = dot.g;
|
||||
dot.ib = dot.b;
|
||||
dot.ia = dot.a;
|
||||
combined = true;
|
||||
} else {
|
||||
dot.ix += (dot.x - dot.ix) * 0.07;
|
||||
dot.iy += (dot.y - dot.iy) * 0.07;
|
||||
dot.iz += (dot.z - dot.iz) * 0.07;
|
||||
dot.ir += (dot.r - dot.ir) * 0.3;
|
||||
dot.ig += (dot.g - dot.ig) * 0.3;
|
||||
dot.ib += (dot.b - dot.ib) * 0.3;
|
||||
dot.ia += (dot.a - dot.ia) * 0.1;
|
||||
combined = false;
|
||||
}
|
||||
|
||||
return this.drowDot(dot);
|
||||
});
|
||||
|
||||
|
||||
if(!combined) {
|
||||
requestAnimationFrame(() => {
|
||||
return this.combineAnimate();
|
||||
});
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
return this.separateAnimate();
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
separateAnimate() {
|
||||
let separated = false;
|
||||
this.ctx.clearRect(0, 0, this.warp.width, this.warp.height);
|
||||
this.dots.map(dot => {
|
||||
if (Math.abs(dot.ix - dot.tx) < 0.1 && Math.abs(dot.iy - dot.ty) < 0.1 && Math.abs(dot.iz - dot.tz) < 0.1) {
|
||||
dot.ix = dot.tx;
|
||||
dot.iy = dot.ty;
|
||||
dot.iz = dot.tz;
|
||||
dot.ir = dot.tr;
|
||||
dot.ig = dot.tg;
|
||||
dot.ib = dot.tb;
|
||||
dot.ia = dot.ta;
|
||||
separated = true;
|
||||
} else {
|
||||
dot.ix += (dot.tx - dot.ix) * 0.07;
|
||||
dot.iy += (dot.ty - dot.iy) * 0.07;
|
||||
dot.iz += (dot.tz - dot.iz) * 0.07;
|
||||
dot.ir += (dot.tr - dot.ir) * 0.02;
|
||||
dot.ig += (dot.tg - dot.ig) * 0.02;
|
||||
dot.ib += (dot.tb - dot.ib) * 0.02;
|
||||
dot.ia += (dot.ta - dot.ia) * 0.03;
|
||||
separated = false;
|
||||
}
|
||||
|
||||
return this.drowDot(dot);
|
||||
});
|
||||
|
||||
|
||||
if(!separated) {
|
||||
requestAnimationFrame(() => {
|
||||
return this.separateAnimate();
|
||||
});
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
return this.picLoop(); //间接递归,使用尾递归优化
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
drowDot(dot) {
|
||||
let scale = this.initz / (this.initz + dot.iz);
|
||||
this.ctx.save();
|
||||
this.ctx.beginPath();
|
||||
this.ctx.fillStyle = `rgba(${Math.floor(dot.ir)}, ${Math.floor(dot.ig)}, ${Math.floor(dot.ib)}, ${dot.ia})`;
|
||||
this.ctx.arc(this.warp.width / 2 + (dot.ix - this.warp.width / 2) * scale, this.warp.height / 2 + (dot.iy - this.warp.height / 2) * scale, this.radius * scale, 0, Math.PI * 2);
|
||||
this.ctx.fill();
|
||||
this.ctx.closePath();
|
||||
this.ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
|
||||
// function init() {
|
||||
// var snake = [41, 40], //snake队列表示蛇身,初始节点存在但不显示
|
||||
// direction = 1, //1表示向右,-1表示向左,20表示向下,-20表示向上
|
||||
// food = 43, //食物的位置
|
||||
// n, //与下次移动的位置有关
|
||||
// box = document.canvasRef && document.canvasRef.current && document.canvasRef.current.getContext('2d');
|
||||
// //从0到399表示box里[0~19]*[0~19]的所有节点,每20px一个节点
|
||||
|
||||
// function draw(seat, color) {
|
||||
// box.fillStyle = color;
|
||||
// box.fillRect(seat % 20 *20 + 1, ~~(seat / 20) * 20 + 1, 18, 18);
|
||||
// //用color填充一个矩形,以前两个参数为x,y坐标,后两个参数为宽和高。
|
||||
// }
|
||||
|
||||
// document.onkeydown = function(evt) { //当键盘上下左右键摁下的时候改变direction
|
||||
// direction = snake[1] - snake[0] == (n = [-1, -20, 1, 20][(evt || event).keyCode - 37] || direction) ? direction : n;
|
||||
// console.log([-1, -20, 1, 20][(evt || event).keyCode - 37]);
|
||||
// };
|
||||
|
||||
// !function() {
|
||||
// snake.unshift(n = snake[0] + direction); //此时的n为下次蛇头出现的位置,n进入队列
|
||||
// if(snake.indexOf(n, 1) > 0 || n < 0 || n > 399 || direction == 1 && n % 20 == 0 || direction == -1 && n % 20 == 19) {
|
||||
// //if语句判断贪吃蛇是否撞到自己或者墙壁,碰到时返回,结束程序
|
||||
// return alert("游戏结束!");
|
||||
// }
|
||||
// draw(n, "lime"); //画出蛇头下次出现的位置
|
||||
// if(n == food) { //如果吃到食物时,产生一个蛇身以外的随机的点,不会去掉蛇尾
|
||||
// while (snake.indexOf(food = ~~(Math.random() * 400)) > 0);
|
||||
// draw(food, "yellow");
|
||||
// } else { //没有吃到食物时正常移动,蛇尾出队列
|
||||
// draw(snake.pop(),"black");
|
||||
// }
|
||||
// setTimeout(arguments.callee, 150); //每隔0.15秒执行函数一次,可以调节蛇的速度
|
||||
// }();
|
||||
// }
|
||||
|
||||
useEffect(() => {
|
||||
// setTimeout(() => init(), 2000)
|
||||
if (canvasRef && canvasRef.current) {
|
||||
new Particale({
|
||||
warp: canvasRef && canvasRef.current,
|
||||
imgsUrl: imgs,
|
||||
radius: 1,
|
||||
});
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="400" height="400" style="background-color: black">对不起,您的浏览器不支持canvas</canvas>
|
||||
</div>
|
||||
<canvas ref={canvasRef} width="500px" height="500px"></canvas>
|
||||
)
|
||||
}
|
98
docs/fea/canvas/demos/demo3/index.jsx
Normal file
98
docs/fea/canvas/demos/demo3/index.jsx
Normal file
@ -0,0 +1,98 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
|
||||
function init() {
|
||||
const theCanvas = canvasRef.current,
|
||||
ctx = theCanvas.getContext('2d'),
|
||||
current_point = {
|
||||
x: null, //当前鼠标x
|
||||
y: null, //当前鼠标y
|
||||
max: 20000,
|
||||
};
|
||||
|
||||
let canvas_width = theCanvas.width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
|
||||
canvas_height = theCanvas.height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,
|
||||
random_points = [],
|
||||
all_points = [];
|
||||
|
||||
theCanvas.style = "position: absolute; top: 0px; left: 0px;";
|
||||
|
||||
|
||||
function draw() {
|
||||
//清屏
|
||||
ctx.clearRect(0, 0, canvas_width, canvas_height);
|
||||
let i,pi,x_dist,y_dist,dist,w;
|
||||
|
||||
//遍历点集合绘制线条,类似于握手问题,两个点只绘制一条线
|
||||
random_points.forEach((p, index) => {
|
||||
p.x += p.xa, //按指定速度移动
|
||||
p.y += p.ya,
|
||||
//小球碰撞则速度取相反数
|
||||
p.xa *= p.x > canvas_width || p.x < 0 ? -1 : 1,
|
||||
p.ya *= p.y > canvas_height || p.y < 0 ? -1 : 1,
|
||||
ctx.fillRect(p.x - 0.5, p.y - 0.5, 1, 1); //绘制点
|
||||
|
||||
for(i = index + 1; i < all_points.length; i++ ) {
|
||||
pi = all_points[i];
|
||||
if(pi.x !== null && pi.y !== null) {
|
||||
x_dist = p.x - pi.x;
|
||||
y_dist = p.y - pi.y;
|
||||
dist = x_dist * x_dist + y_dist * y_dist;
|
||||
//当两点距离小于极限距离时会产生连线,当第二个点是鼠标所产生点时,第一个点在范围内会产生向鼠标点的速度,产生吸附效果
|
||||
dist < pi.max && (pi === current_point && dist >= pi.max / 2 && (p.x -= 0.03 * x_dist, p.y -= 0.03 * y_dist));
|
||||
//根据距离计算连线的透明度,使过度效果流畅
|
||||
w = (pi.max - dist) / pi.max;
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = w / 2;
|
||||
ctx.strokeStyle = `rgba(110,110,110,${w + 0.2})`;
|
||||
ctx.moveTo(p.x, p.y);
|
||||
ctx.lineTo(pi.x, pi.y);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}),requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
|
||||
//绑定事件,判断是否添加鼠标这个点
|
||||
window.onmousemove = e => {
|
||||
e = e || window.event;
|
||||
current_point.x = e.clientX;
|
||||
current_point.y = e.clientY;
|
||||
};
|
||||
window.onmouseout = () => {
|
||||
current_point.x = null;
|
||||
current_point.y = null;
|
||||
};
|
||||
|
||||
|
||||
//随机生成100个点
|
||||
for(let i = 0; i < 100; i++ ) {
|
||||
|
||||
let x = Math.random() * canvas_width, //初始坐标
|
||||
y = Math.random() * canvas_height,
|
||||
xa = 2 * Math.random() - 1, //x速度
|
||||
ya = 2 * Math.random() - 1, //y速度
|
||||
max = 6000; //会产生连线的距离的平方
|
||||
|
||||
random_points[i] = {x, y, xa, ya, max};
|
||||
}
|
||||
//将鼠标的点添加至点集合中
|
||||
all_points = [...random_points,current_point];
|
||||
|
||||
//只是背景特效-所以延迟执行
|
||||
setTimeout(draw, 100);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
init()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div style={{ position: 'relative' }}>
|
||||
<canvas ref={canvasRef} width="500px" height="500px"></canvas>
|
||||
</div>
|
||||
)
|
||||
}
|
0
docs/fea/canvas/demos/demo3/index.less
Normal file
0
docs/fea/canvas/demos/demo3/index.less
Normal file
14
docs/fea/css/demos/demo10/index.less
Normal file
14
docs/fea/css/demos/demo10/index.less
Normal file
@ -0,0 +1,14 @@
|
||||
.box {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #ccc;
|
||||
}
|
||||
.ball {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
position: absolute;
|
||||
background: red;
|
||||
}
|
40
docs/fea/css/demos/demo10/index.tsx
Normal file
40
docs/fea/css/demos/demo10/index.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import React, { useRef, useEffect, FC } from 'react';
|
||||
import './index.less'
|
||||
|
||||
export default (): any => {
|
||||
const ballRef = useRef(null)
|
||||
|
||||
function fun(v) {
|
||||
let Vx = Math.random()*v;
|
||||
let Vy = Math.sqrt(v*v - Vx*Vx);
|
||||
let startX = Math.random()*490;
|
||||
let startY = Math.random()*490;
|
||||
const ball = ballRef.current;
|
||||
Math.random() > 0.5 && (Vx *= -1);
|
||||
Math.random() > 0.5 && (Vy *= -1);
|
||||
ball.style.left = startX + 'px';
|
||||
ball.style.top = startY + 'px';
|
||||
function animate(){
|
||||
if(startX >= 490 || startX <= 0)
|
||||
Vx = -Vx;
|
||||
startX += Vx;
|
||||
if(startY >= 490 || startY <= 0)
|
||||
Vy = -Vy;
|
||||
startY += Vy;
|
||||
ball.style.left = startX + 'px';
|
||||
ball.style.top = startY + 'px';
|
||||
window.requestAnimationFrame(animate);
|
||||
}
|
||||
window.requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="box">
|
||||
<button onClick={() => fun(5)} type="button">开始</button>
|
||||
<div className="ball" ref={ballRef}></div>
|
||||
</div>
|
||||
)
|
||||
}
|
31
docs/fea/css/demos/demo7/index.less
Normal file
31
docs/fea/css/demos/demo7/index.less
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
@keyframes typing {
|
||||
from { width: 0; }
|
||||
}
|
||||
|
||||
@keyframes blink-caret {
|
||||
50% {
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.cssPrint {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
&_h1 {
|
||||
display: inline-block;
|
||||
color: lime;
|
||||
font: bold 200% Consolas;
|
||||
/*font: bold 200% "Source Code Pro";*/
|
||||
/*必须使用等宽字体*/
|
||||
border-right: .1em solid currentColor;
|
||||
width: 28ch;
|
||||
margin: 2em 1em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
animation: typing 3s steps(20, end),
|
||||
blink-caret .5s step-end infinite alternate;
|
||||
/*step-end每个关键帧在end处跳转,infinite无限循环播放,alternate来回播放,normal顺序播放*/
|
||||
}
|
||||
}
|
16
docs/fea/css/demos/demo7/index.tsx
Normal file
16
docs/fea/css/demos/demo7/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React, { useRef, useEffect, FC } from 'react';
|
||||
import './index.less'
|
||||
|
||||
export default (): any => {
|
||||
|
||||
useEffect(() => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="cssPrint">
|
||||
<h1 className="cssPrint_h1">
|
||||
This is Nice Note WebSite By Json!
|
||||
</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
46
docs/fea/css/demos/demo8/index.less
Normal file
46
docs/fea/css/demos/demo8/index.less
Normal file
@ -0,0 +1,46 @@
|
||||
|
||||
.ribbon {
|
||||
display:inline-block;
|
||||
height: 300px;
|
||||
background-color: #000;
|
||||
&::before, &::after {
|
||||
margin-top:.5em;
|
||||
content: "";
|
||||
float:left;
|
||||
border:1.5em solid #fff;
|
||||
}
|
||||
&::before {
|
||||
border-left-color: transparent;
|
||||
}
|
||||
&::after {
|
||||
border-right-color: transparent;
|
||||
}
|
||||
a {
|
||||
float: left;
|
||||
height: 3.5em;
|
||||
color: #000;
|
||||
text-decoration:none;
|
||||
overflow: hidden;
|
||||
&:hover span {
|
||||
margin-top: 0;
|
||||
background-color: #FFD204;
|
||||
}
|
||||
}
|
||||
span {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
line-height: 3em;
|
||||
margin-top: .5em;
|
||||
padding: 0 1em;
|
||||
transition: background .2s, margin-top .2s;
|
||||
&:before {
|
||||
content: "";
|
||||
position:absolute;
|
||||
top:3em;
|
||||
left:0;
|
||||
border-right:0.5em solid #9B8651;
|
||||
border-bottom:0.5em solid #fff;
|
||||
}
|
||||
}
|
||||
}
|
17
docs/fea/css/demos/demo8/index.tsx
Normal file
17
docs/fea/css/demos/demo8/index.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import React, { useRef, useEffect, FC } from 'react';
|
||||
import './index.less'
|
||||
|
||||
export default (): any => {
|
||||
|
||||
useEffect(() => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className='ribbon'>
|
||||
<a href='#'><span>首页</span></a>
|
||||
<a href='#'><span>关于我</span></a>
|
||||
<a href='#'><span>服务</span></a>
|
||||
<a href='#'><span>介绍</span></a>
|
||||
</div>
|
||||
)
|
||||
}
|
63
docs/fea/css/demos/demo9/index.less
Normal file
63
docs/fea/css/demos/demo9/index.less
Normal file
@ -0,0 +1,63 @@
|
||||
.wraper {
|
||||
width: 260px;
|
||||
height: 260px;
|
||||
margin: 128px auto;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
.cube {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
transform-style: preserve-3d;
|
||||
/*transform: rotateX(-30deg) rotateY(-45deg);*/
|
||||
animation: spin 5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.cube>div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, .8);
|
||||
text-align: center;
|
||||
line-height: 260px;
|
||||
color: #fff;
|
||||
font-size: 48px;
|
||||
border: 2px solid #fff;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.front {
|
||||
transform: translateZ(130px);
|
||||
}
|
||||
|
||||
.end {
|
||||
transform: rotateY(180deg) translateZ(130px);
|
||||
}
|
||||
|
||||
.top {
|
||||
transform: rotateX(90deg) translateZ(130px);
|
||||
}
|
||||
|
||||
.bottom {
|
||||
transform: rotateX(-90deg) translateZ(130px);
|
||||
}
|
||||
|
||||
.left {
|
||||
transform: rotateY(-90deg) translateZ(130px);
|
||||
}
|
||||
|
||||
.right {
|
||||
transform: rotateY(90deg) translateZ(130px);
|
||||
}
|
89
docs/fea/css/demos/demo9/index.tsx
Normal file
89
docs/fea/css/demos/demo9/index.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import React, { useRef, useEffect, FC } from 'react';
|
||||
import './index.less'
|
||||
|
||||
export default (): any => {
|
||||
|
||||
/**
|
||||
* 手动控制动画
|
||||
*/
|
||||
function init() {
|
||||
setTimeout(function () {
|
||||
var cube = document.querySelector(".cube"),
|
||||
downX, downY, moveX, moveY, tempX, tempY, degX = 0, degY = 0;
|
||||
|
||||
window.onmousedown = function (e) {
|
||||
e = e || event;
|
||||
downX = e.clientX; //获取鼠标点下去时的坐标
|
||||
downY = e.clientY;
|
||||
console.log('can');
|
||||
|
||||
window.onmousemove = function (e) {
|
||||
e = e || event;
|
||||
moveX = e.clientX - downX; //算出鼠标移动的距离
|
||||
moveY = e.clientY - downY;
|
||||
//根据一定比例将变化反应在盒子上,改变比例5可以调节拖动的速度
|
||||
tempX = degX + moveX / 5;
|
||||
tempY = degY - moveY / 5;
|
||||
cube.style.transform = "rotatex(" + tempY + "deg) rotatey(" + tempX + "deg)";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
window.onmouseup = function (e) {
|
||||
e = e || event;
|
||||
degX += moveX / 5; //鼠标松开时将拖动期间改变的最终结果保存
|
||||
degY += - moveY / 5;
|
||||
window.onmousemove = null; //取消监听
|
||||
};
|
||||
|
||||
// !function () {
|
||||
// var n = 1000;
|
||||
// var wraper = document.querySelector('.wraper');
|
||||
// wraper.style.perspective = n + 'px';
|
||||
// window.onmousewheel = function (e) {
|
||||
// e = e || event;
|
||||
// if (e.wheelDelta) { //判断浏览器IE,谷歌滑轮事件
|
||||
// if (e.wheelDelta > 0) { //当滑轮向上滚动时减小景深
|
||||
// wraper.style.perspective = n - 50 + 'px';
|
||||
// if (n > 350) {
|
||||
// n = n - 50;
|
||||
// }
|
||||
// }
|
||||
// if (e.wheelDelta < 0) { //当滑轮向下滚动时增加景深
|
||||
// wraper.style.perspective = n + 50 + 'px';
|
||||
// n += 50;
|
||||
// }
|
||||
// } else if (e.detail) { //Firefox滑轮事件
|
||||
// if (e.detail > 0) {
|
||||
// wraper.style.perspective = n - 50 + 'px';
|
||||
// if (n > 350) {
|
||||
// n = n - 50;
|
||||
// }
|
||||
// }
|
||||
// if (e.detail < 0) {
|
||||
// wraper.style.perspective = n + 50 + 'px';
|
||||
// n += 50;
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// }();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="wraper">
|
||||
<div className="cube">
|
||||
<div className="front">Front</div>
|
||||
<div className="end">End</div>
|
||||
<div className="left">Left</div>
|
||||
<div className="right">Right</div>
|
||||
<div className="top">Top</div>
|
||||
<div className="bottom">Bottom</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -9,6 +9,22 @@ group:
|
||||
|
||||
## 💊 css
|
||||
|
||||
### 小球动画
|
||||
|
||||
<code src="./demos/demo10/index.tsx" />
|
||||
|
||||
### 3D 方块
|
||||
|
||||
<code src="./demos/demo9/index.tsx" />
|
||||
|
||||
### 3D导航条
|
||||
|
||||
<code src="./demos/demo8/index.tsx" />
|
||||
|
||||
### 键盘打字效果
|
||||
|
||||
<code src="./demos/demo7/index.tsx" />
|
||||
|
||||
### 镂空文字背景
|
||||
|
||||
<code src="./demos/demo1/index.tsx" />
|
||||
|
@ -4,7 +4,7 @@ nav:
|
||||
path: /fea
|
||||
group:
|
||||
title: 💊 设计模式
|
||||
order: 1
|
||||
order: 22
|
||||
---
|
||||
|
||||
## 工厂模式
|
||||
|
176
docs/fea/website/index.md
Normal file
176
docs/fea/website/index.md
Normal file
@ -0,0 +1,176 @@
|
||||
---
|
||||
nav:
|
||||
title: 前端
|
||||
path: /fea
|
||||
group:
|
||||
title: 💊 学习文档
|
||||
order: 1
|
||||
path: /website
|
||||
---
|
||||
|
||||
|
||||
## 💊 学习文档
|
||||
|
||||
### React 方向
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://antv.vision/zh">
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" width="42" />
|
||||
<br />
|
||||
<strong>antv</strong>
|
||||
<div>react 数据可视化方案</div>
|
||||
</a>
|
||||
</td>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://nicecoders.github.io/">
|
||||
<img src="http://jzx-h5.oss-cn-hangzhou.aliyuncs.com/logo.png" width="42" />
|
||||
<br />
|
||||
<strong>NiceCode</strong>
|
||||
<div>聚合工具库</div>
|
||||
</a>
|
||||
</td>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://ant.design">
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" width="42" />
|
||||
<br />
|
||||
<strong>antd</strong>
|
||||
<div>react UI框架</div>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://ahooks.js.org/">
|
||||
<img src="https://ahooks.js.org/logo.svg" />
|
||||
<br />
|
||||
<strong>ahooks</strong>
|
||||
<div>react hook 拓展库</div>
|
||||
</a>
|
||||
</td>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://mobile.ant.design/index-cn">
|
||||
<img src="https://zos.alipayobjects.com/rmsportal/wIjMDnsrDoPPcIV.png" width="42" />
|
||||
<br />
|
||||
<strong>ant-mobile</strong>
|
||||
<div>react 移动端UI框架</div>
|
||||
</a>
|
||||
</td>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://motion.ant.design/index-cn">
|
||||
<img src="https://zos.alipayobjects.com/rmsportal/TOXWfHIUGHvZIyb.svg" width="42" />
|
||||
<br />
|
||||
<strong>ant-motion</strong>
|
||||
<div>react 动效库</div>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="160" align="center">
|
||||
<a target="_blank" href="https://qiankun.umijs.org/">
|
||||
<img src="https://gw.alipayobjects.com/zos/bmw-prod/8a74c1d3-16f3-4719-be63-15e467a68a24/km0cv8vn_w500_h500.png" width="42" />
|
||||
<br />
|
||||
<strong>qiankun</strong>
|
||||
<div>微应用框架</div>
|
||||
</a>
|
||||
</td>
|
||||
<td width="160" align="center">
|
||||
<a target="_blank" href="https://umijs.org">
|
||||
<img src="https://gw.alipayobjects.com/zos/bmw-prod/598d14af-4f1c-497d-b579-5ac42cd4dd1f/k7bjua9c_w132_h130.png" width="42" />
|
||||
<br />
|
||||
<strong>UmiJS</strong>
|
||||
<div>react 企业级应用框架</div>
|
||||
</a>
|
||||
</td>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://pro.ant.design/zh-CN/">
|
||||
<img src="https://pro.ant.design/favicon.png" width="42" />
|
||||
<br />
|
||||
<strong>antd-pro</strong>
|
||||
<div>react 中台解决方案</div>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://scaffold.ant.design">
|
||||
<img src="https://zos.alipayobjects.com/rmsportal/HXZvKsbcQljpFToWbjPj.svg" width="42" />
|
||||
<br />
|
||||
<strong>scaffold</strong>
|
||||
<div>脚手架市场</div>
|
||||
</a>
|
||||
</td>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://nextjs.org/">
|
||||
<img src="https://nextjs.org/static/favicon/safari-pinned-tab.svg" width="42" />
|
||||
<br />
|
||||
<strong>NextJs</strong>
|
||||
<div>服务端渲染方案</div>
|
||||
</a>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Vue 方向
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://cn.vuejs.org/">
|
||||
<img src="https://cn.vuejs.org/images/logo.svg" width="42" />
|
||||
<br />
|
||||
<strong>vue</strong>
|
||||
<div>渐进式框架</div>
|
||||
</a>
|
||||
</td>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://www.iviewui.com/">
|
||||
<img src="https://file.iviewui.com/dist/d6fcbeecd3f5ff1b1dd0a0f68bdf6ce7.svg" width="42" />
|
||||
<br />
|
||||
<strong>IView</strong>
|
||||
<div>vue UI框架</div>
|
||||
</a>
|
||||
</td>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://zh.nuxtjs.org/">
|
||||
<img src="https://zh.nuxtjs.org/_nuxt/icons/icon_512x512.c20795.png" width="42" />
|
||||
<br />
|
||||
<strong>Nuxt</strong>
|
||||
<div>vue 服务端渲染方案</div>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Node 方向
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://antv.vision/zh">
|
||||
<img src="https://gw.alicdn.com/tfs/TB1eGsrk79l0K4jSZFKXXXFjpXa-347-340.png" width="42" />
|
||||
<br />
|
||||
<strong>midway</strong>
|
||||
<div>企业级 node 框架</div>
|
||||
</a>
|
||||
</td>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://cnodejs.org/">
|
||||
<img src="https://static2.cnodejs.org/public/images/cnode_icon_64.png" width="42" />
|
||||
<br />
|
||||
<strong>CNode</strong>
|
||||
<div>Node.js 开源技术社区</div>
|
||||
</a>
|
||||
</td>
|
||||
<td width="240" align="center">
|
||||
<a target="_blank" href="https://www.koajs.com.cn/">
|
||||
<img src="https://tva1.sinaimg.cn/large/008i3skNly1gu7waxo9bxj60jk09s3ym02.jpg" width="42" />
|
||||
<br />
|
||||
<strong>Koa</strong>
|
||||
<div>Node 开发框架</div>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
@ -4,41 +4,11 @@ hero:
|
||||
desc: 💊 Json's 学习笔记
|
||||
actions:
|
||||
- text: 开始学习
|
||||
link: /fea
|
||||
link: /fea/website
|
||||
features:
|
||||
- icon: http://jzx-h5.oss-cn-hangzhou.aliyuncs.com/logo.png
|
||||
title: NiceCode
|
||||
desc: <a href="https://nicecoders.github.io/nicecode/#/">前端工具合集</a>
|
||||
- icon: https://ahooks.js.org/logo.svg
|
||||
title: aHooks
|
||||
desc: <a href="https://ahooks.js.org/zh-CN">react hook 拓展库</a>
|
||||
- icon: https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg
|
||||
title: antd
|
||||
desc: <a href="https://ant.design/index-cn">react 样式库</a>
|
||||
- icon: https://zos.alipayobjects.com/rmsportal/wIjMDnsrDoPPcIV.png
|
||||
title: antd-mobile
|
||||
desc: <a href="https://mobile.ant.design/index-cn">react 移动端样式库</a>
|
||||
- icon: https://zos.alipayobjects.com/rmsportal/TOXWfHIUGHvZIyb.svg
|
||||
title: ant-motion
|
||||
desc: <a href="https://motion.ant.design/index-cn">react 动效库</a>
|
||||
- icon: https://gw.alipayobjects.com/zos/bmw-prod/8a74c1d3-16f3-4719-be63-15e467a68a24/km0cv8vn_w500_h500.png
|
||||
title: qiankun
|
||||
desc: <a href="https://qiankun.umijs.org/">react 微应用</a>
|
||||
- icon: https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg
|
||||
title: antv
|
||||
desc: <a href="https://antv.vision/zh">react 数据可视化</a>
|
||||
- icon: https://pro.ant.design/favicon.png
|
||||
title: antd-pro
|
||||
desc: <a href="https://pro.ant.design/zh-CN/">react 中台解决方案</a>
|
||||
- icon: https://zos.alipayobjects.com/rmsportal/HXZvKsbcQljpFToWbjPj.svg
|
||||
title: scaffold
|
||||
desc: <a href="https://scaffold.ant.design">react 脚手架市场</a>
|
||||
- icon: https://img.alicdn.com/tfs/TB1YHEpwUT1gK0jSZFhXXaAtVXa-28-27.svg
|
||||
title: umi
|
||||
desc: <a href="https://umijs.org/">react 应用框架</a>
|
||||
- icon: https://gw.alicdn.com/tfs/TB1eGsrk79l0K4jSZFKXXXFjpXa-347-340.png
|
||||
title: midway
|
||||
desc: <a href="http://www.midwayjs.org/doc/">企业级 node 框架</a>
|
||||
|
||||
footer: Open-source MIT Licensed | Copyright © 2020<br />Powered by Json
|
||||
---
|
||||
|
Loading…
Reference in New Issue
Block a user