feat: 新版本
@ -1,12 +0,0 @@
|
||||
// .dumi/theme/layout.tsx(本地主题) 或 src/layout.tsx(主题包)
|
||||
import React from 'react';
|
||||
import Layout from 'dumi-theme-default/es/layout';
|
||||
|
||||
export default ({ children, ...props }) => (
|
||||
<Layout {...props}>
|
||||
<>
|
||||
<button>反馈</button>
|
||||
{children}
|
||||
</>
|
||||
</Layout>
|
||||
);
|
@ -1,32 +0,0 @@
|
||||
// .dumi/theme/layout.tsx(本地主题) 或 src/layout.tsx(主题包)
|
||||
import React, { useEffect } from 'react';
|
||||
import Layout from 'dumi-theme-default/es/layout';
|
||||
|
||||
export default ({ children, ...props }) => {
|
||||
const { history } = props;
|
||||
|
||||
useEffect(() => {
|
||||
const header = document.querySelector('.__dumi-default-navbar')
|
||||
const cont = document.querySelector('.__dumi-default-layout-content')
|
||||
const lo = document.querySelector('.__dumi-default-layout')
|
||||
|
||||
if (location.hash === '#/resume' && lo) {
|
||||
|
||||
cont.style.position = 'relative'
|
||||
cont.style.top = '-64px'
|
||||
header.style.display = 'none'
|
||||
} else {
|
||||
cont.style.position = 'relative'
|
||||
cont.style.top = '0'
|
||||
header.style.display = 'flex'
|
||||
}
|
||||
},[location.hash])
|
||||
|
||||
return (
|
||||
<Layout {...props}>
|
||||
<>
|
||||
{children}
|
||||
</>
|
||||
</Layout>
|
||||
)
|
||||
};
|
@ -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: {
|
||||
|
@ -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;
|
||||
|
@ -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 };
|
@ -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 };
|
297
.umi/core/pluginConfig.d.ts
vendored
@ -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
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import { plugin } from './plugin';
|
||||
|
||||
|
||||
export const __mfsu = 1;
|
@ -1,4 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import 'core-js';
|
||||
import 'regenerator-runtime/runtime';
|
||||
export {};
|
@ -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;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
// @ts-nocheck
|
||||
export { history } from './history';
|
||||
export { plugin } from './plugin';
|
@ -1 +0,0 @@
|
||||
{}
|
@ -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": {}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { dynamic } from 'dumi';
|
||||
|
||||
export default {
|
||||
};
|
@ -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} />;
|
58
.umi/umi.ts
@ -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
@ -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-bash的local安装路径
|
||||
"args": []
|
||||
},
|
||||
"Windows PowerShell": {
|
||||
"path": "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
|
||||
}
|
||||
},
|
||||
"terminal.integrated.defaultProfile.windows": "Git-Bash"
|
||||
}
|
@ -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"
|
||||
|
||||
```
|
@ -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)
|
||||
}
|
||||
|
||||
```
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
```
|
@ -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
@ -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
@ -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
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
```
|
@ -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 自增产生影响
|
||||
- 隐式使用法:如果不声明值,则默认使用最后一个表达式的赋值格式
|
||||
|
@ -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
|
||||
|
@ -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命令来检验配置文件是否修改正确。
|
||||
- 
|
||||
-
|
||||
- 0.0.0.0 6379 Redis 的链接地址 6379
|
||||
- logfile /var/log/rinetd.log
|
||||
- 说明:您可以使用 cat /etc/rinetd.conf 命令来检验配置文件是否修改正确。
|
||||
- 
|
||||
-
|
||||
|
||||
## 4.执行如下命令启动 rinetd。
|
||||
## 4.执行如下命令启动 rinetd
|
||||
|
||||
- rinetd
|
||||
- 注意
|
||||
- 您可以通过 echo rinetd >>/etc/rc.local 将 rinetd 设置为自启动。
|
||||
- 若遇到绑定报错,可以执行 pkill rinetd 结束进程,再执行 rinetd启动进程 rinetd。
|
||||
- rinetd 正常启动后, 执行netstat -anp | grep 6379 确认服务是否正常运行。
|
||||
- 
|
||||
## 5.在本地进行验证测试。
|
||||
- rinetd
|
||||
- 注意
|
||||
- 您可以通过 echo rinetd >>/etc/rc.local 将 rinetd 设置为自启动。
|
||||
- 若遇到绑定报错,可以执行 pkill rinetd 结束进程,再执行 rinetd 启动进程 rinetd。
|
||||
- rinetd 正常启动后, 执行 netstat -anp | grep 6379 确认服务是否正常运行。
|
||||
- 
|
||||
|
||||
- 您可以在本地通过 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 的连接密码。
|
||||
- 进行数据写入及查询验证。
|
||||
|
@ -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
@ -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
@ -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
|
||||
|
||||
```
|
@ -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()
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 166 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
75
docs/fc/canvas/font.md
Normal 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();
|
||||
};
|
||||
```
|
12
docs/fc/css/demos/HeartBeat/index.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import './index.less';
|
||||
|
||||
export default function () {
|
||||
return (
|
||||
<div className="cont">
|
||||
<div className="card">
|
||||
{/* <div className={styles.card_heart}>♥</div> */}
|
||||
{/* <h1 className={styles.card_title}>hello! motherfucker!</h1> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 684 KiB After Width: | Height: | Size: 684 KiB |
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
Before Width: | Height: | Size: 329 KiB After Width: | Height: | Size: 329 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 329 KiB After Width: | Height: | Size: 329 KiB |
@ -3,13 +3,12 @@ nav:
|
||||
title: FC
|
||||
path: /funny
|
||||
group:
|
||||
title: css
|
||||
order: 3
|
||||
path: /css
|
||||
title: CSS
|
||||
---
|
||||
|
||||
# css
|
||||
|
||||
# 案例赏析
|
||||
|
||||
### 心跳卡片
|
||||
|
@ -3,12 +3,12 @@ nav:
|
||||
title: 前端
|
||||
path: /fea
|
||||
group:
|
||||
title: 💊 css
|
||||
order: 1
|
||||
path: /css
|
||||
title: CSS
|
||||
---
|
||||
|
||||
## 基础介绍
|
||||
# 基础介绍
|
||||
|
||||
## 布局
|
||||
|
@ -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
@ -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 加速
|
@ -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
@ -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>
|
@ -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() {
|
@ -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>
|
||||
)
|
||||
}
|
77
docs/fc/webgl/demo/map-gl.tsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
107
docs/fc/webgl/demo/mapBox-gl.tsx
Normal 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}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
@ -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 的学习过程和踩坑记录,方便复盘与总结。
|
||||
:::
|
||||
# 介绍
|
||||
|
||||
### 基本对象
|
||||
|
24
docs/fc/webgl/utils/doubleClickZoom.js
Normal 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;
|
69
docs/fc/webgl/utils/drawCircleMode.draw.js
Normal 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-pull’in‘ 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;
|
131
docs/fc/webgl/utils/drawDirectMode.draw.js
Normal 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]}`);
|
||||
//如果对立点和坐标x||y 一致 则返回
|
||||
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;
|
165
docs/fc/webgl/utils/drawLineSelectMode.draw.js
Normal 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-pull’in‘ 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;
|
125
docs/fc/webgl/utils/drawRectMode.draw.js
Normal 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;
|
86
docs/fc/webgl/utils/drawSimpleSelectMode.draw.js
Normal 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;
|
14
docs/fc/webgl/utils/drawStaticMode.draw.js
Normal 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;
|
144
docs/fc/webgl/utils/drawStyle.ts
Normal 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;
|
74
docs/fc/webgl/utils/drowControl.ts
Normal 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;
|
58
docs/fc/webgl/utils/index.ts
Normal 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;
|
||||
};
|
@ -3,11 +3,11 @@ nav:
|
||||
title: 前端
|
||||
path: /fea
|
||||
group:
|
||||
title: 💊 Q&A
|
||||
title: Q&A
|
||||
order: 100
|
||||
---
|
||||
|
||||
# 💊 Q&A
|
||||
# Q&A
|
||||
|
||||
## 页面弹框之后请求一直失败?
|
||||
|
||||
|