├── .npmignore ├── example ├── mock │ └── .gitkeep ├── src │ ├── pages │ │ ├── props.tsx │ │ ├── intro.tsx │ │ ├── components │ │ │ ├── index.tsx │ │ │ ├── Footer.tsx │ │ │ ├── Code.tsx │ │ │ ├── Code4.tsx │ │ │ ├── Background.tsx │ │ │ ├── Code0.tsx │ │ │ ├── Code3.tsx │ │ │ ├── api │ │ │ │ └── index.tsx │ │ │ ├── Code1.tsx │ │ │ └── Code2.tsx │ │ ├── package │ │ │ ├── style.ts │ │ │ ├── utils.ts │ │ │ └── index.tsx │ │ ├── index.less │ │ └── index.tsx │ └── config.tsx ├── public │ └── logo.png ├── .prettierignore ├── README.md ├── .prettierrc ├── typings.d.ts ├── .editorconfig ├── .gitignore ├── .umirc.ts ├── docs │ └── index.html ├── tsconfig.json └── package.json ├── .gitignore ├── .DS_Store ├── lib ├── style.d.ts ├── utils.d.ts ├── index.d.ts ├── index.js └── index.esm.js ├── docs ├── logo.png └── index.html ├── snapshot.png ├── netlify ├── logo.png └── index.html ├── .eslintrc.js ├── src ├── style.ts ├── utils.ts └── index.tsx ├── tsconfig.json ├── LICENSE ├── rollup.build.css ├── rollup.config.js ├── .github └── workflows │ └── main.yml ├── package.json ├── .vscode └── tasks.json └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | example -------------------------------------------------------------------------------- /example/mock/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/src/pages/props.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | build -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanguandong/smart-background/HEAD/.DS_Store -------------------------------------------------------------------------------- /lib/style.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: string[]; 2 | export default _default; 3 | -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanguandong/smart-background/HEAD/docs/logo.png -------------------------------------------------------------------------------- /snapshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanguandong/smart-background/HEAD/snapshot.png -------------------------------------------------------------------------------- /netlify/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanguandong/smart-background/HEAD/netlify/logo.png -------------------------------------------------------------------------------- /example/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuanguandong/smart-background/HEAD/example/public/logo.png -------------------------------------------------------------------------------- /example/.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.md 2 | **/*.svg 3 | **/*.ejs 4 | **/*.html 5 | package.json 6 | .umi 7 | .umi-production 8 | .umi-test 9 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Smart Background Demo 2 | 3 | ## Getting Started 4 | 5 | Install dependencies, 6 | 7 | ```bash 8 | $ yarn 9 | ``` 10 | 11 | Start the dev server, 12 | 13 | ```bash 14 | $ yarn start 15 | ``` 16 | -------------------------------------------------------------------------------- /example/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "printWidth": 80, 5 | "overrides": [ 6 | { 7 | "files": ".prettierrc", 8 | "options": { "parser": "json" } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /example/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.css'; 2 | declare module '*.less'; 3 | declare module '*.png'; 4 | declare module '*.svg' { 5 | export function ReactComponent( 6 | props: React.SVGProps, 7 | ): React.ReactElement; 8 | const url: string; 9 | export default url; 10 | } 11 | -------------------------------------------------------------------------------- /example/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /example/src/config.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { FaCannabis } from 'react-icons/fa' 3 | 4 | export default { 5 | packageName:'smart-background', 6 | name:'smart-background', 7 | homePageUrl: 'https://github.com/yuanguandong/smart-background', 8 | symbol:, 9 | authorHomePage:'https://github.com/yuanguandong', 10 | } -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /npm-debug.log* 6 | /yarn-error.log 7 | /yarn.lock 8 | /package-lock.json 9 | 10 | # production 11 | /dist 12 | 13 | # misc 14 | .DS_Store 15 | 16 | # umi 17 | /src/.umi 18 | /src/.umi-production 19 | /src/.umi-test 20 | /.env.local 21 | -------------------------------------------------------------------------------- /lib/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare function randomNum(minNum: number, maxNum: number): number; 2 | export declare const getPosition: (random: any, fontSize: number) => { 3 | fontSize: number; 4 | top?: undefined; 5 | left?: undefined; 6 | } | { 7 | fontSize: number; 8 | top: string; 9 | left: string; 10 | }; 11 | export declare function generateUuid(len?: number, radix?: number): string; 12 | -------------------------------------------------------------------------------- /example/src/pages/intro.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from 'antd'; 2 | const { Title, Paragraph, Text, Link } = Typography; 3 | 4 | export default () => { 5 | return ( 6 | 7 | An React Component Can Automatically Generate The Background 8 | 一个快速生成元素背景的react组件 9 | 10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /example/.umirc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'umi'; 2 | 3 | let { BASE } = process.env; 4 | 5 | console.log(BASE) 6 | 7 | export default defineConfig({ 8 | favicon: './logo.png', 9 | publicPath: BASE, 10 | base: BASE, 11 | nodeModulesTransform: { 12 | type: 'none', 13 | }, 14 | antd:{}, 15 | routes: [ 16 | { path: '/', component: '@/pages/index' }, 17 | ], 18 | fastRefresh: {}, 19 | outputPath:BASE==='/' ? '../netlify': '../docs' 20 | }); 21 | -------------------------------------------------------------------------------- /example/src/pages/components/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Api } from './api' 2 | export { default as Background } from './Background' 3 | export { default as Code } from './Code' 4 | export { default as Code0 } from './Code0' 5 | export { default as Code1 } from './Code1' 6 | export { default as Code2 } from './Code2' 7 | export { default as Code3 } from './Code3' 8 | export { default as Code4 } from './Code4' 9 | export { default as Footer } from './Footer' 10 | 11 | 12 | -------------------------------------------------------------------------------- /example/docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 13 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /netlify/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 14 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const resolve = _path => path.resolve(__dirname, _path) 3 | const DOMGlobals = ['window', 'document'] 4 | const NodeGlobals = ['module', 'require'] 5 | 6 | 7 | module.exports = { 8 | env: { 9 | browser: true, 10 | es6: true 11 | }, 12 | parser: '@typescript-eslint/parser', // 配置ts解析器 13 | parserOptions: { 14 | project: resolve('./tsconfig.json'), 15 | tsconfigRootDir: resolve('./'), 16 | sourceType: 'module' 17 | }, 18 | // plugins: ['prettier'], 19 | rules: { 20 | 'indent': 'off', 21 | 'no-unused-vars': 'off', 22 | 'no-restricted-globals': 'off', 23 | 'no-console': 'off', 24 | } 25 | }; -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 14 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/style.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | `@keyframes smart-background-scroll-top { 3 | 0% { 4 | transform: translateY(0%); 5 | } 6 | 100% { 7 | transform: translateY(-100%); 8 | } 9 | }`, 10 | 11 | `@keyframes smart-background-scroll-bottom { 12 | 0% { 13 | transform: translateY(0%); 14 | } 15 | 100% { 16 | transform: translateY(100%); 17 | } 18 | }`, 19 | 20 | `@keyframes smart-background-scroll-left { 21 | 0% { 22 | transform: translateX(0%); 23 | } 24 | 100% { 25 | transform: translateX(-100%); 26 | } 27 | }`, 28 | 29 | `@keyframes smart-background-scroll-right { 30 | 0% { 31 | transform: translateX(0%); 32 | } 33 | 100% { 34 | transform: translateX(100%); 35 | } 36 | }`, 37 | ]; 38 | -------------------------------------------------------------------------------- /example/src/pages/package/style.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | `@keyframes smart-background-scroll-top { 3 | 0% { 4 | transform: translateY(0%); 5 | } 6 | 100% { 7 | transform: translateY(-100%); 8 | } 9 | }`, 10 | 11 | `@keyframes smart-background-scroll-bottom { 12 | 0% { 13 | transform: translateY(0%); 14 | } 15 | 100% { 16 | transform: translateY(100%); 17 | } 18 | }`, 19 | 20 | `@keyframes smart-background-scroll-left { 21 | 0% { 22 | transform: translateX(0%); 23 | } 24 | 100% { 25 | transform: translateX(-100%); 26 | } 27 | }`, 28 | 29 | `@keyframes smart-background-scroll-right { 30 | 0% { 31 | transform: translateX(0%); 32 | } 33 | 100% { 34 | transform: translateX(100%); 35 | } 36 | }`, 37 | ]; 38 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "importHelpers": true, 7 | "jsx": "react-jsx", 8 | "esModuleInterop": true, 9 | "sourceMap": true, 10 | "baseUrl": "./", 11 | "strict": true, 12 | "paths": { 13 | "@/*": ["src/*"], 14 | "@@/*": ["src/.umi/*"] 15 | }, 16 | "allowSyntheticDefaultImports": true 17 | }, 18 | "include": [ 19 | "mock/**/*", 20 | "src/**/*", 21 | "config/**/*", 22 | ".umirc.ts", 23 | "typings.d.ts" 24 | ], 25 | "exclude": [ 26 | "node_modules", 27 | "lib", 28 | "es", 29 | "dist", 30 | "typings", 31 | "**/__test__", 32 | "test", 33 | "docs", 34 | "tests" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from "react"; 2 | import "./style.ts"; 3 | declare type Animation = { 4 | type: "left" | "right" | "top" | "bottom"; 5 | speed: number; 6 | }; 7 | interface SmartBackgroundProps { 8 | symbols?: (string | ReactNode | Element)[]; 9 | random?: { 10 | fontSizeRange: number[]; 11 | } | undefined; 12 | underlayColor?: string; 13 | underlayImage?: string; 14 | symbolsStyle?: Object; 15 | rotate?: number; 16 | symbolSize?: number; 17 | gap?: number; 18 | animation?: Animation | undefined; 19 | exact?: boolean; 20 | childrenWrapClassName?: string; 21 | childrenWrapStyle?: React.CSSProperties; 22 | [key: string]: any; 23 | } 24 | declare const SmartBackground: (props: SmartBackgroundProps) => JSX.Element; 25 | export default SmartBackground; 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "outDir": "./lib", // 输出目录 5 | "sourceMap": false, // 是否生成sourceMap 6 | "target": "esnext", // 编译目标 7 | "module": "esnext", // 模块类型 8 | "moduleResolution": "node", 9 | "allowJs": false, // 是否编辑js文件 10 | "strict": true, // 严格模式 11 | "noUnusedLocals": true, // 未使用变量报错 12 | "experimentalDecorators": true, // 启动装饰器 13 | "resolveJsonModule": true, // 加载json 14 | "esModuleInterop": true, 15 | "removeComments": false, // 删除注释 16 | "jsx":"react", 17 | 18 | "declaration": true, // 生成定义文件 19 | "declarationMap": false, // 生成定义sourceMap 20 | "declarationDir": "./lib/types", // 定义文件输出目录 21 | 22 | 23 | "lib": ["esnext", "dom"], // 导入库类型定义 24 | "types": ["node"] // 导入指定类型包 25 | }, 26 | "include": [ 27 | "src/*" // 导入目录 28 | ] 29 | } -------------------------------------------------------------------------------- /example/src/pages/components/Footer.tsx: -------------------------------------------------------------------------------- 1 | import { GithubOutlined } from '@ant-design/icons'; 2 | import { Typography } from 'antd'; 3 | import Config from '../../config'; 4 | const { Title, Paragraph, Text, Link } = Typography; 5 | export default () => { 6 | return ( 7 |
8 | 9 | {Config.name} 10 | If you think it works, please give me a STAR 11 | 12 | Open-source MIT Licensed | Copyright © 2021-present 13 | 14 | 15 | @Yuanguandong 16 | 17 | 18 | 19 | Github 20 | 21 | 22 | 23 |
24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Favori 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rollup.build.css: -------------------------------------------------------------------------------- 1 | @keyframes scroll-top { 2 | 0% { 3 | transform: translateY(0%); 4 | } 5 | 100% { 6 | transform: translateY(-100%); 7 | } 8 | } 9 | @keyframes scroll-bottom { 10 | 0% { 11 | transform: translateY(0%); 12 | } 13 | 100% { 14 | transform: translateY(100%); 15 | } 16 | } 17 | @keyframes scroll-left { 18 | 0% { 19 | transform: translateX(0%); 20 | } 21 | 100% { 22 | transform: translateX(-100%); 23 | } 24 | } 25 | @keyframes scroll-right { 26 | 0% { 27 | transform: translateX(0%); 28 | } 29 | 100% { 30 | transform: translateX(100%); 31 | } 32 | } 33 | @keyframes scroll-top { 34 | 0% { 35 | transform: translateY(0%); 36 | } 37 | 100% { 38 | transform: translateY(-100%); 39 | } 40 | } 41 | @keyframes scroll-bottom { 42 | 0% { 43 | transform: translateY(0%); 44 | } 45 | 100% { 46 | transform: translateY(100%); 47 | } 48 | } 49 | @keyframes scroll-left { 50 | 0% { 51 | transform: translateX(0%); 52 | } 53 | 100% { 54 | transform: translateX(-100%); 55 | } 56 | } 57 | @keyframes scroll-right { 58 | 0% { 59 | transform: translateX(0%); 60 | } 61 | 100% { 62 | transform: translateX(100%); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /example/src/pages/components/Code.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Collapse } from 'antd'; 2 | import { useState } from 'react'; 3 | import { FaCode } from 'react-icons/fa'; 4 | import { Light as SyntaxHighlighter } from 'react-syntax-highlighter'; 5 | import typescript from 'react-syntax-highlighter/dist/esm/languages/hljs/typescript'; 6 | import { atomOneDark } from 'react-syntax-highlighter/dist/esm/styles/hljs'; 7 | const { Panel } = Collapse; 8 | 9 | SyntaxHighlighter.registerLanguage('javascript', typescript); 10 | 11 | export default (props: any) => { 12 | const { content } = props; 13 | 14 | const [show, setShow] = useState(false); 15 | 16 | return ( 17 |
18 | 21 | {show && ( 22 | 28 | {content} 29 | 30 | )} 31 |
32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /example/src/pages/components/Code4.tsx: -------------------------------------------------------------------------------- 1 | export default `import React from 'react'; 2 | import Background from 'smart-background'; 3 | import { Fa500Px,FaApple,FaAdobe,FaAdversal,FaAirbnb } from 'react-icons/fa'; 4 | 5 | export default () => { 6 | return ( 7 |
8 | 14 |
15 | 16 |

17 | Apple 18 |

19 |
20 |
21 |
22 | ); 23 | }; 24 | 25 | const styles = { 26 | container: { width: '100%', position: 'relative', height: 350 }, 27 | content: { 28 | width: '100%', 29 | height: '100%', 30 | display: 'flex', 31 | justifyContent: 'center', 32 | alignItems: 'center', 33 | flexDirection: 'column', 34 | fontSize: 120, 35 | }, 36 | text: { color: '#fff', fontSize: 36, fontWeight: 'bold' }, 37 | }; 38 | 39 | const icons = [ 40 | , 41 | , 42 | , 43 | , 44 | 45 | ] 46 | 47 | `; 48 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "umi dev", 5 | "build:netlify": "BASE=/ umi build", 6 | "build:doc": "BASE=/smart-background/ umi build", 7 | "postinstall": "umi generate tmp", 8 | "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", 9 | "test": "umi-test", 10 | "test:coverage": "umi-test --coverage" 11 | }, 12 | "gitHooks": { 13 | "pre-commit": "lint-staged" 14 | }, 15 | "lint-staged": { 16 | "*.{js,jsx,less,md,json}": [ 17 | "prettier --write" 18 | ], 19 | "*.ts?(x)": [ 20 | "prettier --parser=typescript --write" 21 | ] 22 | }, 23 | "dependencies": { 24 | "@ant-design/pro-layout": "^6.5.0", 25 | "@emotion/react": "^11.4.1", 26 | "@emotion/styled": "^11.3.0", 27 | "@umijs/preset-react": "1.x", 28 | "classnames": "^2.3.1", 29 | "react-icons": "^4.2.0", 30 | "react-resize-detector": "^6.7.6", 31 | "react-syntax-highlighter": "^15.4.3", 32 | "umi": "^3.5.2" 33 | }, 34 | "devDependencies": { 35 | "@types/react": "^17.0.0", 36 | "@types/react-dom": "^17.0.0", 37 | "@umijs/test": "^3.5.2", 38 | "lint-staged": "^10.0.7", 39 | "prettier": "^2.2.0", 40 | "react": "17.x", 41 | "react-dom": "17.x", 42 | "typescript": "^4.1.2", 43 | "yorkie": "^2.0.0" 44 | }, 45 | "__npminstall_done": false 46 | } 47 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import commonjs from "rollup-plugin-commonjs"; // commonjs模块转换插件 3 | import { eslint } from "rollup-plugin-eslint"; // eslint插件 4 | import less from "rollup-plugin-less"; 5 | import resolve from "rollup-plugin-node-resolve"; // 依赖引用插件 6 | import ts from "rollup-plugin-typescript2"; 7 | import { uglify } from "rollup-plugin-uglify"; 8 | import packageJSON from "./package.json"; 9 | 10 | const getPath = (_path) => path.resolve(__dirname, _path); 11 | 12 | const extensions = [".js", ".ts", ".tsx"]; 13 | 14 | // ts 15 | const tsPlugin = ts({ 16 | tsconfig: getPath("./tsconfig.json"), // 导入本地ts配置 17 | extensions, 18 | }); 19 | 20 | // eslint 21 | const esPlugin = eslint({ 22 | throwOnError: true, 23 | include: ["src/**/*.ts"], 24 | exclude: ["node_modules/**", "lib/**"], 25 | }); 26 | 27 | // 基础配置 28 | const commonConf = { 29 | input: getPath("./src/index.tsx"), 30 | external: ["react"], 31 | plugins: [ 32 | uglify({ 33 | output: { 34 | comments: false, 35 | }, 36 | }), 37 | less(), 38 | resolve(extensions), 39 | commonjs(), 40 | esPlugin, 41 | tsPlugin, 42 | ], 43 | }; 44 | // 需要导出的模块类型 45 | const outputMap = [ 46 | { 47 | file: packageJSON.main, // 通用模块 48 | format: "umd", 49 | }, 50 | { 51 | file: packageJSON.module, // es6模块 52 | format: "es", 53 | }, 54 | ]; 55 | 56 | const buildConf = (options) => Object.assign({}, commonConf, options); 57 | 58 | export default outputMap.map((output) => 59 | buildConf({ output: { name: packageJSON.name, ...output } }) 60 | ); 61 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the master branch 8 | push: 9 | branches: [master] 10 | pull_request: 11 | branches: [master] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v2 27 | 28 | # Runs a single command using the runners shell 29 | - name: install 30 | run: cd example && npm i 31 | 32 | # Runs a set of commands using the runners shell 33 | - name: build:doc 34 | run: cd example && npm run build:doc && npm run build:netlify 35 | 36 | - name: Commit report 37 | run: | 38 | git config --global user.name 'yuanguandong' 39 | git config --global user.email '995749721@qq.com' 40 | git pull 41 | git add . 42 | git commit -m "Automated build docs" 43 | git push origin master 44 | 45 | # 部署到github pages 46 | - name: Deploy to gh-pages 47 | uses: peaceiris/actions-gh-pages@v3 48 | with: 49 | deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} 50 | publish_dir: ./docs 51 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | export function randomNum(minNum: number, maxNum: number) { 2 | switch (arguments.length) { 3 | case 1: 4 | return Math.floor(Math.random() * minNum + 1); 5 | break; 6 | case 2: 7 | return Math.floor(Math.random() * (maxNum - minNum + 1) + minNum); 8 | break; 9 | default: 10 | return 0; 11 | break; 12 | } 13 | } 14 | 15 | export const getPosition = (random:any, fontSize:number) => { 16 | if(!random){ 17 | return { 18 | fontSize 19 | } 20 | } 21 | const {fontSizeRange} = random 22 | const [minNum, maxNum] = fontSizeRange; 23 | const fontSizeFin = randomNum(minNum, maxNum); 24 | const top = randomNum(0, 100) + '%'; 25 | const left = randomNum(0, 100) + '%'; 26 | return { fontSize:fontSizeFin, top, left }; 27 | }; 28 | 29 | 30 | /* 31 | 生成uuid 32 | len:number 长度 33 | radix:number 进制 34 | */ 35 | export function generateUuid(len: number = 32, radix: number = 10): string { 36 | const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); 37 | const uuid = []; 38 | let i; 39 | radix = radix || chars.length; 40 | 41 | if (len) { 42 | // Compact form 43 | for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)]; 44 | } else { 45 | // rfc4122, version 4 form 46 | let r; 47 | 48 | // rfc4122 requires these characters 49 | uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; 50 | uuid[14] = '4'; 51 | 52 | // Fill in random data. At i==19 set the high bits of clock sequence as 53 | // per rfc4122, sec. 4.1.5 54 | for (i = 0; i < 36; i++) { 55 | if (!uuid[i]) { 56 | r = 0 | (Math.random() * 16); 57 | uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r]; 58 | } 59 | } 60 | } 61 | 62 | return uuid.join(''); 63 | } -------------------------------------------------------------------------------- /example/src/pages/package/utils.ts: -------------------------------------------------------------------------------- 1 | export function randomNum(minNum: number, maxNum: number) { 2 | switch (arguments.length) { 3 | case 1: 4 | return Math.floor(Math.random() * minNum + 1); 5 | break; 6 | case 2: 7 | return Math.floor(Math.random() * (maxNum - minNum + 1) + minNum); 8 | break; 9 | default: 10 | return 0; 11 | break; 12 | } 13 | } 14 | 15 | export const getPosition = (random:any, fontSize:number) => { 16 | if(!random){ 17 | return { 18 | fontSize 19 | } 20 | } 21 | const {fontSizeRange} = random 22 | const [minNum, maxNum] = fontSizeRange; 23 | const fontSizeFin = randomNum(minNum, maxNum); 24 | const top = randomNum(0, 100) + '%'; 25 | const left = randomNum(0, 100) + '%'; 26 | return { fontSize:fontSizeFin, top, left }; 27 | }; 28 | 29 | 30 | /* 31 | 生成uuid 32 | len:number 长度 33 | radix:number 进制 34 | */ 35 | export function generateUuid(len: number = 32, radix: number = 10): string { 36 | const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); 37 | const uuid = []; 38 | let i; 39 | radix = radix || chars.length; 40 | 41 | if (len) { 42 | // Compact form 43 | for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)]; 44 | } else { 45 | // rfc4122, version 4 form 46 | let r; 47 | 48 | // rfc4122 requires these characters 49 | uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; 50 | uuid[14] = '4'; 51 | 52 | // Fill in random data. At i==19 set the high bits of clock sequence as 53 | // per rfc4122, sec. 4.1.5 54 | for (i = 0; i < 36; i++) { 55 | if (!uuid[i]) { 56 | r = 0 | (Math.random() * 16); 57 | uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r]; 58 | } 59 | } 60 | } 61 | 62 | return uuid.join(''); 63 | } -------------------------------------------------------------------------------- /example/src/pages/components/Background.tsx: -------------------------------------------------------------------------------- 1 | 2 | export default () => { 3 | function randomNum(minNum:number, maxNum:number) { 4 | switch (arguments.length) { 5 | case 1: 6 | return Math.floor(Math.random() * minNum + 1); 7 | break; 8 | case 2: 9 | return Math.floor(Math.random() * (maxNum - minNum + 1) + minNum); 10 | break; 11 | default: 12 | return 0; 13 | break; 14 | } 15 | } 16 | 17 | const getPosition = () => { 18 | const fontSize = randomNum(40,90) 19 | const top = randomNum(0,100)+'%' 20 | const left = randomNum(0,100)+'%' 21 | return {fontSize,top,left}; 22 | }; 23 | 24 | return ( 25 |
26 | 27 |
28 | ○ 29 |
30 |
31 | + 32 |
33 |
34 | - 35 |
36 |
37 | ◇ 38 |
39 |
40 | △ 41 |
42 |
43 | ⊙ 44 |
45 |
46 | Win 47 |
48 |
49 | Commond 50 |
51 |
52 | CTRL 53 |
54 |
55 | ALT 56 |
57 |
58 | SHIFT 59 |
60 |
61 | ); 62 | }; 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "smart-background", 3 | "version": "1.0.11", 4 | "description": "Automatically generate the background of the symbol picture, so you no longer have to worry about the background 自动生成符号背景,让你不再为背景头疼", 5 | "keywords": [ 6 | "react", 7 | "background", 8 | "javascript", 9 | "tsx", 10 | "smart", 11 | "smart-background", 12 | "emoji", 13 | "icon", 14 | "icons", 15 | "background", 16 | "图片", 17 | "背景" 18 | ], 19 | "homepage": "https://yuanguandong.github.io/smart-background/", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/yuanguandong/smart-background" 23 | }, 24 | "author": "yuanguandong", 25 | "license": "MIT", 26 | "main": "lib/index.js", 27 | "module": "lib/index.esm.js", 28 | "scripts": { 29 | "start": "cd example && npm start", 30 | "dev": "set NODE_ENV=developemnt&& rollup -c rollup.config.js -w", 31 | "build": "rm -rf lib && set NODE_ENV=production && rollup -c rollup.config.js", 32 | "init": "mkdir /Volumes/mac/github/smart-background/example/src/pages/package && ln -s /Volumes/mac/github/smart-background/src/* /Volumes/mac/github/smart-background/example/src/pages/package" 33 | }, 34 | "types": "lib/index.d.ts", 35 | "devDependencies": { 36 | "@emotion/react": "^11.4.1", 37 | "@emotion/styled": "^11.3.0", 38 | "@types/react": "^17.0.13", 39 | "@typescript-eslint/parser": "^4.28.2", 40 | "react": "^17.0.2", 41 | "react-resize-detector": "^6.7.6", 42 | "rollup-plugin-commonjs": "^10.1.0", 43 | "rollup-plugin-eslint": "^7.0.0", 44 | "rollup-plugin-less": "^1.1.3", 45 | "rollup-plugin-node-resolve": "^5.2.0", 46 | "rollup-plugin-typescript2": "^0.30.0", 47 | "rollup-plugin-uglify": "^6.0.4", 48 | "tslib": "^2.3.0", 49 | "typescript": "^4.3.5" 50 | }, 51 | "files": [ 52 | "lib" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /example/src/pages/components/Code0.tsx: -------------------------------------------------------------------------------- 1 | export default `import React from 'react'; 2 | import Background from 'smart-background'; 3 | import { FaLaugh } from 'react-icons/fa'; 4 | 5 | export default () => { 6 | return ( 7 |
8 | 9 |
10 | 11 |

JUST DO IT

12 |
13 |
14 |
15 | ); 16 | }; 17 | 18 | const styles = { 19 | container: { width: '100%', position: 'relative', height: 350 }, 20 | content: { 21 | width: '100%', 22 | height: '100%', 23 | display: 'flex', 24 | justifyContent: 'center', 25 | alignItems: 'center', 26 | flexDirection: 'column', 27 | }, 28 | icon: { color: '#fff', fontSize: 120 }, 29 | text: { color: '#fff', fontSize: 36, fontWeight: 'bold' }, 30 | }; 31 | `; 32 | 33 | // import React from 'react'; 34 | // import Background from 'smart-background'; 35 | // import { FaLaugh } from 'react-icons/fa'; 36 | 37 | // export default () => { 38 | // return ( 39 | //
40 | // 41 | //
42 | // 43 | //

JUST DO IT

44 | //
45 | //
46 | //
47 | // ); 48 | // }; 49 | 50 | // const styles = { 51 | // container: { width: '100%', position: 'relative', height: 350 }, 52 | // content: { 53 | // width: '100%', 54 | // height: '100%', 55 | // display: 'flex', 56 | // justifyContent: 'center', 57 | // alignItems: 'center', 58 | // flexDirection: 'column', 59 | // }, 60 | // icon: { color: '#fff', fontSize: 120 }, 61 | // text: { color: '#fff', fontSize: 36, fontWeight: 'bold' }, 62 | // }; 63 | -------------------------------------------------------------------------------- /example/src/pages/components/Code3.tsx: -------------------------------------------------------------------------------- 1 | export default `import React from 'react'; 2 | import Background from 'smart-background'; 3 | import { FaApple } from 'react-icons/fa'; 4 | 5 | export default () => { 6 | return ( 7 |
8 | ( 13 |
24 | )), 25 | ]} 26 | > 27 |
28 | 29 |

30 | Apple 31 |

32 |
33 | 34 |
35 | ); 36 | }; 37 | 38 | const styles = { 39 | container: { width: '100%', position: 'relative', height: 350 }, 40 | content: { 41 | width: '100%', 42 | height: '100%', 43 | display: 'flex', 44 | justifyContent: 'center', 45 | alignItems: 'center', 46 | flexDirection: 'column', 47 | fontSize: 120, 48 | }, 49 | text: { color: '#fff', fontSize: 36, fontWeight: 'bold' }, 50 | }; 51 | 52 | const dots = [ 53 | { 54 | x: '-10%', 55 | y: '-20%', 56 | size: 200, 57 | background: 'linear-gradient(to top, #0ba360 0%, #3cba92 100%)', 58 | borderRadius: '50%', 59 | }, 60 | { 61 | x: '60%', 62 | y: '40%', 63 | size: 500, 64 | background: 65 | 'linear-gradient(to right, #f78ca0 0%, #f9748f 19%, #fd868c 60%, #fe9a8b 100%)', 66 | borderRadius: '50%', 67 | }, 68 | { 69 | x: '-30%', 70 | y: '50%', 71 | size: 450, 72 | background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', 73 | borderRadius: '50%', 74 | }, 75 | ]; 76 | 77 | `; 78 | 79 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // 有关 tasks.json 格式的文档,请参见 3 | // https://go.microsoft.com/fwlink/?LinkId=733558 4 | "version": "2.0.0", 5 | "tasks": [ 6 | //f4 7 | { 8 | "type": "shell", 9 | "command": "git pull gitlab master", 10 | "label": "git pull", 11 | "problemMatcher": [] 12 | }, 13 | //f6 14 | { 15 | "type": "npm", 16 | "script": "gitpush", 17 | "path": "./", 18 | "group": "build", 19 | "label": "git push", 20 | "problemMatcher": [] 21 | }, 22 | //f8 start 23 | { 24 | "type": "shell", 25 | "command": "cd example && npm start", 26 | "label": "start", 27 | "problemMatcher": [] 28 | }, 29 | //f9 build 30 | { 31 | "type": "shell", 32 | "command": "npm run build && npm publish", 33 | "label": "build", 34 | "problemMatcher": [] 35 | }, 36 | //f10 publish 37 | { 38 | "type": "shell", 39 | "command": "cd example && rm -rf /Volumes/mac/github/smart-background/example/node_modules/smart-background && mkdir /Volumes/mac/github/smart-background/example/node_modules/smart-background && ln -s /Volumes/mac/github/smart-background/lib/* /Volumes/mac/github/smart-background/example/node_modules/smart-background", 40 | "label": "publish", 41 | "problemMatcher": [] 42 | }, 43 | //f11 my install 44 | { 45 | "type": "shell", 46 | "command": "cd example && cnpm i", 47 | "label": "my install", 48 | "problemMatcher": [] 49 | }, 50 | //f12 reset 51 | { 52 | "type": "shell", 53 | "command": "cd example && rm -rf node_modules", 54 | "label": "reset", 55 | "problemMatcher": [] 56 | }, 57 | { 58 | "type": "shell", 59 | "command": "git checkout master && git merge feature-0-dev && git add . && git commit -m 'merge' && git push", 60 | "label": "merge", 61 | "problemMatcher": [] 62 | } 63 | 64 | 65 | ] 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /example/src/pages/components/api/index.tsx: -------------------------------------------------------------------------------- 1 | import { Table } from 'antd'; 2 | 3 | export default (props:any)=>{ 4 | 5 | const dataSource = [ 6 | { 7 | prop: `symbols`, 8 | desc: `元素/字符/符号集合`, 9 | type: `(string | ReactNode | Element)[]`, 10 | default: `['●']`, 11 | required: `false` 12 | }, 13 | { 14 | prop: `random`, 15 | desc: `符号是否随机生成位置和大小`, 16 | type: `{ fontSizeRange: number[] } | undefined`, 17 | default: ``, 18 | required: `false` 19 | },{ 20 | prop: `underlayColor`, 21 | desc: `底衬颜色`, 22 | type: `string`, 23 | default: ``, 24 | required: `false` 25 | },{ 26 | prop: `underlayImage`, 27 | desc: `底衬图片`, 28 | type: `string`, 29 | default: ``, 30 | required: `false` 31 | },{ 32 | prop: `symbolsStyle`, 33 | desc: `符号样式`, 34 | type: `React.CSSProperties`, 35 | default: `{color: '#000',opacity: '0.3'}`, 36 | required: `false` 37 | },{ 38 | prop: `rotate`, 39 | desc: `符号旋转角度`, 40 | type: `number`, 41 | default: `0`, 42 | required: `false` 43 | },{ 44 | prop: `symbolSize`, 45 | desc: `符号大小`, 46 | type: `number`, 47 | default: `90`, 48 | required: `false` 49 | },{ 50 | prop: `gap`, 51 | desc: `符号间距`, 52 | type: `number`, 53 | default: `10`, 54 | required: `false` 55 | },{ 56 | prop: `animation`, 57 | desc: `滚动动画`, 58 | type: `{ 59 | type: 'left' | 'right' | 'top' | 'bottom'; 60 | speed: number; 61 | }`, 62 | default: ``, 63 | required: `false` 64 | },{ 65 | prop: `childrenWrapClassName`, 66 | desc: `子组件容器类名`, 67 | type: `string`, 68 | default: ``, 69 | required: `false` 70 | }, 71 | { 72 | prop: `childrenWrapStyle`, 73 | desc: `子组件容器样式`, 74 | type: `React.CSSProperties`, 75 | default: ``, 76 | required: `false` 77 | } 78 | ]; 79 | 80 | const columns = [ 81 | { 82 | title: '属性', 83 | dataIndex: 'prop', 84 | key: 'prop', 85 | }, 86 | { 87 | title: '说明', 88 | dataIndex: 'desc', 89 | key: 'desc', 90 | }, 91 | { 92 | title: '类型', 93 | dataIndex: 'type', 94 | key: 'type', 95 | }, 96 | { 97 | title: '默认值', 98 | dataIndex: 'default', 99 | key: 'default', 100 | }, 101 | { 102 | title: '是否必传', 103 | dataIndex: 'required', 104 | key: 'required', 105 | }, 106 | ]; 107 | 108 | return 109 | } -------------------------------------------------------------------------------- /example/src/pages/components/Code1.tsx: -------------------------------------------------------------------------------- 1 | export default `import React from 'react'; 2 | import Background from 'smart-background'; 3 | 4 | const symbols = ['乾','坤','震','巽','坎','离','艮','兑','天','地','雷','风','水','火','山','泽'] 5 | 6 | export default () => { 7 | return ( 8 |
9 | 18 |
19 | 20 |

乾坤

21 |
22 |
23 |
24 | ); 25 | }; 26 | 27 | const styles = { 28 | container: { width: '100%', position: 'relative', height: 350 }, 29 | content: { 30 | width: '100%', 31 | height: '100%', 32 | display: 'flex', 33 | justifyContent: 'center', 34 | alignItems: 'center', 35 | flexDirection: 'column', 36 | fontWeight: 'bold', 37 | }, 38 | icon: { color: '#fff', fontSize: 120 }, 39 | text: { color: '#fff', fontSize: 36, fontWeight: 'bold' }, 40 | };`; 41 | 42 | 43 | // import React from 'react'; 44 | // import Background from 'smart-background'; 45 | 46 | // export default () => { 47 | // return ( 48 | //
49 | // 74 | //
77 | // 78 | //

乾坤

79 | //
80 | //
81 | //
82 | // ); 83 | // }; 84 | 85 | // const styles = { 86 | // container: { width: '100%', position: 'relative', height: 350 }, 87 | // content: { 88 | // width: '100%', 89 | // height: '100%', 90 | // display: 'flex', 91 | // justifyContent: 'center', 92 | // alignItems: 'center', 93 | // flexDirection: 'column', 94 | // fontWeight: 'bold', 95 | // }, 96 | // icon: { color: '#fff', fontSize: 120 }, 97 | // text: { color: '#fff', fontSize: 36, fontWeight: 'bold' }, 98 | // }; 99 | -------------------------------------------------------------------------------- /example/src/pages/components/Code2.tsx: -------------------------------------------------------------------------------- 1 | export default `import React from 'react'; 2 | import Background from 'smart-background'; 3 | 4 | export default () => { 5 | return ( 6 |
7 | ( 13 |
19 | )), 20 | ]} 21 | animation={{ type: 'top', speed: 5 }} 22 | > 23 |
24 | 😋 25 |

26 | Nice Image 27 |

28 |
29 | 30 |
31 | ); 32 | }; 33 | 34 | const styles = { 35 | container: { width: '100%', position: 'relative', height: 350 }, 36 | content: { 37 | width: '100%', 38 | height: '100%', 39 | display: 'flex', 40 | justifyContent: 'center', 41 | alignItems: 'center', 42 | flexDirection: 'column', 43 | background: 'rgba(0,0,0,0.5)', 44 | fontSize: 120, 45 | }, 46 | img:{ 47 | width: '100%', 48 | height: '100%', 49 | backgroundSize: 'cover', 50 | transform: 'scale(1.2)', 51 | }, 52 | text: { color: '#fff', fontSize: 36, fontWeight: 'bold' }, 53 | }; 54 | 55 | const images = [ 56 | 'https://cdn.dribbble.com/users/3550736/screenshots/16244010/media/cead570591b124ed91c34dc9958f315c.jpg?compress=1&resize=1200x900', 57 | 'https://cdn.dribbble.com/users/3550736/screenshots/16244010/media/f03f7960c2d88f6fec3b43b9e1b5935b.jpg?compress=1&resize=1600x1200', 58 | 'https://cdn.dribbble.com/users/4666085/screenshots/16244479/media/d3d5b6d3e546fa17170b5daa46de375e.png?compress=1&resize=1200x900', 59 | 'https://cdn.dribbble.com/users/4588540/screenshots/16243929/media/430745b49a20f462bbfbdabc77b542f9.png?compress=1&resize=1200x900', 60 | 'https://cdn.dribbble.com/users/4835348/screenshots/16229715/media/5c68b55f75b04e96ff6f110ab2617996.png?compress=1&resize=800x600', 61 | 'https://cdn.dribbble.com/users/323673/screenshots/16223702/media/60b90d6e0f673e0ccee30056b8ae83d2.png?compress=1&resize=1200x900', 62 | 'https://cdn.dribbble.com/users/427857/screenshots/16157651/media/d8739d9147bb28ae6376e2206f67ba60.png?compress=1&resize=1600x1200', 63 | 'https://cdn.dribbble.com/users/427857/screenshots/16157651/media/18fcbf0c65cb47c14f633b162042cc65.png?compress=1&resize=1600x1200', 64 | 'https://cdn.dribbble.com/users/427857/screenshots/16157651/media/ecd0b4a299aabb66c8358b1051a139cd.png?compress=1&resize=1600x1200', 65 | 'https://cdn.dribbble.com/users/6532302/screenshots/16244413/media/c554d3e5bcf8c680ae56852b1b290fa7.jpg?compress=1&resize=1200x900', 66 | 'https://cdn.dribbble.com/users/2192147/screenshots/16242676/media/20f56e6b73bfc7ee4b9d9143f6449ad3.jpg?compress=1&resize=1200x900', 67 | 'https://cdn.dribbble.com/users/730703/screenshots/16207835/media/a9ad81cbcc73c87629471f4546828f2c.gif', 68 | 'https://cdn.dribbble.com/users/86429/screenshots/16241756/media/2d6331f16965e1ee4453b197e4d7f442.jpg?compress=1&resize=800x600', 69 | 'https://cdn.dribbble.com/users/5462867/screenshots/16165195/media/2a7203b0e3d1bbca91be7565d25d3f39.jpg?compress=1&resize=1200x900', 70 | 'https://cdn.dribbble.com/users/500242/screenshots/15428350/media/7b8a007e88d9050fe3d52c3625d2ff24.gif', 71 | ]; 72 | 73 | `; 74 | 75 | -------------------------------------------------------------------------------- /example/src/pages/index.less: -------------------------------------------------------------------------------- 1 | 2 | html,body{ 3 | background: #fff; 4 | } 5 | .App { 6 | position:relative; 7 | min-height:100vh; 8 | .wrap { 9 | width: 50%; 10 | max-width:1024px; 11 | transform: translateX(-50%); 12 | position:absolute; 13 | padding-top: 50px; 14 | left:50%; 15 | margin: 0 auto; 16 | @media screen and (max-width:1200px){ 17 | min-width:90%; 18 | } 19 | } 20 | .background{ 21 | position:absolute; 22 | background:#FCD503; 23 | min-height:480px; 24 | width:100%; 25 | height:30vh; 26 | top: 0; 27 | left:0; 28 | overflow: hidden; 29 | @media screen and (max-width:1200px){ 30 | height:40vh; 31 | min-height:740px; 32 | } 33 | .symbol{ 34 | color:rgba(128,128,128,0.2); 35 | position:absolute; 36 | } 37 | 38 | } 39 | .alignCenter{text-align: center} 40 | .logo { 41 | text-align:center; 42 | font-size: 100px; 43 | font-weight: bold; 44 | line-height: 100px; 45 | user-select: none; 46 | } 47 | .name { 48 | display: inline-block; 49 | font-size: 50px; 50 | font-weight: bold; 51 | vertical-align: top; 52 | } 53 | 54 | .container { 55 | border-radius: 10px; 56 | overflow: hidden; 57 | width: 100%; 58 | height: 350px; 59 | background:#fff; 60 | box-shadow: 5px 5px 20px rgba(0, 0, 0, 0.3); 61 | margin:20px 0; 62 | position: relative; 63 | } 64 | 65 | .block{ 66 | float: left; 67 | display: flex; 68 | justify-content: center; 69 | align-items: center; 70 | transition: all 0.3s; 71 | position: relative; 72 | &:hover { 73 | opacity: 0.9; 74 | transition: all 0.3s; 75 | } 76 | &:focus{ 77 | border:1px solid rgba(128,128,128,0.5); 78 | opacity:0.8; 79 | } 80 | &:active{ 81 | border:1px solid rgba(128,128,128,0.5); 82 | 83 | } 84 | } 85 | 86 | .TopSide { 87 | .block; 88 | background: #20252b; 89 | width: 100%; 90 | height: 50px; 91 | 92 | } 93 | 94 | .LeftSide { 95 | .block; 96 | background: #202020; 97 | height: calc(100% - 50px); 98 | 99 | } 100 | 101 | .RightSide { 102 | .block; 103 | background: #272c34; 104 | height: calc(100% - 50px); 105 | } 106 | 107 | .key { 108 | /* border:1px solid gray; */ 109 | margin: 5px; 110 | background: #fff; 111 | color: #000; 112 | padding: 3px 5px; 113 | border-radius: 3px; 114 | font-size: 12px; 115 | font-weight: bold; 116 | } 117 | 118 | .count { 119 | font-size: 14px; 120 | font-weight: bold; 121 | color: #fff; 122 | margin: 0 10px; 123 | } 124 | 125 | .tip { 126 | height: 50px; 127 | line-height: 50px; 128 | padding-left: 20px; 129 | font-size: 18px; 130 | font-weight: bold; 131 | color: #fff; 132 | position: absolute; 133 | top: 0px; 134 | left: 0px; 135 | } 136 | 137 | 138 | .code{border-radius:10px; overflow:hidden; margin:20px 0px;} 139 | 140 | pre{margin-bottom:0;padding: 30px!important;} 141 | .footer{width:100%; text-align:center; padding:50px;} 142 | .github{ 143 | position:absolute; 144 | right:20px; 145 | top:20px; 146 | width:100px; 147 | height:20px; 148 | background-repeat: no-repeat; 149 | background-position: center; 150 | width: 100px; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Smart Background 2 | [![npm version](https://badge.fury.io/js/smart-background.svg)](#) [![npm version](https://img.shields.io/badge/react-%3E16-green)](#) [![npm version]( https://img.shields.io/github/issues/yuanguandong/smart-background)](#) [![npm version]( https://img.shields.io/github/forks/yuanguandong/smart-background)](#) [![npm version](https://img.shields.io/github/license/yuanguandong/smart-background)](#) [![npm version]( https://img.shields.io/github/stars/yuanguandong/smart-background)](#) 3 | ### An React Component Can Automatically Generate The Background 4 | 一个快速生成元素背景的react组件 5 | 6 | ![repository-open-graph-template副本](./snapshot.png) 7 | 8 | 9 | ### Live demo 10 | https://yuanguandong.github.io/smart-background/ 11 | 12 | ### Install 13 | ```bash 14 | npm i smart-background -S 15 | ``` 16 | 17 | ### How to use 18 | ```js 19 | import React from 'react'; 20 | import Background from 'smart-background'; 21 | import { FaLaugh } from 'react-icons/fa'; 22 | 23 | export default () => { 24 | return ( 25 |
26 | 27 |
28 | 29 |

