├── src ├── utils │ └── index.ts ├── styles │ ├── val.less │ └── reset.css ├── pages │ ├── dw.png │ ├── List │ │ ├── list.less │ │ └── index.tsx │ └── Home │ │ ├── home.less │ │ └── index.tsx ├── Marquee │ ├── MarqueeGroup │ │ ├── marqueegroup.less │ │ └── index.tsx │ ├── index.ts │ ├── marquee.less │ └── Marquee.tsx ├── app.tsx └── index.tsx ├── lib ├── cjs │ ├── MarqueeGroup │ │ └── index.d.ts │ ├── index.d.ts │ ├── Marquee.d.ts │ └── index.js ├── esm │ ├── MarqueeGroup │ │ └── index.d.ts │ ├── index.d.ts │ ├── Marquee.d.ts │ └── index.js └── umd │ ├── MarqueeGroup │ └── index.d.ts │ ├── index.d.ts │ ├── Marquee.d.ts │ └── index.js ├── tsconfig.rollup.json ├── .gitignore ├── global.d.ts ├── index.html ├── babel.config.js ├── config ├── webpack.dev.js ├── webpack.prod.js └── webpack.common.js ├── tsconfig.json ├── rollup.config.js ├── package.json └── README.md /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export default {} 2 | -------------------------------------------------------------------------------- /src/styles/val.less: -------------------------------------------------------------------------------- 1 | @project_prefix: react-app; 2 | 3 | @primary_color: #0099FF; -------------------------------------------------------------------------------- /src/pages/dw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/simple-marquee/master/src/pages/dw.png -------------------------------------------------------------------------------- /src/Marquee/MarqueeGroup/marqueegroup.less: -------------------------------------------------------------------------------- 1 | .marqueegroup { 2 | box-sizing: border-box; 3 | } 4 | -------------------------------------------------------------------------------- /src/app.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Home from '@pages/Home' 3 | 4 | const App = () => { 5 | return ( 6 | 7 | ) 8 | } 9 | 10 | export default App 11 | -------------------------------------------------------------------------------- /lib/cjs/MarqueeGroup/index.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | export interface MarqueeGroupProps { 3 | stop?: boolean; 4 | } 5 | declare const MarqueeGroup: React.FC; 6 | export default MarqueeGroup; 7 | -------------------------------------------------------------------------------- /lib/esm/MarqueeGroup/index.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | export interface MarqueeGroupProps { 3 | stop?: boolean; 4 | } 5 | declare const MarqueeGroup: React.FC; 6 | export default MarqueeGroup; 7 | -------------------------------------------------------------------------------- /lib/umd/MarqueeGroup/index.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | export interface MarqueeGroupProps { 3 | stop?: boolean; 4 | } 5 | declare const MarqueeGroup: React.FC; 6 | export default MarqueeGroup; 7 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import '@styles/reset.css' 4 | 5 | import App from '@pages/Home' 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ) 11 | -------------------------------------------------------------------------------- /tsconfig.rollup.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "declarationDir": "lib", 6 | }, 7 | "include": [ 8 | "src/Marquee/**/*", 9 | "global.d.ts" 10 | ] 11 | } -------------------------------------------------------------------------------- /src/pages/List/list.less: -------------------------------------------------------------------------------- 1 | .list { 2 | border-radius: 16px; 3 | line-height: 32px; 4 | padding: 0 12px; 5 | display: block; 6 | position: relative; 7 | margin-right: 20px; 8 | color: #fff; 9 | box-sizing: border-box; 10 | // background: ; 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | /lib/ 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | package-lock.json 9 | yarn.lock 10 | 11 | # Editor directories and files 12 | .idea 13 | .vscode 14 | *.suo 15 | *.ntvs* 16 | *.njsproj 17 | *.sln 18 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.css'; 2 | 3 | declare module '*.less'; 4 | 5 | declare module '*.scss'; 6 | 7 | declare module '*.sass'; 8 | 9 | declare module '*.svg'; 10 | 11 | declare module '*.png'; 12 | 13 | declare module '*.jpg'; 14 | 15 | declare module '*.jpeg'; 16 | 17 | declare module '*.gif'; 18 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= htmlWebpackPlugin.options.title %> 8 | 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /lib/cjs/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { MarqueeProps } from './Marquee'; 3 | import MarqueeGroup from './MarqueeGroup'; 4 | interface CompoundedComponent extends React.ForwardRefExoticComponent { 5 | MarqueeGroup: typeof MarqueeGroup; 6 | } 7 | declare const Marquee: CompoundedComponent; 8 | export { MarqueeGroup }; 9 | export default Marquee; 10 | -------------------------------------------------------------------------------- /lib/esm/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { MarqueeProps } from './Marquee'; 3 | import MarqueeGroup from './MarqueeGroup'; 4 | interface CompoundedComponent extends React.ForwardRefExoticComponent { 5 | MarqueeGroup: typeof MarqueeGroup; 6 | } 7 | declare const Marquee: CompoundedComponent; 8 | export { MarqueeGroup }; 9 | export default Marquee; 10 | -------------------------------------------------------------------------------- /lib/umd/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { MarqueeProps } from './Marquee'; 3 | import MarqueeGroup from './MarqueeGroup'; 4 | interface CompoundedComponent extends React.ForwardRefExoticComponent { 5 | MarqueeGroup: typeof MarqueeGroup; 6 | } 7 | declare const Marquee: CompoundedComponent; 8 | export { MarqueeGroup }; 9 | export default Marquee; 10 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(false) 3 | 4 | const presets = [ 5 | [ 6 | "@babel/preset-env", { 7 | modules: false 8 | } 9 | ], 10 | '@babel/preset-react', 11 | '@babel/preset-typescript' 12 | ] 13 | const plugins = [ 14 | '@babel/plugin-syntax-dynamic-import' 15 | ] 16 | 17 | return { 18 | presets, 19 | plugins 20 | } 21 | } -------------------------------------------------------------------------------- /src/Marquee/index.ts: -------------------------------------------------------------------------------- 1 | import M, { MarqueeProps } from './Marquee'; 2 | import MarqueeGroup from './MarqueeGroup'; 3 | 4 | interface CompoundedComponent 5 | extends React.ForwardRefExoticComponent { 6 | MarqueeGroup: typeof MarqueeGroup; 7 | } 8 | 9 | const Marquee = M as CompoundedComponent; 10 | Marquee.MarqueeGroup = MarqueeGroup; 11 | 12 | export { 13 | MarqueeGroup 14 | }; 15 | export default Marquee; 16 | -------------------------------------------------------------------------------- /lib/cjs/Marquee.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | export interface MarqueeProps extends React.HTMLAttributes { 3 | speed?: number; 4 | indent?: number; 5 | direction?: 'horizontal' | 'vertical' | 'horizontal-reverse' | 'vertical-reverse'; 6 | stop?: boolean; 7 | pauseOnHover?: boolean; 8 | } 9 | declare const _default: React.NamedExoticComponent; 10 | export default _default; 11 | -------------------------------------------------------------------------------- /lib/esm/Marquee.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | export interface MarqueeProps extends React.HTMLAttributes { 3 | speed?: number; 4 | indent?: number; 5 | direction?: 'horizontal' | 'vertical' | 'horizontal-reverse' | 'vertical-reverse'; 6 | stop?: boolean; 7 | pauseOnHover?: boolean; 8 | } 9 | declare const _default: React.NamedExoticComponent; 10 | export default _default; 11 | -------------------------------------------------------------------------------- /lib/umd/Marquee.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | export interface MarqueeProps extends React.HTMLAttributes { 3 | speed?: number; 4 | indent?: number; 5 | direction?: 'horizontal' | 'vertical' | 'horizontal-reverse' | 'vertical-reverse'; 6 | stop?: boolean; 7 | pauseOnHover?: boolean; 8 | } 9 | declare const _default: React.NamedExoticComponent; 10 | export default _default; 11 | -------------------------------------------------------------------------------- /src/Marquee/marquee.less: -------------------------------------------------------------------------------- 1 | .marquee { 2 | width: 100%; 3 | opacity: 1; 4 | .marquee_wrap { 5 | flex: 0 0 auto; 6 | width: auto; 7 | opacity: 1; 8 | will-change: auto; 9 | overflow: hidden; 10 | } 11 | &.horizontal, &.horizontal-reverse { 12 | display: flex; 13 | .marquee_wrap { 14 | display: inline-flex; 15 | min-width: 100%; 16 | } 17 | } 18 | 19 | &.vertical, &.vertical-reverse { 20 | .marquee_wrap { 21 | min-height: 100%; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/pages/List/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './list.less'; 3 | // import PropTypes from 'prop-types'; 4 | 5 | export interface ListProps { 6 | style: any 7 | }; 8 | 9 | const List: React.FC = ({ 10 | children, 11 | style 12 | }) => { 13 | return ( 14 |
15 | {children ?? 'HELLO WORLD'} 16 |
17 | ); 18 | }; 19 | 20 | //List.propTypes = { 21 | // props: PropTypes.string 22 | //}; 23 | 24 | export default List; 25 | -------------------------------------------------------------------------------- /src/Marquee/MarqueeGroup/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { isValidElement } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | export interface MarqueeGroupProps { 5 | stop?: boolean; 6 | } 7 | 8 | const MarqueeGroup: React.FC = ({ 9 | stop = false, 10 | children 11 | }) => { 12 | const renderChildren = () => { 13 | return React.Children.map(children, (element, index) => { 14 | if (isValidElement(element)) { 15 | return React.cloneElement(element, { 16 | key: index, 17 | stop 18 | }); 19 | } else { 20 | return element; 21 | } 22 | }) || null; 23 | }; 24 | 25 | return ( 26 |
27 | {renderChildren()} 28 |
29 | ); 30 | }; 31 | 32 | MarqueeGroup.propTypes = { 33 | stop: PropTypes.bool 34 | }; 35 | 36 | MarqueeGroup.displayName = 'MarqueeGroup'; 37 | 38 | export default MarqueeGroup; 39 | -------------------------------------------------------------------------------- /config/webpack.dev.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | const path = require('path'); 4 | const webpackPromptPlugin = require('webpack-prompt-plugin') 5 | 6 | const resolve = function (dir) { 7 | return path.resolve(__dirname, dir); 8 | } 9 | 10 | module.exports = merge(common, { 11 | mode: 'development', 12 | entry: { 13 | app: './src/index.tsx' 14 | }, 15 | output: { 16 | path: resolve('dist'), 17 | publicPath: '/', 18 | filename: 'js/[name]-[hash].js' 19 | }, 20 | 21 | module: { 22 | rules: [ 23 | ] 24 | }, 25 | 26 | plugins: [ 27 | new webpackPromptPlugin() 28 | ], 29 | 30 | devServer: { 31 | // 当使用 HTML5 History API 时,任意的 404 响应都可能需要被替代为 index.html。通过传入以下启用: 32 | // contentBase: "./", 33 | host: '0.0.0.0', 34 | // 端口号 35 | port: 1994, 36 | //当有编译器错误或警告时,在浏览器中显示全屏覆盖。默认禁用。如果您只想显示编译器错误: 37 | noInfo: true, 38 | // 配置端口号 39 | overlay: true, 40 | historyApiFallback: true 41 | } 42 | }) 43 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "allowJs": true, 11 | "noImplicitAny": true, 12 | "skipLibCheck": true, 13 | "esModuleInterop": true, 14 | "allowSyntheticDefaultImports": true, 15 | "strict": true, 16 | "experimentalDecorators": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "module": "esnext", 19 | "moduleResolution": "node", 20 | "resolveJsonModule": true, 21 | "isolatedModules": false, 22 | "noEmit": false, 23 | "jsx": "react", 24 | "typeRoots": [ 25 | "node_modules/@types" 26 | ], 27 | "paths": { 28 | "@api/*": ["src/api/*"], 29 | "@assets/*": ["src/assets/*"], 30 | "@components/*": ["src/components/*"], 31 | "@constance/*": ["src/constance/*"], 32 | "@store/*": ["src/store/*"], 33 | "@styles/*": ["src/styles/*"], 34 | "@pages/*": ["src/pages/*"], 35 | "@utils/*": ["src/utils/*"], 36 | "@router/*": ["src/router/*"], 37 | } 38 | }, 39 | "include": [ 40 | "src/**/*", 41 | "global.d.ts" 42 | ] 43 | } -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | // import path from 'path'; 2 | import json from '@rollup/plugin-json'; 3 | import commonjs from '@rollup/plugin-commonjs'; 4 | import resolve from '@rollup/plugin-node-resolve'; 5 | import babel from '@rollup/plugin-babel'; 6 | import typescript2 from 'rollup-plugin-typescript2'; 7 | import clear from 'rollup-plugin-clear'; 8 | import postcss from 'rollup-plugin-postcss'; 9 | import { terser } from 'rollup-plugin-terser'; 10 | 11 | const GLOBALS_MAP = { 12 | globals: { 13 | 'react': 'React', 14 | 'classnames': 'classnames', 15 | 'prop-types': 'prop-types' 16 | } 17 | }; 18 | 19 | export default { 20 | input: `src/Marquee/index.ts`, 21 | output: [{ 22 | file: `lib/cjs/index.js`, 23 | format: 'cjs', 24 | exports: 'named', 25 | ...GLOBALS_MAP 26 | }, { 27 | file: `lib/esm/index.js`, 28 | format: 'esm', 29 | ...GLOBALS_MAP 30 | }, { 31 | file: `lib/umd/index.js`, 32 | format: 'umd', 33 | name: 'index.js', 34 | exports: 'named', 35 | ...GLOBALS_MAP 36 | }], 37 | external: [ 38 | 'react', 39 | 'classnames', 40 | 'prop-types' 41 | ], 42 | plugins: [ 43 | clear({ 44 | targets: ['lib'] 45 | }), 46 | json(), 47 | postcss({ 48 | modules: true, 49 | extensions: [".less", ".css"], 50 | use: [ 51 | ["less", { 52 | javascriptEnabled: true 53 | }] 54 | ], 55 | minimize: true 56 | }), 57 | typescript2({ 58 | tsconfig: './tsconfig.rollup.json' 59 | }), 60 | babel({ 61 | exclude: 'node_modules/**', 62 | babelHelpers: 'runtime' 63 | }), 64 | resolve({ 65 | mainFields: 'main', 66 | modulesOnly: true 67 | }), 68 | commonjs({ 69 | include: 'node_modules/**', 70 | sourceMap: true, 71 | }), 72 | terser({ 73 | output: { 74 | comments: false 75 | } 76 | }) 77 | ] 78 | }; -------------------------------------------------------------------------------- /src/pages/Home/home.less: -------------------------------------------------------------------------------- 1 | .home { 2 | display: flex; 3 | align-items: center; 4 | justify-content: flex-start; 5 | flex-direction: column; 6 | padding: 20px 10px 50px 10px; 7 | box-sizing: border-box; 8 | // min-width: 100vw; 9 | // min-height: 100vh; 10 | position: relative; 11 | color: #333; 12 | line-height: 1.5; 13 | 14 | code { 15 | color: rgb(5, 201, 175); 16 | } 17 | 18 | h1 { 19 | color: #CC9999; 20 | text-align: center; 21 | z-index: 1; 22 | line-height: 1.5; 23 | } 24 | 25 | .area { 26 | position: relative; 27 | .title { 28 | position: sticky; 29 | top: 0; 30 | z-index: 1; 31 | background-color: #fff; 32 | padding: 10px 0; 33 | display: flex; 34 | align-items: center; 35 | } 36 | 37 | .horizontal-item { 38 | margin: 0; 39 | background: red; 40 | } 41 | 42 | .marquee_group { 43 | border-radius: 8px; 44 | width: 440px; 45 | max-width: 100%; 46 | background-color: #ccc; 47 | padding: 12px; 48 | box-sizing: border-box; 49 | overflow: hidden; 50 | } 51 | 52 | // .horizontal_reverse { 53 | // padding: 12px 0; 54 | // } 55 | 56 | .marquee_group_vertical { 57 | border-radius: 8px; 58 | width: 440px; 59 | max-width: 100%; 60 | background-color: #ccc; 61 | padding: 0 12px; 62 | height: 100px; 63 | box-sizing: border-box; 64 | } 65 | } 66 | 67 | .badge { 68 | display: inline-flex; 69 | align-items: center; 70 | justify-content: center; 71 | line-height: 1; 72 | font-size: 10px; 73 | text-transform: uppercase; 74 | padding: 6px 8px; 75 | border-radius: 4px; 76 | background-color: dodgerblue; 77 | color: #fff; 78 | font-weight: 600; 79 | margin-left: 10px; 80 | } 81 | 82 | button { 83 | background-color: #55aa11; 84 | color: #fff; 85 | margin: 20px 0; 86 | border: none; 87 | border-radius: 4px; 88 | padding: 4px 12px; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-marquee", 3 | "version": "0.0.5", 4 | "description": "marquee components for react", 5 | "author": "daiwei", 6 | "main": "lib/umd/index.js", 7 | "module": "lib/esm/index.js", 8 | "commonjs": "lib/cjs/index.js", 9 | "license": "ISC", 10 | "files": [ 11 | "lib", 12 | "README.md" 13 | ], 14 | "scripts": { 15 | "dev": "webpack-dev-server --config ./config/webpack.dev.js --progress", 16 | "lib": "rollup -c", 17 | "build": "webpack --progress --config ./config/webpack.prod.js" 18 | }, 19 | "keywords": [ 20 | "marquee", 21 | "react-marquee" 22 | ], 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/IFmiss/simple-marquee.git" 26 | }, 27 | "dependencies": { 28 | "classnames": "^2.2.6", 29 | "react": "^16.12.0" 30 | }, 31 | "peerDependencies": { 32 | "react": ">=16.8.0" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "^7.8.3", 36 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 37 | "@babel/preset-env": "^7.8.3", 38 | "@babel/preset-react": "^7.8.3", 39 | "@babel/preset-typescript": "^7.9.0", 40 | "@rollup/plugin-babel": "^5.2.2", 41 | "@rollup/plugin-commonjs": "^17.0.0", 42 | "@rollup/plugin-json": "^4.1.0", 43 | "@rollup/plugin-node-resolve": "^11.1.0", 44 | "@types/classnames": "^2.2.9", 45 | "@types/react": "^16.9.17", 46 | "@types/react-dom": "^16.9.5", 47 | "autoprefixer": "^9.7.4", 48 | "babel-loader": "^8.0.6", 49 | "clean-webpack-plugin": "^3.0.0", 50 | "css-loader": "^3.4.2", 51 | "html-webpack-plugin": "^3.2.0", 52 | "less": "^3.10.3", 53 | "less-loader": "^5.0.0", 54 | "mini-css-extract-plugin": "^0.9.0", 55 | "optimize-css-assets-webpack-plugin": "^5.0.3", 56 | "postcss": "^8.2.4", 57 | "postcss-loader": "^3.0.0", 58 | "react-dom": "^17.0.1", 59 | "rollup": "^2.38.0", 60 | "rollup-plugin-clear": "^2.0.7", 61 | "rollup-plugin-postcss": "^4.0.0", 62 | "rollup-plugin-terser": "^7.0.2", 63 | "rollup-plugin-typescript2": "^0.29.0", 64 | "simple-marquee": "^0.0.2", 65 | "style-loader": "^1.1.2", 66 | "style-resources-loader": "^1.3.3", 67 | "terser-webpack-plugin": "^2.3.2", 68 | "ts-loader": "^6.2.1", 69 | "typescript": "^3.7.5", 70 | "uglifyjs-webpack-plugin": "^2.2.0", 71 | "url-loader": "^3.0.0", 72 | "webpack": "^4.41.5", 73 | "webpack-bundle-analyzer": "^3.6.0", 74 | "webpack-cli": "^3.3.10", 75 | "webpack-dev-server": "^3.10.1", 76 | "webpack-merge": "^4.2.2", 77 | "webpack-prompt-plugin": "^1.1.2" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /config/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | const path = require('path'); 3 | const TerserPlugin = require('terser-webpack-plugin'); 4 | const common = require('./webpack.common.js'); 5 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 6 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; 7 | 8 | // css压缩打包相关 9 | var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 10 | 11 | // 打包清除dist目录 12 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 13 | 14 | module.exports = merge(common, { 15 | mode: 'production', 16 | entry: { 17 | app: './src/index.tsx', 18 | }, 19 | externals: { 20 | // 使用 externals 需要在 index.html 中配置需要的 库 的cdn地址 21 | // react: 'React', 22 | }, 23 | output: { 24 | path: path.resolve(__dirname, './../dist'), 25 | publicPath: '/', 26 | filename: 'js/[name]-[hash].js', 27 | libraryTarget: 'umd' 28 | }, 29 | module: { 30 | rules: [ 31 | { 32 | test: /\.js$/, 33 | use: { 34 | loader: 'babel-loader', 35 | } 36 | }, 37 | ] 38 | }, 39 | plugins: [ 40 | // 清除 41 | new CleanWebpackPlugin({ 42 | cleanOnceBeforeBuildPatterns: path.resolve(__dirname, 'dist') 43 | }), 44 | 45 | // css 压缩 46 | new OptimizeCssAssetsPlugin({}), 47 | 48 | // new BundleAnalyzerPlugin() 49 | ], 50 | optimization: { 51 | namedModules: true, 52 | minimizer: [ 53 | new TerserPlugin({ 54 | cache: true, 55 | parallel: true, 56 | sourceMap: true, // Must be set to true if using source-maps in production 57 | terserOptions: { 58 | // https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions 59 | } 60 | }), 61 | ], 62 | splitChunks: { 63 | chunks: "all", 64 | minSize: 30000, 65 | minChunks: 3, 66 | maxAsyncRequests: 5, 67 | maxInitialRequests: 3, 68 | name: true, 69 | cacheGroups: { 70 | default: { 71 | minChunks: 2, 72 | priority: -20, 73 | reuseExistingChunk: true, 74 | }, 75 | vendors: { 76 | test: /[\\/]node_modules[\\/]/, 77 | chunks: "initial", 78 | name: "vendor", 79 | priority: 10, 80 | enforce: true, 81 | }, 82 | commons: { 83 | name: 'vendors', 84 | chunks: 'all', 85 | minChunks: 2, 86 | maxInitialRequests: 5, // The default limit is too small to showcase the effect 87 | minSize: 0 // This is example is too small to create commons chunks 88 | } 89 | } 90 | }, 91 | runtimeChunk: "single", 92 | minimizer: [ 93 | new UglifyJsPlugin({ 94 | test: /\.js(\?.*)?$/i 95 | }), 96 | ] 97 | } 98 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple-marquee 2 | **marquee component for react by requestAnimationFrame** 3 | 4 | ### [demo](https://simple-marquee-git-master-ifmiss.vercel.app/) 5 | 6 | ### start 7 | ```code 8 | npm i simple-marquee 9 | ``` 10 | **OR** 11 | ```code 12 | yarn add simple-marquee 13 | ``` 14 | 15 | ### import 16 | ```js 17 | import Marquee from 'simple-marquee'; 18 | const { 19 | MarqueeGroup 20 | } = Marquee; 21 | ``` 22 | **OR** 23 | ```js 24 | import Marquee, { 25 | MarqueeGroup 26 | } from 'simple-marquee'; 27 | ``` 28 | 29 | ### Marquee text 30 | ```tsx 31 | 34 | 2020年结束了,2021年开始了,为新的一年,加油 !!!!⛽️      35 | 36 | ``` 37 | 38 | ### Marquee list 39 | ```tsx 40 | 41 | 48 | 枯藤老树昏鸦 51 | 小桥流水人家 54 | 古道西风瘦马 57 | 58 | 65 | 夕阳西下 68 | 断肠人在天涯 71 | 离离原上草 74 | 一岁一枯荣 77 | 78 | 82 | 一岁一枯荣 85 | 野火烧不尽 88 | 春风吹又生 91 | 92 | 93 | ``` 94 | 95 | ### MarqueeGroup props 96 | - `stop` 【boolean】 Control animation group state 97 | 98 | ### Marquee props 99 | - `speed` 【number】 animation speed `default` 1 100 | - `indent`【number】 animation content indent `default` 0 101 | - `direction`animation direction `default` horizontal 102 | - 'horizontal' 103 | - 'vertical' 104 | - 'horizontal-reverse' 105 | - 'vertical-reverse' 106 | - `stop` 【boolean】 Control animation state 107 | - `pauseOnHover`【boolean】 Control animation state when mouseover `default` false 108 | 109 | -------------------------------------------------------------------------------- /config/webpack.common.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 5 | 6 | const resolve = (dir) => { 7 | return path.resolve(__dirname, dir) 8 | } 9 | 10 | const devMode = process.env.NODE_ENV === "development" 11 | 12 | module.exports = { 13 | plugins: [ 14 | new HtmlWebpackPlugin({ 15 | filename: 'index.html', 16 | template: 'index.html', 17 | favicon: 'src/pages/dw.png', 18 | inject: true, 19 | title: "simple-marquee", 20 | minify: { 21 | removeComments: true 22 | } 23 | }), 24 | new MiniCssExtractPlugin ({ 25 | filename: "css/[name]-[hash].css", 26 | chunkFilename: "css/[name]-[hash].css" 27 | }), 28 | ], 29 | 30 | module: { 31 | rules: [ 32 | { 33 | test: /\.(js|jsx|ts|tsx)$/, 34 | loader: 'babel-loader', 35 | query: { 36 | compact: false 37 | } 38 | }, 39 | { 40 | test: /\.(c)ss$/, 41 | use: [ 42 | devMode ? 'style-loader' : MiniCssExtractPlugin.loader, 43 | { 44 | loader: 'css-loader', 45 | options: { 46 | modules:true 47 | } 48 | }, 49 | { 50 | loader:"postcss-loader", 51 | options: { 52 | plugins: () => [ 53 | require('autoprefixer')() 54 | ] 55 | } 56 | }, 57 | ] 58 | }, 59 | { 60 | test: /\.less$/, 61 | use: [ 62 | devMode ? 'style-loader' : MiniCssExtractPlugin.loader, 63 | { 64 | loader: 'css-loader', 65 | options: { 66 | modules: true 67 | } 68 | }, 69 | "less-loader", 70 | { 71 | loader: 'style-resources-loader', 72 | options: { 73 | patterns: path.resolve(__dirname, './../src/styles/val.less') 74 | } 75 | } 76 | ], 77 | }, 78 | { 79 | test: /\.(ttf|eot|woff|woff2)$/, 80 | use: [ 81 | { 82 | loader: 'url-loader' 83 | } 84 | ] 85 | }, 86 | { 87 | test: /\.(png|jpg|gif)$/, 88 | use: [ 89 | { 90 | loader: 'url-loader', 91 | options: { 92 | limit: 8192 93 | } 94 | } 95 | ] 96 | } 97 | ] 98 | }, 99 | 100 | resolve: { 101 | alias: { 102 | '@api': resolve('./../src/api'), 103 | '@assets': resolve('./../src/assets'), 104 | '@components': resolve('./../src/components'), 105 | '@constance': resolve('./../src/constance'), 106 | '@store': resolve('./../src/store'), 107 | '@styles': resolve('./../src/styles'), 108 | '@pages': resolve('./../src/pages'), 109 | '@utils': resolve('./../src/utils'), 110 | '@router': resolve('./../src/router') 111 | }, 112 | extensions: ['.tsx', '.ts', '.jsx', '.js'] 113 | }, 114 | } 115 | -------------------------------------------------------------------------------- /lib/esm/index.js: -------------------------------------------------------------------------------- 1 | import e,{memo as r,useRef as t,useState as n,useMemo as a,useCallback as o,useEffect as i,isValidElement as u}from"react";import l from"classnames";import c from"prop-types";var m=function(){return m=Object.assign||function(e){for(var r,t=1,n=arguments.length;tt?t:t+(t-n)-p:t-p}),[H,N]),A=a((function(){var e,r,t=(null===(e=C.current)||void 0===e?void 0:e.getBoundingClientRect()[R])||0,n=(null===(r=O.current)||void 0===r?void 0:r.getBoundingClientRect()[R])||0;return P?t-n:0}),[P,N]);i((function(){j(T())}),[]),i((function(){return y||x&&B?S():(K(),function(){cancelAnimationFrame(u)})}),[N,y,x,B]);var S=function(){u&&cancelAnimationFrame(u)},K=o((function(){u=requestAnimationFrame((function(){!function(){var e,r;P?j(N(null!==(r=null===(e=C.current)||void 0===e?void 0:e.getBoundingClientRect()[R])&&void 0!==r?r:0)?j(0):j((function(e){return e+s}))}()}))}),[N,y,s]),Z={transform:H?"translate3d(0, -"+N+"px, 0)":"translate3d(-"+N+"px, 0, 0)"},G=l(_.marquee,_[q],w),k=l(_.marquee_wrap);return e.createElement("div",m({},b,{className:G,ref:O,onMouseEnter:function(){return M(!0)},onMouseLeave:function(){return M(!1)},style:m(m({},g),{overflow:"hidden"})}),e.createElement("div",{className:k,ref:C,style:Z},d),e.createElement("div",{className:k,style:Z},d))};s.propTypes={speed:c.number,indent:c.number,direction:c.oneOf(["horizontal","vertical","horizontal-reverse","vertical-reverse"]),stop:c.bool,pauseOnHover:c.bool},s.displayName="Marquee";var d=r(s),v=function(r){var t=r.stop,n=void 0!==t&&t,a=r.children;return e.createElement("div",null,e.Children.map(a,(function(r,t){return u(r)?e.cloneElement(r,{key:t,stop:n}):r}))||null)};v.propTypes={stop:c.bool},v.displayName="MarqueeGroup";var p=d;p.MarqueeGroup=v;export{v as MarqueeGroup,p as default}; 2 | -------------------------------------------------------------------------------- /lib/cjs/index.js: -------------------------------------------------------------------------------- 1 | "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),r=require("classnames"),t=require("prop-types");function a(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var n=a(e),u=a(r),o=a(t),i=function(){return i=Object.assign||function(e){for(var r,t=1,a=arguments.length;tt?t:t+(t-a)-d:t-d}),[N,w]),B=e.useMemo((function(){var e,r,t=(null===(e=g.current)||void 0===e?void 0:e.getBoundingClientRect()[F])||0,a=(null===(r=z.current)||void 0===r?void 0:r.getBoundingClientRect()[F])||0;return j?t-a:0}),[j,w]);e.useEffect((function(){O(P())}),[]),e.useEffect((function(){return v||q&&C?R():(H(),function(){cancelAnimationFrame(t)})}),[w,v,q,C]);var R=function(){t&&cancelAnimationFrame(t)},H=e.useCallback((function(){t=requestAnimationFrame((function(){!function(){var e,r;j?O(w(null!==(r=null===(e=g.current)||void 0===e?void 0:e.getBoundingClientRect()[F])&&void 0!==r?r:0)?O(0):O((function(e){return e+o}))}()}))}),[w,v,o]),S={transform:N?"translate3d(0, -"+w+"px, 0)":"translate3d(-"+w+"px, 0, 0)"},T=u.default(l.marquee,l[_],y),A=u.default(l.marquee_wrap);return n.default.createElement("div",i({},x,{className:T,ref:z,onMouseEnter:function(){return E(!0)},onMouseLeave:function(){return E(!1)},style:i(i({},h),{overflow:"hidden"})}),n.default.createElement("div",{className:A,ref:g,style:S},c),n.default.createElement("div",{className:A,style:S},c))};c.propTypes={speed:o.default.number,indent:o.default.number,direction:o.default.oneOf(["horizontal","vertical","horizontal-reverse","vertical-reverse"]),stop:o.default.bool,pauseOnHover:o.default.bool},c.displayName="Marquee";var s=e.memo(c),d=function(r){var t=r.stop,a=void 0!==t&&t,u=r.children;return n.default.createElement("div",null,n.default.Children.map(u,(function(r,t){return e.isValidElement(r)?n.default.cloneElement(r,{key:t,stop:a}):r}))||null)};d.propTypes={stop:o.default.bool},d.displayName="MarqueeGroup";var m=s;m.MarqueeGroup=d,exports.MarqueeGroup=d,exports.default=m; 2 | -------------------------------------------------------------------------------- /lib/umd/index.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("classnames"),require("prop-types")):"function"==typeof define&&define.amd?define(["exports","react","classnames","prop-types"],t):t(((e="undefined"!=typeof globalThis?globalThis:e||self).index=e.index||{},e.index.js={}),e.React,e.classnames,e["prop-types"])}(this,(function(e,t,r,n){"use strict";function a(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=a(t),u=a(r),i=a(n),l=function(){return l=Object.assign||function(e){for(var t,r=1,n=arguments.length;rr?r:r+(r-n)-d:r-d}),[N,w]),R=t.useMemo((function(){var e,t,r=(null===(e=g.current)||void 0===e?void 0:e.getBoundingClientRect()[F])||0,n=(null===(t=b.current)||void 0===t?void 0:t.getBoundingClientRect()[F])||0;return E?r-n:0}),[E,w]);t.useEffect((function(){O(P())}),[]),t.useEffect((function(){return p||q&&j?B():(T(),function(){cancelAnimationFrame(r)})}),[w,p,q,j]);var B=function(){r&&cancelAnimationFrame(r)},T=t.useCallback((function(){r=requestAnimationFrame((function(){!function(){var e,t;E?O(w(null!==(t=null===(e=g.current)||void 0===e?void 0:e.getBoundingClientRect()[F])&&void 0!==t?t:0)?O(0):O((function(e){return e+a}))}()}))}),[w,p,a]),H={transform:N?"translate3d(0, -"+w+"px, 0)":"translate3d(-"+w+"px, 0, 0)"},S=u.default(s.marquee,s[f],y),A=u.default(s.marquee_wrap);return o.default.createElement("div",l({},x,{className:S,ref:b,onMouseEnter:function(){return C(!0)},onMouseLeave:function(){return C(!1)},style:l(l({},h),{overflow:"hidden"})}),o.default.createElement("div",{className:A,ref:g,style:H},i),o.default.createElement("div",{className:A,style:H},i))};c.propTypes={speed:i.default.number,indent:i.default.number,direction:i.default.oneOf(["horizontal","vertical","horizontal-reverse","vertical-reverse"]),stop:i.default.bool,pauseOnHover:i.default.bool},c.displayName="Marquee";var d=t.memo(c),m=function(e){var r=e.stop,n=void 0!==r&&r,a=e.children;return o.default.createElement("div",null,o.default.Children.map(a,(function(e,r){return t.isValidElement(e)?o.default.cloneElement(e,{key:r,stop:n}):e}))||null)};m.propTypes={stop:i.default.bool},m.displayName="MarqueeGroup";var f=d;f.MarqueeGroup=m,e.MarqueeGroup=m,e.default=f,Object.defineProperty(e,"__esModule",{value:!0})})); 2 | -------------------------------------------------------------------------------- /src/Marquee/Marquee.tsx: -------------------------------------------------------------------------------- 1 | import React, { 2 | useRef, 3 | useState, 4 | CSSProperties, 5 | useMemo, 6 | useEffect, 7 | useCallback, 8 | memo 9 | } from 'react'; 10 | import styles from './marquee.less'; 11 | import classNames from 'classnames'; 12 | import PropTypes from 'prop-types'; 13 | 14 | export interface MarqueeProps extends React.HTMLAttributes { 15 | speed?: number; 16 | indent?: number; 17 | direction?: 18 | 'horizontal' 19 | | 'vertical' 20 | | 'horizontal-reverse' 21 | | 'vertical-reverse'; 22 | stop?: boolean; 23 | pauseOnHover?: boolean; 24 | } 25 | 26 | const Marquee: React.FC = ({ 27 | speed = 1, 28 | children, 29 | indent = 0, 30 | direction = 'horizontal', 31 | stop = false, 32 | pauseOnHover = false, 33 | style, 34 | className, 35 | ...rest 36 | }) => { 37 | const mgRef = useRef(null); 38 | const mgWrapRef = useRef(null); 39 | const [move, setMove] = useState(indent); 40 | const [hovered, setHovered] = useState(false); 41 | let timerMarquee: any; 42 | 43 | const isReverse = useMemo(() => { 44 | return ( 45 | direction === 'horizontal-reverse' || 46 | direction === 'vertical-reverse' 47 | ) 48 | }, [direction]) 49 | 50 | const isVertical = useMemo(() => { 51 | return ( 52 | direction === 'vertical' || 53 | direction === 'vertical-reverse' 54 | ) 55 | }, [direction]); 56 | 57 | const rectProp = useMemo(() => { 58 | return isVertical 59 | ? 'height' 60 | : 'width'; 61 | }, [isVertical]); 62 | 63 | const getPos = useCallback(() => { 64 | const mgWrapRectProp = mgWrapRef.current?.getBoundingClientRect()[rectProp] || 0; 65 | const mgRectProp = mgRef.current?.getBoundingClientRect()[rectProp] || 0; 66 | return isReverse 67 | ? ( 68 | mgRectProp > mgWrapRectProp 69 | ? mgWrapRectProp 70 | : mgWrapRectProp + (mgWrapRectProp - mgRectProp) - indent 71 | ) 72 | : mgWrapRectProp - indent; 73 | }, [isVertical, move]); 74 | 75 | const reverseTransPos = useMemo(() => { 76 | const mgWrapRectProp = mgWrapRef.current?.getBoundingClientRect()[rectProp] || 0; 77 | const mgRectProp = mgRef.current?.getBoundingClientRect()[rectProp] || 0; 78 | return isReverse 79 | ? mgWrapRectProp - mgRectProp 80 | : 0 81 | }, [isReverse, move]); 82 | 83 | useEffect(() => { 84 | setMove(getPos()); 85 | }, []); 86 | 87 | useEffect(() => { 88 | if (stop) { 89 | return stopThis() 90 | } 91 | if (pauseOnHover && hovered) { 92 | return stopThis() 93 | } 94 | runAnimate(); 95 | 96 | return () => { 97 | cancelAnimationFrame(timerMarquee) 98 | }; 99 | }, [move, stop, pauseOnHover, hovered]); 100 | 101 | const stopThis = () => { 102 | timerMarquee && cancelAnimationFrame(timerMarquee); 103 | } 104 | 105 | // 动画执行 106 | const runAnimate = useCallback(() => { 107 | const f = () => { 108 | if (isReverse) { 109 | if (move < reverseTransPos) { 110 | setMove(getPos()); 111 | } else { 112 | setMove(m => m - speed); 113 | } 114 | } else { 115 | if (move > (mgWrapRef.current?.getBoundingClientRect()[rectProp] ?? 0)) { 116 | setMove(0); 117 | } else { 118 | setMove(m => m + speed); 119 | } 120 | } 121 | }; 122 | 123 | timerMarquee = requestAnimationFrame(() => { 124 | f(); 125 | }); 126 | }, [move, stop, speed]); 127 | 128 | const wrapStyle: CSSProperties = { 129 | transform: isVertical ? `translate3d(0, -${move}px, 0)` : `translate3d(-${move}px, 0, 0)` 130 | }; 131 | 132 | const marqueeCls = classNames( 133 | styles.marquee, 134 | styles[direction], 135 | className 136 | ); 137 | 138 | const marqueeWrapCls = classNames(styles.marquee_wrap); 139 | 140 | return ( 141 |
setHovered(true)} 145 | onMouseLeave={() => setHovered(false)} 146 | style={{ 147 | ...style, 148 | overflow: 'hidden' 149 | }}> 150 |
151 | {children} 152 |
153 |
154 | {children} 155 |
156 |
157 | ); 158 | }; 159 | 160 | Marquee.propTypes = { 161 | speed: PropTypes.number, 162 | indent: PropTypes.number, 163 | direction: PropTypes.oneOf([ 164 | 'horizontal', 165 | 'vertical', 166 | 'horizontal-reverse', 167 | 'vertical-reverse' 168 | ]), 169 | stop: PropTypes.bool, 170 | pauseOnHover: PropTypes.bool, 171 | }; 172 | 173 | Marquee.displayName = 'Marquee'; 174 | 175 | export default memo(Marquee); 176 | -------------------------------------------------------------------------------- /src/pages/Home/index.tsx: -------------------------------------------------------------------------------- 1 | import List from '@pages/List'; 2 | import React, { useState } from 'react' 3 | import Marquee, { 4 | MarqueeGroup 5 | } from '../../Marquee'; 6 | // import MarqueeGroup from '../../Marquee/MarqueeGroup'; 7 | import styles from './home.less' 8 | 9 | interface IHomeProps { } 10 | 11 | const color = '6f7f81' 12 | 13 | const Home: React.FC = (props) => { 14 | const [stopMarquee, setStopMarquee] = useState({ 15 | stop: true, 16 | index: -1 17 | }); 18 | 19 | const onBtnClick = (index: number) => { 20 | setStopMarquee(s => ({ 21 | stop: index === s.index ? !s.stop : true, 22 | index 23 | })) 24 | } 25 | 26 | const isPlay = (index: number) => { 27 | return stopMarquee.index === index && stopMarquee.stop 28 | } 29 | 30 | return ( 31 |
32 |

