Add Cmder to the right click

Locate the installation package, run as administrator and execute the code cmder/Register AllCopy the code

Taobao mirror installation

npm i -g cnpm --registry=https://registry.npm.taobao.org
Copy the code

Unloading global dependencies

npm uninstall -g <package>
Copy the code

Code cloud resets the password

git config --system --unset credential.helper
Copy the code

High precision permission control – custom directive

We usually add v-if/V-show to an element for permission management, but if the judgment criteria are verbose and multiple places need to be judged, this method of code is not only elegant but also redundant.

In this case, we can handle the global custom directive: we first create a new array.js file, which is used to store permissions related global functions;

// array.js export function checkArray(key) { let arr = ['1', '2', '3', '4', 'demo']; let index = arr.indexOf(key); if (index > -1) { return true; } else {return false; // No permissions}}Copy the code

Then mount the array file globally:

// main.js import { checkArray } from './common/array'; Vue.directive('permission', { inserted(el, binding) { let permission = binding.value; If (permission) {let hasPermission = checkArray(permission); if (! HasPermission) {/ / no permissions remove Dom elements el parentNode && el. ParentNode. RemoveChild (el); }}}});Copy the code

Finally, we can judge the page through the custom command V-permission:

<div class=" BTNS "> <button v-permission="'1'"> Permission button 1</button> // <button V-permission ="'10'"> Permission button 2</button> // no display </button v-permission="'demo'">Copy the code

Quickly set up a local static server

  • Run as an administrator
npm install anywhere -g
Copy the code
  • Run this command in the directory to be shared
anywhere -p 8000 
Copy the code

Two-dimensional code generation

<! DOCTYPE html> <html> <head> <meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> <script SRC = "https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js" > < / script > < script SRC = "https://cdn.bootcdn.net/ajax/libs/qrcodejs/1.0.0/qrcode.min.js" > < / script > < / head > < body > < div class = "main" > < div Class = "inwrap" > < h1 > QRCode generated qr code < / h1 > < h2 > example < / h2 > < div class = "example" > < / h3 > < h3 > simple ways < div id = "qrcode_1" > < / div > < / div > < div class = "example" > < h3 > set the parameter mode < / h3 > < div id = "qrcode_2" > < / div > < / div > < / div > < / div > < script > (function () {new QRCode(document.getElementById("qrcode_1"), "your content"); }) (); (function () { var qrcode = new QRCode("qrcode_2", { text: "http://h5.cgsd163.com/register?recommendMemberNo=600074", width: 256, height: 256, colorDark: "#000000", colorLight: "#ffffff", correctLevel: QRCode.CorrectLevel.H, }); }) (); </script> </body> </html>Copy the code

Arouse the qq

Qq promotion

<a target="_blank" href="http://wpa.qq.com/msgrd?v=3&uin=1354427009&site=qq&menu=yes" > <img border="0" SRC ="http://wpa.qq.com/pa?p=2:1354427009:52" Alt =" Click here to message me "title=" Click here to message me" /> </a>Copy the code

Pop-up numeric keypad

<! <input type="tel"> <! <input pattern="\d*">Copy the code

Call some function of the system

<! <a href="tel:10086"> Call :10086 </a> <! - send SMS - > < a href = "SMS: 10086" > send text messages to: 10086 < / a > <! <a href="mailto:[email protected]"> </a> <! <input type="file" Accept ="image/*"> <input type="file" Accept ="video/*"> <! <input type="file" multiple>Copy the code

Open the native app

< a href = "weixin: / /" > open WeChat < / a > < a href = "alipays: / /" > open the alipay < / a > < a href = "alipays: / / platformapi/startapp? = 10000007 "> open alipay scan function < / a > < a href =" alipays: / / platformapi/startapp? AppId =60000002"> </a>Copy the code

Image compression

tinypng

CSS online triangle code generator

CSS online triangle code generator

Various types of turntable raffle demo

Lucky draw

Add text to a GIF

Add text to a GIF

Generate random images

Generate random images

Map data

Map data

Start a static server locally

npm install http-server -g 
http-server -p 8001 
Copy the code

Normalize (adapt to the browser default)

normalize.css

import 'normalize.css/normalize.css';
Copy the code

Personalized page

Personalized page

Export Excel table data from VUE

npm install vue-json-excel -S // main.js import JsonExcel from "vue-json-excel"; Vue.component("downloadExcel", JsonExcel); <download-excel class = "export-excel-wrapper" :data = "json_data" :fields = "json_fields" name = "filename.xls"> <! Button --> <! Button --> <! -- <el-button type="primary" size="small"> </el-button> -->Copy the code

The element-UI DateTimePicker setting can only select a time before or after the current time

<el-date-picker V-model ="form.timeValue" type="daterange" :picker-options="pickerOptions" // set here :clearable="false" Value format=" YYYY-MM-DD "range-separator=" to" start-placeholder=" start date "end-placeholder=" end date "> </el-date-picker> data() { return { pickerOptions: {disabledDate (time) {return time.getTime() < date.now () // Select the time after the current time return time.gettime () > date.now () // Select time before current time}},}}Copy the code

Vue mobile for different phones (using Vant)

yarn add amfe-flexible postcss-pxtorem // main.js import 'amfe-flexible/index.js' // index.html <script> Document. The documentElement. Style. FontSize = document. DocumentElement. ClientWidth / 3.75 + 'px'; </script> // postcss.config.js module.exports = { plugins: { 'autoprefixer': {}, 'postcss-pxtorem': { rootValue: 37.5, propList: ['*']}}}Copy the code

Vue copies text content with the click of a button

Import Clipboard from 'Clipboard' <section class='tag-read' data-clipboard-text=" @click="handleCopy" ></section> handleCopy () { const clipboard = new Clipboard('.tag-read') clipboard.on('success', E => {console.log(' copy successful ') // release memory clipboard.destroy()}) clipboard.on('error', E => {// does not support copying console.log(' this browser does not support automatic copying ') // Free memory clipboard.destroy()})}Copy the code

avataaars generator

avataaars generator

This tool helps you create interesting personified avatars. There are so many different styles and styles to choose from, and you can quickly export your avatar footage in a variety of formats once you’ve finished your design. The tool now supports PNG and SVG, and generates React code or image links directly. Best of all, the tool is completely free!

brumm.af

brumm.af

