feat: 新版本

This commit is contained in:
NICE CODE BY DEV 2024-01-18 16:29:26 +08:00
parent 4afd399a9f
commit e67d650faf
659 changed files with 93349 additions and 13150 deletions

View File

@ -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>
);

View File

@ -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>
)
};

View File

@ -1,6 +1,7 @@
import { defineConfig } from 'dumi';
export default defineConfig({
title: 'nicenote',
themeConfig: {
name: 'nicenote',
navs: [
@ -11,6 +12,7 @@ export default defineConfig({
},
]
},
sitemap: { hostname: 'j710328466.github.io', exclude: [] },
favicons: ['https://jzx-h5.oss-cn-hangzhou.aliyuncs.com/logo.ico'],
// logo: 'http://jzx-h5.oss-cn-hangzhou.aliyuncs.com/logo.png',
outputPath: 'docs-dist',
@ -21,8 +23,7 @@ export default defineConfig({
publicPath: '/',
resolve: {
docDirs: ['docs'],
atomDirs: [{ type: 'component', dir: 'src' }],
codeBlockMode: 'passive',
atomDirs: [{ type: 'component', dir: 'src' }]
},
locales: [{ id: 'zh-CN', name: '中文' }], // 2.0 默认值
analyze: {

View File

@ -1,53 +0,0 @@
// @ts-nocheck
if (window.g_initWebpackHotDevClient) {
function tryApplyUpdates(onHotUpdateSuccess?: Function) {
// @ts-ignore
if (!module.hot) {
window.location.reload();
return;
}
function isUpdateAvailable() {
// @ts-ignore
return window.g_getMostRecentCompilationHash() !== __webpack_hash__;
}
// TODO: is update available?
// @ts-ignore
if (!isUpdateAvailable() || module.hot.status() !== 'idle') {
return;
}
function handleApplyUpdates(err: Error | null, updatedModules: any) {
if (err || !updatedModules || window.g_getHadRuntimeError()) {
window.location.reload();
return;
}
onHotUpdateSuccess?.();
if (isUpdateAvailable()) {
// While we were updating, there was a new update! Do it again.
tryApplyUpdates();
}
}
// @ts-ignore
module.hot.check(true).then(
function (updatedModules: any) {
handleApplyUpdates(null, updatedModules);
},
function (err: Error) {
handleApplyUpdates(err, null);
},
);
}
window.g_initWebpackHotDevClient({
tryApplyUpdates,
});
}
export const __mfsu = 1;

View File

@ -1,21 +0,0 @@
// @ts-nocheck
import { createBrowserHistory, History } from '/Users/teddyj/Library/Mobile Documents/com~apple~CloudDocs/Documents/workspace/dev/nicenote/node_modules/_@umijs_runtime@3.5.41@@umijs/runtime';
let options = {
"basename": "/"
};
if ((<any>window).routerBase) {
options.basename = (<any>window).routerBase;
}
// remove initial history because of ssr
let history: History = process.env.__IS_SERVER ? null : createBrowserHistory(options);
export const createHistory = (hotReload = false) => {
if (!hotReload) {
history = createBrowserHistory(options);
}
return history;
};
export { history };

View File

@ -1,8 +0,0 @@
// @ts-nocheck
import { Plugin } from '/Users/teddyj/Library/Mobile Documents/com~apple~CloudDocs/Documents/workspace/dev/nicenote/node_modules/_@umijs_runtime@3.5.41@@umijs/runtime';
const plugin = new Plugin({
validKeys: ['modifyClientRenderOpts','patchRoutes','rootContainer','render','onRouteChange','__mfsu',],
});
export { plugin };

View File

@ -1,297 +0,0 @@
// Created by Umi Plugin
export interface IConfigFromPlugins {
"404"?: boolean
routes?: {
/**
* Any valid URL path
*/
path?: string
/**
* A React component to render only when the location matches.
*/
component?: (string | (() => any))
wrappers?: string[]
/**
* navigate to a new location
*/
redirect?: string
/**
* When true, the active class/style will only be applied if the location is matched exactly.
*/
exact?: boolean
routes?: any[]
[k: string]: any
}[]
history?: {
type?: ("browser" | "hash" | "memory")
options?: {
}
}
polyfill?: {
imports?: string[]
}
alias?: {
}
analyze?: {
analyzerMode?: ("server" | "static" | "disabled")
analyzerHost?: string
analyzerPort?: any
openAnalyzer?: boolean
generateStatsFile?: boolean
statsFilename?: string
logLevel?: ("info" | "warn" | "error" | "silent")
defaultSizes?: ("stat" | "parsed" | "gzip")
[k: string]: any
}
/**
* postcss autoprefixer, default flexbox: no-2009
*/
autoprefixer?: {
}
base?: string
chainWebpack?: (() => any)
chunks?: string[]
/**
* more css-loader options see https://webpack.js.org/loaders/css-loader/#options
*/
cssLoader?: {
url?: (boolean | (() => any))
import?: (boolean | (() => any))
modules?: (boolean | string | {
})
sourceMap?: boolean
importLoaders?: number
onlyLocals?: boolean
esModule?: boolean
localsConvention?: ("asIs" | "camelCase" | "camelCaseOnly" | "dashes" | "dashesOnly")
}
cssModulesTypescriptLoader?: {
mode?: ("emit" | "verify")
}
cssnano?: {
}
copy?: any[]
define?: {
}
devScripts?: {
}
/**
* devServer configs
*/
devServer?: {
/**
* devServer port, default 8000
*/
port?: number
host?: string
https?: ({
key?: string
cert?: string
http2?: boolean
[k: string]: any
} | boolean)
headers?: {
}
writeToDisk?: (boolean | (() => any))
[k: string]: any
}
devtool?: string
/**
* Code splitting for performance optimization
*/
dynamicImport?: {
/**
* loading the component before loaded
*/
loading?: string
}
/**
* Code splitting for import statement syntax
*/
dynamicImportSyntax?: {
}
exportStatic?: {
htmlSuffix?: boolean
dynamicRoot?: boolean
supportWin?: boolean
/**
* extra render paths only enable in ssr
*/
extraRoutePaths?: (() => any)
}
externals?: ({
} | string | (() => any))
extraBabelIncludes?: any[]
extraBabelPlugins?: any[]
extraBabelPresets?: any[]
extraPostCSSPlugins?: any[]
/**
* fork-ts-checker-webpack-plugin options see https://github.com/TypeStrong/fork-ts-checker-webpack-plugin#options
*/
forkTSChecker?: {
async?: boolean
typescript?: (boolean | {
})
eslint?: {
}
issue?: {
}
formatter?: (string | {
})
logger?: {
}
[k: string]: any
}
fastRefresh?: {
}
hash?: boolean
ignoreMomentLocale?: boolean
inlineLimit?: number
lessLoader?: {
}
manifest?: {
fileName?: string
publicPath?: ""
basePath?: string
writeToFileEmit?: boolean
}
/**
* open mfsu feature
*/
mfsu?: {
development?: {
output?: string
}
production?: {
output?: string
}
mfName?: string
exportAllMembers?: {
}
chunks?: string[]
ignoreNodeBuiltInModules?: boolean
}
mountElementId?: ""
mpa?: {
}
nodeModulesTransform?: {
type?: ("all" | "none")
exclude?: string[]
}
outputPath?: ""
plugins?: string[]
postcssLoader?: {
}
presets?: string[]
proxy?: {
}
publicPath?: string
runtimePublicPath?: boolean
ssr?: {
/**
* force execing Page getInitialProps functions
*/
forceInitial?: boolean
/**
* remove window.g_initialProps in html
*/
removeWindowInitialProps?: boolean
/**
* disable serve-side render in umi dev mode.
*/
devServerRender?: boolean
mode?: ("stream" | "string")
/**
* static markup in static site
*/
staticMarkup?: boolean
}
singular?: boolean
styleLoader?: {
}
targets?: {
}
terserOptions?: {
}
theme?: {
}
runtimeHistory?: {
}
webpack5?: {
lazyCompilation?: {
entries?: boolean
imports?: boolean
test?: any
}
}
workerLoader?: {
}
favicon?: string
headScripts?: any[]
links?: any[]
metas?: any[]
scripts?: any[]
styles?: any[]
title?: string
mock?: {
exclude?: string[]
}
themeConfig?: {
}
logo?: (string | boolean)
mode?: any
description?: string
locales?: string[][]
resolve?: {
}
menus?: {
}
navs?: (any[] | {
})
algolia?: {
appId?: string
apiKey?: string
indexName?: string
debug?: boolean
}
sitemap?: {
hostname?: string
excludes?: string[]
}
apiParser?: {
}
[k: string]: any
}

View File

@ -1,5 +0,0 @@
// @ts-nocheck
import { plugin } from './plugin';
export const __mfsu = 1;

View File

@ -1,4 +0,0 @@
// @ts-nocheck
import 'core-js';
import 'regenerator-runtime/runtime';
export {};

View File

@ -1,129 +0,0 @@
// @ts-nocheck
import React from 'react';
import { ApplyPluginsType } from '/Users/teddyj/Library/Mobile Documents/com~apple~CloudDocs/Documents/workspace/dev/nicenote/node_modules/_@umijs_runtime@3.5.41@@umijs/runtime';
import * as umiExports from './umiExports';
import { plugin } from './plugin';
export function getRoutes() {
const routes = [
{
"path": "/~demos/:uuid",
"layout": false,
"wrappers": [require('../dumi/layout').default],
"component": ((props) => {
const React = require('react');
const { default: getDemoRenderArgs } = require('/Users/teddyj/Library/Mobile Documents/com~apple~CloudDocs/Documents/workspace/dev/nicenote/node_modules/_@umijs_preset-dumi@1.1.53@@umijs/preset-dumi/lib/plugins/features/demo/getDemoRenderArgs');
const { default: Previewer } = require('dumi-theme-default/es/builtins/Previewer.js');
const { usePrefersColor, context } = require('dumi/theme');
const { demos } = React.useContext(context);
const [renderArgs, setRenderArgs] = React.useState([]);
// update render args when props changed
React.useLayoutEffect(() => {
setRenderArgs(getDemoRenderArgs(props, demos));
}, [props.match.params.uuid, props.location.query.wrapper, props.location.query.capture]);
// for listen prefers-color-schema media change in demo single route
usePrefersColor();
switch (renderArgs.length) {
case 1:
// render demo directly
return renderArgs[0];
case 2:
// render demo with previewer
return React.createElement(
Previewer,
renderArgs[0],
renderArgs[1],
);
default:
return `Demo ${props.match.params.uuid} not found :(`;
}
})
},
{
"path": "/_demos/:uuid",
"redirect": "/~demos/:uuid"
},
{
"__dumiRoot": true,
"layout": false,
"path": "/",
"wrappers": [require('../dumi/layout').default, require('/Users/teddyj/Library/Mobile Documents/com~apple~CloudDocs/Documents/workspace/dev/nicenote/node_modules/_dumi-theme-default@1.1.24@dumi-theme-default/es/layout.js').default],
"routes": [
{
"path": "/guide",
"component": require('/Users/teddyj/Library/Mobile Documents/com~apple~CloudDocs/Documents/workspace/dev/nicenote/new/docs/guide.md').default,
"exact": true,
"meta": {
"filePath": "docs/guide.md",
"updatedTime": 1698630719271,
"slugs": [],
"title": "Guide"
},
"title": "Guide - nicenote"
},
{
"path": "/",
"component": require('/Users/teddyj/Library/Mobile Documents/com~apple~CloudDocs/Documents/workspace/dev/nicenote/new/docs/index.md').default,
"exact": true,
"meta": {
"filePath": "docs/index.md",
"updatedTime": 1698630719273,
"title": "A static site based on dumi",
"hero": {
"title": "Site",
"description": "nicenote,nicecode,学习,总结",
"actions": [
{
"text": "Hello",
"link": "/"
},
{
"text": "World",
"link": "/"
}
]
},
"features": [
{
"title": "Hello",
"emoji": "💎",
"description": "Put hello description here"
},
{
"title": "World",
"emoji": "🌈",
"description": "Put world description here"
},
{
"title": "!",
"emoji": "🚀",
"description": "Put ! description here"
}
],
"slugs": []
},
"title": "A static site based on dumi - nicenote"
}
],
"title": "nicenote",
"component": (props) => props.children
}
];
// allow user to extend routes
plugin.applyPlugins({
key: 'patchRoutes',
type: ApplyPluginsType.event,
args: { routes },
});
return routes;
}

View File

@ -1,3 +0,0 @@
// @ts-nocheck
export { history } from './history';
export { plugin } from './plugin';

View File

@ -1 +0,0 @@
{}

View File

@ -1,32 +0,0 @@
{
"menus": {
"en-US": {
"*": [
{
"path": "/",
"title": "A static site based on dumi",
"meta": {}
},
{
"path": "/guide",
"title": "Guide",
"meta": {}
}
]
}
},
"locales": [
{
"name": "en-US",
"label": "English"
}
],
"navs": {},
"title": "nicenote",
"mode": "doc",
"repository": {
"url": "",
"branch": "master"
},
"theme": {}
}

View File

@ -1,6 +0,0 @@
// @ts-nocheck
import React from 'react';
import { dynamic } from 'dumi';
export default {
};

View File

@ -1,8 +0,0 @@
// @ts-nocheck
import React from 'react';
import config from '@@/dumi/config';
import demos from '@@/dumi/demos';
import apis from '@@/dumi/apis';
import Layout from '/Users/teddyj/Library/Mobile Documents/com~apple~CloudDocs/Documents/workspace/dev/nicenote/node_modules/_@umijs_preset-dumi@1.1.53@@umijs/preset-dumi/lib/theme/layout';
export default (props) => <Layout {...props} config={config} demos={demos} apis={apis} />;

View File

@ -1,58 +0,0 @@
// @ts-nocheck
import './core/polyfill';
import '@@/core/devScripts';
import { plugin } from './core/plugin';
import './core/pluginRegister';
import { createHistory } from './core/history';
import { ApplyPluginsType } from '/Users/teddyj/Library/Mobile Documents/com~apple~CloudDocs/Documents/workspace/dev/nicenote/node_modules/_@umijs_runtime@3.5.41@@umijs/runtime';
import { renderClient } from '/Users/teddyj/Library/Mobile Documents/com~apple~CloudDocs/Documents/workspace/dev/nicenote/node_modules/_@umijs_renderer-react@3.5.41@@umijs/renderer-react/dist/index.js';
import { getRoutes } from './core/routes';
const getClientRender = (args: { hot?: boolean; routes?: any[] } = {}) => plugin.applyPlugins({
key: 'render',
type: ApplyPluginsType.compose,
initialValue: () => {
const opts = plugin.applyPlugins({
key: 'modifyClientRenderOpts',
type: ApplyPluginsType.modify,
initialValue: {
routes: args.routes || getRoutes(),
plugin,
history: createHistory(args.hot),
isServer: process.env.__IS_SERVER,
rootElement: 'root',
defaultTitle: ``,
},
});
return renderClient(opts);
},
args,
});
const clientRender = getClientRender();
export default clientRender();
window.g_umi = {
version: '3.5.41',
};
// hot module replacement
// @ts-ignore
if (module.hot) {
// @ts-ignore
module.hot.accept('./core/routes', () => {
const ret = require('./core/routes');
if (ret.then) {
ret.then(({ getRoutes }) => {
getClientRender({ hot: true, routes: getRoutes() })();
});
} else {
getClientRender({ hot: true, routes: ret.getRoutes() })();
}
});
}

39
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,39 @@
{
"editor.detectIndentation": false,
"editor.formatOnPaste": true,
"editor.tabSize": 4,
"editor.formatOnSave": true,
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": false
},
"css.lint.important": "ignore",
"scss.lint.important": "ignore",
"liveServer.settings.CustomBrowser": "chrome",
"cSpell.words": ["ahooks", "antd", "Mapbox"],
"terminal.integrated.profiles.windows": {
"PowerShell": {
"source": "PowerShell",
"icon": "terminal-powershell"
},
"Command Prompt": {
"path": [
"${env:windir}\\Sysnative\\cmd.exe",
"${env:windir}\\System32\\cmd.exe"
],
"args": [],
"icon": "terminal-cmd"
},
"Git-Bash": {
// "source": "Git Bash"
"path": "E:\\app\\Git\\git-bash.exe", //git-bashlocal
"args": []
},
"Windows PowerShell": {
"path": "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
}
},
"terminal.integrated.defaultProfile.windows": "Git-Bash"
}

View File

@ -1,105 +0,0 @@
---
nav:
title: 后端
path: /bed
group:
title: Go
order: 2
---
## 第一章
```js
// main.go
package main
import (
"fmt"
"learnGo/chapter1/B"
"reflect"
"time"
)
func main() {
// test1()
// test2()
// test3()
test4()
}
func test1() {
var i int
var j float32
var t complex64
var q bool
fmt.Printf("i 的默认值:%d\n", i)
fmt.Printf("j 的默认值:")
fmt.Print(j)
fmt.Print("\n")
fmt.Print("t 的默认值:")
fmt.Print(t)
fmt.Print("\n")
fmt.Print("q 的默认值:")
fmt.Print(q)
fmt.Print("\n")
}
func test2() {
a, _, c := 1, "fun", 3.2
var t int8 = 4
// var b float32 = 3.01
q := float32(t)
fmt.Print(reflect.TypeOf(q))
fmt.Print("\n")
fmt.Print(q)
fmt.Print("\n")
fmt.Print(a, c)
fmt.Print("\n")
fmt.Print(B.Car)
fmt.Print("\n")
}
// iota 的使用
func test3() {
// 隐式使用法
const (
a, b = iota + 1, iota + 3
c, d
)
fmt.Print("a 的常量值是:")
fmt.Print(a)
fmt.Print("\n")
fmt.Print("b 的常量值是:")
fmt.Print(b)
fmt.Print("\n")
fmt.Print("c 的常量值是:")
fmt.Print(c)
fmt.Print("\n")
fmt.Print("d 的常量值是:")
fmt.Print(d)
fmt.Print("\n")
}
// goto break continue
func test4() {
One:
fmt.Print("我是代码块一!")
time.Sleep(1 * time.Second)
goto One
}
// B/b.go
package B
// test 小写为该文件全局变量
var test = "fuc"
// Car 首字母大写为可导出变量
var Car = "jeep"
```

View File

@ -1,179 +0,0 @@
---
nav:
title: 后端
path: /bed
group:
title: Go
order: 3
---
## 第二章
```js
package main
import (
"bufio"
"fmt"
"io"
"log"
"net/url"
"os"
"regexp"
"strconv"
"strings"
"time"
)
// Reader 读取模块
type Reader interface {
Read(rc chan []byte)
}
// Writer 写入模块
type Writer interface {
Write(wc chan *Message)
}
// LogProcess 定义一个类
type LogProcess struct {
rc chan []byte
wc chan *Message
read Reader // 读取文件路径
write Writer // influx data source
}
// ReadFromFile 读取结构体
type ReadFromFile struct {
path string
}
// WriteToInfluxDB 写入结构体
type WriteToInfluxDB struct {
influxDBDsn string
}
// Message 结构体
type Message struct {
TimeLocal time.Time
bytesSent int
Path, Method, Scheme, Status string
UpstreamTime, RequestTime float64
}
// Read 读取模块
func (r *ReadFromFile) Read(rc chan []byte) {
// 打开文件
f, err := os.Open(r.path)
if err != nil {
panic(fmt.Sprintf("open file error: %s", err.Error()))
}
// 从文件末尾开始逐行读取内容
// 把字符指针移到文件末尾
f.Seek(0, 2)
rd := bufio.NewReader(f)
for {
line, err := rd.ReadBytes('\n')
if err == io.EOF {
time.Sleep(500 * time.Millisecond)
continue
} else if err != nil {
panic(fmt.Sprintf("ReadBytes error: %s", err.Error()))
}
rc <- line[:len(line)-1]
}
}
// Writer 写入
func (w *WriteToInfluxDB) Write(wc chan *Message) {
for v := range wc {
fmt.Println(v)
}
}
// Process 解析模块
func (l *LogProcess) Process() {
/**
172.0.0.12 - - [04/Mar/2018:13:49:52 +0000] http "GET /foo?query=t HTTP/1.0" 200 2133 "-" "KeepAliveClient" "-" 1.005 1.854
*/
// 正则表达式
r := regexp.MustCompile(`([\d\.]+)\s+([^ \[]+)\s+([^ \[]+)\s+\[([^\]]+)\]\s+([a-z]+)\s+\"([^"]+)\"\s+(\d{3})\s+(\d+)\s+\"([^"]+)\"\s+\"(.*?)\"\s+\"([\d\.-]+)\"\s+([\d\.-]+)\s+([\d\.-]+)`)
loc, _ := time.LoadLocation("Asia/ShangHai")
for v := range l.rc {
ret := r.FindStringSubmatch(string(v))
if len(ret) != 14 {
log.Println("FindStringSubmatch fail:", string(v))
continue
}
message := &Message{}
t, err := time.ParseInLocation("02/Jan/2006:15:04:05 +0000", ret[4], loc)
if err != nil {
log.Println("ParseInLocation: fail", err.Error(), ret[4])
}
message.TimeLocal = t
byteSent, _ := strconv.Atoi(ret[8])
message.bytesSent = byteSent
// GET /foo?query=t HTTP/1.0
reqSli := strings.Split(ret[6], " ")
if len(reqSli) != 3 {
log.Println("strings.Split fail ", ret[6])
continue
}
message.Method = reqSli[0]
u, err := url.Parse(reqSli[1])
if err != nil {
log.Println("url parse fail:", err)
continue
}
message.Path = u.Path
message.Scheme = ret[5]
message.Status = ret[7]
upstreamTime, _ := strconv.ParseFloat(ret[12], 64)
requestTime, _ := strconv.ParseFloat(ret[13], 64)
message.UpstreamTime = upstreamTime
message.RequestTime = requestTime
l.wc <- message
}
}
func main() {
r := &ReadFromFile{
path: "temp/access.log",
}
w := &WriteToInfluxDB{
influxDBDsn: "username&password..",
}
lp := &LogProcess{
rc: make(chan []byte),
wc: make(chan *Message),
read: r,
write: w,
}
// gorotine 并发执行,提升效率
go lp.read.Read(lp.rc)
go lp.Process()
go lp.write.Write(lp.wc)
time.Sleep(30 * time.Second)
}
```

View File

@ -1,34 +0,0 @@
---
nav:
title: 后端
path: /bed
group:
title: Go
order: 4
---
## 第三章
```js
package main
import (
"log"
"net/http"
)
func wsHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
}
func main() {
// 当有请求访问ws时执行此回调方法
http.HandleFunc("/ws", wsHandler)
// 监听127.0.0.1:7777
err := http.ListenAndServe("0.0.0.0:7777", nil)
if err != nil {
log.Fatal("ListenAndServe", err.Error())
}
}
```

View File

@ -1,7 +1,7 @@
---
nav:
title: 后端
path: /bed
title: 其它语言
path: /other
group:
title: Go
order: 2
@ -13,33 +13,33 @@ group:
package main
import (
"fmt"
"io"
"strings"
"fmt"
"io"
"strings"
)
// ReadFrom 定义函数
func ReadFrom(reader io.Reader, num int) ([]byte, error) {
p := make([]byte, num)
p := make([]byte, num)
n, err := reader.Read(p)
n, err := reader.Read(p)
if n > 0 {
return p[:n], nil
}
if n > 0 {
return p[:n], nil
}
return p, err
return p, err
}
// SampleReadFromString 输出例子
func SampleReadFromString() {
data, _ := ReadFrom(strings.NewReader("from string"), 12)
data, _ := ReadFrom(strings.NewReader("from string"), 12)
fmt.Println(data)
fmt.Println(data)
}
func main() {
SampleReadFromString()
SampleReadFromString()
}
```