JUST DO IT

30 |
31 |
32 |
33 | ); 34 | }; 35 | 36 | const styles = { 37 | container: { width: '100%', position: 'relative', height: 350 }, 38 | content: { 39 | width: '100%', 40 | height: '100%', 41 | display: 'flex', 42 | justifyContent: 'center', 43 | alignItems: 'center', 44 | flexDirection: 'column', 45 | }, 46 | icon: { color: '#fff', fontSize: 120 }, 47 | text: { color: '#fff', fontSize: 36, fontWeight: 'bold' }, 48 | }; 49 | ``` 50 | 51 | ### Props 52 | 53 | | property | description | type | defaultValue | required | 54 | | --------------------- | -------------------------- | -------------------------------------------------------------- | ------------------------------- | -------- | 55 | | symbols | 元素/字符/符号集合 | (string \| ReactNode \| Element)[ ] | ['●'] | false | 56 | | random | 符号是否随机生成位置和大小 | { fontSizeRange: number[] } \| undefined | | false | 57 | | underlayColor | 底衬颜色 | string | | false | 58 | | underlayImage | 底衬图片 | string | | false | 59 | | symbolsStyle | 符号样式 | Object | {color: '#000', opacity: '0.3'} | false | 60 | | rotate | 符号旋转角度 | number | 0 | false | 61 | | symbolSize | 符号大小 | number | 90 | false | 62 | | gap | 符号间距 | number | 10 | false | 63 | | animation | 滚动动画 | {type: 'left' \| 'right' \| 'top' \| 'bottom'; speed: number;} | | false | 64 | | exact | 精确模式 | boolean | false | false | 65 | | childrenWrapClassName | 子组件容器类名 | string | | false | 66 | | childrenWrapStyle | 子组件容器类名 | React.CSSProperties | | false | 67 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import React, { ReactNode, useEffect, useMemo, useState } from "react"; 3 | import { useResizeDetector } from "react-resize-detector"; 4 | import stylesArr from "./style"; 5 | import "./style.ts"; 6 | import { getPosition } from "./utils"; 7 | 8 | const REPEAT = [1, 2]; 9 | 10 | const styles: any = { 11 | SmartBackground: { 12 | overflow: "hidden", 13 | position: "absolute", 14 | top: 0, 15 | left: 0, 16 | width: "100%", 17 | height: "100%", 18 | zIndex: 0, 19 | backgroundRepeat: "repeat", 20 | backgroundSize: "cover", 21 | backgroundPosition: "center", 22 | pointerEvents: "none", 23 | }, 24 | }; 25 | 26 | type Animation = { 27 | type: "left" | "right" | "top" | "bottom"; 28 | speed: number; 29 | }; 30 | 31 | interface SmartBackgroundProps { 32 | symbols?: (string | ReactNode | Element)[]; //字符/符号集合 33 | random?: { fontSizeRange: number[] } | undefined; //符号是否随机生成位置和大小 34 | underlayColor?: string; //底衬颜色 35 | underlayImage?: string; //底衬图片 36 | symbolsStyle?: Object; //符号样式 37 | rotate?: number; //符号旋转角度 38 | symbolSize?: number; //符号大小 39 | gap?: number; //符号间距 40 | animation?: Animation | undefined; //滚动动画 41 | exact?: boolean; //精确模式 42 | childrenWrapClassName?: string; //子组件容器类名 43 | childrenWrapStyle?: React.CSSProperties; //子组件容器类名 44 | [key: string]: any; 45 | } 46 | 47 | const SmartBackground = (props: SmartBackgroundProps) => { 48 | const { 49 | underlayColor, 50 | underlayImage, 51 | symbols = ["●"], 52 | 53 | symbolsStyle, 54 | random, 55 | rotate = 0, 56 | symbolSize: _fontSize = 90, 57 | gap: _gap = 10, 58 | animation, 59 | exact = false, 60 | childrenWrapClassName, 61 | childrenWrapStyle, 62 | 63 | children, 64 | style, 65 | className, 66 | ...restProps 67 | } = props; 68 | 69 | // const backgroundRef = useRef(null); 70 | const { 71 | width: containerWidth, 72 | height: containerHeight, 73 | ref, 74 | } = useResizeDetector(); 75 | const [amount, setAmount] = useState(symbols.length); 76 | 77 | const fontSize = Number(_fontSize); 78 | const gap = Number(_gap) / 2; 79 | const CubeWidth = fontSize + 2 * Number(gap || 1); 80 | // const CubeWidth = fontSize 81 | 82 | const syncAmount = () => { 83 | if (exact || random) { 84 | return; 85 | } 86 | if (containerWidth && containerHeight) { 87 | const moWidth = containerWidth % CubeWidth; 88 | const moHeight = containerHeight % CubeWidth; 89 | 90 | const widthFin = containerWidth - moWidth; 91 | const heightFin = containerHeight - moHeight; 92 | 93 | const amount = Math.floor( 94 | (widthFin * heightFin) / (CubeWidth * CubeWidth) 95 | ); 96 | setAmount(amount); 97 | } 98 | }; 99 | 100 | const repeatArr = exact || random ? [1] : REPEAT; 101 | 102 | useEffect(() => { 103 | syncAmount(); 104 | }, [containerWidth, containerHeight]); 105 | 106 | const symbolsFin = useMemo(() => { 107 | let arr = new Array(amount); 108 | for (let i = 0; i < amount; i++) { 109 | arr[i] = symbols[i % symbols.length]; 110 | } 111 | return arr; 112 | }, [amount, symbols]); 113 | 114 | useEffect(() => { 115 | let styleSheet: any = document.styleSheets[0]; 116 | const styleLength = styleSheet.cssRules.length; 117 | let hasLoadCss = false; 118 | for (let i = styleLength - 1; i > 0; i--) { 119 | if ( 120 | styleSheet["cssRules"][i] && 121 | styleSheet["cssRules"][i]["name"] === "smart-background-scroll-top" 122 | ) { 123 | hasLoadCss = true; 124 | break; 125 | } 126 | } 127 | if (hasLoadCss) { 128 | return; 129 | } 130 | stylesArr.forEach((style) => { 131 | styleSheet.insertRule(style, styleLength); 132 | }); 133 | }, []); 134 | 135 | return ( 136 | <> 137 |
147 | {repeatArr.map((index) => ( 148 | 155 | {symbolsFin.map((item, index) => { 156 | return ( 157 | 166 | {item} 167 | 168 | ); 169 | })} 170 | 171 | ))} 172 |
173 | {children && ( 174 |
186 | {children} 187 |
188 | )} 189 | 190 | ); 191 | }; 192 | export default SmartBackground; 193 | 194 | const SymbolList = styled.div<{ 195 | index: number; 196 | animation: Animation | undefined; 197 | random: { fontSizeRange: number[] } | undefined; 198 | exact?: boolean; 199 | [key: string]: any; 200 | }>` 201 | width: 100%; 202 | height: 100%; 203 | display: ${(p) => (p.random || p.exact ? "block" : "flex")}; 204 | position: ${(p) => (p.exact ? "relative" : "")}; 205 | flex-direction: row; 206 | animation: ${(p) => { 207 | if (!p.animation) { 208 | return; 209 | } 210 | return `smart-background-scroll-${p.animation.type} ${ 211 | 100 / (p.animation.speed || 5) 212 | }s linear infinite`; 213 | }}; 214 | flex-wrap: wrap; 215 | justify-content: space-around; 216 | position: ${(p) => { 217 | if (!p.animation) { 218 | return; 219 | } 220 | if ( 221 | p.animation.type === "left" || 222 | p.animation.type === "right" || 223 | p.animation.type === "bottom" 224 | ) { 225 | return "absolute"; 226 | } 227 | }}; 228 | top: ${(p) => { 229 | if (!p.animation) { 230 | return; 231 | } 232 | if (p.animation.type === "left" || p.animation.type === "right") { 233 | return "0"; 234 | } 235 | if (p.animation.type === "bottom") { 236 | return p.index === 1 ? 0 : "-100%"; 237 | } 238 | }}; 239 | left: ${(p) => { 240 | if (!p.animation) { 241 | return; 242 | } 243 | if (p.animation.type === "left") { 244 | return p.index === 1 ? 0 : "100%"; 245 | } 246 | if (p.animation.type === "right") { 247 | return p.index === 1 ? 0 : "-100%"; 248 | } 249 | }}; 250 | `; 251 | 252 | const SymbolContainer = styled.div<{ 253 | fontSize?: number; 254 | rotate?: number; 255 | gap?: number; 256 | symbolsStyle?: object; 257 | random?: { fontSizeRange: number[] } | undefined; 258 | exact?: boolean; 259 | [key: string]: any; 260 | }>(({ fontSize, gap = 100, rotate, symbolsStyle, random, exact }) => { 261 | let fontSizeFin = fontSize || 90; 262 | return { 263 | color: "#000", 264 | opacity: "0.3", 265 | width: exact ? "fit-content" : fontSizeFin + 2 * gap, 266 | height: exact ? "fit-content" : fontSizeFin + 2 * gap, 267 | lineHeight: fontSizeFin + "px", 268 | textAlign: "center", 269 | padding: gap, 270 | transform: exact ? "none" : `rotate(${rotate}deg)`, 271 | ...symbolsStyle, 272 | position: random ? "absolute" : "unset", 273 | ...getPosition(random, fontSizeFin), 274 | display: "inline-block", 275 | }; 276 | }); 277 | -------------------------------------------------------------------------------- /example/src/pages/package/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import React, { ReactNode, useEffect, useMemo, useState } from "react"; 3 | import { useResizeDetector } from "react-resize-detector"; 4 | import stylesArr from "./style"; 5 | import "./style.ts"; 6 | import { getPosition } from "./utils"; 7 | 8 | const REPEAT = [1, 2]; 9 | 10 | const styles: any = { 11 | SmartBackground: { 12 | overflow: "hidden", 13 | position: "absolute", 14 | top: 0, 15 | left: 0, 16 | width: "100%", 17 | height: "100%", 18 | zIndex: 0, 19 | backgroundRepeat: "repeat", 20 | backgroundSize: "cover", 21 | backgroundPosition: "center", 22 | pointerEvents: "none", 23 | }, 24 | }; 25 | 26 | type Animation = { 27 | type: "left" | "right" | "top" | "bottom"; 28 | speed: number; 29 | }; 30 | 31 | interface SmartBackgroundProps { 32 | symbols?: (string | ReactNode | Element)[]; //字符/符号集合 33 | random?: { fontSizeRange: number[] } | undefined; //符号是否随机生成位置和大小 34 | underlayColor?: string; //底衬颜色 35 | underlayImage?: string; //底衬图片 36 | symbolsStyle?: Object; //符号样式 37 | rotate?: number; //符号旋转角度 38 | symbolSize?: number; //符号大小 39 | gap?: number; //符号间距 40 | animation?: Animation | undefined; //滚动动画 41 | exact?: boolean; //精确模式 42 | childrenWrapClassName?: string; //子组件容器类名 43 | childrenWrapStyle?: React.CSSProperties; //子组件容器类名 44 | [key: string]: any; 45 | } 46 | 47 | const SmartBackground = (props: SmartBackgroundProps) => { 48 | const { 49 | underlayColor, 50 | underlayImage, 51 | symbols = ["●"], 52 | 53 | symbolsStyle, 54 | random, 55 | rotate = 0, 56 | symbolSize: _fontSize = 90, 57 | gap: _gap = 10, 58 | animation, 59 | exact = false, 60 | childrenWrapClassName, 61 | childrenWrapStyle, 62 | 63 | children, 64 | style, 65 | className, 66 | ...restProps 67 | } = props; 68 | 69 | // const backgroundRef = useRef(null); 70 | const { 71 | width: containerWidth, 72 | height: containerHeight, 73 | ref, 74 | } = useResizeDetector(); 75 | const [amount, setAmount] = useState(symbols.length); 76 | 77 | const fontSize = Number(_fontSize); 78 | const gap = Number(_gap) / 2; 79 | const CubeWidth = fontSize + 2 * Number(gap || 1); 80 | // const CubeWidth = fontSize 81 | 82 | const syncAmount = () => { 83 | if (exact || random) { 84 | return; 85 | } 86 | if (containerWidth && containerHeight) { 87 | const moWidth = containerWidth % CubeWidth; 88 | const moHeight = containerHeight % CubeWidth; 89 | 90 | const widthFin = containerWidth - moWidth; 91 | const heightFin = containerHeight - moHeight; 92 | 93 | const amount = Math.floor( 94 | (widthFin * heightFin) / (CubeWidth * CubeWidth) 95 | ); 96 | setAmount(amount); 97 | } 98 | }; 99 | 100 | const repeatArr = exact || random ? [1] : REPEAT; 101 | 102 | useEffect(() => { 103 | syncAmount(); 104 | }, [containerWidth, containerHeight]); 105 | 106 | const symbolsFin = useMemo(() => { 107 | let arr = new Array(amount); 108 | for (let i = 0; i < amount; i++) { 109 | arr[i] = symbols[i % symbols.length]; 110 | } 111 | return arr; 112 | }, [amount, symbols]); 113 | 114 | useEffect(() => { 115 | let styleSheet: any = document.styleSheets[0]; 116 | const styleLength = styleSheet.cssRules.length; 117 | let hasLoadCss = false; 118 | for (let i = styleLength - 1; i > 0; i--) { 119 | if ( 120 | styleSheet["cssRules"][i] && 121 | styleSheet["cssRules"][i]["name"] === "smart-background-scroll-top" 122 | ) { 123 | hasLoadCss = true; 124 | break; 125 | } 126 | } 127 | if (hasLoadCss) { 128 | return; 129 | } 130 | stylesArr.forEach((style) => { 131 | styleSheet.insertRule(style, styleLength); 132 | }); 133 | }, []); 134 | 135 | return ( 136 | <> 137 |
147 | {repeatArr.map((index) => ( 148 | 155 | {symbolsFin.map((item, index) => { 156 | return ( 157 | 166 | {item} 167 | 168 | ); 169 | })} 170 | 171 | ))} 172 |
173 | {children && ( 174 |
186 | {children} 187 |
188 | )} 189 | 190 | ); 191 | }; 192 | export default SmartBackground; 193 | 194 | const SymbolList = styled.div<{ 195 | index: number; 196 | animation: Animation | undefined; 197 | random: { fontSizeRange: number[] } | undefined; 198 | exact?: boolean; 199 | [key: string]: any; 200 | }>` 201 | width: 100%; 202 | height: 100%; 203 | display: ${(p) => (p.random || p.exact ? "block" : "flex")}; 204 | position: ${(p) => (p.exact ? "relative" : "")}; 205 | flex-direction: row; 206 | animation: ${(p) => { 207 | if (!p.animation) { 208 | return; 209 | } 210 | return `smart-background-scroll-${p.animation.type} ${ 211 | 100 / (p.animation.speed || 5) 212 | }s linear infinite`; 213 | }}; 214 | flex-wrap: wrap; 215 | justify-content: space-around; 216 | position: ${(p) => { 217 | if (!p.animation) { 218 | return; 219 | } 220 | if ( 221 | p.animation.type === "left" || 222 | p.animation.type === "right" || 223 | p.animation.type === "bottom" 224 | ) { 225 | return "absolute"; 226 | } 227 | }}; 228 | top: ${(p) => { 229 | if (!p.animation) { 230 | return; 231 | } 232 | if (p.animation.type === "left" || p.animation.type === "right") { 233 | return "0"; 234 | } 235 | if (p.animation.type === "bottom") { 236 | return p.index === 1 ? 0 : "-100%"; 237 | } 238 | }}; 239 | left: ${(p) => { 240 | if (!p.animation) { 241 | return; 242 | } 243 | if (p.animation.type === "left") { 244 | return p.index === 1 ? 0 : "100%"; 245 | } 246 | if (p.animation.type === "right") { 247 | return p.index === 1 ? 0 : "-100%"; 248 | } 249 | }}; 250 | `; 251 | 252 | const SymbolContainer = styled.div<{ 253 | fontSize?: number; 254 | rotate?: number; 255 | gap?: number; 256 | symbolsStyle?: object; 257 | random?: { fontSizeRange: number[] } | undefined; 258 | exact?: boolean; 259 | [key: string]: any; 260 | }>(({ fontSize, gap = 100, rotate, symbolsStyle, random, exact }) => { 261 | let fontSizeFin = fontSize || 90; 262 | return { 263 | color: "#000", 264 | opacity: "0.3", 265 | width: exact ? "fit-content" : fontSizeFin + 2 * gap, 266 | height: exact ? "fit-content" : fontSizeFin + 2 * gap, 267 | lineHeight: fontSizeFin + "px", 268 | textAlign: "center", 269 | padding: gap, 270 | transform: exact ? "none" : `rotate(${rotate}deg)`, 271 | ...symbolsStyle, 272 | position: random ? "absolute" : "unset", 273 | ...getPosition(random, fontSizeFin), 274 | display: "inline-block", 275 | }; 276 | }); 277 | -------------------------------------------------------------------------------- /example/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from 'antd'; 2 | import React, { useState } from 'react'; 3 | import { 4 | Fa500Px, 5 | FaAdobe, 6 | FaAdversal, 7 | FaAirbnb, 8 | FaAlipay, 9 | FaAmazon, 10 | FaAmazonPay, 11 | FaApple, 12 | FaAppStoreIos, 13 | FaBehanceSquare, 14 | FaGoogle, 15 | FaInstagramSquare, 16 | FaLaugh, 17 | FaMagento, 18 | FaOpera, 19 | FaYinYang 20 | } from 'react-icons/fa'; 21 | import { Helmet } from 'umi'; 22 | import packageInfo from '../config'; 23 | import { 24 | Api, 25 | Code, 26 | Code0, 27 | Code1, 28 | Code2, 29 | Code3, 30 | Code4, 31 | Footer 32 | } from './components'; 33 | import './index.less'; 34 | import Intro from './intro'; 35 | // import Background from 'smart-background'; 36 | import Background from './package'; 37 | const { Title, Paragraph, Text, Link } = Typography; 38 | 39 | const images = [ 40 | 'https://cdn.dribbble.com/users/3550736/screenshots/16244010/media/cead570591b124ed91c34dc9958f315c.jpg?compress=1&resize=1200x900', 41 | 'https://cdn.dribbble.com/users/3550736/screenshots/16244010/media/f03f7960c2d88f6fec3b43b9e1b5935b.jpg?compress=1&resize=1600x1200', 42 | 'https://cdn.dribbble.com/users/4666085/screenshots/16244479/media/d3d5b6d3e546fa17170b5daa46de375e.png?compress=1&resize=1200x900', 43 | 'https://cdn.dribbble.com/users/4588540/screenshots/16243929/media/430745b49a20f462bbfbdabc77b542f9.png?compress=1&resize=1200x900', 44 | 'https://cdn.dribbble.com/users/4835348/screenshots/16229715/media/5c68b55f75b04e96ff6f110ab2617996.png?compress=1&resize=800x600', 45 | 'https://cdn.dribbble.com/users/323673/screenshots/16223702/media/60b90d6e0f673e0ccee30056b8ae83d2.png?compress=1&resize=1200x900', 46 | 'https://cdn.dribbble.com/users/427857/screenshots/16157651/media/d8739d9147bb28ae6376e2206f67ba60.png?compress=1&resize=1600x1200', 47 | 'https://cdn.dribbble.com/users/427857/screenshots/16157651/media/18fcbf0c65cb47c14f633b162042cc65.png?compress=1&resize=1600x1200', 48 | 'https://cdn.dribbble.com/users/427857/screenshots/16157651/media/ecd0b4a299aabb66c8358b1051a139cd.png?compress=1&resize=1600x1200', 49 | 'https://cdn.dribbble.com/users/6532302/screenshots/16244413/media/c554d3e5bcf8c680ae56852b1b290fa7.jpg?compress=1&resize=1200x900', 50 | 'https://cdn.dribbble.com/users/2192147/screenshots/16242676/media/20f56e6b73bfc7ee4b9d9143f6449ad3.jpg?compress=1&resize=1200x900', 51 | 'https://cdn.dribbble.com/users/730703/screenshots/16207835/media/a9ad81cbcc73c87629471f4546828f2c.gif', 52 | 'https://cdn.dribbble.com/users/86429/screenshots/16241756/media/2d6331f16965e1ee4453b197e4d7f442.jpg?compress=1&resize=800x600', 53 | 'https://cdn.dribbble.com/users/5462867/screenshots/16165195/media/2a7203b0e3d1bbca91be7565d25d3f39.jpg?compress=1&resize=1200x900', 54 | 'https://cdn.dribbble.com/users/500242/screenshots/15428350/media/7b8a007e88d9050fe3d52c3625d2ff24.gif', 55 | ]; 56 | 57 | const icons = [ 58 | , 59 | , 60 | , 61 | , 62 | , 63 | , 64 | , 65 | , 66 | , 67 | , 68 | , 69 | , 70 | , 71 | , 72 | ]; 73 | 74 | const icons1 = [ 75 | , 76 | , 77 | , 78 | , 79 | , 80 | ]; 81 | 82 | const dots = [ 83 | { 84 | x: '-10%', 85 | y: '-20%', 86 | size: 200, 87 | background: 'linear-gradient(to top, #0ba360 0%, #3cba92 100%)', 88 | borderRadius: '50%', 89 | }, 90 | { 91 | x: '60%', 92 | y: '40%', 93 | size: 500, 94 | background: 95 | 'linear-gradient(to right, #f78ca0 0%, #f9748f 19%, #fd868c 60%, #fe9a8b 100%)', 96 | borderRadius: '50%', 97 | }, 98 | { 99 | x: '-30%', 100 | y: '50%', 101 | size: 450, 102 | background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', 103 | borderRadius: '50%', 104 | }, 105 | ]; 106 | 107 | interface IProps {} 108 | const Index: React.FunctionComponent = () => { 109 | const [collapsed, setCollapsed] = useState(false); 110 | const onCtrlB = () => { 111 | setCollapsed(!collapsed); 112 | }; 113 | console.log('packageInfo',packageInfo) 114 | return ( 115 | <> 116 | 117 | 118 | 119 | Smart Background —— An React Component Automatically generate the 120 | background 121 | 122 | 123 | 124 | 125 |
126 | 131 | 132 | 143 | 151 | 152 | 153 |
154 |
155 |
156 | {/* react-keyevent */} 157 |
158 | {packageInfo.symbol} 159 |
160 | 161 | Smart Background 162 | 163 |
164 |
165 | 166 |
167 | 171 |
181 | 182 |

183 | JUST DO IT 184 |

185 |
186 |
187 |
188 | {/* Easy To Use 容易使用 */} 189 | 默认以圆点作为符号 190 | 191 | Animation 动画 192 | 支持四个方向的滚动循环动画,可以控制速度 193 | GPU rendering, Does not occupy the js process 194 | GPU 渲染, 动画不占用javascript线程 195 |
196 | 222 |
233 | 234 |

乾坤

235 |
236 |
237 |
238 | 239 | Curtain 幕布 240 | 使用适当的实现可以很方便的实现一个图片幕布墙 241 |
242 | ( 248 |
257 | )), 258 | ]} 259 | animation={{ type: 'top', speed: 5 }} 260 | > 261 |
273 | 😋 274 |

275 | Nice Image 276 |

277 |
278 | 279 |
280 | 281 | Exact 精确模式 282 | 使用精确模式,可以让元素固定在某一位置 283 |
284 | ( 289 |
300 | )), 301 | ]} 302 | > 303 |
314 | 315 |

316 | Apple 317 |

318 |
319 | 320 |
321 | 322 | Random 随机模式 323 | 使用随机模式,可以让元素位置随机显示 324 |
325 | 331 |
342 | 343 |

344 | Apple 345 |

346 |
347 |
348 |
349 | 350 | Why 为什么? 351 | 在开发过程中,我们经常会遇到使用背景的地方,比如登录页面,用户信息页面,封面图…… 352 | 寻找契合业务主题的背景十分耗费精力,总觉得做的背景不合适,如果直接用图片呢,逻辑是比较简单,但寻找到一张契合业务主题的图片也不是那么容易,所以想到用符号生成幕布一样的背景,从这个出发点做了这个组件,滚动的图片墙可能这个需求比较常见,用SmartBackground可以很快速的实现,希望可以帮到您,别忘了star哟 353 | Props 354 | 355 | 356 | Tip 注意 357 | Background的高度默认继承父级元素 358 | 如果Background有children,需要给Background的父级元素添加position:relative属性 359 |
360 |
361 |
362 | 363 | ); 364 | }; 365 | 366 | export default Index; 367 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("react"),require("react-dom")):"function"==typeof define&&define.amd?define(["react","react-dom"],t):(e="undefined"!=typeof globalThis?globalThis:e||self)["smart-background"]=t(e.React,e.reactDom)}(this,function(L,o){"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function t(r){if(r&&r.__esModule)return r;var n=Object.create(null);return r&&Object.keys(r).forEach(function(e){var t;"default"!==e&&(t=Object.getOwnPropertyDescriptor(r,e),Object.defineProperty(n,e,t.get?t:{enumerable:!0,get:function(){return r[e]}}))}),n.default=r,Object.freeze(n)}var s=t(L),H=e(L),r="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};var n,a,i=(function(e){function t(){return e.exports=t=Object.assign||function(e){for(var t=1;t css`color: ${props.color}`\nIt can be called directly with props or interpolated in a styled call like this\nlet SomeComponent = styled('div')`${dynamicStyle}`");break;case"string":var i;"production"!==process.env.NODE_ENV&&(i=[],a=r.replace(ze,function(e,t,r){var n="animation"+i.length;return i.push("const "+n+" = keyframes`"+r.replace(/^@keyframes animation-\w+/,"")+"`"),"${"+n+"}"}),i.length&&console.error("`keyframes` output got interpolated into plain string, please wrap it with `css`.\n\nInstead of doing this:\n\n"+[].concat(i,["`"+a+"`"]).join("\n")+"\n\nYou should wrap it with `css` like this:\n\ncss`"+a+"`"))}if(null==t)return r;var s=t[r];return void 0!==s?s:r}"production"!==process.env.NODE_ENV&&(Se=/(attr|counters?|url|(((repeating-)?(linear|radial))|conic)-gradient)\(|(no-)?(open|close)-quote/,Ce=["normal","none","initial","inherit","unset"],Re=Ve,Ae=/^-ms-/,Te=/-(.)/g,je={},Ve=function(e,t){if("content"===e&&("string"!=typeof t||-1===Ce.indexOf(t)&&!Se.test(t)&&(t.charAt(0)!==t.charAt(t.length-1)||'"'!==t.charAt(0)&&"'"!==t.charAt(0))))throw new Error("You seem to be using a value for 'content' without quotes, try replacing it with `content: '\""+t+"\"'`");t=Re(e,t);return""===t||_e(e)||-1===e.indexOf("-")||void 0!==je[e]||(je[e]=!0,console.error("Using kebab-case for css properties in objects is not supported. Did you mean "+e.replace(Ae,"ms-").replace(Te,function(e,t){return t.toUpperCase()})+"?")),t});var He,qe,We=/label:\s*([^\s;\n{]+)\s*(;|$)/g;"production"!==process.env.NODE_ENV&&(He=/\/\*#\ssourceMappingURL=data:application\/json;\S+\s+\*\//g);function Ye(e,t,r){if(1===e.length&&"object"==typeof e[0]&&null!==e[0]&&void 0!==e[0].styles)return e[0];var n=!0,o="";qe=void 0;var a=e[0];null==a||void 0===a.raw?(n=!1,o+=Le(r,t,a)):("production"!==process.env.NODE_ENV&&void 0===a[0]&&console.error($e),o+=a[0]);for(var i,s=1;s>>16)<<16),r=1540483477*(65535&(t^=t>>>24))+(59797*(t>>>16)<<16)^1540483477*(65535&r)+(59797*(r>>>16)<<16);switch(o){case 3:r^=(255&e.charCodeAt(n+2))<<16;case 2:r^=(255&e.charCodeAt(n+1))<<8;case 1:r=1540483477*(65535&(r^=255&e.charCodeAt(n)))+(59797*(r>>>16)<<16)}return(((r=1540483477*(65535&(r^=r>>>13))+(59797*(r>>>16)<<16))^r>>>15)>>>0).toString(36)}(o)+l;return"production"!==process.env.NODE_ENV?{name:u,styles:o,map:i,next:qe,toString:function(){return"You have tried to stringify object returned from `css` function. It isn't supposed to be used directly (e.g. as value of the `className` prop), but rather handed to emotion so it can handle it (e.g. as value of `css` prop)."}}:{name:u,styles:o,next:qe}}var Fe="undefined"!=typeof document,Ue=Object.prototype.hasOwnProperty,Be=L.createContext("undefined"!=typeof HTMLElement?le({key:"css"}):null);"production"!==process.env.NODE_ENV&&(Be.displayName="EmotionCacheContext"),Be.Provider;var Ge=function(n){return L.forwardRef(function(e,t){var r=L.useContext(Be);return n(e,r,t)})};Fe||(Ge=function(r){return function(e){var t=L.useContext(Be);return null===t?(t=le({key:"css"}),L.createElement(Be.Provider,{value:t},r(e,t))):r(e,t)}});var Xe=L.createContext({});"production"!==process.env.NODE_ENV&&(Xe.displayName="EmotionThemeContext");var Je="__EMOTION_TYPE_PLEASE_DO_NOT_USE__",Ke="__EMOTION_LABEL_PLEASE_DO_NOT_USE__",Ze=Ge(function(e,t,r){var n=e.css;"string"==typeof n&&void 0!==t.registered[n]&&(n=t.registered[n]);var o=e[Je],a=[n],n="";"string"==typeof e.className?n=Oe(t.registered,a,e.className):null!=e.className&&(n=e.className+" ");a=Ye(a,void 0,L.useContext(Xe));"production"===process.env.NODE_ENV||-1!==a.name.indexOf("-")||(i=e[Ke])&&(a=Ye([a,"label:"+i+";"]));var i=xe(t,a,"string"==typeof o);n+=t.key+"-"+a.name;var s,c={};for(s in e)!Ue.call(e,s)||"css"===s||s===Je||"production"!==process.env.NODE_ENV&&s===Ke||(c[s]=e[s]);c.ref=r,c.className=n;o=L.createElement(o,c);if(Fe||void 0===i)return o;for(var l=a.name,u=a.next;void 0!==u;)l+=" "+u.name,u=u.next;return L.createElement(L.Fragment,null,L.createElement("style",((a={})["data-emotion"]=t.key+" "+l,a.dangerouslySetInnerHTML={__html:i},a.nonce=t.sheet.nonce,a)),o)});"production"!==process.env.NODE_ENV&&(Ze.displayName="EmotionCssPropInternal");function Qe(e){return"string"==typeof e&&96e.random||e.exact?"block":"flex"}; 33 | position: ${e=>e.exact?"relative":""}; 34 | flex-direction: row; 35 | animation: ${e=>{if(e.animation)return`smart-background-scroll-${e.animation.type} ${100/(e.animation.speed||5)}s linear infinite`}}; 36 | flex-wrap: wrap; 37 | justify-content: space-around; 38 | position: ${e=>{if(e.animation)return"left"===e.animation.type||"right"===e.animation.type||"bottom"===e.animation.type?"absolute":void 0}}; 39 | top: ${e=>{if(e.animation)return"left"===e.animation.type||"right"===e.animation.type?"0":"bottom"===e.animation.type?1===e.index?0:"-100%":void 0}}; 40 | left: ${e=>{if(e.animation)return"left"===e.animation.type?1===e.index?0:"100%":"right"===e.animation.type?1===e.index?0:"-100%":void 0}}; 41 | `,tr=at.div(({fontSize:e,gap:t=100,rotate:r,symbolsStyle:n,random:o,exact:a})=>{e=e||90;return{color:"#000",opacity:"0.3",width:a?"fit-content":e+2*t,height:a?"fit-content":e+2*t,lineHeight:e+"px",textAlign:"center",padding:t,transform:a?"none":`rotate(${r}deg)`,...n,position:o?"absolute":"unset",...((e,t)=>{if(!e)return{fontSize:t};var t=e["fontSizeRange"],[e,t]=t;return{fontSize:Kt(e,t),top:Kt(0,100)+"%",left:Kt(0,100)+"%"}})(o,e),display:"inline-block"}});return e=>{const{underlayColor:t,underlayImage:r,symbols:n=["●"],symbolsStyle:o,random:a,rotate:i=0,symbolSize:s=90,gap:c=10,animation:l,exact:u=!1,childrenWrapClassName:d,childrenWrapStyle:f,children:p,style:h,className:m,...v}=e,{width:y,height:g,ref:b}=(E=(w=void 0===w?{}:w).skipOnMount,k=w.refreshMode,O=void 0===(e=w.refreshRate)?1e3:e,x=w.refreshOptions,_=void 0===(e=w.handleWidth)||e,N=void 0===(e=w.handleHeight)||e,e=w.targetRef,S=w.observerOptions,C=w.onResize,R=L.useRef(void 0!==E&&E),E=L.useRef(null),A=null!=e?e:E,T=L.useRef(),E=(e=L.useState({width:void 0,height:void 0}))[0],j=e[1],Xt(function(){if(!Ft()){var r=Ut(C,j,_,N);T.current=Wt(function(e){(_||N)&&e.forEach(function(e){var t=e&&e.contentRect||{},e=t.width,t=t.height;R.current||Ft()||r({width:e,height:t}),R.current=!1})},k,O,x);var t=new window.ResizeObserver(T.current);return A.current&&t.observe(A.current,S),function(){t.disconnect();var e=T.current;e&&e.cancel&&e.cancel()}}},[k,O,x,_,N,C,S,A.current]),It({ref:A},E));var w,E,k,O,x,_,N,S,C,R,A,T,j;const[P,$]=L.useState(n.length),D=Number(s),M=Number(c)/2,z=D+2*Number(M||1),I=u||a?[1]:Zt;L.useEffect(()=>{var e,t;u||a||y&&g&&(e=y%z,t=g%z,e=y-e,t=g-t,t=Math.floor(e*t/(z*z)),$(t))},[y,g]);const V=L.useMemo(()=>{let t=new Array(P);for(let e=0;e{let t=document.styleSheets[0];const r=t.cssRules.length;let n=!1;for(let e=r-1;0{t.insertRule(e,r)})},[]),H.default.createElement(H.default.Fragment,null,H.default.createElement("div",{style:{...Qt.SmartBackground,backgroundColor:t,backgroundImage:r,...h},ref:b,...v},I.map(e=>H.default.createElement(er,{key:e,animation:l,random:a,exact:u,index:e},V.map((e,t)=>H.default.createElement(tr,{key:t,symbolsStyle:o,rotate:i,random:a,gap:M,fontSize:D,exact:u},e))))),p&&H.default.createElement("div",{className:d,style:{position:"absolute",width:"100%",height:"100%",top:0,left:0,overflowY:"auto",...f}},p))}}); 42 | -------------------------------------------------------------------------------- /lib/index.esm.js: -------------------------------------------------------------------------------- 1 | import*as React from"react";import React__default,{forwardRef,useContext,createContext,createElement,Fragment,cloneElement,useRef,useState,isValidElement,createRef,PureComponent,useEffect,useLayoutEffect,useMemo}from"react";import{findDOMNode}from"react-dom";var commonjsGlobal="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function unwrapExports(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function createCommonjsModule(e,t){return e(t={exports:{}},t.exports),t.exports}var _extends_1=createCommonjsModule(function(e){function t(){return e.exports=t=Object.assign||function(e){for(var t=1;t>>16)<<16),r=1540483477*(65535&(t^=t>>>24))+(59797*(t>>>16)<<16)^1540483477*(65535&r)+(59797*(r>>>16)<<16);switch(o){case 3:r^=(255&e.charCodeAt(n+2))<<16;case 2:r^=(255&e.charCodeAt(n+1))<<8;case 1:r=1540483477*(65535&(r^=255&e.charCodeAt(n)))+(59797*(r>>>16)<<16)}return(((r=1540483477*(65535&(r^=r>>>13))+(59797*(r>>>16)<<16))^r>>>15)>>>0).toString(36)}var contentValuePattern,contentValues,oldProcessStyleValue,msPattern,hyphenPattern,hyphenatedCache,unitlessKeys={animationIterationCount:1,borderImageOutset:1,borderImageSlice:1,borderImageWidth:1,boxFlex:1,boxFlexGroup:1,boxOrdinalGroup:1,columnCount:1,columns:1,flex:1,flexGrow:1,flexPositive:1,flexShrink:1,flexNegative:1,flexOrder:1,gridRow:1,gridRowEnd:1,gridRowSpan:1,gridRowStart:1,gridColumn:1,gridColumnEnd:1,gridColumnSpan:1,gridColumnStart:1,msGridRow:1,msGridRowSpan:1,msGridColumn:1,msGridColumnSpan:1,fontWeight:1,lineHeight:1,opacity:1,order:1,orphans:1,tabSize:1,widows:1,zIndex:1,zoom:1,WebkitLineClamp:1,fillOpacity:1,floodOpacity:1,stopOpacity:1,strokeDasharray:1,strokeDashoffset:1,strokeMiterlimit:1,strokeOpacity:1,strokeWidth:1},ILLEGAL_ESCAPE_SEQUENCE_ERROR$1="You have illegal escape sequence in your template literal, most likely inside content's property value.\nBecause you write your CSS inside a JavaScript string you actually have to do double escaping, so for example \"content: '\\00d7';\" should become \"content: '\\\\00d7';\".\nYou can read more about this here:\nhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#ES2018_revision_of_illegal_escape_sequences",UNDEFINED_AS_OBJECT_KEY_ERROR="You have passed in falsy value as style object's key (can happen when in example you pass unexported component as computed key).",hyphenateRegex=/[A-Z]|^ms/g,animationRegex=/_EMO_([^_]+?)_([^]*?)_EMO_/g,isCustomProperty=function(e){return 45===e.charCodeAt(1)},isProcessableValue=function(e){return null!=e&&"boolean"!=typeof e},processStyleName=memoize(function(e){return isCustomProperty(e)?e:e.replace(hyphenateRegex,"-$&").toLowerCase()}),processStyleValue=function(e,t){switch(e){case"animation":case"animationName":if("string"==typeof t)return t.replace(animationRegex,function(e,t,r){return cursor={name:t,styles:r,next:cursor},t})}return 1===unitlessKeys[e]||isCustomProperty(e)||"number"!=typeof t||0===t?t:t+"px"};function handleInterpolation(e,t,r){if(null==r)return"";if(void 0!==r.__emotion_styles){if("production"!==process.env.NODE_ENV&&"NO_COMPONENT_SELECTOR"===r.toString())throw new Error("Component selectors can only be used in conjunction with @emotion/babel-plugin.");return r}switch(typeof r){case"boolean":return"";case"object":if(1===r.anim)return cursor={name:r.name,styles:r.styles,next:cursor},r.name;if(void 0===r.styles)return createStringFromObject(e,t,r);var n=r.next;if(void 0!==n)for(;void 0!==n;)cursor={name:n.name,styles:n.styles,next:cursor},n=n.next;var o=r.styles+";";return"production"!==process.env.NODE_ENV&&void 0!==r.map&&(o+=r.map),o;case"function":if(void 0!==e){var o=cursor,a=r(e);return cursor=o,handleInterpolation(e,t,a)}"production"!==process.env.NODE_ENV&&console.error("Functions that are interpolated in css calls will be stringified.\nIf you want to have a css call based on props, create a function that returns a css call like this\nlet dynamicStyle = (props) => css`color: ${props.color}`\nIt can be called directly with props or interpolated in a styled call like this\nlet SomeComponent = styled('div')`${dynamicStyle}`");break;case"string":var i;"production"!==process.env.NODE_ENV&&(i=[],a=r.replace(animationRegex,function(e,t,r){var n="animation"+i.length;return i.push("const "+n+" = keyframes`"+r.replace(/^@keyframes animation-\w+/,"")+"`"),"${"+n+"}"}),i.length&&console.error("`keyframes` output got interpolated into plain string, please wrap it with `css`.\n\nInstead of doing this:\n\n"+[].concat(i,["`"+a+"`"]).join("\n")+"\n\nYou should wrap it with `css` like this:\n\ncss`"+a+"`"))}if(null==t)return r;var s=t[r];return void 0!==s?s:r}function createStringFromObject(e,t,r){var n="";if(Array.isArray(r))for(var o=0;o{if(!e)return{fontSize:t};var t=e["fontSizeRange"],[e,t]=t;return{fontSize:randomNum(e,t),top:randomNum(0,100)+"%",left:randomNum(0,100)+"%"}},REPEAT=[1,2],styles={SmartBackground:{overflow:"hidden",position:"absolute",top:0,left:0,width:"100%",height:"100%",zIndex:0,backgroundRepeat:"repeat",backgroundSize:"cover",backgroundPosition:"center",pointerEvents:"none"}},SmartBackground=e=>{const{underlayColor:t,underlayImage:r,symbols:n=["●"],symbolsStyle:o,random:a,rotate:i=0,symbolSize:s=90,gap:c=10,animation:l,exact:u=!1,childrenWrapClassName:d,childrenWrapStyle:p,children:f,style:m,className:h,...y}=e,{width:g,height:v,ref:b}=useResizeDetector(),[E,_]=useState(n.length),w=Number(s),S=Number(c)/2,O=w+2*Number(S||1),x=u||a?[1]:REPEAT;useEffect(()=>{var e,t;u||a||g&&v&&(e=g%O,t=v%O,e=g-e,t=v-t,t=Math.floor(e*t/(O*O)),_(t))},[g,v]);const N=useMemo(()=>{let t=new Array(E);for(let e=0;e{let t=document.styleSheets[0];const r=t.cssRules.length;let n=!1;for(let e=r-1;0{t.insertRule(e,r)})},[]),React__default.createElement(React__default.Fragment,null,React__default.createElement("div",{style:{...styles.SmartBackground,backgroundColor:t,backgroundImage:r,...m},ref:b,...y},x.map(e=>React__default.createElement(SymbolList,{key:e,animation:l,random:a,exact:u,index:e},N.map((e,t)=>React__default.createElement(SymbolContainer,{key:t,symbolsStyle:o,rotate:i,random:a,gap:S,fontSize:w,exact:u},e))))),f&&React__default.createElement("div",{className:d,style:{position:"absolute",width:"100%",height:"100%",top:0,left:0,overflowY:"auto",...p}},f))},SymbolList=newStyled.div` 30 | width: 100%; 31 | height: 100%; 32 | display: ${e=>e.random||e.exact?"block":"flex"}; 33 | position: ${e=>e.exact?"relative":""}; 34 | flex-direction: row; 35 | animation: ${e=>{if(e.animation)return`smart-background-scroll-${e.animation.type} ${100/(e.animation.speed||5)}s linear infinite`}}; 36 | flex-wrap: wrap; 37 | justify-content: space-around; 38 | position: ${e=>{if(e.animation)return"left"===e.animation.type||"right"===e.animation.type||"bottom"===e.animation.type?"absolute":void 0}}; 39 | top: ${e=>{if(e.animation)return"left"===e.animation.type||"right"===e.animation.type?"0":"bottom"===e.animation.type?1===e.index?0:"-100%":void 0}}; 40 | left: ${e=>{if(e.animation)return"left"===e.animation.type?1===e.index?0:"100%":"right"===e.animation.type?1===e.index?0:"-100%":void 0}}; 41 | `,SymbolContainer=newStyled.div(({fontSize:e,gap:t=100,rotate:r,symbolsStyle:n,random:o,exact:a})=>{e=e||90;return{color:"#000",opacity:"0.3",width:a?"fit-content":e+2*t,height:a?"fit-content":e+2*t,lineHeight:e+"px",textAlign:"center",padding:t,transform:a?"none":`rotate(${r}deg)`,...n,position:o?"absolute":"unset",...getPosition(o,e),display:"inline-block"}});export default SmartBackground; 42 | --------------------------------------------------------------------------------