Using this tool, you can create amazing box-shadow effects. You can add multiple layers of shadows and adjust each layer to create the most natural and realistic shadows.

element-china-area-data

Element-china-area-data a few lines of code to achieve three-level linkage between provinces and cities.

The installation

yarn add element-china-area-data
Copy the code
  • ProvinceAndCityData is the second-level linkage data of provinces and cities (without the “all” option)
  • RegionData is the three-level linkage data of provinces and cities (without “all” option)
  • ProvinceAndCityDataPlus is provincial and urban linkage data (with “all” option)
  • RegionDataPlus is three-level linkage data for provinces and cities (with “all” option)
  • The value bound to the “all” option is an empty string.
  • CodeToText is a large object, the attribute is the area code, the attribute value is Chinese characters usage example: CodeToText[‘110000’] output Beijing
  • TextToCode is a large object whose attribute is a Chinese character and whose value is a field code. TextToCode[‘ Beijing ‘].code output 110000,TextToCode[‘ Beijing ‘][‘ Municipal district ‘].code output 110100,TextToCode[‘ Beijing ‘][‘ Municipal district ‘][‘ Chaoyang District ‘].code output 110105

use

import { provinceAndCityData, regionData, provinceAndCityDataPlus, regionDataPlus, CodeToText, TextToCode } from 'element-china-area-data';
Copy the code
<template> <div id="app"> <el-cascader size="large" :options="options" v-model="selectedOptions" @change="handleChange">  </el-cascader> </div> </template> <script> import { regionData } from 'element-china-area-data' export default { data () { return { options: regionData, selectedOptions: [] } }, methods: { handleChange (value) { console.log(value) } } } </script>Copy the code

viewerjs

Viewerjs is a powerful and simple image preview plug-in

The installation

yarn add v-viewer
Copy the code

use

The invocation is referenced and registered within main.js

import Viewer from 'v-viewer' import 'viewerjs/dist/viewer.css' Vue.use(Viewer); SetDefaults ({Options: {'zIndex': 2099,"inline": true, "button": true, "navbar": true, "title": true, "toolbar": true, "tooltip": true, "movable": true, "zoomable": true, "rotatable": true, "scalable": true, "transition": true, "fullscreen": true, "keyboard": true, "url": "data-source" } });Copy the code

Use xxx.vue in the code