105
docs/db/go/chapter1.md Normal file
View File

@ -0,0 +1,105 @@
---
nav:
title: 其它语言
path: /other
group:
title: Go
order: 2
---
## 第一章
```js
// main.go
package main
import (
"fmt"
"learnGo/chapter1/B"
"reflect"
"time"
)
func main() {
// test1()
// test2()
// test3()
test4()
}
func test1() {
var i int
var j float32
var t complex64
var q bool
fmt.Printf("i 的默认值:%d\n", i)
fmt.Printf("j 的默认值:")
fmt.Print(j)
fmt.Print("\n")
fmt.Print("t 的默认值:")
fmt.Print(t)
fmt.Print("\n")
fmt.Print("q 的默认值:")
fmt.Print(q)
fmt.Print("\n")
}
func test2() {
a, _, c := 1, "fun", 3.2
var t int8 = 4
// var b float32 = 3.01
q := float32(t)
fmt.Print(reflect.TypeOf(q))
fmt.Print("\n")
fmt.Print(q)
fmt.Print("\n")
fmt.Print(a, c)
fmt.Print("\n")
fmt.Print(B.Car)
fmt.Print("\n")
}
// iota 的使用
func test3() {
// 隐式使用法
const (
a, b = iota + 1, iota + 3
c, d
)
fmt.Print("a 的常量值是:")
fmt.Print(a)
fmt.Print("\n")
fmt.Print("b 的常量值是:")
fmt.Print(b)
fmt.Print("\n")
fmt.Print("c 的常量值是:")
fmt.Print(c)
fmt.Print("\n")
fmt.Print("d 的常量值是:")
fmt.Print(d)
fmt.Print("\n")
}
// goto break continue
func test4() {
One:
fmt.Print("我是代码块一!")
time.Sleep(1 * time.Second)
goto One
}
// B/b.go
package B
// test 小写为该文件全局变量
var test = "fuc"
// Car 首字母大写为可导出变量
var Car = "jeep"
```