Simple Marquee for React

33 | 34 |

⚠️ 组件使用js实现的 Marquee 功能不适用于多个 Marquee 组件同时展示

35 | 36 | npm i simple-marquee 37 |

OR

38 | yarn add simple-marquee 39 | 40 |
41 |

横向文字

42 | 46 | 2020年结束了,2021年开始了,为新的一年,加油 !!!!⛽️      47 | 48 |
49 | 50 | 51 |
52 |

横向列表

53 | 56 | 白日依山尽 59 | 黄河入海流 62 | 欲穷千里目 65 | 更上一层楼 68 | 春眠不觉晓 71 | 处处闻啼鸟 74 | 夜来风雨声 77 | 花落知多少 80 | 81 |
82 | 83 | 84 |
85 |

Marquee组

86 |
87 | 88 | 95 | 枯藤老树昏鸦 98 | 小桥流水人家 101 | 古道西风瘦马 104 | 105 | 112 | 夕阳西下 115 | 断肠人在天涯 118 | 离离原上草 121 | 一岁一枯荣 124 | 125 | 129 | 一岁一枯荣 132 | 野火烧不尽 135 | 春风吹又生 138 | 139 | 140 |
141 | 142 | 143 |
144 | 145 |
146 |

横向-反方向

147 | 150 | 明天会更好,打工人 153 | 154 | 155 | 156 |
157 | 158 |
159 |