<template> <div class="content"> <h1>Viewer image preview plugin </h1> < Viewer :images="imgs"> <img v-for=" SRC in imgs" : SRC ="src.url" :key="src.title"> </viewer> </div> </template> <script> export default { data () { return { imgs: [ { url: 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=85690711, 3884201894 & FM 27 & gp = = 0. JPG ", the title:' picture 1 '}, {url: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3564877025, 796183547 & FM 27 & gp = = 0. JPG ", the title: }},} </script>Copy the code

Antd-vue is introduced in VUe3

  • The installation
yarn add ant-design-vue@next
Copy the code
  • The version of less loader and less must be specified.vue/cliIs downloaded by default5.0.0The package
Yarn add [email protected] [email protected]Copy the code
  • A new ant – design – vue/index. Ts
import type { App } from 'vue';

import { Button } from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';

export function setupAntd(app: App<Element>) {
  app.use(Button);
}
Copy the code
  • The main ts use
import { createApp } from "vue";
import App from "./App";

import { setupAntd } from "./ant-design-vue/index";

const app = createApp(App);

setupAntd(app);

app
  .mount("#app");
Copy the code
  • Change babel.config.js to:
 module.exports = {
    presets: ["@vue/app"],
 +     plugins: [
 +     [
 +        "import",
 +        { libraryName: "ant-design-vue", libraryDirectory: "es", style: true }
 +     ]
 +   ]
 };
Copy the code

Custom themes

  • createvue.config.jsfile
module.exports = {
  css: {
    loaderOptions: {
      less: {
        lessOptions: {
          modifyVars: {
            "primary-color": "#1DA57A",
            "link-color": "#1DA57A",
            "border-radius-base": "2px",
          },
          javascriptEnabled: true,
        },
      },
    },
  },
};
Copy the code
  • Modify theant-design-vue/index.ts
import type { App } from 'vue';
import { Button } from 'ant-design-vue';

- import 'ant-design-vue/dist/antd.css';
+ import 'ant-design-vue/dist/antd.less';

export function setupAntd(app: App<Element>) {
  app.use(Button);
}
Copy the code

React anti-shock implementation

// utils/index import { useState, useEffect } from "react"; export function useDebounce(value: any, delay = 300) { const [debouncedVal, setDebouncedVal] = useState(value); useEffect(() => { const handler = window.setTimeout(() => { setDebouncedVal(value); }, delay); return () => { clearTimeout(handler); }; }, [value, delay]); return debouncedVal; } // app.js import React, { useState, useEffect, FC, ChangeEvent } from 'react'; import { useDebounce } from '.. /.. /utils/index'; const App: FC = () => { const [val, setVal] = useState(''); const debounceVal = useDebounce(val, 400) const handleChange = (e: ChangeEvent<HTMLInputElement>) => { setVal(e.target.value) }; useEffect(() => { console.log(888) }, [debounceVal]); return ( <div> <input type="text" value={val} onChange={handleChange} /> </div> ) } export default App;Copy the code

React anchor jump implementation

import React from 'react'; Const NewAutoML = () => {// Trace point event const scrollToAnchor = (type: String) => {if (type) {setCurrent(type) // Let anchorElement = document.getelementbyid (type); / / if the corresponding id anchor, will jump to anchor the if (anchorElement) {/ / anchorElement scrollIntoView ({block: 'start' behaviors: 'smooth' } window.scrollTo(0, anchorElement.offsetTop - 100); } } } return ( <div className='na-container'> <div className='na-main'> <div className='na-form na-com-wrap' id='task'> // contents </div> <div className='na-table na-com-wrap' id='num'> // contents </div> <div className='na-tab'> <ul ClassName ='nat-list'> <li onClick={() => scrollToAnchor('task')}> <span> Create a classification task </span> </li> <li onClick={() => ScrollToAnchor ('num')}> <span> Create category number </span> </li> </ul> </div> </div>)} export default NewAutoML;Copy the code

React routing is lazy and permissions are controlled

import React, { PureComponent } from "react"; import { Route, Redirect } from "react-router-dom"; export const asyncComponent = (loadComponent) => class AsyncComponent extends PureComponent { state = { Component: null, }; componentDidMount() { if (this.hasLoadedComponent()) { return; } loadComponent() .then((module) => module.default) .then((Component) => { this.setState({ Component }); }) .catch((err) => { console.error(`Cannot load component in <AsyncComponent />`); throw err; }); } hasLoadedComponent() { return this.state.Component ! == null; } render() { const { Component } = this.state; return Component ? <Component {... this.props} /> : null; }}; export class PrivateRoute extends PureComponent { render() { const { component: Component, ... rest } = this.props; const isLogin = localStorage.getItem("token") ? true : false; return ( <Route {... rest} render={(props) => { return isLogin ? ( <Component {... props} /> ) : ( <Redirect to={{ pathname: "/login", state: { from: props.location.pathname }, }} /> ); }} / >); }}Copy the code

React Mobile ADAPTS to different mobile phones

yarn add customize-cra react-app-rewired amfe-flexible postcss-pxtorem // config-overrides.js const { override, addPostcssPlugins } = require('customize-cra'); Module. exports = override(addPostcssPlugins([require('postcss-pxtorem')({rootValue: 37.5, propList: ['*']}),])); // package.json "scripts": { "serve": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-app-rewired eject" }, // index.js import 'amfe-flexible/index.js'; / / index. The HTML < script > document. The documentElement. Style. FontSize = document. The documentElement. ClientWidth / 3.75 + 'px'; </script>Copy the code

Craco configuration in ANTD4

// craco.config.js const CracoAntDesignPlugin = require('craco-antd'); const CracoAlias = require('craco-alias'); const CracoLessPlugin = require('craco-less'); const path = require('path'); Module.exports = {plugins: [/* ANTD add-ons on demand & style overrides etc. */ {plugin: CracoAntDesignPlugin, options: {customizeThemeLessPath: Path. The join (__dirname, 'SRC/assets/less/antd. Theme. Less'),}}, / * support less module * / {plugin: CracoLessPlugin, options: { cssLoaderOptions: { modules: { localIdentName: '[local]_[hash:base64:5]' }, }, modifyLessRule: function (lessRule, _context) { lessRule.test = /\.(module)\.(less)$/; lessRule.exclude = path.join(__dirname, 'node_modules'); return lessRule;},},}, /* Alias Settings */ {plugin: CracoAlias, options: {source: 'tsconfig', baseUrl: './src', tsConfigPath: './tsconfig.extend.json' } }, ], devServer: (devServerConfig) => { return { ... Gzip compress: true, /* proxy: {'/ API ': {target: 'https://dolphin-test.cootekos.com/', changeOrigin: true, xfwd: false, } } */ } } };Copy the code

ReactclassName is processed dynamically

classnames

import classnames from 'classnames'

<div className=classnames({
    'class1': true,
    'class2': true
    )>
</div>
Copy the code

Custom ANTD Form effect

export const validateServicePhone = (rule, value, callback) => { if (! Value) {return promise. reject(new Error(' Mobile number cannot be empty! ')) } else { const reg = /^1[3-9]\d{9}$/ if (reg.test(value)) { return Promise.resolve(); } else {return promise.reject (new Error(' Please enter the correct phone number! ')) } } } rules: [ { validator: validateServicePhone } ],Copy the code

Common regular expressions

  • Verification number:^ [0-9] * $
  • Verify n digits:^\d{n}$
  • Verify at least n digits:^\d{n,}$
  • Verify m-N digits:^\d{m,n}$
  • Verify numbers beginning with zero and non-zero:^ (0 | [1-9] [0-9] *) $
  • Verify positive real numbers with two decimal places:^ [0-9] + (. [0-9] {2})? $
  • Validation can only be numbers with a maximum of 2 decimal digits:/^\d+(? : \ \ d {1, 2})? $/
  • Verify positive real numbers with 1-3 decimal places:^ [0-9] + (. [0-9] {1, 3})? $
  • Verify non-zero positive integers:^ \ +? [1-9] [0-9] * $
  • Validates non-zero negative integers:^ \ [1-9] [0-9] * $
  • Validate non-negative integers (positive integers + 0)^\d+$
  • Validate non-positive integers (negative integers + 0)^((-\d+)|(0+))$
  • Verify characters of length 3:^. {3} $
  • Verify a string consisting of 26 English letters:^[A-Za-z]+$
  • Validates a string consisting of 26 uppercase English letters:^[A-Z]+$
  • Validates a string consisting of 26 lowercase English letters:^[a-z]+$
  • Validates a string of numbers and 26 English letters:^[A-Za-z0-9]+$
  • Validates a string consisting of numbers, 26 letters, or underscores:^\w+$
  • Verify user password:^ \ w [a zA - Z] {5} in 2 $The value must start with a letter and contain 6 to 18 characters, digits, and underscores (_).
  • Verify that there are& ^ % '; =? $\"Characters such as:[^%&',;=?$\x22]+
  • Verify Chinese characters:^[\u4e00-\u9fa5],{0,}$
  • Verification Email Address:^\w+[-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
  • Verify InternetURL:^http://([\w-]+\.) +[\w-]+(/[\w-./?%&=]*)? $; ^[a-zA-z]+://(w+(-w+)*)(.(w+(-w+)*))*(? S*)? $
  • Verification Phone Number:^ (\ (\ d {3, 4} \ | \ d {3, 4} -)? \ d {7, 8} $: — The correct format is XXXX-XXXXXXX, XXXX-XXXXXXXX, XXX-XXXXXXX, XXX-XXXXXXXX, XXXXXXX, XXXXXXXX.
  • Verify ID number (15 or 18 digits) :^\d{15}|\d{}18$
  • Verify 12 months of a year:^ (0? [1-9] [2-0]) $| 1The correct format is 01 to 09 and 1 to 12.
  • Verify 31 days of a month:^ ((0? [1-9]) | | 2 (1) ([0-9]) | | 31) $30The correct format is 01, 09 and 1, 31.
  • Integer:^ -? \d+$
  • Non-negative floating-point (positive floating-point + 0) :^\d+(\.\d+)? $
  • Are floating point Numbers^ (([0-9] + \. [0-9] * [1-9] [0-9] *) | ([0-9] * [1-9] [0-9] * \ [0-9] +) | ([0-9] * [1-9] [0-9] *)) $
  • Non-positive floating-point (negative floating-point + 0)^((-\d+(\.\d+)?) | (0 + (\. 0 +)? ) $
  • Negative floating point number^ (- (([0-9] + \. [0-9] * [1-9] [0-9] *) | ([0-9] * [1-9] [0-9] * \ [0-9] +) | ([0-9] * [1-9] [0-9] *))) $
  • Floating point Numbers^ (-? \d+)(\.\d+)? $

React+Antd encapsulates Axios to implement the global Loading effect

首页
react常用功能!

最后更新时间:2020-06-02

简介:记录一些工作中,学习中常用的功能!
将一维数组转为二维数组
/**
 *
 * @param num 每一项二维数组需要几项
 * @param arr 原素组,需要转换的数组
 */
export function arrTrans (num: number, arr: any[]): any { // 一维数组转换为二维数组
  const iconsArr: any[] = []; // 声明数组
  arr.forEach((item, index) => {
    const page = Math.floor(index / num); // 计算该元素为第几个素组内
    if (!iconsArr[page]) { // 判断是否存在
      iconsArr[page] = [];
    }
    iconsArr[page].push(item);
  });
  return iconsArr;
}
react中如何修改默认端口
将package.json文件修改为

"serve": "set PORT=8899 && react-app-rewired start",
react中非组件的路由跳转
// utils / history.ts
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();

// 根目录下
import React from 'react';
import { Router } from 'react-router-dom';
import { history } from '../../utils/history'

const App = (): JSX.Element => {
  return (
    <Router history={history}>
      ....
    </Router>
  )
}

// 使用
import { history } from '../utils/history';
history.push('/login')
react防抖的实现
// utils/index
import { useState, useEffect } from "react";

export function useDebounce(value: any, delay = 300) {
  const [debouncedVal, setDebouncedVal] = useState(value);

  useEffect(() => {
    const handler = window.setTimeout(() => {
      setDebouncedVal(value);
    }, delay);
    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);
  return debouncedVal;
}

// app.js
import React, { useState, useEffect, FC, ChangeEvent } from 'react';
import { useDebounce } from '../../utils/index';

const App: FC = () => {
  const [val, setVal] = useState('');
  const debounceVal = useDebounce(val, 400)

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    setVal(e.target.value)
  };

  useEffect(() => {
    console.log(888)
  }, [debounceVal]);

  return (
    <div>
      <input type="text" value={val} onChange={handleChange} />
    </div>
  )
}

export default App;
react中锚点跳转的实现
import React from 'react';

const NewAutoML = () => {
  // 描点事件
  const scrollToAnchor = (type: string) => {
    if (type) {
      setCurrent(type)
      // 找到锚点
      let anchorElement = document.getElementById(type);
      // 如果对应id的锚点存在,就跳转到锚点
      if (anchorElement) {
        //anchorElement.scrollIntoView({ block: 'start', behavior: 'smooth' }
        window.scrollTo(0, anchorElement.offsetTop - 100);
      }
    }
  }

  return (
    <div className='na-container'>
      <div className='na-main'>
        <div className='na-form na-com-wrap' id='task'>
          // 内容
        </div>
        <div className='na-table na-com-wrap' id='num'>
          // 内容
        </div>
      </div>
      <div className='na-tab'>
        <ul className='nat-list'>
          <li onClick={() => scrollToAnchor('task')}>
            <span>创建分类任务</span>
          </li>
          <li onClick={() => scrollToAnchor('num')}>
            <span>创建分类数量</span>
          </li>
        </ul>
      </div>
    </div>
  )
}

export default NewAutoML;
react中路由懒加载,权限控制
import React, { PureComponent } from "react";
import { Route, Redirect } from "react-router-dom";

export const asyncComponent = (loadComponent) =>
  class AsyncComponent extends PureComponent {
    state = {
      Component: null,
    };

    componentDidMount() {
      if (this.hasLoadedComponent()) {
        return;
      }

      loadComponent()
        .then((module) => module.default)
        .then((Component) => {
          this.setState({ Component });
        })
        .catch((err) => {
          console.error(`Cannot load component in <AsyncComponent />`);
          throw err;
        });
    }

    hasLoadedComponent() {
      return this.state.Component !== null;
    }

    render() {
      const { Component } = this.state;
      return Component ? <Component {...this.props} /> : null;
    }
  };

export class PrivateRoute extends PureComponent {
  render() {
    const { component: Component, ...rest } = this.props;
    const isLogin = localStorage.getItem("token") ? true : false;
    return (
      <Route
        {...rest}
        render={(props) => {
          return isLogin ? (
            <Component {...props} />
          ) : (
              <Redirect
                to={{
                  pathname: "/login",
                  state: { from: props.location.pathname },
                }}
              />
            );
        }}
      />
    );
  }
}
react移动端适配不同手机
yarn add customize-cra react-app-rewired amfe-flexible postcss-pxtorem

// config-overrides.js
const { override, addPostcssPlugins  } = require('customize-cra');

module.exports = override(
  addPostcssPlugins(
    [require('postcss-pxtorem')({
      rootValue: 37.5,
      propList: ['*']
    }),]
  )
);

// package.json
"scripts": {
  "serve": "react-app-rewired start",
  "build": "react-app-rewired build",
  "test": "react-app-rewired test",
  "eject": "react-app-rewired eject"
},

// index.js
import 'amfe-flexible/index.js';

// index.html
<script>
  document.documentElement.style.fontSize = document.documentElement.clientWidth / 3.75 + 'px';
</script>
antd4中craco的配置
// craco.config.js
const CracoAntDesignPlugin = require('craco-antd');
const CracoAlias = require('craco-alias');
const CracoLessPlugin = require('craco-less');

const path = require('path');

module.exports = {
  plugins: [
    /* antd组件按需加载&样式覆盖等 */
    {
      plugin: CracoAntDesignPlugin,
      options: {
        customizeThemeLessPath: path.join(
          __dirname,
          'src/assets/less/antd.theme.less'
        ),
      },
    },
    /* 支持less module */
    {
      plugin: CracoLessPlugin,
      options: {
        cssLoaderOptions: {
          modules: { localIdentName: '[local]_[hash:base64:5]' },
        },
        modifyLessRule: function (lessRule, _context) {
          lessRule.test = /\.(module)\.(less)$/;
          lessRule.exclude = path.join(__dirname, 'node_modules');
          return lessRule;
        },
      },
    },
    /* 别名设置 */
    {
      plugin: CracoAlias,
      options: {
        source: 'tsconfig',
        baseUrl: './src',
        tsConfigPath: './tsconfig.extend.json'
      }
    },
  ],
  devServer: (devServerConfig) => {
    return {
      ...devServerConfig,
      // 服务开启gzip
      compress: true,
      /* proxy: {
        '/api': {
          target: 'https://dolphin-test.cootekos.com/',
          changeOrigin: true,
          xfwd: false,
        }
      } */
    }
  }
};
react中引入antd-mobile less
yarn add antd-mobile 
yarn add react-app-rewired customize-cra --dev 
yarn add babel-plugin-import less less-loader   
//package.json
{
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-app-rewired eject"
  },
}

// theme/antd-theme.json
{
  "@brand-primary": "#1DA57A",
  "@brand-primary-tap": "#1DA57A"
}

// config-overrides.js
const { override, fixBabelImports, addLessLoader } = require("customize-cra");
const theme = require("./theme/antd-theme.json");

module.exports = override(
  addLessLoader({
    javascriptEnabled: true,
    modifyVars: theme,
  }),
  fixBabelImports("import", {
    libraryName: "antd-mobile",
    libraryDirectory: "es",
    style: true,
  })
);
如果出现以下报错  
./node_modules/antd-mobile/es/button/style/index.less (./node_modules/css-loader/dist/cjs.js??ref--6-oneOf-7-1!./node_modules/postcss-loader/src??postcss!./node_modules/less-loader/dist/cjs.js??ref--6-oneOf-7-3!./node_modules/antd-mobile/es/button/style/index.less)
ValidationError: Invalid options object. Less Loader has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'source'. These properties are valid:
   object { lessOptions?, prependData?, appendData?, sourceMap? }
解决办法 

yarn remove less-loader

yarn add [email protected]
reactclassName动态处理
classnames

import classnames from 'classnames'

<div className=classnames({
    'class1': true,
    'class2': true
    )>
</div>
自定义antd form效验
export const validateServicePhone = (rule, value, callback) => {
  if (!value) {
    return Promise.reject(new Error('手机号不能为空!'))
  } else {
    const reg = /^1[3-9]\d{9}$/
    if (reg.test(value)) {
      return Promise.resolve();
    } else {
      return Promise.reject(new Error('请输入正确的手机号!'))
    }
  }
}

rules: [
  {
    validator: validateServicePhone

  }
],

用React+Antd封装Axios实现全局Loading效果  
import axios from 'axios';
import React from 'react';
import ReactDOM from 'react-dom';
import { message, Spin } from 'antd';

// axios 配置
axios.defaults.timeout = 5000; // 设置请求超时
axios.defaults.baseURL = process.env.REACT_APP_BASE_URL; // 默认请求地址
axios.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8"; // 请求头的设置
// 设置跨域携带用户凭证
//axios.defaults.withCredentials = true

// 当前正在请求的数量
let requestCount = 0

// 显示loading
function showLoading() {
  if (requestCount === 0) {
    var dom = document.createElement('div')
    dom.setAttribute('id', 'loading')
    document.body.appendChild(dom)
    ReactDOM.render(<Spin tip="加载中..." size="large" />, dom)
  }
  requestCount++
}

// 隐藏loading
function hideLoading() {
  requestCount--
  if (requestCount === 0) {
    document.body.removeChild(document.getElementById('loading'))
  }
}

// 请求
axios.interceptors.request.use(
  (config) => {
    const TOKEN = localStorage.getItem("token") || "";
    if (TOKEN) {
      config.headers.common["Token"] = TOKEN;
    }
    if (config.headers.isLoading !== false) {
      showLoading()
    }
    return config;
  },
  (error) => {
    if (error.config.headers.isLoading !== false) {
      setTimeout(() => {
        hideLoading()
      }, 1000)
    }
    return Promise.reject(error);
  }
);

// 返回
axios.interceptors.response.use(
  (res) => {
    if (res.config.headers.isLoading !== false) {
      setTimeout(() => {
        hideLoading()
      }, 1000)
    }
    const responseCode = res.status;
    // 拦截器配置
    if (responseCode === 200) {
      if (res.data.code === -6) {
        // 被挤出清空token
        localStorage.removeItem("token");
        window.location.reload();
        return;
      }
      if (res.data.code === 1) {
        return Promise.resolve({
          ok: true,
          data: res.data,
        });
      } else {
        message.error(res.data.msg, 1);
        return Promise.resolve({
          ok: false,
          msg: res.data.msg,
        });
      }
    } else {
      message.error("网络链接失败", 3);
      return Promise.reject(res);
    }
    // return res.data; // data数据
  },
  (error) => {
    if (error.config.headers.isLoading !== false) {
      setTimeout(() => {
        hideLoading()
      }, 1000)
    }
    // 请求失败
    message.error("网络链接失败", 3);
    return Promise.reject(error);
  }
);

export default axios;
Copy the code

Add loading styles to shared CSS files:

#loading { position: absolute; top: 0; left: 0; right: 0; bottom: 0; Background: Rgba (0, 0, 0, 0.75); display: flex; align-items: center; justify-content: center; z-index: 9999; font-size: 20px; }Copy the code

Use:

import request from "@/request/ajax"; Export class HomeFeatured {// Default loading effect static async getList() {const res = await request.get("/technique-sharing/getFiveTechniqueSharingList"); Console. log(res)} static async getTwoList() {const res = await request({methods: 'get', url: '/mood-essay/getFiveMoodEssaylList', headers: { 'isLoading': false } }); console.log(res) } }Copy the code

Customizing loading components

// loading.vue <template> <transition name="custom-loading-fade"> <! --loading mask --> <div v-show="visible" class="custom-loading-mask"> <! <div class="custom-loading-spinner"> <img class="custom-spinner-icon" SRC = "http://img5.imgtn.bdimg.com/it/u=4107615877, & FM = 26 & gp = 0. 2378543300 JPG" Alt / > <! <p class="custom-loading-text">{{text}}</p> </div> </div> </transition> </template> <script> export default { data() { return { visible: false, text: "" }; }}; </script> <style lang="less"> .custom-loading-mask { width: 100%; min-height: calc(100vh); Background: Rgba (0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; .custom-loading-spinner { width: 100px; .custom-spinner-icon { width: 70px; height: 50px; } } } </style> // loading/index.js import Vue from 'vue' import LoadingComponent from './loading.vue' // Extend to wrap a component into a subclass const LoadingConstructor = vue.extend (LoadingComponent) let loading = undefined LoadingConstructor. Prototype. Close = function () {/ / if there is a reference loading, If (loading) {loading = undefined} // Hide component this.visible = false // delay 300 milliseconds, SetTimeout (() => {// Remove the attached DOM element if (this.$el && this.$el.parentNode) { Enclosing $el. ParentNode. RemoveChild (enclosing $el)} / / calling component $destroy methods component destroy this. $destroy ()}, 300)} const Loading = (options = {}) => {// If the component is rendered, If (loading) {return loading} // The element to be mounted const parent = document.body // component property const opts = {text: ",... New Vue() const instance = new LoadingConstructor({el: LoadingConstructor) document.createElement('div'), data: Parent.appendchild (instance.$el); // Display loading vie.nexttick (() => {instance.visible = True}) // Assign the component instance to loading loading = instance return instance} vue.prototype.$loading = loading export default loading // use <script> import Loading from "@/components/index.js"; Export default {created() {const loading = loading ({text: "loading..." }); // Close setTimeout(() => {load.close (); }, 3000); }}; </script>Copy the code

Vue custom command

Import Vue from 'Vue' import LoadingComponent from './loading' // Construct a component subclass with vue. extend const LoadingContructor = Vue.extend(LoadingComponent) // Define a directive named loading vue. directive('loading', {/** * is called only once, when the directive is first bound to an element, @param {*} The element that the el directive binds to * @param {*} The information that the binding directive passes in, including {name:' directive name ', value: 'directive binding value ',arg: } */ bind(el, binding) {const instance = new LoadingContructor({el: document.createElement('div'), data: {} }) el.appendChild(instance.$el) el.instance = instance Vue.nextTick(() => { el.instance.visible = binding.value }) }, @param {*} el * @param {*} binding */ update(el, Binding) {// If (binding. OldValue! == binding.value) {el.instance. Visible = binding.value}}, /** * * @param {*} el */ unbind(el) {const mask = el.instance.$el if (mask.parentnode) { Mask. ParentNode. RemoveChild (mask)} el. The instance. The $destroy (el). The instance = undefined}}) / / using the < template > < div v-loading="visible"></div> </template> <script> export default { data() { return { visible: false } }, created() { this.visible = true fetch().then(() => { this.visible = false }) } } </script>Copy the code

Vue based on small range drag

<template> <transition-group tag="div" class="container"> <div class="item" v-for="(item) in items" :key="item.key" :style="{background:item.color,width:'80px',height:'80px',}" draggable="true" @dragstart="handleDragStart($event, item)" @dragover.prevent="handleDragOver($event, item)" @dragenter="handleDragEnter($event, item)" @dragend="handleDragEnd($event, item)" ></div> </transition-group> </template> <script> export default { name: "Toolbar", data() { return { items: JSON.parse(localStorage.getItem('list')) || [ { key: 1, color: "red" }, { key: 2, color: "yellow" }, { key: 3, color: "green" } ], dragging: null }; }, methods: { handleDragStart(e, item) { this.dragging = item; }, handleDragEnd(e, item) { this.dragging = null; } / / the first make the div elements can be placed, or rewrite dragenter/dragover handleDragOver (e) {e.d ataTransfer. DropEffect = "move"; // e.dataTransfer.dropEffect="move"; // Set for the drop target in the dragenter! }, handleDragEnter(e, item) { e.dataTransfer.effectAllowed = "move"; // Set the dragstart event if (item === this.dragging) {return; } const newItems = [...this.items]; console.log(newItems); const src = newItems.indexOf(this.dragging); const dst = newItems.indexOf(item); newItems.splice(dst, 0, ... newItems.splice(src, 1)); this.items = newItems; localStorage.setItem('list', JSON.stringify(newItems)) } }, created() { console.log(localStorage.getItem('list')) } }; </script> <style scoped> .container { width: 80px; height: 300px; position: absolute; left: 0; display: flex; flex-direction: column; padding: 0; } .item { margin-top: 10px; The transition: all linear 0.3 s; } </style>Copy the code

Implement elemental-UI el-Dialog popbox drag in vue

// app.vue <template> <div> <el-button type="primary" @click="dialogVisible = true"> </el-button> <el-dialog Sync ="dialogVisible" V-dialogDrag width="30%" :close-on-click-modal="false"> <span> This is a message </span> <span slot="footer" class="dialog-footer"> <el-button @click="dialogVisible = false" Type ="primary" @click="dialogVisible = false"> </el-button> </span> </el-dialog> </div> </template> <script> export default { data() { return { dialogVisible: false }; }}; </script> // New cache. Js file import Vue from 'Vue' // V-dialogDrag: Directive ('dialogDrag', {bind(el, binding, vnode, oldVnode) { const dialogHeaderEl = el.querySelector('.el-dialog__header') const dragDom = el.querySelector('.el-dialog') = 'move' dialogHeaderEl. Style. Cursor / / get the original properties ie dom elements. CurrentStyle firefox window. Google getComputedStyle (dom elements, null); const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, Null) dialogHeaderEl. Onmousedown = (e) = > {/ / mouse press, Calculate distance from the current element distance visual areas const disX = e.c. with our fabrication: lientX - dialogHeaderEl. OffsetLeft const disY = e.c. with our fabrication: lientY - dialogHeaderEl. OffsetTop / / Replace let styL with a px regular match. StyT / / attention in ie the first access to the value of the component with 50% mobile assignment for px after the if (sty. Left. Includes (' % ')) {styL = + document. Body. ClientWidth * (+sty.left.replace(/\%/g, '') / 100) styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100) } else { styL = +sty.left.replace(/\px/g, '') styT = +sty.top.replace(/\px/g, ")} document.onmousemove = function(e) { Const l = e.clientx-disx const t = e.clienty - disY dragdom.style. left = '${l + styL}px Dragdom.style. top = '${t + styT}px' // binding. Value ({x: e.pagex,y: e.pagey})} document.onmouseup = function(e) { document.onmousemove = null document.onmouseup = null } } } }) // v-dialogDragWidth: Directive ('dialogDragWidth', {bind(el, binding, vnode, OldVnode) {const dragDom = binding.value.$el.querySelector('.el-dialog') el.onMouseDown = (e) => { Const disX = e.clientx-el.offsetLeft document.onmousemove = function(e) {e.preventDefault() // Disable default event when moving // Via event delegate, Const l = e.clientx-disx dragdom.style. width = '${l}px'} document.onmouseup = function(e) { Document.onmousemove = null document.onmouseup = null}}}}) Import './utils/ cache. Js' // Add V-dialogDrag where el-dialog is usedCopy the code

Vue implements more than two lines of display expansion, click hide

<template> <div class="view"> <div class="text more" ref="more"> placeholder </div> <div class="content" v-for="(item, index) in curData" :key="index"> <div class="text"> <div :class="{'retract': item.status}" :style="{'max-height':item.status ? textHeight: ''}" ref="textContainer"> {{item.desc}} </div> <div class="btn"> <p v-if="item.status" @click="item.status = False "> a < / p > < p v - if =" item. The status = = false "@ click =" item. The status = true "> put < / p > < / div > < / div > < / div > < / div > < / template > <script> export default { data() { return { curData: [ { desc: 'Characters, plot and environment are the three elements of a novel. Plot generally consists of four parts: beginning, development, climax and ending. Environment includes natural environment and social environment. Can be divided into long and medium-length novels according to the length and capacity, short and miniature novels (affair). According to the content of the performance can be divided into myth, XianXia, martial arts, science fiction, suspense, passing on the ancient, modern, romantic youth, game, etc. According to the system can be divided into an implicative meaning in this novel, novel, epistolary novel, autobiographical novel. According to the linguistic form can be divided into classical Chinese and vernacular novel '}, {desc: 'Psychological description, action description, language description, appearance description, expression description'},], textHeight: null}}, methods: $refs.more.scrollHeight let oneHeight = this.$refs.more.scrollHeight let twoHeight = oneHeight * 2 || 40 this.textHeight = `${twoHeight}px` let txtDom = this.$refs.textContainer for (let i = 0;  i < txtDom.length; i++) { let curHeight = txtDom[i].offsetHeight if (curHeight > twoHeight) { this.$set(this.curData, i, Object.assign({}, this.curData[i], { status: true })) } else { this.$set(this.curData, i, Object.assign({}, this.curData[i], { status: null })) } } } }, mounted() { this.curData.forEach((ele, index) => { this.$set(this.curData, index, Object.assign({}, ele, { status: $nextTick(() => {this.calculateText()}) window.onresize = () => {this.calculateText()}) window.onresize = () => { this.curData.forEach((ele, index) => { this.$set(this.curData, index, Object.assign({}, ele, { status: null })) }) setTimeout(() => { this.calculateText() }, 0) } }, destroyed(){ window.onresize = null; }}; </script> <style> .content { display: flex; margin-bottom: 30px; } .text { position: relative; font-size: 14px; line-height: 20px; letter-spacing: 2px; color: #666666; text-align: justify; } .retract { position: relative; overflow: hidden; background: #fff; } .retract:after { content: '... '; position: absolute; bottom: 0; right: 2px; width: 25px; padding-left: 5px; padding-right: 30px; background: linear-gradient(to right, transparent, #fff 0); } .btn { position: absolute; right: 0; bottom: 0; font-size: 14px; line-height: 19px; letter-spacing: 2px; color: #FFAD41; cursor: pointer; } p { margin: 0; } .more { font-size: 14px; line-height: 20px; letter-spacing: 2px; color: #666666; visibility: hidden; } </style>Copy the code

Based on VUE to achieve the head on the display slide disappear function

<template> <div class="about"> <div class="a-header" :class="['header--'+(isTopbarBlock ? 'show' : 'hidden')]"> <div class="header-top"></div> <div class="header-bottom"></div> </div> <div class="a-main"></div> </div> </template> <script> export default { data() { return { isTopbarBlock: true } }, mounted() { let scrollingElement = document.scrollingElement let scrollTop = 0 let that = this window.addEventListener('scroll', () => { if (scrollingElement.scrollTop < 300) { that.isTopbarBlock = true return } if (scrollingElement.scrollTop > scrollTop) { that.isTopbarBlock = false } else if (scrollingElement.scrollTop < scrollTop) { that.isTopbarBlock = true }  scrollTop = scrollingElement.scrollTop }) }, } </script> <style lang="scss" scoped> .about { .a-header { width: 100%; height: 160px; position: fixed; top: 0; left: 0; transition: all .2s; .header-top { width: 100%; height: 100px; background: yellow; } .header-bottom { width: 100%; height: 60px; background: green; } } .header--show{ transform: translateY(0); The transition: all 1.2 s; } .header--hidden{ transform: translateY(-100px); The transition: all 1.2 s; } .a-main { height: 5000px; Background: rgba (0, 0, 5); } } </style>Copy the code

Vue implements waterfall flow

<template> <div class="waterfall"> <div class="waterfall-left"> <div class="waterfall-left-item" v-for="(item, index) in waterLeft" :key="index" > {{ item.content }} </div> </div> <div class="waterfall-right"> <div class="waterfall-right-item" v-for="(ele, index) in waterRight" :key="index" > {{ ele.content }} </div> </div> </div> </template> <script> export default { name: "Home", data() { return { waterInfo: [ { content: "There are ten million kinds of meet, and have a meeting is called love at first sight, art in the world of mortals glance, you can gaze deep in my heart, and I start step by step for you at a glance for the fall, just want to have your warm with it, weak water three thousand take a gourd ladle drink, just want to enjoy your world, quiet read. Keep a promise to those love, into your warm city, If there is a storm on your journey, I would like to turn my missing into a silk woven into a dense friendship umbrella, let us in the storm towards the scenery! "Just because in the crowd see your one eye, make me forget your face, you know the string broken whine? Offer them for your kind of love love poison, despite repeated cycle. And you know the odds of string sound sad, finger keypad can change itself, and don't ask, don't past turned around, and no separation v. past life, let it flower is like a world, life love you. Silently, love each other. The silence, Let's go on until the world is pure white. "}, {content: "I wake up to see if the sky is floating, clouds made of rain or snow clouds."}, {content: Is who, roll up autumn wind that helpless bleak? Is who, in the soil of falling flowers immersed in the infinite deep feeling? Is who, light up autumn rain such as needle such as silk ray of light? In the depths of autumn rain, smell cold and cool taste, let the feeling twine filar silk continuously autumn whisper. "In the flood of time, we will always grow up." "The chaos of the airport will stop in a short time, people here have their own direction, in a hurry to take off, in a hurry to descend, taking away the stories of others, leaving their own memories." It may also be cruel. It may not be secular. It may also be naive." "There are ten million kinds of meet, and have a meeting is called love at first sight, art in the world of mortals glance, you can gaze deep in my heart, and I start step by step for you at a glance for the fall, just want to have your warm with it, weak water three thousand take a gourd ladle drink, just want to enjoy your world, quiet read. Keep a promise to those love, into your warm city, If there is a storm on your journey, I would like to turn my missing into a silk woven into a dense friendship umbrella, let us in the storm towards the scenery! "Just because in the crowd see your one eye, make me forget your face, you know the string broken whine? Offer them for your kind of love love poison, despite repeated cycle. And you know the odds of string sound sad, finger keypad can change itself, and don't ask, don't past turned around, and no separation v. past life, let it flower is like a world, life love you. Silently, love each other. The silence, Let's go on until the world is pure white. "}, {content: "I wake up to see if the sky is floating, clouds made of rain or snow clouds."}, {content: Is who, roll up autumn wind that helpless bleak? Is who, in the soil of falling flowers immersed in the infinite deep feeling? Is who, light up autumn rain such as needle such as silk ray of light? In the depths of autumn rain, smell cold and cool taste, let the feeling twine filar silk continuously autumn whisper. "In the flood of time, we will always grow up." "The chaos of the airport will stop in a short time, people here have their own direction, in a hurry to take off, in a hurry to descend, taking away the stories of others, leaving their own memories." [],},], waterLeft: [], waterRight: [],},}; }, mounted() { this.getData(); }, methods: { getData() { this.waterInfo.forEach((item, index) => { if (index % 2 == 0) { this.waterLeft.push(item); } else { this.waterRight.push(item); }}); ,}}}; </script> <style lang="scss" scoped> .waterfall { width: 100%; height: 100%; overflow: auto; display: flex; flex-wrap: nowrap; } .waterfall-left, .waterfall-right { width: 50%; height: 100%; display: flex; justify-content: center; flex-wrap: wrap; } .waterfall-left-item, .waterfall-right-item { width: 100%; height: auto; padding: 20px; font-size: 14px; margin-bottom: 20px; } </style>Copy the code

Custom log

// log.js const log = {}; / * * * @ description returns the color value of the style * @ param {String} type style name [primary | success | warning | danger | text] * / function typeColor(type = "default") { let color = ""; switch (type) { case "default": color = "#303133"; break; case "primary": color = "#409EFF"; break; case "success": color = "#67C23A"; break; case "warning": color = "#E6A23C"; break; case "danger": color = "#F56C6C"; break; default: break; } return color; } / * * * @ the description to print a [title | text] style information * @ param {String} the title title text * @ param {String} info info text * @param {String} type style */ log.capsule = function(title, info, type = "primary") { console.log( `%c ${title} %c ${info} %c`, "background:#35495E; padding: 1px; border-radius: 3px 0 0 3px; color: #fff;" , `background:${typeColor( type )}; padding: 1px; border-radius: 0 3px 3px 0; color: #fff; `, "background:transparent" ); }; / * * * @ the description printed color words * / log in colorful = function (textArr) {the console. The log (` % c ${textArr. The map ((t) = > t.t ext | | "").join("%c")}`, ... textArr.map((t) => `color: ${typeColor(t.type)}; `)); }; log.default = function(text) { log.colorful([{ text }]); }; log.primary = function(text) { log.colorful([{ text, type: "primary" }]); }; log.success = function(text) { log.colorful([{ text, type: "success" }]); }; log.warning = function(text) { log.colorful([{ text, type: "warning" }]); }; log.danger = function(text) { log.colorful([{ text, type: "danger" }]); }; export default log; // home.js log.default(">>> I am some default prompt "); Log.primary (">>> I am some markup prompt "); Log.success (">>> I am some success prompts "); Log. warning(">>> I am some warning "); Danger (">>> I am some error ");Copy the code

Click to show love

<script> ! (function (e, t, a) { function n() { c( ".heart{width: 10px; height: 10px; position: fixed; background: #f00; transform: rotate(45deg); -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); }.heart:after,.heart:before{content: ''; width: inherit; height: inherit; background: inherit; border-radius: 50%; -webkit-border-radius: 50%; -moz-border-radius: 50%; position: fixed; }.heart:after{top: -5px; }.heart:before{left: -5px; }" ), o(), r(); } function r() { for (var e = 0; e < d.length; e++) d[e].alpha <= 0 ? (t.body.removeChild(d[e].el), d.splice(e, 1)) : (d [e] y -, (d [e] scale + = 0.004), (d [e]. Alpha - = 0.013), (d [e]. El. Style. CssText = "left" [e] + d + x "px; top:" + d[e].y + "px; opacity:" + d[e].alpha + "; transform:scale(" + d[e].scale + "," + d[e].scale + ") rotate(45deg); background:" + d[e].color + "; z-index:99999")); requestAnimationFrame(r); } function o() { var t = "function" == typeof e.onclick && e.onclick; e.onclick = function (e) { t && t(), i(e); }; } function i(e) { var a = t.createElement("div"); (a.className = "heart"), d.push({ el: a, x: e.clientX - 5, y: e.clientY - 5, scale: 1, alpha: 1, color: s(), }), t.body.appendChild(a); } function c(e) { var a = t.createElement("style"); a.type = "text/css"; try { a.appendChild(t.createTextNode(e)); } catch (t) { a.styleSheet.cssText = e; } t.getElementsByTagName("head")[0].appendChild(a); } function s() { return ( "rgb(" + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + ")" ); } var d = []; (e.requestAnimationFrame = (function () { return ( e.requestAnimationFrame || e.webkitRequestAnimationFrame || e.mozRequestAnimationFrame || e.oRequestAnimationFrame || e.msRequestAnimationFrame || function (e) { setTimeout(e, 1e3 / 60); }); })()), n(); })(window, document); </script>Copy the code