179
docs/db/go/chapter2.md Normal file
View File

@ -0,0 +1,179 @@
---
nav:
title: 其它语言
path: /other
group:
title: Go
order: 3
---
## 第二章
```js
package main
import (
"bufio"
"fmt"
"io"
"log"
"net/url"
"os"
"regexp"
"strconv"
"strings"
"time"
)
// Reader 读取模块
type Reader interface {
Read(rc chan []byte)
}
// Writer 写入模块
type Writer interface {
Write(wc chan *Message)
}
// LogProcess 定义一个类
type LogProcess struct {
rc chan []byte
wc chan *Message
read Reader // 读取文件路径
write Writer // influx data source
}
// ReadFromFile 读取结构体
type ReadFromFile struct {
path string
}
// WriteToInfluxDB 写入结构体
type WriteToInfluxDB struct {
influxDBDsn string
}
// Message 结构体
type Message struct {
TimeLocal time.Time
bytesSent int
Path, Method, Scheme, Status string
UpstreamTime, RequestTime float64
}
// Read 读取模块
func (r *ReadFromFile) Read(rc chan []byte) {
// 打开文件
f, err := os.Open(r.path)
if err != nil {
panic(fmt.Sprintf("open file error: %s", err.Error()))
}
// 从文件末尾开始逐行读取内容
// 把字符指针移到文件末尾
f.Seek(0, 2)
rd := bufio.NewReader(f)
for {
line, err := rd.ReadBytes('\n')
if err == io.EOF {
time.Sleep(500 * time.Millisecond)
continue
} else if err != nil {
panic(fmt.Sprintf("ReadBytes error: %s", err.Error()))
}
rc <- line[:len(line)-1]
}
}
// Writer 写入
func (w *WriteToInfluxDB) Write(wc chan *Message) {
for v := range wc {
fmt.Println(v)
}
}
// Process 解析模块
func (l *LogProcess) Process() {
/**
172.0.0.12 - - [04/Mar/2018:13:49:52 +0000] http "GET /foo?query=t HTTP/1.0" 200 2133 "-" "KeepAliveClient" "-" 1.005 1.854
*/
// 正则表达式
r := regexp.MustCompile(`([\d\.]+)\s+([^ \[]+)\s+([^ \[]+)\s+\[([^\]]+)\]\s+([a-z]+)\s+\"([^"]+)\"\s+(\d{3})\s+(\d+)\s+\"([^"]+)\"\s+\"(.*?)\"\s+\"([\d\.-]+)\"\s+([\d\.-]+)\s+([\d\.-]+)`)
loc, _ := time.LoadLocation("Asia/ShangHai")
for v := range l.rc {
ret := r.FindStringSubmatch(string(v))
if len(ret) != 14 {
log.Println("FindStringSubmatch fail:", string(v))
continue
}
message := &Message{}
t, err := time.ParseInLocation("02/Jan/2006:15:04:05 +0000", ret[4], loc)
if err != nil {
log.Println("ParseInLocation: fail", err.Error(), ret[4])
}
message.TimeLocal = t
byteSent, _ := strconv.Atoi(ret[8])
message.bytesSent = byteSent
// GET /foo?query=t HTTP/1.0
reqSli := strings.Split(ret[6], " ")
if len(reqSli) != 3 {
log.Println("strings.Split fail ", ret[6])
continue
}
message.Method = reqSli[0]
u, err := url.Parse(reqSli[1])
if err != nil {
log.Println("url parse fail:", err)
continue
}
message.Path = u.Path
message.Scheme = ret[5]
message.Status = ret[7]
upstreamTime, _ := strconv.ParseFloat(ret[12], 64)
requestTime, _ := strconv.ParseFloat(ret[13], 64)
message.UpstreamTime = upstreamTime
message.RequestTime = requestTime
l.wc <- message
}
}
func main() {
r := &ReadFromFile{
path: "temp/access.log",
}
w := &WriteToInfluxDB{
influxDBDsn: "username&password..",
}
lp := &LogProcess{
rc: make(chan []byte),
wc: make(chan *Message),
read: r,
write: w,
}
// gorotine 并发执行,提升效率
go lp.read.Read(lp.rc)
go lp.Process()
go lp.write.Write(lp.wc)
time.Sleep(30 * time.Second)
}
```

34
docs/db/go/chapter3.md Normal file
View File

@ -0,0 +1,34 @@
---
nav:
title: 其它语言
path: /other
group:
title: Go
order: 4
---
## 第三章
```js
package main
import (
"log"
"net/http"
)
func wsHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
}
func main() {
// 当有请求访问ws时执行此回调方法
http.HandleFunc("/ws", wsHandler)
// 监听127.0.0.1:7777
err := http.ListenAndServe("0.0.0.0:7777", nil)
if err != nil {
log.Fatal("ListenAndServe", err.Error())
}
}
```

View File

@ -1,7 +1,7 @@
---
nav:
title: 后端
path: /bed
title: 其它语言
path: /other
group:
title: Go
order: 1
@ -42,7 +42,7 @@ group:
- 介绍
- 每次碰到 const 会被重置为 0如果没碰到每新增一个常量声明就会自增 +1
- 使用技巧
- 跳值使用法:使用 _ , 每一个 _ 能使 iota 的值跳过 1 , 要跳多少就给多少个 \_
- 跳值使用法:使用 _, 每一个_ 能使 iota 的值跳过 1 , 要跳多少就给多少个 \_
- 插队使用法:在变量声明中再插入一个赋值的变量不会对 iota 自增产生影响
- 隐式使用法:如果不声明值,则默认使用最后一个表达式的赋值格式

View File

@ -1,30 +1,36 @@
---
nav:
title: 数据库
path: /db
title: 其它语言
path: /other
group:
title: 💊 mongoDB
title: mongoDB
order: 1
---
# 💊 mongoDB
## mac 安装
### 1.1 到官网下载指定版本的安装包
[https://www.mongodb.com/try/download/community?jmp=nav](https://www.mongodb.com/try/download/community?jmp=nav)
### 1.2 将安装包放到指定文件夹
```javascript
open /usr/local
open / usr / local;
```
###
### 1.3 配置数据库data目录
###
### 1.3 配置数据库 data 目录
```javascript
sudo mkdir -p /data/db
```
##
##
### 1.4 配置环境变量
```javascript
# 1. 打开 bash 文件
open ~/.bash_profile
@ -35,19 +41,23 @@ PATH=$PATH:/usr/local/mongoDB/bin
# 3. 保存 bash并触发生效
source ~/.bash_profile
```
##
### 1.5 启动 mongodb
##
### 1.5 启动 mongodb
```javascript
mongod --dbpath /usr/local/mongodb/db
mongod --dbpath /usr/local/mongodb/db -f /etc/mongod.conf
```
## centOS安装
## centOS 安装
### 2.1 添加源
- sudo vi /etc/yum.repos.d/mongodb-org-4.0.repo
### 2.2 添加配置信息
- [mongodb-org-4.0]
@ -56,42 +66,49 @@ mongod --dbpath /usr/local/mongodb/db -f /etc/mongod.conf
- gpgcheck=1
- enabled=1
- gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc
### 2.3 安装
- sudo yum install -y mongodb-org
### 2.4 检验
- rpm -qa |grep mongodb
- rpm -ql mongodb-org-server
### 2.4 启动服务
- 建议加上 --auth 权限验证
- service mongod start
- systemctl start mongod.service
> 通过本地文件启动mongod -f /usr/local/mongodb/mongodb.conf
### 2.5 查看端口
- netstat -natp | grep 27017
### 2.6 查看是否成功
- ps -aux | grep mongod # 查看数据库的进程是否存在
### 2.7 开机自启
- chkconfig mongod on
- systemctl enable mongod.service
### 2.8 关闭
```javascript
pkill mongod
```
## 目录介绍
### 配置文件:
## 目录介绍
### 配置文件
/etc/mongod.conf
```json
port=27017 #端口
dbpath=/usr/local/mongodb/db #数据库文件存放目录
@ -103,18 +120,25 @@ journal=true #每次写入会记录一条操作日志
bind_ip=0.0.0.0 #可外部访问
auth=true #用户认证
```
###
### 数据库保存目录:
###
### 数据库保存目录
- /usr/local/mongodb/db
### 日志目录:
### 日志目录
- /usr/local/mongodb/logs/mongodb.log
> 如果需要修改数据目录和日志目录,只需修改 /etc/mongod.conf 中的 storage.dbPath 和 systemLog.path 即可。
##
##
## 卸载
### 4.1 关闭服务
```json
service mongod stop
@ -123,58 +147,70 @@ or
systemctl stop mongod.service
```
### 4.2 删除相关的包
```json
yum erase $(rpm -qa | grep mongodb-org)
```
### 4.3 删除目录和文件
```json
rm -r /var/log/mongodbrm -r /var/lib/mongo
```
### 4.4 彻底卸载
```json
sudo yum erase $(rpm -qa | grep mongodb-org) # 卸载
MongoDB sudo rm -r /var/log/mongodb # 删除日志文件
sudo rm -r /var/lib/mongo # 删除数据文件
```
## 常见命令
### 登录
```javascript
• mongo '数据库名' -u '用户名' -p '密码'
```
### 重启
```javascript
sudo service mongod restart
```
### 查看日志
```javascript
/var/log/mongo/mongod.log
```
### 命令行打开
```javascript
• sudo mongod --port 27017 --dbpath /data/db --bind_ip 0.0.0.0 --auth -f /etc/mongod.conf
```
###
### 用户权限
###
### 用户权限
#### 权限列表
- .1. 数据库用户角色read、readWrite;
- .2. 数据库管理角色dbAdmin、dbOwner、userAdmin
- .3. 集群管理角色clusterAdmin、clusterManager、clusterMonitor、hostManager
- .4. 备份恢复角色backup、restore
- .5. 所有数据库角色readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
- .6. 超级用户角色root
- // 这里还有几个角色间接或直接提供了系统超级用户的访问dbOwner 、userAdmin、userAdminAnyDatabase
- .7. 内部角色__system
- .3.  集群管理角色clusterAdmin、clusterManager、clusterMonitor、hostManager
- .4.  备份恢复角色backup、restore
- .5.  所有数据库角色readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
- .6.  超级用户角色root
- //  这里还有几个角色间接或直接提供了系统超级用户的访问dbOwner 、userAdmin、userAdminAnyDatabase
- .7.  内部角色:\_\_system
#### 新增用户
```javascript
// 切换根数据库
use admin
@ -183,21 +219,24 @@ db.createUser({user:"h5creator",pwd:"123456mjw",roles:[{role:"dbAdmin",db:"test"
// 登录用户
db.auth(${name}, ${pwd})
```
###
###
#### 修改用户权限
```javascript
db.grantRolesToUser("h5creator",[{role:"readWrite", db:"test"}])
db.grantRolesToUser('h5creator', [{ role: 'readWrite', db: 'test' }]);
```
#### 更新字段
```javascript
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>, // 如果不存在update的记录是否插入objNew,true为插入默认是false不插入。
multi: <boolean>, // 默认是false,只更新找到的第一条记录如果这个参数为true,就把按条件查出来多条记录全部更新
upsert: <boolean>, // 如果不存在update的记录是否插入objNew,true为插入默认是false不插入。
multi: <boolean>, // 默认是false,只更新找到的第一条记录如果这个参数为true,就把按条件查出来多条记录全部更新
writeConcern: <document> // 抛出异常的级别
}
)
@ -209,35 +248,41 @@ db.collection.update({},{"$unset":{"key":""}},{multi:true})
db.getCollection('article').update({}, {$set: {type:NumberInt('1')}}, {multi: true})
```
### 开发对外端口
#### 方案一
```javascript
• systemctl status firewalld # 查看防火墙状态
• firewall-cmd --zone=public --add-port=27017/tcp --permanent # mongodb默认端口号
• firewall-cmd --reload # 重新加载防火墙
• firewall-cmd --zone=public --query-port=27017/tcp # 查看端口号是否开放成功输出yes开放成功no则失败
```
#### 方案二
```javascript
• iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 27017 -j ACCEPT
```
## 偶遇问题
### 1) Failed to unlink socket file /tmp/mongodb-27017.sock Operation not permitted
- 解决方案:删除该文件
### 2) Unable to lock file: /var/lib/mongo/mongod.lock
- 解决方案:清空该文件内容
### 3) 无法持续运行在后台
- 解决方案mongod --fork --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/logs/mongodb2.log --logappend
4. 无法启动 mongodb
> 1. 进入 mongod 上一次启动的时候指定的 data 目录 --dbpath=/data/mongodb
>
删除掉该文件:
> rm /data/db/mongo.lock
> 2. 修复
>
./mongod --repair
4. 无法启动 mongodb
> 1. 进入 mongod 上一次启动的时候指定的 data 目录 --dbpath=/data/mongodb
>
> 删除掉该文件:
> rm /data/db/mongo.lock 2. 修复
>
> ./mongod --repair

View File

@ -1,44 +1,48 @@
---
nav:
title: 数据库
path: /db
title: 其它语言
path: /other
group:
title: 💊 redis
title: redis
order: 2
---
# 安装与配置
- redis-cli -h 101.132.156.228 -a  r-uf60a44b13666134:'123456Jzx'
## 在云服务器 ECS Linux 中安装 rinetd。
- wget [http://www.boutell.com/rinetd/http/rinetd.tar.gz&&tar](http://www.boutell.com/rinetd/http/rinetd.tar.gz&&tar) -xvf rinetd.tar.gz&&cd rinetd
- sed -i 's/65536/65535/g' rinetd.c (修改端口范围)
- mkdir /usr/man&&make&&make install
- > 注意rinetd 安装包下载地址不确保下载可用性,您可以自行搜索安装包进行下载使用。
## 2.打开配置文件 rinetd.conf。
## 在云服务器 ECS Linux 中安装 rinetd
- wget [http://www.boutell.com/rinetd/http/rinetd.tar.gz&&tar](http://www.boutell.com/rinetd/http/rinetd.tar.gz&&tar) -xvf rinetd.tar.gz&&cd rinetd
- sed -i 's/65536/65535/g' rinetd.c (修改端口范围)
- mkdir /usr/man&&make&&make install
- > 注意rinetd 安装包下载地址不确保下载可用性,您可以自行搜索安装包进行下载使用。
## 2.打开配置文件 rinetd.conf
- vi /etc/rinetd.conf
- vi /etc/rinetd.conf
> 注意:配置文件可能为 /usr/local/etc/redis.conf
## 3.在配置文件中输入如下内容:
## 3.在配置文件中输入如下内容
- 0.0.0.0 6379 Redis 的链接地址 6379
- logfile /var/log/rinetd.log
- 说明您可以使用 cat /etc/rinetd.conf命令来检验配置文件是否修改正确。
- ![](https://cdn.nlark.com/yuque/0/2020/jpeg/195884/1597937067295-ee517e33-e17f-4d26-bfce-f262d66758bd.jpeg#height=47&id=nkYr8&originHeight=47&originWidth=647&originalType=binary&ratio=1&size=0&status=done&style=none&width=647)
-
- 0.0.0.0 6379 Redis 的链接地址 6379
- logfile /var/log/rinetd.log
- 说明:您可以使用  cat /etc/rinetd.conf 命令来检验配置文件是否修改正确。
- ![](https://cdn.nlark.com/yuque/0/2020/jpeg/195884/1597937067295-ee517e33-e17f-4d26-bfce-f262d66758bd.jpeg#height=47&id=nkYr8&originHeight=47&originWidth=647&originalType=binary&ratio=1&size=0&status=done&style=none&width=647)
-
## 4.执行如下命令启动 rinetd
## 4.执行如下命令启动 rinetd
- rinetd
- 注意
- 您可以通过 echo rinetd >>/etc/rc.local 将 rinetd 设置为自启动。
- 若遇到绑定报错可以执行 pkill rinetd 结束进程再执行 rinetd启动进程 rinetd。
- rinetd 正常启动后, 执行netstat -anp | grep 6379 确认服务是否正常运行。
- ![](https://cdn.nlark.com/yuque/0/2020/jpeg/195884/1597937067341-f158e903-fe66-4375-95ff-cfeda4b2a232.jpeg#height=61&id=jafrb&originHeight=61&originWidth=980&originalType=binary&ratio=1&size=0&status=done&style=none&width=980)
## 5.在本地进行验证测试。
- rinetd
- 注意
- 您可以通过  echo rinetd >>/etc/rc.local  将 rinetd 设置为自启动。
- 若遇到绑定报错,可以执行  pkill rinetd  结束进程,再执行  rinetd 启动进程 rinetd。
- rinetd 正常启动后, 执行 netstat -anp | grep 6379  确认服务是否正常运行。
- ![](https://cdn.nlark.com/yuque/0/2020/jpeg/195884/1597937067341-f158e903-fe66-4375-95ff-cfeda4b2a232.jpeg#height=61&id=jafrb&originHeight=61&originWidth=980&originalType=binary&ratio=1&size=0&status=done&style=none&width=980)
- 您可以在本地通过 redis-cli 连接 ECS Linux 服务器后进行登录验证,比如安装了 rinetd 的服务器的 IP 是 1.1.1.1即redis-cli -h 1.1.1.1 -a Redis的实例ID:Redis密码。或者通过 telent 连接 ECS Linux 服务器后进行操作验证。假设 ECS Linux 服务器的 IP 是 1.1.1.1 telnet 1.1.1.1 6379。
- 连接上 ECS Linux 服务器后,输入连接 Redis 的密码auth Redis的连接密码。
- 进行数据写入及查询验证。
## 5.在本地进行验证测试
- 您可以在本地通过 redis-cli 连接 ECS Linux 服务器后进行登录验证,比如安装了 rinetd 的服务器的 IP 是 1.1.1.1,即 redis-cli -h 1.1.1.1 -a Redis 的实例 ID:Redis 密码。或者通过 telent 连接 ECS Linux 服务器后进行操作验证。假设 ECS Linux 服务器的 IP 是 1.1.1.1,即  telnet 1.1.1.1 6379。
- 连接上 ECS Linux 服务器后,输入连接 Redis 的密码auth Redis 的连接密码。
- 进行数据写入及查询验证。

View File

@ -1,9 +1,9 @@
---
nav:
title: 数据库
path: /db
title: 其它语言
path: /other
group:
title: 💊 redis
title: redis
order: 1
---
@ -11,13 +11,10 @@ group:
## 下载
[官方网站](https://redis.io/)
## 安装
```javascript
解压
tar zxvf redis-4.0.8.tar.gz
@ -31,18 +28,16 @@ sudo make test
sudo make install
```
## 启动
```javascript
redis-server
redis - server;
```
## 配置
### 新建目录
```
sudo mkdir redis-4.0.8/bin
sudo mkdir redis-4.0.8/etc
@ -59,8 +54,7 @@ cp src/redis-cli bin
cp src/redis-server bin
```
## 修改redis.conf
## 修改 redis.conf
```t
#修改为守护模式
@ -101,28 +95,30 @@ appendonly no
appendfsync everysec
```
## 启动服务
### 启动
> ./bin/redis-server etc/redis.conf
### 查看日志
> tail -f log-redis.log
### OK
> ./bin/redis-cli
## 基本命令
### 查看所有数据
> keys *
> keys \*
### 插入键值对
> set a b
### 查看数据
> get a
> get a

410
docs/fc/canvas/base.md Normal file
View File

@ -0,0 +1,410 @@
---
nav:
title: FC
path: /funny
group:
order: 2
path: /canvas
title: Canvas
---
# 基础形状
### 矩形
```jsx
import React, { useRef, useEffect } from 'react';
export default () => {
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');
// 实心矩形
ctx.fillStyle = 'red';
ctx.fillRect(25, 25, 50, 50);
// 空心矩形
ctx.lineWidth = 1;
ctx.fillStyle = 'black';
ctx.strokeRect(75, 75, 50, 50);
// 清除区域
ctx.clearRect(50, 50, 50, 50);
}
useEffect(() => {
fillRect();
}, []);
return (
<div>
<canvas ref={canvasRef} width="200" height="200" />
</div>
);
};
```
### 三角形
```jsx
import React, { useRef, useEffect } from 'react';
export default () => {
const canvasRef = useRef();
/**
* 绘制三角形
*/
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, 155);
ctx.lineTo(25, 90);
ctx.lineTo(155, 90);
ctx.closePath();
ctx.stroke();
}
useEffect(() => {
tri();
}, []);
return (
<div>
<canvas ref={canvasRef} width="200" height="200" />
</div>
);
};
```
### 绘制圆弧
```jsx
import React, { useRef, useEffect } from 'react';
export default () => {
const canvasRef = useRef();
// 绘制圆弧
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;
ctx.beginPath();
ctx.arc(x, y, r, startAngle, endAngle, false);
ctx.fill();
ctx.beginPath();
ctx.arc(x, y, r, startAngle, endAngle, true);
ctx.stroke();
}
useEffect(() => {
drawArc();
}, []);
return (
<div>
<canvas ref={canvasRef} width="200" height="200" />
</div>
);
};
```
### 绘制贝塞尔曲线
```jsx
import React, { useRef, useEffect } from 'react';
export default () => {
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');
// 二次贝塞尔曲线
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);
}
useEffect(() => {
bezier();
}, []);
return (
<div>
<canvas ref={canvasRef} width="200" height="200" />
</div>
);
};
```
### 绘制三次贝塞尔曲线
```jsx
import React, { useRef, useEffect } from 'react';
export default () => {
const canvasRef = useRef();
/**
* 绘制三次贝塞尔曲线
*
*/
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.font = '18px bold 黑体';
ctx.fillStyle = 'black';
ctx.fillText('爱心', 120, 160);
}
useEffect(() => {
beziers();
}, []);
return (
<div>
<canvas ref={canvasRef} width="200" height="200" />
</div>
);
};
```
### 绘制笑脸
```jsx
import React, { useRef, useEffect } from 'react';
export default () => {
const canvasRef = useRef();
/**
* 绘制笑脸
*
*/
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.font = '18px bold 黑体';
ctx.fillStyle = 'black';
ctx.fillText('笑脸', 120, 160);
}
useEffect(() => {
fillSmile();
}, []);
return (
<div>
<canvas ref={canvasRef} width="200" height="200" />
</div>
);
};
```
### 吃豆人
```jsx
import React, { useRef, useEffect } from 'react';
export default () => {
const canvasRef = useRef();
/**
* 吃豆人
*
*/
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);
// 绘制吃豆人
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();
// 绘制小点
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();
}
}
useEffect(() => {
bean();
}, []);
return (
<div>
<canvas ref={canvasRef} width="200" height="200" />
</div>
);
};
```
```js
```

302
docs/fc/canvas/color.md Normal file
View File

@ -0,0 +1,302 @@
---
nav:
title: FC
path: /funny
group:
order: 2
title: Canvas
---
# 颜色
```jsx
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();
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);
}
}
}
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 _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>
);
};
```
```js
```

View File

@ -3,12 +3,12 @@ nav:
title: FC
path: /funny
group:
title: canvas
order: 4
order: 99
path: /canvas
title: Canvas
---
# canvas
# 案例欣赏
### 粒子背景
@ -28,7 +28,7 @@ group:
### 大转盘doing
```jsx
```js
import React, { useRef, useEffect } from 'react';
class Global {
@ -67,7 +67,7 @@ class Global {
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
@ -85,7 +85,7 @@ class Global {
*/
drawText(context, t, x, y, w, lineHeight = 20){
let chr = t.split(''),
temp = '',
temp = '',
row = [];
for (let a = 0; a < chr.length; a++){
@ -109,11 +109,11 @@ class Global {
/**
* 定义圆角矩形的方法
* @param {Obj} context
* @param {Num} cornerX
* @param {Num} cornerY
* @param {Num} width
* @param {Num} height
* @param {Num} cornerRadius
* @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);
@ -189,7 +189,7 @@ class RouletteWheel extends Global{
ctx.closePath(); //绘制路径
ctx.fill();
}
ctx.beginPath(); //开始绘制路径
ctx.moveTo(250, 250); //将当前位置移动到新的目标点
ctx.arc(250, 250, 250, Math.PI / 2, Math.PI);
@ -240,8 +240,7 @@ export default () => {
### 星空
```jsx
```js
import React, { useRef, useEffect } from 'react';
/**
@ -250,7 +249,7 @@ import React, { useRef, useEffect } from 'react';
class NightSky {
constructor(opt) {
this.opt = {
width: 500,
width: 500,
height: 500,
num: 120,
canvas: null,
@ -359,8 +358,7 @@ export default () => {
### 移动doing
```jsx
```js
import React, { useRef, useEffect } from 'react';
class Move {
@ -373,7 +371,7 @@ class Move {
para: {
num: 100,
color: false, // 颜色 如果是false 则是随机渐变颜色
r: 0.9, // 圆每次增加的半径
r: 0.9, // 圆每次增加的半径
o: 0.09, // 判断圆消失的条件,数值越大,消失的越快
},
...opt
@ -403,7 +401,7 @@ class Move {
opt.round_arr.push({
mouseX,
mouseY,
r: opt.option.para.r, // 设置半径每次增大的数值
r: opt.option.para.r, // 设置半径每次增大的数值
o: 1, // 判断圆消失的条件,数值越大,消失得越快
})
tempSum = 0
@ -424,11 +422,11 @@ class Move {
}
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);
@ -436,7 +434,7 @@ class Move {
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--;
@ -469,8 +467,7 @@ export default () => {
### 棒棒糖
```jsx
```js
import React, { useRef, useEffect } from 'react';
class Lollipop {
@ -500,7 +497,7 @@ class Lollipop {
/**
* 画圆
* @param {*} ctx
* @param {*} ctx
*/
_drawCircle(ctx) {
ctx.beginPath()
@ -514,7 +511,7 @@ class Lollipop {
/**
* 棍子
* @param {*} ctx
* @param {*} ctx
*/
_drawStick(ctx) {
ctx.beginPath()

View File

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 166 KiB

View File

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 150 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

75
docs/fc/canvas/font.md Normal file
View File

@ -0,0 +1,75 @@
---
nav:
title: FC
path: /funny
group:
order: 3
title: Canvas
---
# 字体
```jsx
import React, { useRef, useEffect } from 'react';
export default () => {
const canvasRef = useRef();
const canvasRef2 = useRef();
const canvasRef3 = useRef();
// 填充文本
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.font = '40px serif';
ctx.fillText('1. 你是个智障!', 50, 50);
}
function demo2() {
let canvas = canvasRef2.current;
let ctx = canvas.getContext('2d');
ctx.font = '48px serif';
ctx.strokeText('2. hello world', 10, 50);
}
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);
}
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>
);
};
```
```ts
window.onload = function () {
demo1();
demo2();
demo3();
};
```

View File

@ -0,0 +1,12 @@
import './index.less';
export default function () {
return (
<div className="cont">
<div className="card">
{/* <div className={styles.card_heart}>&#x2665;</div> */}
{/* <h1 className={styles.card_title}>hello! motherfucker!</h1> */}
</div>
</div>
);
}

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 684 KiB

After

Width:  |  Height:  |  Size: 684 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

Before

Width:  |  Height:  |  Size: 329 KiB

After

Width:  |  Height:  |  Size: 329 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 329 KiB

After

Width:  |  Height:  |  Size: 329 KiB

View File

@ -3,13 +3,12 @@ nav:
title: FC
path: /funny
group:
title: css
order: 3
path: /css
title: CSS
---
# css
# 案例赏析
### 心跳卡片

View File

@ -3,12 +3,12 @@ nav:
title: 前端
path: /fea
group:
title: 💊 css
order: 1
path: /css
title: CSS
---
## 基础介绍
# 基础介绍
## 布局

View File

@ -3,12 +3,12 @@ nav:
title: 前端
path: /fea
group:
title: 💊 css
order: 3
order: 2
path: /css
title: CSS
---
## SASS 语法
# SASS 语法
### if 语句
@ -178,21 +178,21 @@ index(1px solid red, 1px)
```css
$map: (
$key1: $value1;
$key2: $value2;
$key1: $value1;
$key2: $value2;
)
$theme-color: (
default: (
bgcolor: #fff;
text-color: #444;
link-color: #39f;
),
primary: (
bgcolor: #000;
text-color: #fff;
link-color: #93f;
)
default: (
bgcolor: #fff;
text-color: #444;
link-color: #39f;
),
primary: (
bgcolor: #000;
text-color: #fff;
link-color: #93f;
)
)
```

38
docs/fc/index.md Normal file
View File

@ -0,0 +1,38 @@
---
nav:
title: FC
path: /funny
group:
order: 1
path: /code
---
# 说明
FC(funny code),记录一些有意思的酷炫 code demo.
## 迪士尼动画 12 原则
1. 挤压与拉伸
2. 预备动作
3. 表情与呈像方式
4. 逐帧画法与关键帧画法
5. 动作的惯性跟随和重叠
6. 慢入与慢出
7. 弧形运动轨迹
8. 次要动作
9. 节奏
10. 夸张
11. 熟练的手绘技法
12. 吸引力
## 提升动画性能技巧
简单页面渲染顺序htmlTree - cssTree - jsTree - layout(重排) - paint(重绘) - composite(组合)
从上面的流程可以发现,重排一定会触发重绘,但是重绘不一定会触发重排
- translate 替换为 left\top\right\bottom
- scale 替换为 width\height
- opacity 替换为 display\visibility
- 设置 translate3D, 打开 GPU 加速

View File

@ -1,10 +1,10 @@
---
nav:
title: 前端
path: /fea
title: FC
path: /funny
group:
title: 💊 webGL
order: 100
title: webGL
order: 1
---
# 常见问题

22
docs/fc/webgl/demo.md Normal file
View File

@ -0,0 +1,22 @@
---
nav:
title: FC
path: /funny
group:
title: webGL
order: 3
---
# 案例赏析
## 创建基础元素
<code src="./demo/base.tsx" ></code>
## 创建一个地球的场景
<code src="./demo/earth.tsx" ></code>
## 地图
<code src="./demo/map-gl.tsx" ></code>

View File

@ -9,7 +9,7 @@ export default () => {
const ref = useRef(null)
useEffect(() => {
ref.current && init(ref.current)
ref.current && init(ref.current)
}, [])
const init = (dom: HTMLElement) => {
@ -18,11 +18,11 @@ export default () => {
// 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 cameraHelper = new THREE.CameraHelper(camera);
scene.add(cameraHelper);
// 渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true, canvas: dom });
const renderer = new THREE.WebGLRenderer({ antialias: true, canvas: dom });
// renderer.setSize(window.innerWidth, window.innerHeight);
THREE.Cache.enabled = true;
@ -33,10 +33,10 @@ export default () => {
cube.position.y = -1
scene.add(cube);
const edges = new THREE.EdgesGeometry( geometry );
const line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( { color: 0xffffff } ) );
const edges = new THREE.EdgesGeometry(geometry);
const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0xffffff }));
line.position.y = -1
scene.add( line );
scene.add(line);
// 创建一个胶囊
const capGeometry = new THREE.CapsuleGeometry(1, 1, 10, 20)
@ -91,12 +91,12 @@ export default () => {
// 车削缓冲几何体()
const points = [];
for ( let i = 0; i < 10; i ++ ) {
points.push( new THREE.Vector2( Math.sin( i * 0.2 ) * 10 + 5, ( i - 5 ) * 2 ) );
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 );
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
@ -104,26 +104,26 @@ export default () => {
// 多面缓冲几何体,自定义坐标
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,
-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
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 );
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 );
scene.add(poly);
// 创建一个圆环(内半径、外半径、圆环的分段)
const ringGeometry = new THREE.RingGeometry(1, 3)
@ -148,7 +148,7 @@ export default () => {
heartShape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);
const shapeGeo = new THREE.ExtrudeGeometry( heartShape, {
const shapeGeo = new THREE.ExtrudeGeometry(heartShape, {
steps: 2,
depth: 1,
bevelEnabled: true,
@ -156,13 +156,13 @@ export default () => {
bevelSize: 1,
bevelSegments: 5
});
const shapeMat = new THREE.MeshLambertMaterial( { color: 0x00ff00 } );
const heart = new THREE.Mesh( shapeGeo, shapeMat ) ;
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 );
scene.add(heart);
// 球体
const ballGeometry = new THREE.SphereGeometry(1, 19)
@ -193,13 +193,13 @@ export default () => {
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 );
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 );
scene.add(knot);
//创建线段
const lineMaterial = new THREE.LineBasicMaterial({ color: 0x0000ff })
@ -208,14 +208,14 @@ export default () => {
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 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!', {
fontLoader.load('https://fancy-content-test.oss-cn-beijing.aliyuncs.com/helvetiker_regular.typeface.json', function (font) {
const textGeometry = new TextGeometry('XXX, I Love U!', {
font: font,
size: 6,
depth: 1,
@ -228,40 +228,40 @@ export default () => {
});
textGeometry.computeBoundingBox();
const centerOffset = - 0.5 * ( textGeometry.boundingBox.max.x - textGeometry.boundingBox.min.x );
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
new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }), // front
new THREE.MeshPhongMaterial({ color: 0xf00fff }) // side
];
textMesh = new THREE.Mesh( textGeometry, materials );
textMesh = new THREE.Mesh(textGeometry, materials);
textMesh.position.z = 10;
textMesh.position.x = centerOffset;
scene.add( textMesh )
} );
scene.add(textMesh)
});
// --------------------- 光线 ------------------------------
// 半球光
const fillLight = new THREE.HemisphereLight( 0x8dc1de, 0x00668d, 1.5 );
fillLight.position.set( 0, 1, 1 );
scene.add( fillLight );
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 );
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.top = 30;
directionalLight.shadow.camera.bottom = - 30;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
@ -270,7 +270,7 @@ export default () => {
scene.add(directionalLight);
const controls = new OrbitControls( camera, renderer.domElement );
const controls = new OrbitControls(camera, renderer.domElement);
controls.autoRotate = true
function render() {

View File

@ -1,4 +1,4 @@
import React, { useRef, useEffect } from 'react';
import React, { useRef, useEffect, useState } from 'react';
import * as THREE from 'three';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
import { resizeRendererToDisplaySize, addControls } from '../utils'
@ -35,10 +35,16 @@ class AxisGridHelper {
export default () => {
const ref = useRef(null)
const controlRef = useRef(null)
useEffect(() => {
ref.current && init(ref.current)
})
return () => {
console.log('controlRef.current', controlRef.current)
controlRef.current?.destroy()
controlRef.current = null
}
}, [ref])
const init = (dom: any) => {
@ -47,7 +53,9 @@ export default () => {
camera.position.set(0, 50, 0)
camera.up.set(0, 0, 1)
camera.lookAt(0, 0, 0)
const gui = new GUI();
const gui = new GUI({ container: document.getElementById( 'earth-cont' ) });
controlRef.current = gui
{
const light = new THREE.PointLight(0xffffff, 500)
@ -117,7 +125,7 @@ export default () => {
addControls(camera, renderer);
function render(time) {
time *= 0.001
time *= 0.0001
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
@ -137,6 +145,8 @@ export default () => {
return (
<canvas ref={ref} id="earth" style={{ width: '100%', height: '500px'}} />
<div id="earth-cont">
<canvas ref={ref} id="earth" style={{ width: '100%', height: '500px'}} />
</div>
)
}

View File

@ -0,0 +1,77 @@
import React, { useCallback, useEffect, useState } from 'react';
import Map, { MapRef } from 'react-map-gl';
import DrawControl from '../utils/drowControl';
const TOKEN = 'pk.eyJ1IjoiZGluZ2xpMTIzIiwiYSI6ImNra204ODhjczBobTgyeHJ6MmJpZHMxNWgifQ.NbKrXh_hb2gvjr5CEMDnyQ'
export const MapConfig = {
mapboxAccessToken: TOKEN,
maxZoom: 18,
minZoom: 4,
style: { height: 600 },
dragRotate: false,
initialViewState: {
longitude: -91.874,
latitude: 42.76,
zoom: 12
},
mapStyle: 'mapbox://styles/mapbox/satellite-v9'
};
export default () => {
const [features, setFeatures] = useState({});
const drawRef = React.useRef<MapboxDraw>();
const mapRef = React.useRef<MapRef>();
const [drawMode, setDrawMode] = React.useState<string>("draw_rect");
const onUpdate = useCallback(e => {
setFeatures(currFeatures => {
const newFeatures = { ...currFeatures };
for (const f of e.features) {
newFeatures[f.id] = f;
}
console.log(newFeatures)
return newFeatures;
});
}, []);
const onDelete = useCallback(e => {
setFeatures(currFeatures => {
const newFeatures = { ...currFeatures };
for (const f of e.features) {
delete newFeatures[f.id];
}
console.log(newFeatures)
return newFeatures;
});
}, []);
const changeModeTo = (mode: string) => {
drawRef.current?.changeMode(mode as string);
setDrawMode(mode);
};
return (
<>
<button
onClick={() => changeModeTo('drag_circle')}>
Change to drag_circle
</button>
<Map
ref={mapRef}
{...MapConfig}
>
<DrawControl
mapRef={mapRef}
ref={drawRef}
position="top-left"
defaultMode={drawMode}
onCreate={onUpdate}
onUpdate={onUpdate}
onDelete={onDelete}
/>
</Map>
</>
)
}

View File

@ -0,0 +1,107 @@
import * as React from 'react';
import DeckGL from '@deck.gl/react';
import { EditableGeoJsonLayer } from '@nebula.gl/layers';
import { Toolbox } from '@nebula.gl/editor';
import { ViewMode } from '@nebula.gl/edit-modes';
import { StaticMap } from 'react-map-gl';
const MAPBOX_ACCESS_TOKEN =
'pk.eyJ1IjoiZGluZ2xpMTIzIiwiYSI6ImNra204ODhjczBobTgyeHJ6MmJpZHMxNWgifQ.NbKrXh_hb2gvjr5CEMDnyQ';
const initialViewState = {
longitude: -122.43,
latitude: 37.775,
zoom: 12,
};
export function Example() {
const [geoJson, setGeoJson] = React.useState({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'Polygon',
coordinates: [
[
[-122.46212548792364, 37.79026033616934],
[-122.48435831844807, 37.77160302698496],
[-122.45884849905971, 37.74414218845571],
[-122.42863676726826, 37.76266965836386],
[-122.46212548792364, 37.79026033616934],
],
],
},
},
{
type: 'Feature',
properties: {},
geometry: {
type: 'Polygon',
coordinates: [
[
[-122.4136573004723, 37.78826678755718],
[-122.44875601708893, 37.782670574261324],
[-122.43793598592286, 37.74322062447909],
[-122.40836932539945, 37.75125290412125],
[-122.4136573004723, 37.78826678755718],
],
],
},
},
],
});
const [selectedFeatureIndexes, setSelectedFeatureIndexes] = React.useState([0]);
const [mode, setMode] = React.useState(() => ViewMode);
const [modeConfig, setModeConfig] = React.useState({});
const layer = new EditableGeoJsonLayer({
data: geoJson,
mode,
modeConfig,
selectedFeatureIndexes,
onEdit: ({ updatedData }) => {
setGeoJson(updatedData);
},
});
return (
<>
<DeckGL
initialViewState={initialViewState}
controller={{
doubleClickZoom: false,
}}
layers={[layer]}
getCursor={layer.getCursor.bind(layer)}
onClick={(info) => {
if (mode === ViewMode)
if (info) {
setSelectedFeatureIndexes([info.index]);
} else {
setSelectedFeatureIndexes([]);
}
}}
>
<StaticMap mapboxApiAccessToken={MAPBOX_ACCESS_TOKEN} />
</DeckGL>
<Toolbox
geoJson={geoJson}
mode={mode}
modeConfig={modeConfig}
onSetMode={setMode}
onSetModeConfig={setModeConfig}
onImport={(imported) =>
setGeoJson({
...geoJson,
features: [...geoJson.features, ...imported.features],
})
}
onSetGeoJson={setGeoJson}
/>
</>
);
}

View File

@ -1,19 +1,13 @@
---
nav:
title: 前端
path: /fea
title: FC
path: /funny
group:
title: 💊 webGL
order: 1
title: webGL
order: 10
---
# 基础
## 介绍
:::info{title=记录日志}
本文档从 2023 年 10 月 20 开始,主要用来记录 threejs 的学习过程和踩坑记录,方便复盘与总结。
:::
# 介绍
### 基本对象

View File

@ -0,0 +1,24 @@
const doubleClickZoom = {
enable(ctx) {
setTimeout(() => {
if (
!ctx.map ||
!ctx.map.doubleClickZoom ||
!ctx._ctx ||
!ctx._ctx.store ||
!ctx._ctx.store.getInitialConfigValue
)
return;
if (!ctx._ctx.store.getInitialConfigValue('doubleClickZoom')) return;
ctx.map.doubleClickZoom.enable();
}, 0);
},
disable(ctx) {
setTimeout(() => {
if (!ctx.map || !ctx.map.doubleClickZoom) return;
ctx.map.doubleClickZoom.disable();
}, 0);
},
};
export default doubleClickZoom;

View File

@ -0,0 +1,69 @@
import MapboxDraw from '@mapbox/mapbox-gl-draw';
import doubleClickZoom from './doubleClickZoom';
import * as turf from '@turf/turf';
const { circle, distance, helpers: turfHelpers } = turf;
const drawCircleMode = { ...MapboxDraw.modes.draw_polygon };
drawCircleMode.onSetup = function () {
const polygon = this.newFeature({
type: 'Feature',
properties: {
isCircle: true,
center: [],
},
geometry: {
type: 'Polygon',
coordinates: [[]],
},
});
this.addFeature(polygon);
this.clearSelectedFeatures();
doubleClickZoom.disable(this);
// dragPan.disable(this);
this.updateUIClasses({ mouse: 'add' });
this.activateUIButton('Polygon');
this.setActionableState({
trash: true,
});
return {
polygon,
currentVertexPosition: 0,
};
};
drawCircleMode.onClick = drawCircleMode.onTap = function (state, e) {
const currentCenter = state.polygon.properties.center;
if (currentCenter.length === 0) {
// dragPan.disable(this)
state.polygon.properties.center = [e.lngLat.lng, e.lngLat.lat];
} else {
// dragPan.enable(this);
return this.changeMode('simple_select', { featureIds: [state.polygon.id] });
}
};
drawCircleMode.onDrag = drawCircleMode.onMouseMove = function (state, e) {
const center = state.polygon.properties.center;
if (center.length > 0) {
const distanceInKm = distance(
turfHelpers.point(center),
turfHelpers.point([e.lngLat.lng, e.lngLat.lat]),
{
units: 'kilometers',
}
);
const circleFeature = circle(center, distanceInKm);
state.polygon.incomingCoords(circleFeature.geometry.coordinates);
state.polygon.properties.radiusInKm = distanceInKm;
state.polygon.properties.lastClickCoord = [e.lngLat.lng, e.lngLat.lat];
}
};
//它决定当前 Drew 数据存储中的哪些特性将在地图上呈现。
//所有传递给“显示”的特性都将被渲染,因此可以为每个内部特性传递多个显示特性。
//有关如何制作显示特性的建议,请参阅‘ styling-pullin API.md
drawCircleMode.toDisplayFeatures = function (state, geojson, display) {
const isActivePolygon = geojson.properties.id === state.polygon.id;
geojson.properties.active = isActivePolygon ? 'true' : 'false';
display(geojson);
};
export default drawCircleMode;

View File

@ -0,0 +1,131 @@
import MapboxDraw from '@mapbox/mapbox-gl-draw';
import createSupplementaryPoints from '@mapbox/mapbox-gl-draw';
import moveFeatures from '@mapbox/mapbox-gl-draw';
import constrainFeatureMovement from '@mapbox/mapbox-gl-draw';
import createVertex from '@mapbox/mapbox-gl-draw';
import * as turf from '@turf/turf';
const { constants } = MapboxDraw;
const { circle, distance, helpers: turfHelpers } = turf;
function createSupplementaryPointsForCircle(geojson) {
const { properties, geometry } = geojson;
if (!properties.user_isCircle) return null;
const supplementaryPoints = [];
const vertices = geometry.coordinates[0].slice(0, -1);
for (let index = 0; index < vertices.length; index += Math.round(vertices.length / 4)) {
supplementaryPoints.push(createVertex(properties.id, vertices[index], `0.${index}`, false));
}
return supplementaryPoints;
}
const drawDirectMode = { ...MapboxDraw.modes.direct_select };
drawDirectMode.dragFeature = function (state, e, delta) {
moveFeatures(this.getSelected(), delta);
this.getSelected()
.filter((feature) => feature.properties.isCircle)
.map((circle) => circle.properties.center)
.forEach((center) => {
center[0] += delta.lng;
center[1] += delta.lat;
});
state.dragMoveLocation = e.lngLat;
};
drawDirectMode.dragVertex = function (state, e, delta) {
//圆处理
if (state.feature.properties.isCircle) {
const center = state.feature.properties.center;
const movedVertex = [e.lngLat.lng, e.lngLat.lat];
const radius = distance(turfHelpers.point(center), turfHelpers.point(movedVertex), {
units: 'kilometers',
});
const circleFeature = circle(center, radius);
state.feature.incomingCoords(circleFeature.geometry.coordinates);
state.feature.properties.radiusInKm = radius;
return;
}
//矩形处理
if (state.feature.properties.isRect) {
state.selectedCoordPaths.forEach((coordPath) => {
const selectCoord = state.feature.getCoordinate(coordPath);
//更新边缘2点
const [featureIndex, coordIndex] = coordPath.split('.');
const coordinates = state.feature.getCoordinates()[featureIndex];
//对立点判断
const coordPosMap = {
1: '3',
2: '0',
3: '1',
0: '2',
};
const mapCoord = state.feature.getCoordinate(`${featureIndex}.${coordPosMap[coordIndex]}`);
//如果对立点和坐标xy 一致 则返回
if (mapCoord[0] === e.lngLat.lng || mapCoord[1] === e.lngLat.lat) {
return;
}
for (let i = 0; i < 4; i++) {
const coord = coordinates[i];
if (coordIndex == i) {
continue;
}
if (coord[0] === selectCoord[0]) {
state.feature.updateCoordinate(`${featureIndex}.${i}`, e.lngLat.lng, coord[1]);
continue;
}
if (coord[1] === selectCoord[1]) {
state.feature.updateCoordinate(`${featureIndex}.${i}`, coord[0], e.lngLat.lat);
continue;
}
}
//更新拖拽的点
state.feature.updateCoordinate(coordPath, e.lngLat.lng, e.lngLat.lat);
});
return;
}
//其他走回默认
const selectedCoords = state.selectedCoordPaths.map((coordPath) =>
state.feature.getCoordinate(coordPath)
);
const selectedCoordPoints = selectedCoords.map((coords) => ({
type: constants.geojsonTypes.FEATURE,
properties: {},
geometry: {
type: constants.geojsonTypes.POINT,
coordinates: coords,
},
}));
const constrainedDelta = constrainFeatureMovement(selectedCoordPoints, delta);
for (let i = 0; i < selectedCoords.length; i++) {
const coord = selectedCoords[i];
state.feature.updateCoordinate(
state.selectedCoordPaths[i],
coord[0] + constrainedDelta.lng,
coord[1] + constrainedDelta.lat
);
}
};
drawDirectMode.toDisplayFeatures = function (state, geojson, push) {
if (state.featureId === geojson.properties.id) {
geojson.properties.active = constants.activeStates.ACTIVE;
push(geojson);
const supplementaryPoints = geojson.properties.user_isCircle
? createSupplementaryPointsForCircle(geojson)
: createSupplementaryPoints(geojson, {
map: this.map,
midpoints: true,
selectedPaths: state.selectedCoordPaths,
});
supplementaryPoints.forEach(push);
} else {
geojson.properties.active = constants.activeStates.INACTIVE;
push(geojson);
}
this.fireActionable(state);
};
export default drawDirectMode;

View File

@ -0,0 +1,165 @@
import MapboxDraw from '@mapbox/mapbox-gl-draw';
import doubleClickZoom from './doubleClickZoom';
const { constants, lib } = MapboxDraw;
const drawLineSelectMode = {
//当模式启动时,这个函数将被调用。
//draw.changeMode(drawLineSelectMode,params}); 切换模式时params = opts
//返回的值应该是一个对象,并将传递给所有其他生命周期函数
onSetup: function (opts) {
const featureId = opts.featureId;
let line;
let currentVertexPosition = 0;
let direction = 'forward';
if (featureId) {
line = this.getFeature(featureId);
if (!line) {
throw new Error('Could not find a feature with the provided featureId');
}
let from = opts.from;
if (from && from.type === 'Feature' && from.geometry && from.geometry.type === 'Point') {
from = from.geometry;
}
if (from && from.type === 'Point' && from.coordinates && from.coordinates.length === 2) {
from = from.coordinates;
}
if (!from || !Array.isArray(from)) {
throw new Error(
'Please use the `from` property to indicate which point to continue the line from'
);
}
const lastCoord = line.coordinates.length - 1;
if (
line.coordinates[lastCoord][0] === from[0] &&
line.coordinates[lastCoord][1] === from[1]
) {
currentVertexPosition = lastCoord + 1;
line.addCoordinate(currentVertexPosition, ...line.coordinates[lastCoord]);
} else if (line.coordinates[0][0] === from[0] && line.coordinates[0][1] === from[1]) {
direction = 'backwards';
currentVertexPosition = 0;
line.addCoordinate(currentVertexPosition, ...line.coordinates[0]);
} else {
throw new Error(
'`from` should match the point at either the start or the end of the provided LineString'
);
}
} else {
line = this.newFeature({
type: constants.geojsonTypes.FEATURE,
properties: {},
geometry: {
type: constants.geojsonTypes.LINE_STRING,
coordinates: [],
},
});
currentVertexPosition = 0;
this.addFeature(line);
}
this.clearSelectedFeatures();
doubleClickZoom.disable(this);
this.updateUIClasses({ mouse: 'add' }); //"+"
this.setActionableState({
//添加地图事件'draw.actionable'
trash: true,
combineFeatures: false,
uncombineFeatures: false,
});
return {
line,
currentVertexPosition,
direction,
};
},
clickAnywhere: function (state, e) {
if (
(state.currentVertexPosition > 0 &&
lib.isEventAtCoordinates(e, state.line.coordinates[state.currentVertexPosition - 1])) ||
(state.direction === 'backwards' &&
lib.isEventAtCoordinates(e, state.line.coordinates[state.currentVertexPosition + 1]))
) {
return this.changeMode(constants.modes.SIMPLE_SELECT, {
featureIds: [state.line.id],
});
}
this.updateUIClasses({ mouse: constants.cursors.ADD });
state.line.updateCoordinate(state.currentVertexPosition, e.lngLat.lng, e.lngLat.lat);
if (state.direction === 'forward') {
state.currentVertexPosition++;
state.line.updateCoordinate(state.currentVertexPosition, e.lngLat.lng, e.lngLat.lat);
} else {
state.line.addCoordinate(0, e.lngLat.lng, e.lngLat.lat);
}
},
clickOnVertex: function (state) {
//点击在已经有点上
state.line.properties.name = 'line_select';
return this.changeMode(constants.modes.SIMPLE_SELECT, { featureIds: [state.line.id] });
},
onMouseMove: function (state, e) {
state.line.updateCoordinate(state.currentVertexPosition, e.lngLat.lng, e.lngLat.lat);
if (lib.CommonSelectors.isVertex(e)) {
//再次将鼠标放在之前已经有的点上,改变鼠标样式
this.updateUIClasses({ mouse: constants.cursors.POINTER });
}
},
onClick: function (state, e) {
//再次将鼠标放在之前已经有的点上
if (lib.CommonSelectors.isVertex(e)) return this.clickOnVertex(state, e);
this.clickAnywhere(state, e);
},
onKeyUp: function (state, e) {
if (lib.CommonSelectors.isEnterKey(e)) {
this.changeMode(constants.modes.SIMPLE_SELECT, { featureIds: [state.line.id] });
} else if (lib.CommonSelectors.isEscapeKey(e)) {
this.deleteFeature([state.line.id], { silent: true });
this.changeMode(constants.modes.SIMPLE_SELECT);
}
},
onStop: function (state) {
doubleClickZoom.enable(this);
if (this.getFeature(state.line.id) === undefined) return;
state.line.removeCoordinate(`${state.currentVertexPosition}`); //双击停止时,最后两个点位是一样的
if (state.line.isValid()) {
//'draw.create'
this.map.fire(constants.events.CREATE, {
features: [state.line.toGeoJSON()],
});
} else {
this.deleteFeature([state.line.id], { silent: true });
this.changeMode(constants.modes.SIMPLE_SELECT, {}, { silent: true });
}
},
onTrash: function (state) {
this.deleteFeature([state.line.id], { silent: true });
this.changeMode(constants.modes.SIMPLE_SELECT);
},
//它决定当前 Drew 数据存储中的哪些特性将在地图上呈现。
//所有传递给“显示”的特性都将被渲染,因此可以为每个内部特性传递多个显示特性。
//有关如何制作显示特性的建议,请参阅‘ styling-pullin API.md
toDisplayFeatures: function (state, geojson, display) {
const isActiveLine = geojson.properties.id === state.line.id;
geojson.properties.active = isActiveLine
? constants.activeStates.ACTIVE
: constants.activeStates.INACTIVE;
if (!isActiveLine) {
display(geojson);
return;
}
if (geojson.geometry.coordinates.length < 2) return;
geojson.properties.meta = 'line_distance';
geojson.properties.name = 'line_distance';
display(
lib.createVertex(
state.line.id,
geojson.geometry.coordinates[
state.direction === 'forward' ? geojson.geometry.coordinates.length - 2 : 1
],
`${state.direction === 'forward' ? geojson.geometry.coordinates.length - 2 : 1}`,
false
)
);
display(geojson);
},
};
export default drawLineSelectMode;

View File

@ -0,0 +1,125 @@
const doubleClickZoom = {
enable: (ctx) => {
setTimeout(() => {
// First check we've got a map and some context.
if (
!ctx.map ||
!ctx.map.doubleClickZoom ||
!ctx._ctx ||
!ctx._ctx.store ||
!ctx._ctx.store.getInitialConfigValue
)
return;
// Now check initial state wasn't false (we leave it disabled if so)
if (!ctx._ctx.store.getInitialConfigValue('doubleClickZoom')) return;
ctx.map.doubleClickZoom.enable();
}, 0);
},
disable(ctx) {
setTimeout(() => {
if (!ctx.map || !ctx.map.doubleClickZoom) return;
// Always disable here, as it's necessary in some cases.
ctx.map.doubleClickZoom.disable();
}, 0);
},
};
const DrawRectangle = {
// When the mode starts this function will be called.
onSetup: function () {
const rectangle = this.newFeature({
type: 'Feature',
properties: {
isRect: true,
},
geometry: {
type: 'Polygon',
coordinates: [[]],
},
});
this.addFeature(rectangle);
this.clearSelectedFeatures();
doubleClickZoom.disable(this);
this.updateUIClasses({ mouse: 'add' });
this.setActionableState({
trash: true,
});
return {
rectangle,
};
},
// support mobile taps
onTap: function (state, e) {
// emulate 'move mouse' to update feature coords
if (state.startPoint) this.onMouseMove(state, e);
// emulate onClick
this.onClick(state, e);
},
// Whenever a user clicks on the map, Draw will call `onClick`
onClick: function (state, e) {
// if state.startPoint exist, means its second click
//change to simple_select mode
if (
state.startPoint &&
state.startPoint[0] !== e.lngLat.lng &&
state.startPoint[1] !== e.lngLat.lat
) {
this.updateUIClasses({ mouse: 'pointer' });
state.endPoint = [e.lngLat.lng, e.lngLat.lat];
this.changeMode('simple_select', { featuresId: state.rectangle.id });
}
// on first click, save clicked point coords as starting for rectangle
const startPoint = [e.lngLat.lng, e.lngLat.lat];
state.startPoint = startPoint;
},
onMouseMove: function (state, e) {
// if startPoint, update the feature coordinates, using the bounding box concept
// we are simply using the startingPoint coordinates and the current Mouse Position
// coordinates to calculate the bounding box on the fly, which will be our rectangle
if (state.startPoint) {
state.rectangle.updateCoordinate('0.0', state.startPoint[0], state.startPoint[1]); //minX, minY - the starting point
state.rectangle.updateCoordinate('0.1', e.lngLat.lng, state.startPoint[1]); // maxX, minY
state.rectangle.updateCoordinate('0.2', e.lngLat.lng, e.lngLat.lat); // maxX, maxY
state.rectangle.updateCoordinate('0.3', state.startPoint[0], e.lngLat.lat); // minX,maxY
state.rectangle.updateCoordinate('0.4', state.startPoint[0], state.startPoint[1]); //minX,minY - ending point (equals to starting point)
}
},
// Whenever a user clicks on a key while focused on the map, it will be sent here
onKeyUp: function (state, e) {
if (e.keyCode === 27) return this.changeMode('simple_select');
},
onStop: function (state) {
doubleClickZoom.enable(this);
this.updateUIClasses({ mouse: 'none' });
this.activateUIButton();
// check to see if we've deleted this feature
if (this.getFeature(state.rectangle.id) === undefined) return;
//remove last added coordinate
state.rectangle.removeCoordinate('0.4');
if (state.rectangle.isValid()) {
this.map.fire('draw.create', {
features: [state.rectangle.toGeoJSON()],
});
} else {
this.deleteFeature([state.rectangle.id], { silent: true });
this.changeMode('simple_select', {}, { silent: true });
}
},
toDisplayFeatures: function (state, geojson, display) {
const isActivePolygon = geojson.properties.id === state.rectangle.id;
geojson.properties.active = isActivePolygon ? 'true' : 'false';
if (!isActivePolygon) return display(geojson);
// Only render the rectangular polygon if it has the starting point
if (!state.startPoint) return;
return display(geojson);
},
onTrash: function (state) {
this.deleteFeature([state.rectangle.id], { silent: true });
this.changeMode('simple_select');
},
};
export default DrawRectangle;

View File

@ -0,0 +1,86 @@
import MapboxDraw from '@mapbox/mapbox-gl-draw';
import createSupplementaryPoints from '@mapbox/mapbox-gl-draw';
import moveFeatures from '@mapbox/mapbox-gl-draw';
import createVertex from '@mapbox/mapbox-gl-draw';
import { lineToPoly } from './index';
const { constants } = MapboxDraw;
function createSupplementaryPointsForCircle(geojson) {
const { properties, geometry } = geojson;
if (!properties.user_isCircle) return null;
const supplementaryPoints = [];
const vertices = geometry.coordinates[0].slice(0, -1);
for (let index = 0; index < vertices.length; index += Math.round(vertices.length / 4)) {
supplementaryPoints.push(createVertex(properties.id, vertices[index], `0.${index}`, false));
}
return supplementaryPoints;
}
const drawSimpleSelectMode = { ...MapboxDraw.modes.simple_select };
drawSimpleSelectMode.dragMove = function (state, e) {
// Dragging when drag move is enabled
state.dragMoving = true;
e.originalEvent.stopPropagation();
const delta = {
lng: e.lngLat.lng - state.dragMoveLocation.lng,
lat: e.lngLat.lat - state.dragMoveLocation.lat,
};
moveFeatures(this.getSelected(), delta);
this.getSelected()
.filter((feature) => feature.properties.isCircle)
.map((circle) => circle.properties.center)
.forEach((center) => {
center[0] += delta.lng;
center[1] += delta.lat;
});
state.dragMoveLocation = e.lngLat;
};
drawSimpleSelectMode.toDisplayFeatures = function (state, geojson, display) {
geojson.properties.active = this.isSelected(geojson.properties.id)
? constants.activeStates.ACTIVE
: constants.activeStates.INACTIVE;
if (geojson.properties.user_name === 'line_select') {
const union = lineToPoly(geojson);
display(union);
}
display(geojson);
this.fireActionable();
//如果是线 每次都创建点
if (
geojson?.properties.active !== constants.activeStates.ACTIVE &&
geojson.geometry.type === constants.geojsonTypes.LINE_STRING &&
geojson.properties.user_name !== 'line_select'
) {
const points = createSupplementaryPoints(geojson);
points.forEach(display);
}
if (
geojson.properties.active !== constants.activeStates.ACTIVE ||
geojson.geometry.type === constants.geojsonTypes.POINT
) {
return;
}
let supplementaryPoints;
if (geojson.properties.user_isCircle) {
supplementaryPoints = createSupplementaryPointsForCircle(geojson);
} else {
supplementaryPoints = createSupplementaryPoints(geojson);
}
supplementaryPoints.forEach(display);
// if(geojson.properties.)
};
//阻止框选图形拖拽
drawSimpleSelectMode.onTap = drawSimpleSelectMode.onClick = function () { };
export default drawSimpleSelectMode;

View File

@ -0,0 +1,14 @@
import doubleClickZoom from './doubleClickZoom';
var StaticMode = {};
StaticMode.onSetup = function () {
this.setActionableState(); // default actionable state is false for all actions
doubleClickZoom.disable(this); //静态model 不运行双击
return {};
};
StaticMode.toDisplayFeatures = function (state, geojson, display) {
display(geojson);
};
export default StaticMode;

View File

@ -0,0 +1,144 @@
//自定义画框样式
const mapboxDrawStyle = [
// ACTIVE (being drawn)
// line stroke
{
id: 'gl-draw-line',
type: 'line',
filter: [
'all',
['==', '$type', 'LineString'],
['!=', 'mode', 'static'],
['==', 'active', 'true'],
],
layout: {
'line-cap': 'round',
'line-join': 'round',
},
paint: {
'line-color': 'rgba(246,67,72,1)',
'line-dasharray': [0.2, 2],
'line-width': 2,
},
},
{
id: 'gl-draw-line-not-active',
type: 'line',
filter: ['all', ['==', '$type', 'LineString'], ['!=', 'active', 'true']],
layout: {
'line-cap': 'round',
'line-join': 'round',
},
paint: {
'line-color': 'rgba(246,67,72,1)',
'line-width': 2,
},
},
// polygon fill
{
id: 'gl-draw-polygon-fill',
type: 'fill',
filter: ['all', ['==', '$type', 'Polygon'], ['!=', 'mode', 'static']],
paint: {
'fill-color': 'rgba(246,67,72,0.2)',
'fill-outline-color': 'rgba(246,67,72,0.2)',
},
},
// polygon outline stroke
// This doesn't style the first edge of the polygon, which uses the line stroke styling instead
{
id: 'gl-draw-polygon-stroke-active',
type: 'line',
filter: [
'all',
['==', '$type', 'Polygon'],
['!=', 'mode', 'static'],
['==', 'active', 'false'],
],
layout: {
'line-cap': 'round',
'line-join': 'round',
},
paint: {
'line-color': 'rgba(246,67,72,1)',
'line-width': 2,
},
},
{
id: 'gl-draw-polygon-stroke-active-select',
type: 'line',
filter: ['all', ['==', '$type', 'Polygon'], ['!=', 'mode', 'static'], ['==', 'active', 'true']],
layout: {
'line-cap': 'round',
'line-join': 'round',
},
paint: {
'line-color': 'rgba(246,67,72,1)',
'line-dasharray': [0.2, 2],
'line-width': 2,
},
},
// vertex point halos
{
id: 'gl-draw-polygon-and-line-vertex-halo-active',
type: 'circle',
filter: ['all', ['==', 'meta', 'vertex'], ['==', '$type', 'Point'], ['!=', 'mode', 'static']],
paint: {
'circle-radius': 5,
'circle-color': '#FFF',
},
},
// vertex points
{
id: 'gl-draw-polygon-and-line-vertex-active',
type: 'circle',
filter: ['all', ['==', 'meta', 'vertex'], ['==', '$type', 'Point'], ['!=', 'mode', 'static']],
paint: {
'circle-radius': 3,
'circle-color': 'rgba(246,67,72,1)',
},
},
// INACTIVE (static, already drawn)
// line stroke
{
id: 'gl-draw-line-static',
type: 'line',
filter: ['all', ['==', '$type', 'LineString'], ['==', 'mode', 'static']],
layout: {
'line-cap': 'round',
'line-join': 'round',
},
paint: {
'line-color': '#E13F3F',
'line-width': 3,
},
},
// polygon fill
{
id: 'gl-draw-polygon-fill-static',
type: 'fill',
filter: ['all', ['==', '$type', 'Polygon'], ['==', 'mode', 'static']],
paint: {
'fill-color': 'rgba(225, 63, 63, 0.2)',
'fill-outline-color': 'rgba(225, 63, 63, 0.2)',
},
},
// polygon outline
{
id: 'gl-draw-polygon-stroke-static',
type: 'line',
filter: ['all', ['==', '$type', 'Polygon'], ['==', 'mode', 'static']],
layout: {
'line-cap': 'round',
'line-join': 'round',
},
paint: {
'line-color': '#E13F3F',
'line-width': 3,
},
},
];
export default mapboxDrawStyle;

View File

@ -0,0 +1,74 @@
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css';
import React from 'react'
import MapboxDraw from '@mapbox/mapbox-gl-draw';
import { useControl } from 'react-map-gl';
import type { MapboxMap, MapRef, ControlPosition } from 'react-map-gl';
import drawLineSelectMode from './drawLineSelectMode.draw.js';
import drawRectMode from './drawRectMode.draw.js';
import drawStaticMode from './drawStaticMode.draw.js';
import {
CircleMode,
DragCircleMode,
DirectMode,
SimpleSelectMode
} from 'mapbox-gl-draw-circle';
import mapboxDrawStyle from './drawStyle';
type DrawControlProps = ConstructorParameters<typeof MapboxDraw>[0] & {
position?: ControlPosition;
onCreate?: (evt: { features: object[] }) => void;
onUpdate?: (evt: { features: object[]; action: string }) => void;
onDelete?: (evt: { features: object[] }) => void;
};
const DrawControl = React.forwardRef((props: DrawControlProps, ref) => {
if (!props.mapRef) {
return
}
const drawRef = useControl<MapboxDraw>(
() => new MapboxDraw({
styles: mapboxDrawStyle,
displayControlsDefault: false, // 取消默认的按钮
controls: {
polygon: false,
trash: false
},
maxZoom: 18,
minZoom: 4,
dragRotate: false,
modes: {
...MapboxDraw.modes,
draw_circle: CircleMode,
drag_circle: DragCircleMode,
direct_select: DirectMode,
simple_select: SimpleSelectMode,
draw_line_select: drawLineSelectMode,
draw_line_string: drawLineSelectMode,
draw_rect: drawRectMode,
static: drawStaticMode,
}
}),
({ map }: { map: MapRef }) => {
map.on('draw.create', props.onCreate);
map.on('draw.update', props.onUpdate);
map.on('draw.delete', props.onDelete);
},
({ map }: { map: MapRef }) => {
map.off('draw.create', props.onCreate);
map.off('draw.update', props.onUpdate);
map.off('draw.delete', props.onDelete);
},
{
position: props.position
}
);
React.useImperativeHandle(ref, () => drawRef, [drawRef]); // This way I exposed drawRef outside the component
return null;
})
export default DrawControl;

View File

@ -0,0 +1,58 @@
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import union from '@turf/union';
import * as turf from '@turf/turf';
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
return controls
}
// 计算与纬线的角度,正方向向上
const calcAng = (point, p) => (Math.atan2(point[1] - p[1], point[0] - p[0]) * 180) / Math.PI + 180;
// 多段折线转polygon,直线左右默认范围50米
export const lineToPoly = (geojson, r = 50) => {
const linesPolygon = [];
const circlePolygon = [];
for (let i = 0; i < geojson.geometry.coordinates.length - 1; i++) {
const [pointA, pointB] = [geojson.geometry.coordinates[i], geojson.geometry.coordinates[i + 1]];
const line = turf.lineString([pointA, pointB]);
const ang = calcAng(pointA, pointB);
// 与经线的夹角为偏移方向,右正左负, 右偏移-左偏移 = 180
const translatedPolyA = turf.transformTranslate(line, r / 1000, -ang);
const translatedPolyB = turf.transformTranslate(line, r / 1000, -ang + 180);
const _line = turf.lineString([
// 逆时针闭合
...translatedPolyA.geometry.coordinates.reverse(),
...translatedPolyB.geometry.coordinates,
]);
const linePolygon = turf.lineToPolygon(_line);
linesPolygon.push(linePolygon);
circlePolygon.push(turf.circle(pointA, r / 1000));
if (i == geojson.geometry.coordinates.length - 2) {
circlePolygon.push(turf.circle(pointB, r / 1000));
}
}
let _union;
try {
//todo: 新版本union和老版本行为不一致 先用老版本 后续观察原因
// _union = turf.union(...circlePolygon, ...linesPolygon);
_union = union(...circlePolygon, ...linesPolygon);
} catch (e) {
console.error(e);
}
return _union;
};

View File

@ -3,11 +3,11 @@ nav:
title: 前端
path: /fea
group:
title: 💊 Q&A
title: Q&A
order: 100
---
# 💊 Q&A
# Q&A
## 页面弹框之后请求一直失败?

Some files were not shown because too many files have changed in this diff Show More