竖向

160 | 163 | hello 166 | this is a react component 169 | react simple marquee 172 | 173 | 174 |
175 | 176 |
177 |

竖向-反方向

178 | 181 | 11111111111 185 | 22222222 189 | 33333333 193 | 194 | 195 |
196 | 197 |
198 |

Pause On Hover new

199 | 203 | 2020年结束了,2021年开始了,为新的一年,加油 !!!!⛽️      204 | 205 |
206 |
207 | ) 208 | } 209 | 210 | export default Home 211 | -------------------------------------------------------------------------------- /src/styles/reset.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /** 4 | * 1. Change the default font family in all browsers (opinionated). 5 | * 2. Correct the line height in all browsers. 6 | * 3. Prevent adjustments of font size after orientation changes in 7 | * IE on Windows Phone and in iOS. 8 | */ 9 | 10 | /* Document 11 | ========================================================================== */ 12 | 13 | html { 14 | font-family: sans-serif; /* 1 */ 15 | line-height: 1.15; /* 2 */ 16 | -ms-text-size-adjust: 100%; /* 3 */ 17 | -webkit-text-size-adjust: 100%; /* 3 */ 18 | } 19 | 20 | /* Sections 21 | ========================================================================== */ 22 | 23 | /** 24 | * Remove the margin in all browsers (opinionated). 25 | */ 26 | 27 | body { 28 | margin: 0; 29 | } 30 | 31 | /** 32 | * Add the correct display in IE 9-. 33 | */ 34 | 35 | article, 36 | aside, 37 | footer, 38 | header, 39 | nav, 40 | section { 41 | display: block; 42 | } 43 | 44 | /** 45 | * Correct the font size and margin on `h1` elements within `section` and 46 | * `article` contexts in Chrome, Firefox, and Safari. 47 | */ 48 | 49 | h1 { 50 | font-size: 2em; 51 | margin: 0.67em 0; 52 | } 53 | 54 | /* Grouping content 55 | ========================================================================== */ 56 | 57 | /** 58 | * Add the correct display in IE 9-. 59 | * 1. Add the correct display in IE. 60 | */ 61 | 62 | figcaption, 63 | figure, 64 | main { /* 1 */ 65 | display: block; 66 | } 67 | 68 | /** 69 | * Add the correct margin in IE 8. 70 | */ 71 | 72 | figure { 73 | margin: 1em 40px; 74 | } 75 | 76 | /** 77 | * 1. Add the correct box sizing in Firefox. 78 | * 2. Show the overflow in Edge and IE. 79 | */ 80 | 81 | hr { 82 | box-sizing: content-box; /* 1 */ 83 | height: 0; /* 1 */ 84 | overflow: visible; /* 2 */ 85 | } 86 | 87 | /** 88 | * 1. Correct the inheritance and scaling of font size in all browsers. 89 | * 2. Correct the odd `em` font sizing in all browsers. 90 | */ 91 | 92 | pre { 93 | font-family: monospace, monospace; /* 1 */ 94 | font-size: 1em; /* 2 */ 95 | } 96 | 97 | /* Text-level semantics 98 | ========================================================================== */ 99 | 100 | /** 101 | * 1. Remove the gray background on active links in IE 10. 102 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. 103 | */ 104 | 105 | a { 106 | background-color: transparent; /* 1 */ 107 | -webkit-text-decoration-skip: objects; /* 2 */ 108 | } 109 | 110 | /** 111 | * Remove the outline on focused links when they are also active or hovered 112 | * in all browsers (opinionated). 113 | */ 114 | 115 | a:active, 116 | a:hover { 117 | outline-width: 0; 118 | } 119 | 120 | /** 121 | * 1. Remove the bottom border in Firefox 39-. 122 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 123 | */ 124 | 125 | abbr[title] { 126 | border-bottom: none; /* 1 */ 127 | text-decoration: underline; /* 2 */ 128 | text-decoration: underline dotted; /* 2 */ 129 | } 130 | 131 | /** 132 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. 133 | */ 134 | 135 | b, 136 | strong { 137 | font-weight: inherit; 138 | } 139 | 140 | /** 141 | * Add the correct font weight in Chrome, Edge, and Safari. 142 | */ 143 | 144 | b, 145 | strong { 146 | font-weight: bolder; 147 | } 148 | 149 | /** 150 | * 1. Correct the inheritance and scaling of font size in all browsers. 151 | * 2. Correct the odd `em` font sizing in all browsers. 152 | */ 153 | 154 | code, 155 | kbd, 156 | samp { 157 | font-family: monospace, monospace; /* 1 */ 158 | font-size: 1em; /* 2 */ 159 | } 160 | 161 | /** 162 | * Add the correct font style in Android 4.3-. 163 | */ 164 | 165 | dfn { 166 | font-style: italic; 167 | } 168 | 169 | /** 170 | * Add the correct background and color in IE 9-. 171 | */ 172 | 173 | mark { 174 | background-color: #ff0; 175 | color: #000; 176 | } 177 | 178 | /** 179 | * Add the correct font size in all browsers. 180 | */ 181 | 182 | small { 183 | font-size: 80%; 184 | } 185 | 186 | /** 187 | * Prevent `sub` and `sup` elements from affecting the line height in 188 | * all browsers. 189 | */ 190 | 191 | sub, 192 | sup { 193 | font-size: 75%; 194 | line-height: 0; 195 | position: relative; 196 | vertical-align: baseline; 197 | } 198 | 199 | sub { 200 | bottom: -0.25em; 201 | } 202 | 203 | sup { 204 | top: -0.5em; 205 | } 206 | 207 | /* Embedded content 208 | ========================================================================== */ 209 | 210 | /** 211 | * Add the correct display in IE 9-. 212 | */ 213 | 214 | audio, 215 | video { 216 | display: inline-block; 217 | } 218 | 219 | /** 220 | * Add the correct display in iOS 4-7. 221 | */ 222 | 223 | audio:not([controls]) { 224 | display: none; 225 | height: 0; 226 | } 227 | 228 | /** 229 | * Remove the border on images inside links in IE 10-. 230 | */ 231 | 232 | img { 233 | border-style: none; 234 | } 235 | 236 | /** 237 | * Hide the overflow in IE. 238 | */ 239 | 240 | svg:not(:root) { 241 | overflow: hidden; 242 | } 243 | 244 | /* Forms 245 | ========================================================================== */ 246 | 247 | /** 248 | * 1. Change the font styles in all browsers (opinionated). 249 | * 2. Remove the margin in Firefox and Safari. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | font-family: sans-serif; /* 1 */ 258 | font-size: 100%; /* 1 */ 259 | line-height: 1.15; /* 1 */ 260 | margin: 0; /* 2 */ 261 | } 262 | 263 | /** 264 | * Show the overflow in IE. 265 | * 1. Show the overflow in Edge. 266 | */ 267 | 268 | button, 269 | input { /* 1 */ 270 | overflow: visible; 271 | } 272 | 273 | /** 274 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 275 | * 1. Remove the inheritance of text transform in Firefox. 276 | */ 277 | 278 | button, 279 | select { /* 1 */ 280 | text-transform: none; 281 | } 282 | 283 | /** 284 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 285 | * controls in Android 4. 286 | * 2. Correct the inability to style clickable types in iOS and Safari. 287 | */ 288 | 289 | button, 290 | html [type="button"], /* 1 */ 291 | [type="reset"], 292 | [type="submit"] { 293 | -webkit-appearance: button; /* 2 */ 294 | } 295 | 296 | /** 297 | * Remove the inner border and padding in Firefox. 298 | */ 299 | 300 | button::-moz-focus-inner, 301 | [type="button"]::-moz-focus-inner, 302 | [type="reset"]::-moz-focus-inner, 303 | [type="submit"]::-moz-focus-inner { 304 | border-style: none; 305 | padding: 0; 306 | } 307 | 308 | /** 309 | * Restore the focus styles unset by the previous rule. 310 | */ 311 | 312 | button:-moz-focusring, 313 | [type="button"]:-moz-focusring, 314 | [type="reset"]:-moz-focusring, 315 | [type="submit"]:-moz-focusring { 316 | outline: 1px dotted ButtonText; 317 | } 318 | 319 | /** 320 | * Change the border, margin, and padding in all browsers (opinionated). 321 | */ 322 | 323 | fieldset { 324 | border: 1px solid #c0c0c0; 325 | margin: 0 2px; 326 | padding: 0.35em 0.625em 0.75em; 327 | } 328 | 329 | /** 330 | * 1. Correct the text wrapping in Edge and IE. 331 | * 2. Correct the color inheritance from `fieldset` elements in IE. 332 | * 3. Remove the padding so developers are not caught out when they zero out 333 | * `fieldset` elements in all browsers. 334 | */ 335 | 336 | legend { 337 | box-sizing: border-box; /* 1 */ 338 | color: inherit; /* 2 */ 339 | display: table; /* 1 */ 340 | max-width: 100%; /* 1 */ 341 | padding: 0; /* 3 */ 342 | white-space: normal; /* 1 */ 343 | } 344 | 345 | /** 346 | * 1. Add the correct display in IE 9-. 347 | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. 348 | */ 349 | 350 | progress { 351 | display: inline-block; /* 1 */ 352 | vertical-align: baseline; /* 2 */ 353 | } 354 | 355 | /** 356 | * Remove the default vertical scrollbar in IE. 357 | */ 358 | 359 | textarea { 360 | overflow: auto; 361 | } 362 | 363 | /** 364 | * 1. Add the correct box sizing in IE 10-. 365 | * 2. Remove the padding in IE 10-. 366 | */ 367 | 368 | [type="checkbox"], 369 | [type="radio"] { 370 | box-sizing: border-box; /* 1 */ 371 | padding: 0; /* 2 */ 372 | } 373 | 374 | /** 375 | * Correct the cursor style of increment and decrement buttons in Chrome. 376 | */ 377 | 378 | [type="number"]::-webkit-inner-spin-button, 379 | [type="number"]::-webkit-outer-spin-button { 380 | height: auto; 381 | } 382 | 383 | /** 384 | * 1. Correct the odd appearance in Chrome and Safari. 385 | * 2. Correct the outline style in Safari. 386 | */ 387 | 388 | [type="search"] { 389 | -webkit-appearance: textfield; /* 1 */ 390 | outline-offset: -2px; /* 2 */ 391 | } 392 | 393 | /** 394 | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. 395 | */ 396 | 397 | [type="search"]::-webkit-search-cancel-button, 398 | [type="search"]::-webkit-search-decoration { 399 | -webkit-appearance: none; 400 | } 401 | 402 | /** 403 | * 1. Correct the inability to style clickable types in iOS and Safari. 404 | * 2. Change font properties to `inherit` in Safari. 405 | */ 406 | 407 | ::-webkit-file-upload-button { 408 | -webkit-appearance: button; /* 1 */ 409 | font: inherit; /* 2 */ 410 | } 411 | 412 | /* Interactive 413 | ========================================================================== */ 414 | 415 | /* 416 | * Add the correct display in IE 9-. 417 | * 1. Add the correct display in Edge, IE, and Firefox. 418 | */ 419 | 420 | details, /* 1 */ 421 | menu { 422 | display: block; 423 | } 424 | 425 | /* 426 | * Add the correct display in all browsers. 427 | */ 428 | 429 | summary { 430 | display: list-item; 431 | } 432 | 433 | /* Scripting 434 | ========================================================================== */ 435 | 436 | /** 437 | * Add the correct display in IE 9-. 438 | */ 439 | 440 | canvas { 441 | display: inline-block; 442 | } 443 | 444 | /** 445 | * Add the correct display in IE. 446 | */ 447 | 448 | template { 449 | display: none; 450 | } 451 | 452 | /* Hidden 453 | ========================================================================== */ 454 | 455 | /** 456 | * Add the correct display in IE 10-. 457 | */ 458 | 459 | [hidden] { 460 | display: none; 461 | } --------------------------------------------------------------------------------