├── .npmignore ├── .npmrc ├── es ├── index.js ├── Vertify │ ├── tool.js │ ├── index.css │ └── index.js └── index.d.ts ├── dist ├── index.d.ts ├── Vertify │ ├── tool.d.ts │ └── index.d.ts ├── index.umd.min.css ├── index.umd.css ├── index.umd.min.js └── index.umd.js ├── src ├── index.ts ├── Vertify │ ├── tool.ts │ ├── index.md │ ├── index.less │ └── index.tsx └── index.d.ts ├── typings.d.ts ├── push.sh ├── .prettierignore ├── .fatherrc.ts ├── .prettierrc ├── .editorconfig ├── .gitignore ├── lib ├── index.js ├── Vertify │ ├── tool.js │ ├── index.css │ └── index.js └── index.d.ts ├── .umirc.ts ├── tsconfig.json ├── .github └── workflows │ └── npm-publish.yml ├── index.d.ts ├── docs └── index.md ├── package.json └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | .github/ -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry = "https://registry.npmjs.com/" 2 | -------------------------------------------------------------------------------- /es/index.js: -------------------------------------------------------------------------------- 1 | export { default as Vertify } from './Vertify'; 2 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | export { default as Vertify } from './Vertify'; 2 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Vertify } from './Vertify'; 2 | -------------------------------------------------------------------------------- /typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.css'; 2 | declare module '*.less'; 3 | -------------------------------------------------------------------------------- /push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | yarn build 3 | git add . 4 | git commit -m $1 5 | git push -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.svg 2 | **/*.ejs 3 | **/*.html 4 | package.json 5 | .umi 6 | .umi-production 7 | .umi-test 8 | -------------------------------------------------------------------------------- /.fatherrc.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | esm: 'babel', 3 | cjs: 'babel', 4 | umd: {}, 5 | extractCSS: true, 6 | lessInBabelMode: true, 7 | runtimeHelpers: true, 8 | }; 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "printWidth": 80, 5 | "overrides": [ 6 | { 7 | "files": ".prettierrc", 8 | "options": { "parser": "json" } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /dist/Vertify/tool.d.ts: -------------------------------------------------------------------------------- 1 | declare function getRandomNumberByRange(start: number, end: number): number; 2 | declare function sum(x: number, y: number): number; 3 | declare function square(x: number): number; 4 | export { getRandomNumberByRange, sum, square }; 5 | -------------------------------------------------------------------------------- /es/Vertify/tool.js: -------------------------------------------------------------------------------- 1 | function getRandomNumberByRange(start, end) { 2 | return Math.round(Math.random() * (end - start) + start); 3 | } 4 | 5 | function sum(x, y) { 6 | return x + y; 7 | } 8 | 9 | function square(x) { 10 | return x * x; 11 | } 12 | 13 | export { getRandomNumberByRange, sum, square }; 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /src/Vertify/tool.ts: -------------------------------------------------------------------------------- 1 | function getRandomNumberByRange(start: number, end: number) { 2 | return Math.round(Math.random() * (end - start) + start); 3 | } 4 | 5 | function sum(x: number, y: number) { 6 | return x + y; 7 | } 8 | 9 | function square(x: number) { 10 | return x * x; 11 | } 12 | 13 | export { getRandomNumberByRange, sum, square }; 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /npm-debug.log* 6 | /yarn-error.log 7 | /yarn.lock 8 | /package-lock.json 9 | 10 | # production 11 | # /dist 12 | /docs-dist 13 | 14 | # misc 15 | .DS_Store 16 | 17 | # umi 18 | .umi 19 | .umi-production 20 | .umi-test 21 | .env.local 22 | 23 | # ide 24 | /.vscode 25 | /.idea 26 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _interopRequireDefault = require('@babel/runtime/helpers/interopRequireDefault'); 4 | 5 | Object.defineProperty(exports, '__esModule', { 6 | value: true, 7 | }); 8 | Object.defineProperty(exports, 'Vertify', { 9 | enumerable: true, 10 | get: function get() { 11 | return _Vertify.default; 12 | }, 13 | }); 14 | 15 | var _Vertify = _interopRequireDefault(require('./Vertify')); 16 | -------------------------------------------------------------------------------- /.umirc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'dumi'; 2 | 3 | export default defineConfig({ 4 | title: 'slider-vertify', 5 | favicon: 'http://h5.dooring.cn/uploads/logo_1742fd359da.png', 6 | logo: 'http://h5.dooring.cn/uploads/logo_1742fd359da.png', 7 | outputPath: '../dooring-bs/server/static/slider-vertify', 8 | base: '/slider-vertify/', 9 | publicPath: '/slider-vertify/', 10 | // more config: https://d.umijs.org/config 11 | }); 12 | -------------------------------------------------------------------------------- /lib/Vertify/tool.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true, 5 | }); 6 | exports.getRandomNumberByRange = getRandomNumberByRange; 7 | exports.sum = sum; 8 | exports.square = square; 9 | 10 | function getRandomNumberByRange(start, end) { 11 | return Math.round(Math.random() * (end - start) + start); 12 | } 13 | 14 | function sum(x, y) { 15 | return x + y; 16 | } 17 | 18 | function square(x) { 19 | return x * x; 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "importHelpers": true, 7 | "jsx": "react", 8 | "esModuleInterop": true, 9 | "sourceMap": true, 10 | "baseUrl": "./", 11 | "strict": true, 12 | "types": [], 13 | "paths": { 14 | "@/*": ["src/*"], 15 | "@@/*": ["src/.umi/*"] 16 | }, 17 | "allowSyntheticDefaultImports": true 18 | }, 19 | "exclude": [ 20 | "node_modules", 21 | "lib", 22 | "es", 23 | "dist", 24 | "typings", 25 | "**/__test__", 26 | "test", 27 | "docs", 28 | "tests" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | pull_request: 8 | branches: 9 | - main 10 | push: 11 | branches: 12 | - main 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v2 19 | - uses: actions/setup-node@v2 20 | with: 21 | node-version: 14 22 | - run: yarn 23 | - run: yarn build 24 | 25 | publish-npm: 26 | needs: build 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v2 30 | - uses: actions/setup-node@v2 31 | with: 32 | node-version: 14 33 | registry-url: https://registry.npmjs.com/ 34 | - run: npm publish --access public 35 | env: 36 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | 3 | interface IVertifyProp { 4 | /** 5 | * @description canvas宽度 6 | * @default 320 7 | */ 8 | width: number; 9 | /** 10 | * @description canvas高度 11 | * @default 160 12 | */ 13 | height: number; 14 | /** 15 | * @description 滑块边长 16 | * @default 42 17 | */ 18 | l: number; 19 | /** 20 | * @description 滑块半径 21 | * @default 9 22 | */ 23 | r: number; 24 | /** 25 | * @description 是否可见 26 | * @default true 27 | */ 28 | visible: boolean; 29 | /** 30 | * @description 滑块文本 31 | * @default 向右滑动填充拼图 32 | */ 33 | text: string | ReactNode; 34 | /** 35 | * @description 刷新按钮icon, 为icon的url地址 36 | * @default - 37 | */ 38 | refreshIcon: string; 39 | /** 40 | * @description 用于获取随机图片的url地址 41 | * @default https://picsum.photos/${id}/${width}/${height}, 具体参考https://picsum.photos/, 只需要实现类似接口即可 42 | */ 43 | imgUrl: string; 44 | /** 45 | * @description 验证成功回调 46 | * @default ():void => {} 47 | */ 48 | onSuccess: VoidFunction; 49 | /** 50 | * @description 验证失败回调 51 | * @default ():void => {} 52 | */ 53 | onFail: VoidFunction; 54 | /** 55 | * @description 刷新时回调 56 | * @default ():void => {} 57 | */ 58 | onRefresh: VoidFunction; 59 | } 60 | 61 | declare const Vertify: React.FC; 62 | 63 | export { Vertify }; 64 | -------------------------------------------------------------------------------- /es/index.d.ts: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | 3 | export interface IVertifyProp { 4 | /** 5 | * @description canvas宽度 6 | * @default 320 7 | */ 8 | width?: number; 9 | /** 10 | * @description canvas高度 11 | * @default 160 12 | */ 13 | height?: number; 14 | /** 15 | * @description 滑块边长 16 | * @default 42 17 | */ 18 | l?: number; 19 | /** 20 | * @description 滑块半径 21 | * @default 9 22 | */ 23 | r?: number; 24 | /** 25 | * @description 是否可见 26 | * @default true 27 | */ 28 | visible?: boolean; 29 | /** 30 | * @description 滑块文本 31 | * @default 向右滑动填充拼图 32 | */ 33 | text?: string | ReactNode; 34 | /** 35 | * @description 刷新按钮icon, 为icon的url地址 36 | * @default - 37 | */ 38 | refreshIcon?: string; 39 | /** 40 | * @description 用于获取随机图片的url地址 41 | * @default https://picsum.photos/${id}/${width}/${height}, 具体参考https://picsum.photos/, 只需要实现类似接口即可 42 | */ 43 | imgUrl?: string; 44 | /** 45 | * @description 验证成功回调 46 | * @default ():void => {} 47 | */ 48 | onSuccess?: VoidFunction; 49 | /** 50 | * @description 验证失败回调 51 | * @default ():void => {} 52 | */ 53 | onFail?: VoidFunction; 54 | /** 55 | * @description 刷新时回调 56 | * @default ():void => {} 57 | */ 58 | onRefresh?: VoidFunction; 59 | } 60 | 61 | declare const Vertify: React.FC; 62 | 63 | export { Vertify }; 64 | -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | 3 | export interface IVertifyProp { 4 | /** 5 | * @description canvas宽度 6 | * @default 320 7 | */ 8 | width?: number; 9 | /** 10 | * @description canvas高度 11 | * @default 160 12 | */ 13 | height?: number; 14 | /** 15 | * @description 滑块边长 16 | * @default 42 17 | */ 18 | l?: number; 19 | /** 20 | * @description 滑块半径 21 | * @default 9 22 | */ 23 | r?: number; 24 | /** 25 | * @description 是否可见 26 | * @default true 27 | */ 28 | visible?: boolean; 29 | /** 30 | * @description 滑块文本 31 | * @default 向右滑动填充拼图 32 | */ 33 | text?: string | ReactNode; 34 | /** 35 | * @description 刷新按钮icon, 为icon的url地址 36 | * @default - 37 | */ 38 | refreshIcon?: string; 39 | /** 40 | * @description 用于获取随机图片的url地址 41 | * @default https://picsum.photos/${id}/${width}/${height}, 具体参考https://picsum.photos/, 只需要实现类似接口即可 42 | */ 43 | imgUrl?: string; 44 | /** 45 | * @description 验证成功回调 46 | * @default ():void => {} 47 | */ 48 | onSuccess?: VoidFunction; 49 | /** 50 | * @description 验证失败回调 51 | * @default ():void => {} 52 | */ 53 | onFail?: VoidFunction; 54 | /** 55 | * @description 刷新时回调 56 | * @default ():void => {} 57 | */ 58 | onRefresh?: VoidFunction; 59 | } 60 | 61 | declare const Vertify: React.FC; 62 | 63 | export { Vertify }; 64 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | 3 | export interface IVertifyProp { 4 | /** 5 | * @description canvas宽度 6 | * @default 320 7 | */ 8 | width?: number; 9 | /** 10 | * @description canvas高度 11 | * @default 160 12 | */ 13 | height?: number; 14 | /** 15 | * @description 滑块边长 16 | * @default 42 17 | */ 18 | l?: number; 19 | /** 20 | * @description 滑块半径 21 | * @default 9 22 | */ 23 | r?: number; 24 | /** 25 | * @description 是否可见 26 | * @default true 27 | */ 28 | visible?: boolean; 29 | /** 30 | * @description 滑块文本 31 | * @default 向右滑动填充拼图 32 | */ 33 | text?: string | ReactNode; 34 | /** 35 | * @description 刷新按钮icon, 为icon的url地址 36 | * @default - 37 | */ 38 | refreshIcon?: string; 39 | /** 40 | * @description 用于获取随机图片的url地址 41 | * @default https://picsum.photos/${id}/${width}/${height}, 具体参考https://picsum.photos/, 只需要实现类似接口即可 42 | */ 43 | imgUrl?: string; 44 | /** 45 | * @description 验证成功回调 46 | * @default ():void => {} 47 | */ 48 | onSuccess?: VoidFunction; 49 | /** 50 | * @description 验证失败回调 51 | * @default ():void => {} 52 | */ 53 | onFail?: VoidFunction; 54 | /** 55 | * @description 刷新时回调 56 | * @default ():void => {} 57 | */ 58 | onRefresh?: VoidFunction; 59 | } 60 | 61 | declare const Vertify: React.FC; 62 | 63 | export { Vertify }; 64 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ## Hello react-slider-vertify! 2 | 3 | react-slider-vertify 是一款前端实现的滑动验证码组件, 我们可以通过它轻松的控制验证的整个生命周期(刷新时, 验证成功时, 验证失败时的回调), 并拥有一定的配置化能力. 4 | 5 | ![demo.gif](http://cdn.dooring.cn/dr/slider.gif) 6 | 7 | ### More Production 8 | 9 | | name | Description | 10 | | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | 11 | | [H5-Dooring](https://github.com/MrXujiang/h5-Dooring) | 让 H5 制作像搭积木一样简单, 轻松搭建 H5 页面, H5 网站, PC 端网站, LowCode 平台. | 12 | | [V6.Dooring](https://github.com/MrXujiang/v6.dooring.public) | 可视化大屏解决方案, 提供一套可视化编辑引擎, 助力个人或企业轻松定制自己的可视化大屏应用. | 13 | | [dooring-electron-lowcode](https://github.com/MrXujiang/dooring-electron-lowcode) | 基于 electron 的 H5-Dooring 编辑器桌面端. | 14 | | [PC-Dooring](https://github.com/MrXujiang/pc-Dooring) | 网格式拖拽搭建 PC 端页面. | 15 | | [DooringX](https://github.com/H5-Dooring/dooringx) | 快速高效搭建可视化拖拽平台. | 16 | 17 | ## 赞助 | Sponsored 18 | 19 | 开源不易, 有了您的赞助, 我们会做的更好~ 20 | 21 | 22 | 23 | ## 技术反馈和交流群 | Technical feedback and communication 24 | 25 | 微信:beautifulFront 26 | 27 | 28 | -------------------------------------------------------------------------------- /dist/Vertify/index.d.ts: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | import './index.less'; 3 | interface VertifyType { 4 | spliced: boolean; 5 | verified: boolean; 6 | left: number; 7 | destX: number; 8 | } 9 | interface IVertifyProp { 10 | /** 11 | * @description canvas宽度 12 | * @default 320 13 | */ 14 | width?: number; 15 | /** 16 | * @description canvas高度 17 | * @default 160 18 | */ 19 | height?: number; 20 | /** 21 | * @description 滑块边长 22 | * @default 42 23 | */ 24 | l?: number; 25 | /** 26 | * @description 滑块半径 27 | * @default 9 28 | */ 29 | r?: number; 30 | /** 31 | * @description 是否可见 32 | * @default true 33 | */ 34 | visible?: boolean; 35 | /** 36 | * @description 滑块文本 37 | * @default 向右滑动填充拼图 38 | */ 39 | text?: string | ReactNode; 40 | /** 41 | * @description 刷新按钮icon, 为icon的url地址 42 | * @default - 43 | */ 44 | refreshIcon?: string; 45 | /** 46 | * @description 用于获取随机图片的url地址 47 | * @default https://picsum.photos/${id}/${width}/${height}, 具体参考https://picsum.photos/, 只需要实现类似接口即可 48 | */ 49 | imgUrl?: string; 50 | /** 51 | * @description 拖拽滑块时的回调, 参数为当前滑块拖拽的距离 52 | * @default (l: number):void => {} 53 | */ 54 | onDraw?: (l: number) => {}; 55 | /** 56 | * @description 用户的自定义验证逻辑 57 | * @default (arg: VertifyType) => VertifyType 58 | */ 59 | onCustomVertify?: (arg: VertifyType) => VertifyType; 60 | /** 61 | * @description 验证成功回调 62 | * @default ():void => {} 63 | */ 64 | onSuccess?: VoidFunction; 65 | /** 66 | * @description 验证失败回调 67 | * @default ():void => {} 68 | */ 69 | onFail?: VoidFunction; 70 | /** 71 | * @description 刷新时回调 72 | * @default ():void => {} 73 | */ 74 | onRefresh?: VoidFunction; 75 | } 76 | declare const _default: React.MemoExoticComponent< 77 | ({ 78 | width, 79 | height, 80 | l, 81 | r, 82 | imgUrl, 83 | text, 84 | refreshIcon, 85 | visible, 86 | onDraw, 87 | onCustomVertify, 88 | onSuccess, 89 | onFail, 90 | onRefresh, 91 | }: IVertifyProp) => JSX.Element 92 | >; 93 | export default _default; 94 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@alex_xu/react-slider-vertify", 3 | "version": "1.1.9", 4 | "author": "alex_xu", 5 | "description": "Slide verification component based on react", 6 | "keywords": [ 7 | "slider-vertify", 8 | "react-slider-vertify", 9 | "component", 10 | "components", 11 | "design", 12 | "framework", 13 | "frontend", 14 | "react", 15 | "react-component", 16 | "ui", 17 | "徐小夕" 18 | ], 19 | "scripts": { 20 | "start": "dumi dev", 21 | "docs:build": "dumi build", 22 | "docs:deploy": "gh-pages -d docs-dist", 23 | "build": "father-build", 24 | "deploy": "npm run docs:build && npm run docs:deploy", 25 | "release": "npm run build && npm publish --access public", 26 | "prettier": "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"", 27 | "test": "umi-test", 28 | "test:coverage": "umi-test --coverage" 29 | }, 30 | "contributors": [ 31 | "alex_xu" 32 | ], 33 | "files": [ 34 | "dist", 35 | "lib", 36 | "es" 37 | ], 38 | "main": "lib/index.js", 39 | "module": "es/index.js", 40 | "typings": "dist/index.d.ts", 41 | "browserslist": [ 42 | "last 2 version", 43 | "Firefox ESR", 44 | "> 1%", 45 | "ie >= 9" 46 | ], 47 | "gitHooks": { 48 | "pre-commit": "lint-staged" 49 | }, 50 | "lint-staged": { 51 | "*.{js,jsx,less,md,json}": [ 52 | "prettier --write" 53 | ], 54 | "*.ts?(x)": [ 55 | "prettier --parser=typescript --write" 56 | ] 57 | }, 58 | "homepage": "https://github.com/MrXujiang/react-slider-vertify", 59 | "bugs": { 60 | "url": "https://github.com/MrXujiang/react-slider-vertify/issues" 61 | }, 62 | "repository": { 63 | "type": "git", 64 | "url": "git+https://github.com:MrXujiang/react-slider-vertify.git" 65 | }, 66 | "dependencies": { 67 | "@babel/runtime": "^7.15.4" 68 | }, 69 | "peerDependencies": { 70 | "react": "^16.9.23" 71 | }, 72 | "resolutions": { 73 | "@types/react": "^16.9.23" 74 | }, 75 | "devDependencies": { 76 | "@umijs/test": "^3.0.5", 77 | "dumi": "^1.0.16", 78 | "father-build": "^1.17.2", 79 | "gh-pages": "^3.0.0", 80 | "lint-staged": "^10.0.7", 81 | "prettier": "^2.2.1", 82 | "yorkie": "^2.0.0" 83 | }, 84 | "license": "MIT", 85 | "publishConfig": { 86 | "registry": "https://registry.npmjs.com" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /dist/index.umd.min.css: -------------------------------------------------------------------------------- 1 | .vertifyWrap{position:relative}.vertifyWrap .block{position:absolute;left:0;top:0;cursor:pointer;cursor:-webkit-grab;cursor:grab}.vertifyWrap .block:active{cursor:-webkit-grabbing;cursor:grabbing}.vertifyWrap .sliderContainer{position:relative;text-align:center;width:310px;height:40px;line-height:40px;margin-top:15px;background:#f7f9fa;color:#45494c;border:1px solid #e4e7eb}.vertifyWrap .sliderContainer_active .slider{height:38px;top:-1px;border:1px solid #486cd6}.vertifyWrap .sliderContainer_active .sliderMask{height:38px;border-width:1px}.vertifyWrap .sliderContainer_success .slider{height:38px;top:-1px;border:1px solid #0db87f;background-color:#0ca14a!important}.vertifyWrap .sliderContainer_success .sliderMask{height:38px;border:1px solid #0db87f;background-color:#d2f4ef}.vertifyWrap .sliderContainer_success .sliderIcon{background-position:0 -26px!important}.vertifyWrap .sliderContainer_fail .slider{height:38px;top:-1px;border:1px solid #f57a7a;background-color:#f57a7a!important}.vertifyWrap .sliderContainer_fail .sliderMask{height:38px;border:1px solid #f57a7a;background-color:#fce1e1}.vertifyWrap .sliderContainer_fail .sliderIcon{top:14px;background-position:0 -82px!important}.vertifyWrap .sliderContainer_active .sliderText,.vertifyWrap .sliderContainer_fail .sliderText,.vertifyWrap .sliderContainer_success .sliderText{display:none}.vertifyWrap .sliderMask{position:absolute;left:0;top:0;height:40px;border:0 solid #486cd6;background:#d1e9fe}.vertifyWrap .slider{position:absolute;top:0;left:0;width:40px;height:40px;background:#fff;-webkit-box-shadow:0 0 3px rgba(0,0,0,.3);box-shadow:0 0 3px rgba(0,0,0,.3);-webkit-transition:background .2s linear;transition:background .2s linear;cursor:pointer;cursor:-webkit-grab;cursor:grab}.vertifyWrap .slider:active{cursor:-webkit-grabbing;cursor:grabbing}.vertifyWrap .slider:hover{background:#486cd6}.vertifyWrap .sliderIcon{font-size:18px;color:#000}.vertifyWrap .slider:hover .sliderIcon{color:#fff}.vertifyWrap .refreshIcon{position:absolute;right:5px;top:5px;width:30px;height:30px;cursor:pointer;background-size:32px}.vertifyWrap .loadingContainer{position:absolute;left:0;top:0;width:310px;height:155px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;font-size:14px;color:#45494c;z-index:2;background:#edf0f2}.vertifyWrap .loadingIcon{width:32px;height:32px;margin-bottom:10px;background:url(http://cdn.dooring.cn/dr/icon12.png);background-size:32px;-webkit-animation:loading-icon-rotate .8s linear infinite;animation:loading-icon-rotate .8s linear infinite}@-webkit-keyframes loading-icon-rotate{0%{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes loading-icon-rotate{0%{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}} -------------------------------------------------------------------------------- /src/Vertify/index.md: -------------------------------------------------------------------------------- 1 | ### 基本使用: 2 | 3 | ```tsx 4 | import React from 'react'; 5 | import { Vertify } from '@alex_xu/react-slider-vertify'; 6 | 7 | export default () => { 8 | return ; 9 | }; 10 | ``` 11 | 12 | ### 设置宽高: 13 | 14 | ```tsx 15 | import React from 'react'; 16 | import { Vertify } from '@alex_xu/react-slider-vertify'; 17 | 18 | export default () => { 19 | return ; 20 | }; 21 | ``` 22 | 23 | ### 设置滑块边长和半径: 24 | 25 | ```tsx 26 | import React from 'react'; 27 | import { Vertify } from '@alex_xu/react-slider-vertify'; 28 | 29 | export default () => { 30 | return ; 31 | }; 32 | ``` 33 | 34 | ### 设置成功, 失败, 刷新时的回调: 35 | 36 | ```tsx 37 | import React from 'react'; 38 | import { Vertify } from '@alex_xu/react-slider-vertify'; 39 | 40 | export default () => { 41 | return ( 42 | alert('success')} 46 | onFail={() => alert('fail')} 47 | onRefresh={() => alert('refresh')} 48 | /> 49 | ); 50 | }; 51 | ``` 52 | 53 | ### 用户自定义验证逻辑: 54 | 55 | 组件暴露了`onCustomVertify`方法, 并接受收了入参`vertify`对象, 我们可以控制 `spliced` 和`verified` 属性来控制是否验证成功, 即函数返回值必须包含 `spliced` 和 `verified` 两个布尔值的属性对象 56 | 57 | ```tsx 58 | import React from 'react'; 59 | import { Vertify } from '@alex_xu/react-slider-vertify'; 60 | 61 | export default () => { 62 | const handleCustomVertify = (vertify) => { 63 | console.log(vertify, Math.abs(left - destX) < 5); 64 | const { destX, left, spliced, verified } = vertify; 65 | return { 66 | spliced: Math.abs(left - destX) < 5, 67 | verified, 68 | }; 69 | }; 70 | return ( 71 | alert('success')} 76 | onFail={() => alert('fail')} 77 | onRefresh={() => alert('refresh')} 78 | /> 79 | ); 80 | }; 81 | ``` 82 | 83 | ### 动态设置显示/ 隐藏: 84 | 85 | ```tsx 86 | import React, { useState } from 'react'; 87 | import { Vertify } from '@alex_xu/react-slider-vertify'; 88 | 89 | export default () => { 90 | const [visible, setVisible] = useState(false); 91 | const show = () => { 92 | setVisible(true); 93 | }; 94 | const hide = () => { 95 | setVisible(false); 96 | }; 97 | const style = { 98 | display: 'inline-block', 99 | marginRight: '20px', 100 | marginBottom: '20px', 101 | width: '100px', 102 | padding: '5px 20px', 103 | color: '#fff', 104 | textAlign: 'center', 105 | cursor: 'pointer', 106 | background: '#1991FA', 107 | }; 108 | return ( 109 | <> 110 |
111 | 显示 112 |
113 |
114 | 隐藏 115 |
116 | alert('success')} 121 | onFail={() => alert('fail')} 122 | onRefresh={() => alert('refresh')} 123 | /> 124 | 125 | ); 126 | }; 127 | ``` 128 | 129 | 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm downloads](https://img.shields.io/npm/dm/@alex_xu/react-slider-vertify.svg?style=flat-square)](http://npm-stat.com/charts.html?package=@alex_xu/react-slider-vertify) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/@alex_xu/react-slider-vertify) ![APM](https://img.shields.io/npm/l/@alex_xu/react-slider-vertify?style=flat-square) 2 | ## Hello react-slider-vertify! 3 | 4 | react-slider-vertify 是一款前端实现的滑动验证码组件, 我们可以通过它轻松的控制验证的整个生命周期(刷新时, 验证成功时, 验证失败时的回调), 并拥有一定的配置化能力. 5 | 6 | ![demo.gif](http://cdn.dooring.cn/dr/slider.gif) 7 | 8 | ## doc 9 | 10 | [react-slider-vertify](http://h5.dooring.cn/slider-vertify/) 11 | 12 | ## Getting Started 13 | 14 | Install dependencies, 15 | 16 | ```bash 17 | $ npm i @alex_xu/react-slider-vertify 18 | ``` 19 | 20 | ## Use 21 | 22 | ```tsx 23 | import React, { useState } from 'react'; 24 | import { Vertify } from '@alex_xu/react-slider-vertify'; 25 | 26 | export default () => { 27 | const [visible, setVisible] = useState(false); 28 | const show = () => { 29 | setVisible(true); 30 | }; 31 | const hide = () => { 32 | setVisible(false); 33 | }; 34 | const style = { 35 | display: 'inline-block', 36 | marginRight: '20px', 37 | marginBottom: '20px', 38 | width: '100px', 39 | padding: '5px 20px', 40 | color: '#fff', 41 | textAlign: 'center', 42 | cursor: 'pointer', 43 | background: '#1991FA', 44 | }; 45 | return ( 46 | <> 47 |
48 | 显示 49 |
50 |
51 | 隐藏 52 |
53 | alert('success')} 58 | onFail={() => alert('fail')} 59 | onRefresh={() => alert('refresh')} 60 | /> 61 | 62 | ); 63 | }; 64 | ``` 65 | 66 | ## More Production 67 | 68 | | name | Description | 69 | | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | 70 | | [H5-Dooring](https://github.com/MrXujiang/h5-Dooring) | 让 H5 制作像搭积木一样简单, 轻松搭建 H5 页面, H5 网站, PC 端网站, LowCode 平台. | 71 | | [V6.Dooring](https://github.com/MrXujiang/v6.dooring.public) | 可视化大屏解决方案, 提供一套可视化编辑引擎, 助力个人或企业轻松定制自己的可视化大屏应用. | 72 | | [dooring-electron-lowcode](https://github.com/MrXujiang/dooring-electron-lowcode) | 基于 electron 的 H5-Dooring 编辑器桌面端. | 73 | | [DooringX](https://github.com/H5-Dooring/dooringx) | 快速高效搭建可视化拖拽平台. | 74 | | [Mitu](https://github.com/H5-Dooring/mitu-editor) | 一款轻量级且可扩展的图片/图形编辑器解决方案. | 75 | | [react-rotate-captcha](https://github.com/cgfeel/react-rotate-captcha) | 一个开箱即用的旋转验证码React组件 | 76 | 77 | ## Inspired by 78 | 79 | https://github.com/yeild/jigsaw 80 | 81 | ## 赞助 | Sponsored 82 | 83 | 开源不易, 有了您的赞助, 我们会做的更好~ 84 | 85 | 86 | 87 | ## 技术反馈和交流群 | Technical feedback and communication 88 | 89 | 微信:beautifulFront 90 | 91 | 92 | -------------------------------------------------------------------------------- /es/Vertify/index.css: -------------------------------------------------------------------------------- 1 | .vertifyWrap { 2 | position: relative; 3 | } 4 | .vertifyWrap .block { 5 | position: absolute; 6 | left: 0; 7 | top: 0; 8 | cursor: pointer; 9 | cursor: grab; 10 | } 11 | .vertifyWrap .block:active { 12 | cursor: grabbing; 13 | } 14 | .vertifyWrap .sliderContainer { 15 | position: relative; 16 | text-align: center; 17 | width: 310px; 18 | height: 40px; 19 | line-height: 40px; 20 | margin-top: 15px; 21 | background: #f7f9fa; 22 | color: #45494c; 23 | border: 1px solid #e4e7eb; 24 | } 25 | .vertifyWrap .sliderContainer_active .slider { 26 | height: 38px; 27 | top: -1px; 28 | border: 1px solid #486cd6; 29 | } 30 | .vertifyWrap .sliderContainer_active .sliderMask { 31 | height: 38px; 32 | border-width: 1px; 33 | } 34 | .vertifyWrap .sliderContainer_success .slider { 35 | height: 38px; 36 | top: -1px; 37 | border: 1px solid #0db87f; 38 | background-color: #0ca14a !important; 39 | } 40 | .vertifyWrap .sliderContainer_success .sliderMask { 41 | height: 38px; 42 | border: 1px solid #0db87f; 43 | background-color: #d2f4ef; 44 | } 45 | .vertifyWrap .sliderContainer_success .sliderIcon { 46 | background-position: 0 -26px !important; 47 | } 48 | .vertifyWrap .sliderContainer_fail .slider { 49 | height: 38px; 50 | top: -1px; 51 | border: 1px solid #f57a7a; 52 | background-color: #f57a7a !important; 53 | } 54 | .vertifyWrap .sliderContainer_fail .sliderMask { 55 | height: 38px; 56 | border: 1px solid #f57a7a; 57 | background-color: #fce1e1; 58 | } 59 | .vertifyWrap .sliderContainer_fail .sliderIcon { 60 | top: 14px; 61 | background-position: 0 -82px !important; 62 | } 63 | .vertifyWrap .sliderContainer_active .sliderText, 64 | .vertifyWrap .sliderContainer_success .sliderText, 65 | .vertifyWrap .sliderContainer_fail .sliderText { 66 | display: none; 67 | } 68 | .vertifyWrap .sliderMask { 69 | position: absolute; 70 | left: 0; 71 | top: 0; 72 | height: 40px; 73 | border: 0 solid #486cd6; 74 | background: #d1e9fe; 75 | } 76 | .vertifyWrap .slider { 77 | position: absolute; 78 | top: 0; 79 | left: 0; 80 | width: 40px; 81 | height: 40px; 82 | background: #fff; 83 | box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); 84 | transition: background 0.2s linear; 85 | cursor: pointer; 86 | cursor: grab; 87 | } 88 | .vertifyWrap .slider:active { 89 | cursor: grabbing; 90 | } 91 | .vertifyWrap .slider:hover { 92 | background: #486cd6; 93 | } 94 | .vertifyWrap .sliderIcon { 95 | font-size: 18px; 96 | color: #000; 97 | } 98 | .vertifyWrap .slider:hover .sliderIcon { 99 | color: #fff; 100 | } 101 | .vertifyWrap .refreshIcon { 102 | position: absolute; 103 | right: 5px; 104 | top: 5px; 105 | width: 30px; 106 | height: 30px; 107 | cursor: pointer; 108 | background-size: 32px; 109 | } 110 | .vertifyWrap .loadingContainer { 111 | position: absolute; 112 | left: 0; 113 | top: 0; 114 | width: 310px; 115 | height: 155px; 116 | display: flex; 117 | flex-direction: column; 118 | justify-content: center; 119 | align-items: center; 120 | font-size: 14px; 121 | color: #45494c; 122 | z-index: 2; 123 | background: #edf0f2; 124 | } 125 | .vertifyWrap .loadingIcon { 126 | width: 32px; 127 | height: 32px; 128 | margin-bottom: 10px; 129 | background: url(http://cdn.dooring.cn/dr/icon12.png); 130 | background-size: 32px; 131 | animation: loading-icon-rotate 0.8s linear infinite; 132 | } 133 | @keyframes loading-icon-rotate { 134 | from { 135 | transform: rotate(0); 136 | } 137 | to { 138 | transform: rotate(360deg); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /lib/Vertify/index.css: -------------------------------------------------------------------------------- 1 | .vertifyWrap { 2 | position: relative; 3 | } 4 | .vertifyWrap .block { 5 | position: absolute; 6 | left: 0; 7 | top: 0; 8 | cursor: pointer; 9 | cursor: grab; 10 | } 11 | .vertifyWrap .block:active { 12 | cursor: grabbing; 13 | } 14 | .vertifyWrap .sliderContainer { 15 | position: relative; 16 | text-align: center; 17 | width: 310px; 18 | height: 40px; 19 | line-height: 40px; 20 | margin-top: 15px; 21 | background: #f7f9fa; 22 | color: #45494c; 23 | border: 1px solid #e4e7eb; 24 | } 25 | .vertifyWrap .sliderContainer_active .slider { 26 | height: 38px; 27 | top: -1px; 28 | border: 1px solid #486cd6; 29 | } 30 | .vertifyWrap .sliderContainer_active .sliderMask { 31 | height: 38px; 32 | border-width: 1px; 33 | } 34 | .vertifyWrap .sliderContainer_success .slider { 35 | height: 38px; 36 | top: -1px; 37 | border: 1px solid #0db87f; 38 | background-color: #0ca14a !important; 39 | } 40 | .vertifyWrap .sliderContainer_success .sliderMask { 41 | height: 38px; 42 | border: 1px solid #0db87f; 43 | background-color: #d2f4ef; 44 | } 45 | .vertifyWrap .sliderContainer_success .sliderIcon { 46 | background-position: 0 -26px !important; 47 | } 48 | .vertifyWrap .sliderContainer_fail .slider { 49 | height: 38px; 50 | top: -1px; 51 | border: 1px solid #f57a7a; 52 | background-color: #f57a7a !important; 53 | } 54 | .vertifyWrap .sliderContainer_fail .sliderMask { 55 | height: 38px; 56 | border: 1px solid #f57a7a; 57 | background-color: #fce1e1; 58 | } 59 | .vertifyWrap .sliderContainer_fail .sliderIcon { 60 | top: 14px; 61 | background-position: 0 -82px !important; 62 | } 63 | .vertifyWrap .sliderContainer_active .sliderText, 64 | .vertifyWrap .sliderContainer_success .sliderText, 65 | .vertifyWrap .sliderContainer_fail .sliderText { 66 | display: none; 67 | } 68 | .vertifyWrap .sliderMask { 69 | position: absolute; 70 | left: 0; 71 | top: 0; 72 | height: 40px; 73 | border: 0 solid #486cd6; 74 | background: #d1e9fe; 75 | } 76 | .vertifyWrap .slider { 77 | position: absolute; 78 | top: 0; 79 | left: 0; 80 | width: 40px; 81 | height: 40px; 82 | background: #fff; 83 | box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); 84 | transition: background 0.2s linear; 85 | cursor: pointer; 86 | cursor: grab; 87 | } 88 | .vertifyWrap .slider:active { 89 | cursor: grabbing; 90 | } 91 | .vertifyWrap .slider:hover { 92 | background: #486cd6; 93 | } 94 | .vertifyWrap .sliderIcon { 95 | font-size: 18px; 96 | color: #000; 97 | } 98 | .vertifyWrap .slider:hover .sliderIcon { 99 | color: #fff; 100 | } 101 | .vertifyWrap .refreshIcon { 102 | position: absolute; 103 | right: 5px; 104 | top: 5px; 105 | width: 30px; 106 | height: 30px; 107 | cursor: pointer; 108 | background-size: 32px; 109 | } 110 | .vertifyWrap .loadingContainer { 111 | position: absolute; 112 | left: 0; 113 | top: 0; 114 | width: 310px; 115 | height: 155px; 116 | display: flex; 117 | flex-direction: column; 118 | justify-content: center; 119 | align-items: center; 120 | font-size: 14px; 121 | color: #45494c; 122 | z-index: 2; 123 | background: #edf0f2; 124 | } 125 | .vertifyWrap .loadingIcon { 126 | width: 32px; 127 | height: 32px; 128 | margin-bottom: 10px; 129 | background: url(http://cdn.dooring.cn/dr/icon12.png); 130 | background-size: 32px; 131 | animation: loading-icon-rotate 0.8s linear infinite; 132 | } 133 | @keyframes loading-icon-rotate { 134 | from { 135 | transform: rotate(0); 136 | } 137 | to { 138 | transform: rotate(360deg); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/Vertify/index.less: -------------------------------------------------------------------------------- 1 | .vertifyWrap { 2 | position: relative; 3 | 4 | .block { 5 | position: absolute; 6 | left: 0; 7 | top: 0; 8 | cursor: pointer; 9 | cursor: grab; 10 | } 11 | 12 | .block:active { 13 | cursor: grabbing; 14 | } 15 | 16 | .sliderContainer { 17 | position: relative; 18 | text-align: center; 19 | width: 310px; 20 | height: 40px; 21 | line-height: 40px; 22 | margin-top: 15px; 23 | background: #f7f9fa; 24 | color: #45494c; 25 | border: 1px solid #e4e7eb; 26 | } 27 | 28 | .sliderContainer_active .slider { 29 | height: 38px; 30 | top: -1px; 31 | border: 1px solid #486cd6; 32 | } 33 | 34 | .sliderContainer_active .sliderMask { 35 | height: 38px; 36 | border-width: 1px; 37 | } 38 | 39 | .sliderContainer_success .slider { 40 | height: 38px; 41 | top: -1px; 42 | border: 1px solid #0db87f; 43 | background-color: #0ca14a !important; 44 | } 45 | 46 | .sliderContainer_success .sliderMask { 47 | height: 38px; 48 | border: 1px solid #0db87f; 49 | background-color: #d2f4ef; 50 | } 51 | 52 | .sliderContainer_success .sliderIcon { 53 | background-position: 0 -26px !important; 54 | } 55 | 56 | .sliderContainer_fail .slider { 57 | height: 38px; 58 | top: -1px; 59 | border: 1px solid #f57a7a; 60 | background-color: #f57a7a !important; 61 | } 62 | 63 | .sliderContainer_fail .sliderMask { 64 | height: 38px; 65 | border: 1px solid #f57a7a; 66 | background-color: #fce1e1; 67 | } 68 | 69 | .sliderContainer_fail .sliderIcon { 70 | top: 14px; 71 | background-position: 0 -82px !important; 72 | } 73 | 74 | .sliderContainer_active .sliderText, 75 | .sliderContainer_success .sliderText, 76 | .sliderContainer_fail .sliderText { 77 | display: none; 78 | } 79 | 80 | .sliderMask { 81 | position: absolute; 82 | left: 0; 83 | top: 0; 84 | height: 40px; 85 | border: 0 solid #486cd6; 86 | background: #d1e9fe; 87 | } 88 | 89 | .slider { 90 | position: absolute; 91 | top: 0; 92 | left: 0; 93 | width: 40px; 94 | height: 40px; 95 | background: #fff; 96 | box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); 97 | transition: background 0.2s linear; 98 | cursor: pointer; 99 | cursor: grab; 100 | } 101 | 102 | .slider:active { 103 | cursor: grabbing; 104 | } 105 | 106 | .slider:hover { 107 | background: #486cd6; 108 | } 109 | 110 | .sliderIcon { 111 | font-size: 18px; 112 | color: #000; 113 | } 114 | 115 | .slider:hover .sliderIcon { 116 | color: #fff; 117 | } 118 | 119 | .refreshIcon { 120 | position: absolute; 121 | right: 5px; 122 | top: 5px; 123 | width: 30px; 124 | height: 30px; 125 | cursor: pointer; 126 | background-size: 32px; 127 | } 128 | 129 | .loadingContainer { 130 | position: absolute; 131 | left: 0; 132 | top: 0; 133 | width: 310px; 134 | height: 155px; 135 | display: flex; 136 | flex-direction: column; 137 | justify-content: center; 138 | align-items: center; 139 | font-size: 14px; 140 | color: #45494c; 141 | z-index: 2; 142 | background: #edf0f2; 143 | } 144 | 145 | .loadingIcon { 146 | width: 32px; 147 | height: 32px; 148 | margin-bottom: 10px; 149 | background: url(http://cdn.dooring.cn/dr/icon12.png); 150 | background-size: 32px; 151 | animation: loading-icon-rotate 0.8s linear infinite; 152 | } 153 | 154 | @keyframes loading-icon-rotate { 155 | from { 156 | transform: rotate(0); 157 | } 158 | to { 159 | transform: rotate(360deg); 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /dist/index.umd.css: -------------------------------------------------------------------------------- 1 | .vertifyWrap { 2 | position: relative; 3 | } 4 | .vertifyWrap .block { 5 | position: absolute; 6 | left: 0; 7 | top: 0; 8 | cursor: pointer; 9 | cursor: -webkit-grab; 10 | cursor: grab; 11 | } 12 | .vertifyWrap .block:active { 13 | cursor: -webkit-grabbing; 14 | cursor: grabbing; 15 | } 16 | .vertifyWrap .sliderContainer { 17 | position: relative; 18 | text-align: center; 19 | width: 310px; 20 | height: 40px; 21 | line-height: 40px; 22 | margin-top: 15px; 23 | background: #f7f9fa; 24 | color: #45494c; 25 | border: 1px solid #e4e7eb; 26 | } 27 | .vertifyWrap .sliderContainer_active .slider { 28 | height: 38px; 29 | top: -1px; 30 | border: 1px solid #486cd6; 31 | } 32 | .vertifyWrap .sliderContainer_active .sliderMask { 33 | height: 38px; 34 | border-width: 1px; 35 | } 36 | .vertifyWrap .sliderContainer_success .slider { 37 | height: 38px; 38 | top: -1px; 39 | border: 1px solid #0db87f; 40 | background-color: #0ca14a !important; 41 | } 42 | .vertifyWrap .sliderContainer_success .sliderMask { 43 | height: 38px; 44 | border: 1px solid #0db87f; 45 | background-color: #d2f4ef; 46 | } 47 | .vertifyWrap .sliderContainer_success .sliderIcon { 48 | background-position: 0 -26px !important; 49 | } 50 | .vertifyWrap .sliderContainer_fail .slider { 51 | height: 38px; 52 | top: -1px; 53 | border: 1px solid #f57a7a; 54 | background-color: #f57a7a !important; 55 | } 56 | .vertifyWrap .sliderContainer_fail .sliderMask { 57 | height: 38px; 58 | border: 1px solid #f57a7a; 59 | background-color: #fce1e1; 60 | } 61 | .vertifyWrap .sliderContainer_fail .sliderIcon { 62 | top: 14px; 63 | background-position: 0 -82px !important; 64 | } 65 | .vertifyWrap .sliderContainer_active .sliderText, 66 | .vertifyWrap .sliderContainer_success .sliderText, 67 | .vertifyWrap .sliderContainer_fail .sliderText { 68 | display: none; 69 | } 70 | .vertifyWrap .sliderMask { 71 | position: absolute; 72 | left: 0; 73 | top: 0; 74 | height: 40px; 75 | border: 0 solid #486cd6; 76 | background: #d1e9fe; 77 | } 78 | .vertifyWrap .slider { 79 | position: absolute; 80 | top: 0; 81 | left: 0; 82 | width: 40px; 83 | height: 40px; 84 | background: #fff; 85 | -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); 86 | box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); 87 | -webkit-transition: background 0.2s linear; 88 | transition: background 0.2s linear; 89 | cursor: pointer; 90 | cursor: -webkit-grab; 91 | cursor: grab; 92 | } 93 | .vertifyWrap .slider:active { 94 | cursor: -webkit-grabbing; 95 | cursor: grabbing; 96 | } 97 | .vertifyWrap .slider:hover { 98 | background: #486cd6; 99 | } 100 | .vertifyWrap .sliderIcon { 101 | font-size: 18px; 102 | color: #000; 103 | } 104 | .vertifyWrap .slider:hover .sliderIcon { 105 | color: #fff; 106 | } 107 | .vertifyWrap .refreshIcon { 108 | position: absolute; 109 | right: 5px; 110 | top: 5px; 111 | width: 30px; 112 | height: 30px; 113 | cursor: pointer; 114 | background-size: 32px; 115 | } 116 | .vertifyWrap .loadingContainer { 117 | position: absolute; 118 | left: 0; 119 | top: 0; 120 | width: 310px; 121 | height: 155px; 122 | display: -webkit-box; 123 | display: -ms-flexbox; 124 | display: flex; 125 | -webkit-box-orient: vertical; 126 | -webkit-box-direction: normal; 127 | -ms-flex-direction: column; 128 | flex-direction: column; 129 | -webkit-box-pack: center; 130 | -ms-flex-pack: center; 131 | justify-content: center; 132 | -webkit-box-align: center; 133 | -ms-flex-align: center; 134 | align-items: center; 135 | font-size: 14px; 136 | color: #45494c; 137 | z-index: 2; 138 | background: #edf0f2; 139 | } 140 | .vertifyWrap .loadingIcon { 141 | width: 32px; 142 | height: 32px; 143 | margin-bottom: 10px; 144 | background: url(http://cdn.dooring.cn/dr/icon12.png); 145 | background-size: 32px; 146 | -webkit-animation: loading-icon-rotate 0.8s linear infinite; 147 | animation: loading-icon-rotate 0.8s linear infinite; 148 | } 149 | @-webkit-keyframes loading-icon-rotate { 150 | from { 151 | -webkit-transform: rotate(0); 152 | transform: rotate(0); 153 | } 154 | to { 155 | -webkit-transform: rotate(360deg); 156 | transform: rotate(360deg); 157 | } 158 | } 159 | @keyframes loading-icon-rotate { 160 | from { 161 | -webkit-transform: rotate(0); 162 | transform: rotate(0); 163 | } 164 | to { 165 | -webkit-transform: rotate(360deg); 166 | transform: rotate(360deg); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /es/Vertify/index.js: -------------------------------------------------------------------------------- 1 | import _slicedToArray from '@babel/runtime/helpers/esm/slicedToArray'; 2 | 3 | var _this = this; 4 | 5 | import React, { useRef, useState, useEffect, memo } from 'react'; 6 | import { getRandomNumberByRange, sum, square } from './tool'; 7 | import './index.css'; 8 | export default /*#__PURE__*/ memo(function (_ref) { 9 | var _ref$width = _ref.width, 10 | width = _ref$width === void 0 ? 320 : _ref$width, 11 | _ref$height = _ref.height, 12 | height = _ref$height === void 0 ? 160 : _ref$height, 13 | _ref$l = _ref.l, 14 | l = _ref$l === void 0 ? 42 : _ref$l, 15 | _ref$r = _ref.r, 16 | r = _ref$r === void 0 ? 9 : _ref$r, 17 | imgUrl = _ref.imgUrl, 18 | text = _ref.text, 19 | _ref$refreshIcon = _ref.refreshIcon, 20 | refreshIcon = 21 | _ref$refreshIcon === void 0 22 | ? 'http://cdn.dooring.cn/dr/icon12.png' 23 | : _ref$refreshIcon, 24 | _ref$visible = _ref.visible, 25 | visible = _ref$visible === void 0 ? true : _ref$visible, 26 | onDraw = _ref.onDraw, 27 | onCustomVertify = _ref.onCustomVertify, 28 | onSuccess = _ref.onSuccess, 29 | onFail = _ref.onFail, 30 | onRefresh = _ref.onRefresh; 31 | 32 | var _useState = useState(false), 33 | _useState2 = _slicedToArray(_useState, 2), 34 | isLoading = _useState2[0], 35 | setLoading = _useState2[1]; 36 | 37 | var _useState3 = useState(0), 38 | _useState4 = _slicedToArray(_useState3, 2), 39 | sliderLeft = _useState4[0], 40 | setSliderLeft = _useState4[1]; 41 | 42 | var _useState5 = useState('sliderContainer'), 43 | _useState6 = _slicedToArray(_useState5, 2), 44 | sliderClass = _useState6[0], 45 | setSliderClass = _useState6[1]; 46 | 47 | var _useState7 = useState(text), 48 | _useState8 = _slicedToArray(_useState7, 2), 49 | textTip = _useState8[0], 50 | setTextTip = _useState8[1]; 51 | 52 | var canvasRef = useRef(null); 53 | var blockRef = useRef(null); 54 | var imgRef = useRef(null); 55 | var isMouseDownRef = useRef(false); 56 | var trailRef = useRef([]); 57 | var originXRef = useRef(0); 58 | var originYRef = useRef(0); 59 | var xRef = useRef(0); 60 | var yRef = useRef(0); 61 | var PI = Math.PI; 62 | var L = l + r * 2 + 3; // 滑块实际边长 63 | 64 | var drawPath = function drawPath(ctx, x, y, operation) { 65 | ctx.beginPath(); 66 | ctx.moveTo(x, y); 67 | ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI); 68 | ctx.lineTo(x + l, y); 69 | ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI); 70 | ctx.lineTo(x + l, y + l); 71 | ctx.lineTo(x, y + l); 72 | ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true); 73 | ctx.lineTo(x, y); 74 | ctx.lineWidth = 2; 75 | ctx.fillStyle = 'rgba(255, 255, 255, 0.7)'; 76 | ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)'; 77 | ctx.stroke(); 78 | ctx.globalCompositeOperation = 'destination-over'; 79 | operation === 'fill' ? ctx.fill() : ctx.clip(); 80 | }; 81 | 82 | var getRandomImgSrc = function getRandomImgSrc() { 83 | return ( 84 | imgUrl || 85 | 'https://picsum.photos/id/' 86 | .concat(getRandomNumberByRange(0, 1084), '/') 87 | .concat(width, '/') 88 | .concat(height) 89 | ); 90 | }; 91 | 92 | var createImg = function createImg(onload) { 93 | var img = new Image(); 94 | img.crossOrigin = 'Anonymous'; 95 | img.onload = onload; 96 | 97 | img.onerror = function () { 98 | img.setSrc(getRandomImgSrc()); // 图片加载失败的时候重新加载其他图片 99 | }; 100 | 101 | img.setSrc = function (src) { 102 | var isIE = window.navigator.userAgent.indexOf('Trident') > -1; 103 | 104 | if (isIE) { 105 | // IE浏览器无法通过img.crossOrigin跨域,使用ajax获取图片blob然后转为dataURL显示 106 | var xhr = new XMLHttpRequest(); 107 | 108 | xhr.onloadend = function (e) { 109 | var file = new FileReader(); // FileReader仅支持IE10+ 110 | 111 | file.readAsDataURL(e.target.response); 112 | 113 | file.onloadend = function (e) { 114 | var _e$target; 115 | 116 | img.src = 117 | e === null || e === void 0 118 | ? void 0 119 | : (_e$target = e.target) === null || _e$target === void 0 120 | ? void 0 121 | : _e$target.result; 122 | }; 123 | }; 124 | 125 | xhr.open('GET', src); 126 | xhr.responseType = 'blob'; 127 | xhr.send(); 128 | } else img.src = src; 129 | }; 130 | 131 | img.setSrc(getRandomImgSrc()); 132 | return img; 133 | }; 134 | 135 | var draw = function draw(img) { 136 | var canvasCtx = canvasRef.current.getContext('2d'); 137 | var blockCtx = blockRef.current.getContext('2d'); // 随机位置创建拼图形状 138 | 139 | xRef.current = getRandomNumberByRange(L + 10, width - (L + 10)); 140 | yRef.current = getRandomNumberByRange(10 + r * 2, height - (L + 10)); 141 | drawPath(canvasCtx, xRef.current, yRef.current, 'fill'); 142 | drawPath(blockCtx, xRef.current, yRef.current, 'clip'); // 画入图片 143 | 144 | canvasCtx.drawImage(img, 0, 0, width, height); 145 | blockCtx.drawImage(img, 0, 0, width, height); // 提取滑块并放到最左边 146 | 147 | var y1 = yRef.current - r * 2 - 1; 148 | var ImageData = blockCtx.getImageData(xRef.current - 3, y1, L, L); 149 | blockRef.current.width = L; 150 | blockCtx.putImageData(ImageData, 0, y1); 151 | }; 152 | 153 | var initImg = function initImg() { 154 | var img = createImg(function () { 155 | setLoading(false); 156 | draw(img); 157 | }); 158 | imgRef.current = img; 159 | }; 160 | 161 | var reset = function reset() { 162 | var canvasCtx = canvasRef.current.getContext('2d'); 163 | var blockCtx = blockRef.current.getContext('2d'); // 重置样式 164 | 165 | setSliderLeft(0); 166 | setSliderClass('sliderContainer'); 167 | blockRef.current.width = width; 168 | blockRef.current.style.left = 0 + 'px'; // 清空画布 169 | 170 | canvasCtx.clearRect(0, 0, width, height); 171 | blockCtx.clearRect(0, 0, width, height); // 重新加载图片 172 | 173 | setLoading(true); 174 | imgRef.current.setSrc(getRandomImgSrc()); 175 | }; 176 | 177 | var handleRefresh = function handleRefresh() { 178 | reset(); 179 | typeof onRefresh === 'function' && onRefresh(); 180 | }; 181 | 182 | var verify = function verify() { 183 | var arr = trailRef.current; // 拖动时y轴的移动距离 184 | 185 | var average = arr.reduce(sum) / arr.length; 186 | var deviations = arr.map(function (x) { 187 | return x - average; 188 | }); 189 | var stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length); 190 | var left = parseInt(blockRef.current.style.left); 191 | return { 192 | spliced: Math.abs(left - xRef.current) < 10, 193 | verified: stddev !== 0, 194 | left: left, 195 | destX: xRef.current, 196 | }; 197 | }; 198 | 199 | var handleDragStart = function handleDragStart(e) { 200 | originXRef.current = e.clientX || e.touches[0].clientX; 201 | originYRef.current = e.clientY || e.touches[0].clientY; 202 | isMouseDownRef.current = true; 203 | }; 204 | 205 | var handleDragMove = function handleDragMove(e) { 206 | if (!isMouseDownRef.current) return false; 207 | e.preventDefault(); 208 | var eventX = e.clientX || e.touches[0].clientX; 209 | var eventY = e.clientY || e.touches[0].clientY; 210 | var moveX = eventX - originXRef.current; 211 | var moveY = eventY - originYRef.current; 212 | if (moveX < 0 || moveX + 38 >= width) return false; 213 | setSliderLeft(moveX); 214 | var blockLeft = ((width - 40 - 20) / (width - 40)) * moveX; 215 | blockRef.current.style.left = blockLeft + 'px'; 216 | setSliderClass('sliderContainer sliderContainer_active'); 217 | trailRef.current.push(moveY); 218 | onDraw && onDraw(blockLeft); 219 | }; 220 | 221 | var handleDragEnd = function handleDragEnd(e) { 222 | if (!isMouseDownRef.current) return false; 223 | isMouseDownRef.current = false; 224 | var eventX = e.clientX || e.changedTouches[0].clientX; 225 | if (eventX === originXRef.current) return false; 226 | setSliderClass('sliderContainer'); 227 | 228 | var _ref2 = onCustomVertify ? onCustomVertify(verify()) : verify(), 229 | spliced = _ref2.spliced, 230 | verified = _ref2.verified; 231 | 232 | if (spliced) { 233 | if (verified) { 234 | setSliderClass('sliderContainer sliderContainer_success'); 235 | typeof onSuccess === 'function' && onSuccess(); 236 | } else { 237 | setSliderClass('sliderContainer sliderContainer_fail'); 238 | setTextTip('请再试一次'); 239 | reset(); 240 | } 241 | } else { 242 | setSliderClass('sliderContainer sliderContainer_fail'); 243 | typeof onFail === 'function' && onFail(); 244 | setTimeout(reset.bind(_this), 1000); 245 | } 246 | }; 247 | 248 | useEffect( 249 | function () { 250 | if (visible) { 251 | imgRef.current ? reset() : initImg(); 252 | } 253 | }, 254 | [visible], 255 | ); 256 | return /*#__PURE__*/ React.createElement( 257 | 'div', 258 | { 259 | className: 'vertifyWrap', 260 | style: { 261 | width: width + 'px', 262 | margin: '0 auto', 263 | display: visible ? '' : 'none', 264 | }, 265 | onMouseMove: handleDragMove, 266 | onMouseUp: handleDragEnd, 267 | onTouchMove: handleDragMove, 268 | onTouchEnd: handleDragEnd, 269 | }, 270 | /*#__PURE__*/ React.createElement( 271 | 'div', 272 | { 273 | className: 'canvasArea', 274 | }, 275 | /*#__PURE__*/ React.createElement('canvas', { 276 | ref: canvasRef, 277 | width: width, 278 | height: height, 279 | }), 280 | /*#__PURE__*/ React.createElement('canvas', { 281 | ref: blockRef, 282 | className: 'block', 283 | width: width, 284 | height: height, 285 | onMouseDown: handleDragStart, 286 | onTouchStart: handleDragStart, 287 | }), 288 | ), 289 | /*#__PURE__*/ React.createElement( 290 | 'div', 291 | { 292 | className: sliderClass, 293 | style: { 294 | pointerEvents: isLoading ? 'none' : 'auto', 295 | width: width + 'px', 296 | }, 297 | }, 298 | /*#__PURE__*/ React.createElement( 299 | 'div', 300 | { 301 | className: 'sliderMask', 302 | style: { 303 | width: sliderLeft + 'px', 304 | }, 305 | }, 306 | /*#__PURE__*/ React.createElement( 307 | 'div', 308 | { 309 | className: 'slider', 310 | style: { 311 | left: sliderLeft + 'px', 312 | }, 313 | onMouseDown: handleDragStart, 314 | onTouchStart: handleDragStart, 315 | }, 316 | /*#__PURE__*/ React.createElement( 317 | 'div', 318 | { 319 | className: 'sliderIcon', 320 | }, 321 | '\u2192', 322 | ), 323 | ), 324 | ), 325 | /*#__PURE__*/ React.createElement( 326 | 'div', 327 | { 328 | className: 'sliderText', 329 | }, 330 | textTip, 331 | ), 332 | ), 333 | /*#__PURE__*/ React.createElement('div', { 334 | className: 'refreshIcon', 335 | onClick: handleRefresh, 336 | style: { 337 | backgroundImage: 'url('.concat(refreshIcon, ')'), 338 | }, 339 | }), 340 | /*#__PURE__*/ React.createElement( 341 | 'div', 342 | { 343 | className: 'loadingContainer', 344 | style: { 345 | width: width + 'px', 346 | height: height + 'px', 347 | display: isLoading ? '' : 'none', 348 | }, 349 | }, 350 | /*#__PURE__*/ React.createElement('div', { 351 | className: 'loadingIcon', 352 | }), 353 | /*#__PURE__*/ React.createElement('span', null, '\u52A0\u8F7D\u4E2D...'), 354 | ), 355 | ); 356 | }); 357 | -------------------------------------------------------------------------------- /src/Vertify/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState, useEffect, ReactNode, memo } from 'react'; 2 | import { getRandomNumberByRange, sum, square } from './tool'; 3 | import './index.less'; 4 | 5 | interface VertifyType { 6 | spliced: boolean; 7 | verified: boolean; // 简单验证拖动轨迹,为零时表示Y轴上下没有波动,可能非人为操作 8 | left: number; // 滑块的移动位置 9 | destX: number; // 滑块的目标位置 10 | } 11 | 12 | interface IVertifyProp { 13 | /** 14 | * @description canvas宽度 15 | * @default 320 16 | */ 17 | width?: number; 18 | /** 19 | * @description canvas高度 20 | * @default 160 21 | */ 22 | height?: number; 23 | /** 24 | * @description 滑块边长 25 | * @default 42 26 | */ 27 | l?: number; 28 | /** 29 | * @description 滑块半径 30 | * @default 9 31 | */ 32 | r?: number; 33 | /** 34 | * @description 是否可见 35 | * @default true 36 | */ 37 | visible?: boolean; 38 | /** 39 | * @description 滑块文本 40 | * @default 向右滑动填充拼图 41 | */ 42 | text?: string | ReactNode; 43 | /** 44 | * @description 刷新按钮icon, 为icon的url地址 45 | * @default - 46 | */ 47 | refreshIcon?: string; 48 | /** 49 | * @description 用于获取随机图片的url地址 50 | * @default https://picsum.photos/${id}/${width}/${height}, 具体参考https://picsum.photos/, 只需要实现类似接口即可 51 | */ 52 | imgUrl?: string; 53 | /** 54 | * @description 拖拽滑块时的回调, 参数为当前滑块拖拽的距离 55 | * @default (l: number):void => {} 56 | */ 57 | onDraw?: (l: number) => {}; 58 | /** 59 | * @description 用户的自定义验证逻辑 60 | * @default (arg: VertifyType) => VertifyType 61 | */ 62 | onCustomVertify?: (arg: VertifyType) => VertifyType; 63 | /** 64 | * @description 验证成功回调 65 | * @default ():void => {} 66 | */ 67 | onSuccess?: VoidFunction; 68 | /** 69 | * @description 验证失败回调 70 | * @default ():void => {} 71 | */ 72 | onFail?: VoidFunction; 73 | /** 74 | * @description 刷新时回调 75 | * @default ():void => {} 76 | */ 77 | onRefresh?: VoidFunction; 78 | } 79 | 80 | export default memo( 81 | ({ 82 | width = 320, 83 | height = 160, 84 | l = 42, 85 | r = 9, 86 | imgUrl, 87 | text, 88 | refreshIcon = 'http://cdn.dooring.cn/dr/icon12.png', 89 | visible = true, 90 | onDraw, 91 | onCustomVertify, 92 | onSuccess, 93 | onFail, 94 | onRefresh, 95 | }: IVertifyProp) => { 96 | const [isLoading, setLoading] = useState(false); 97 | const [sliderLeft, setSliderLeft] = useState(0); 98 | const [sliderClass, setSliderClass] = useState('sliderContainer'); 99 | const [textTip, setTextTip] = useState(text); 100 | const canvasRef = useRef(null); 101 | const blockRef = useRef(null); 102 | const imgRef = useRef(null); 103 | const isMouseDownRef = useRef(false); 104 | const trailRef = useRef([]); 105 | const originXRef = useRef(0); 106 | const originYRef = useRef(0); 107 | const xRef = useRef(0); 108 | const yRef = useRef(0); 109 | const PI = Math.PI; 110 | const L = l + r * 2 + 3; // 滑块实际边长 111 | 112 | const drawPath = ( 113 | ctx: any, 114 | x: number, 115 | y: number, 116 | operation: 'fill' | 'clip', 117 | ) => { 118 | ctx.beginPath(); 119 | ctx.moveTo(x, y); 120 | ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI); 121 | ctx.lineTo(x + l, y); 122 | ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI); 123 | ctx.lineTo(x + l, y + l); 124 | ctx.lineTo(x, y + l); 125 | ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true); 126 | ctx.lineTo(x, y); 127 | ctx.lineWidth = 2; 128 | ctx.fillStyle = 'rgba(255, 255, 255, 0.7)'; 129 | ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)'; 130 | ctx.stroke(); 131 | ctx.globalCompositeOperation = 'destination-over'; 132 | operation === 'fill' ? ctx.fill() : ctx.clip(); 133 | }; 134 | 135 | const getRandomImgSrc = () => { 136 | return ( 137 | imgUrl || 138 | `https://picsum.photos/id/${getRandomNumberByRange( 139 | 0, 140 | 1084, 141 | )}/${width}/${height}` 142 | ); 143 | }; 144 | 145 | const createImg = (onload: VoidFunction) => { 146 | const img = new Image(); 147 | img.crossOrigin = 'Anonymous'; 148 | img.onload = onload; 149 | img.onerror = () => { 150 | (img as any).setSrc(getRandomImgSrc()); // 图片加载失败的时候重新加载其他图片 151 | }; 152 | 153 | (img as any).setSrc = (src: string) => { 154 | const isIE = window.navigator.userAgent.indexOf('Trident') > -1; 155 | if (isIE) { 156 | // IE浏览器无法通过img.crossOrigin跨域,使用ajax获取图片blob然后转为dataURL显示 157 | const xhr = new XMLHttpRequest(); 158 | xhr.onloadend = function (e: any) { 159 | const file = new FileReader(); // FileReader仅支持IE10+ 160 | file.readAsDataURL(e.target.response); 161 | file.onloadend = function (e) { 162 | img.src = e?.target?.result as string; 163 | }; 164 | }; 165 | xhr.open('GET', src); 166 | xhr.responseType = 'blob'; 167 | xhr.send(); 168 | } else img.src = src; 169 | }; 170 | 171 | (img as any).setSrc(getRandomImgSrc()); 172 | return img; 173 | }; 174 | 175 | const draw = (img: HTMLImageElement) => { 176 | const canvasCtx = canvasRef.current.getContext('2d'); 177 | const blockCtx = blockRef.current.getContext('2d'); 178 | // 随机位置创建拼图形状 179 | xRef.current = getRandomNumberByRange(L + 10, width - (L + 10)); 180 | yRef.current = getRandomNumberByRange(10 + r * 2, height - (L + 10)); 181 | drawPath(canvasCtx, xRef.current, yRef.current, 'fill'); 182 | drawPath(blockCtx, xRef.current, yRef.current, 'clip'); 183 | 184 | // 画入图片 185 | canvasCtx.drawImage(img, 0, 0, width, height); 186 | blockCtx.drawImage(img, 0, 0, width, height); 187 | 188 | // 提取滑块并放到最左边 189 | const y1 = yRef.current - r * 2 - 1; 190 | const ImageData = blockCtx.getImageData(xRef.current - 3, y1, L, L); 191 | blockRef.current.width = L; 192 | blockCtx.putImageData(ImageData, 0, y1); 193 | }; 194 | 195 | const initImg = () => { 196 | const img = createImg(() => { 197 | setLoading(false); 198 | draw(img); 199 | }); 200 | imgRef.current = img; 201 | }; 202 | 203 | const reset = () => { 204 | const canvasCtx = canvasRef.current.getContext('2d'); 205 | const blockCtx = blockRef.current.getContext('2d'); 206 | // 重置样式 207 | setSliderLeft(0); 208 | setSliderClass('sliderContainer'); 209 | blockRef.current.width = width; 210 | blockRef.current.style.left = 0 + 'px'; 211 | 212 | // 清空画布 213 | canvasCtx.clearRect(0, 0, width, height); 214 | blockCtx.clearRect(0, 0, width, height); 215 | 216 | // 重新加载图片 217 | setLoading(true); 218 | imgRef.current.setSrc(getRandomImgSrc()); 219 | }; 220 | 221 | const handleRefresh = () => { 222 | reset(); 223 | typeof onRefresh === 'function' && onRefresh(); 224 | }; 225 | 226 | const verify = () => { 227 | const arr = trailRef.current; // 拖动时y轴的移动距离 228 | const average = arr.reduce(sum) / arr.length; 229 | const deviations = arr.map((x) => x - average); 230 | const stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length); 231 | const left = parseInt(blockRef.current.style.left); 232 | return { 233 | spliced: Math.abs(left - xRef.current) < 10, 234 | verified: stddev !== 0, // 简单验证拖动轨迹,为零时表示Y轴上下没有波动,可能非人为操作 235 | left, 236 | destX: xRef.current, 237 | }; 238 | }; 239 | 240 | const handleDragStart = function (e: any) { 241 | originXRef.current = e.clientX || e.touches[0].clientX; 242 | originYRef.current = e.clientY || e.touches[0].clientY; 243 | isMouseDownRef.current = true; 244 | }; 245 | 246 | const handleDragMove = (e: any) => { 247 | if (!isMouseDownRef.current) return false; 248 | e.preventDefault(); 249 | const eventX = e.clientX || e.touches[0].clientX; 250 | const eventY = e.clientY || e.touches[0].clientY; 251 | const moveX = eventX - originXRef.current; 252 | const moveY = eventY - originYRef.current; 253 | if (moveX < 0 || moveX + 38 >= width) return false; 254 | setSliderLeft(moveX); 255 | const blockLeft = ((width - 40 - 20) / (width - 40)) * moveX; 256 | blockRef.current.style.left = blockLeft + 'px'; 257 | 258 | setSliderClass('sliderContainer sliderContainer_active'); 259 | trailRef.current.push(moveY); 260 | onDraw && onDraw(blockLeft); 261 | }; 262 | 263 | const handleDragEnd = (e: any) => { 264 | if (!isMouseDownRef.current) return false; 265 | isMouseDownRef.current = false; 266 | const eventX = e.clientX || e.changedTouches[0].clientX; 267 | if (eventX === originXRef.current) return false; 268 | setSliderClass('sliderContainer'); 269 | const { spliced, verified } = onCustomVertify 270 | ? onCustomVertify(verify()) 271 | : verify(); 272 | if (spliced) { 273 | if (verified) { 274 | setSliderClass('sliderContainer sliderContainer_success'); 275 | typeof onSuccess === 'function' && onSuccess(); 276 | } else { 277 | setSliderClass('sliderContainer sliderContainer_fail'); 278 | setTextTip('请再试一次'); 279 | reset(); 280 | } 281 | } else { 282 | setSliderClass('sliderContainer sliderContainer_fail'); 283 | typeof onFail === 'function' && onFail(); 284 | setTimeout(reset.bind(this), 1000); 285 | } 286 | }; 287 | 288 | useEffect(() => { 289 | if (visible) { 290 | imgRef.current ? reset() : initImg(); 291 | } 292 | }, [visible]); 293 | 294 | return ( 295 |
307 |
308 | 309 | 317 |
318 |
325 |
326 |
332 |
333 |
334 |
335 |
{textTip}
336 |
337 |
342 |
350 |
351 | 加载中... 352 |
353 |
354 | ); 355 | }, 356 | ); 357 | -------------------------------------------------------------------------------- /lib/Vertify/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _interopRequireDefault = require('@babel/runtime/helpers/interopRequireDefault'); 4 | 5 | var _interopRequireWildcard = require('@babel/runtime/helpers/interopRequireWildcard'); 6 | 7 | Object.defineProperty(exports, '__esModule', { 8 | value: true, 9 | }); 10 | exports.default = void 0; 11 | 12 | var _slicedToArray2 = _interopRequireDefault( 13 | require('@babel/runtime/helpers/slicedToArray'), 14 | ); 15 | 16 | var _react = _interopRequireWildcard(require('react')); 17 | 18 | var _tool = require('./tool'); 19 | 20 | require('./index.css'); 21 | 22 | var _this = void 0; 23 | 24 | var _default = /*#__PURE__*/ (0, _react.memo)(function (_ref) { 25 | var _ref$width = _ref.width, 26 | width = _ref$width === void 0 ? 320 : _ref$width, 27 | _ref$height = _ref.height, 28 | height = _ref$height === void 0 ? 160 : _ref$height, 29 | _ref$l = _ref.l, 30 | l = _ref$l === void 0 ? 42 : _ref$l, 31 | _ref$r = _ref.r, 32 | r = _ref$r === void 0 ? 9 : _ref$r, 33 | imgUrl = _ref.imgUrl, 34 | text = _ref.text, 35 | _ref$refreshIcon = _ref.refreshIcon, 36 | refreshIcon = 37 | _ref$refreshIcon === void 0 38 | ? 'http://cdn.dooring.cn/dr/icon12.png' 39 | : _ref$refreshIcon, 40 | _ref$visible = _ref.visible, 41 | visible = _ref$visible === void 0 ? true : _ref$visible, 42 | onDraw = _ref.onDraw, 43 | onCustomVertify = _ref.onCustomVertify, 44 | onSuccess = _ref.onSuccess, 45 | onFail = _ref.onFail, 46 | onRefresh = _ref.onRefresh; 47 | 48 | var _useState = (0, _react.useState)(false), 49 | _useState2 = (0, _slicedToArray2.default)(_useState, 2), 50 | isLoading = _useState2[0], 51 | setLoading = _useState2[1]; 52 | 53 | var _useState3 = (0, _react.useState)(0), 54 | _useState4 = (0, _slicedToArray2.default)(_useState3, 2), 55 | sliderLeft = _useState4[0], 56 | setSliderLeft = _useState4[1]; 57 | 58 | var _useState5 = (0, _react.useState)('sliderContainer'), 59 | _useState6 = (0, _slicedToArray2.default)(_useState5, 2), 60 | sliderClass = _useState6[0], 61 | setSliderClass = _useState6[1]; 62 | 63 | var _useState7 = (0, _react.useState)(text), 64 | _useState8 = (0, _slicedToArray2.default)(_useState7, 2), 65 | textTip = _useState8[0], 66 | setTextTip = _useState8[1]; 67 | 68 | var canvasRef = (0, _react.useRef)(null); 69 | var blockRef = (0, _react.useRef)(null); 70 | var imgRef = (0, _react.useRef)(null); 71 | var isMouseDownRef = (0, _react.useRef)(false); 72 | var trailRef = (0, _react.useRef)([]); 73 | var originXRef = (0, _react.useRef)(0); 74 | var originYRef = (0, _react.useRef)(0); 75 | var xRef = (0, _react.useRef)(0); 76 | var yRef = (0, _react.useRef)(0); 77 | var PI = Math.PI; 78 | var L = l + r * 2 + 3; // 滑块实际边长 79 | 80 | var drawPath = function drawPath(ctx, x, y, operation) { 81 | ctx.beginPath(); 82 | ctx.moveTo(x, y); 83 | ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI); 84 | ctx.lineTo(x + l, y); 85 | ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI); 86 | ctx.lineTo(x + l, y + l); 87 | ctx.lineTo(x, y + l); 88 | ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true); 89 | ctx.lineTo(x, y); 90 | ctx.lineWidth = 2; 91 | ctx.fillStyle = 'rgba(255, 255, 255, 0.7)'; 92 | ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)'; 93 | ctx.stroke(); 94 | ctx.globalCompositeOperation = 'destination-over'; 95 | operation === 'fill' ? ctx.fill() : ctx.clip(); 96 | }; 97 | 98 | var getRandomImgSrc = function getRandomImgSrc() { 99 | return ( 100 | imgUrl || 101 | 'https://picsum.photos/id/' 102 | .concat((0, _tool.getRandomNumberByRange)(0, 1084), '/') 103 | .concat(width, '/') 104 | .concat(height) 105 | ); 106 | }; 107 | 108 | var createImg = function createImg(onload) { 109 | var img = new Image(); 110 | img.crossOrigin = 'Anonymous'; 111 | img.onload = onload; 112 | 113 | img.onerror = function () { 114 | img.setSrc(getRandomImgSrc()); // 图片加载失败的时候重新加载其他图片 115 | }; 116 | 117 | img.setSrc = function (src) { 118 | var isIE = window.navigator.userAgent.indexOf('Trident') > -1; 119 | 120 | if (isIE) { 121 | // IE浏览器无法通过img.crossOrigin跨域,使用ajax获取图片blob然后转为dataURL显示 122 | var xhr = new XMLHttpRequest(); 123 | 124 | xhr.onloadend = function (e) { 125 | var file = new FileReader(); // FileReader仅支持IE10+ 126 | 127 | file.readAsDataURL(e.target.response); 128 | 129 | file.onloadend = function (e) { 130 | var _e$target; 131 | 132 | img.src = 133 | e === null || e === void 0 134 | ? void 0 135 | : (_e$target = e.target) === null || _e$target === void 0 136 | ? void 0 137 | : _e$target.result; 138 | }; 139 | }; 140 | 141 | xhr.open('GET', src); 142 | xhr.responseType = 'blob'; 143 | xhr.send(); 144 | } else img.src = src; 145 | }; 146 | 147 | img.setSrc(getRandomImgSrc()); 148 | return img; 149 | }; 150 | 151 | var draw = function draw(img) { 152 | var canvasCtx = canvasRef.current.getContext('2d'); 153 | var blockCtx = blockRef.current.getContext('2d'); // 随机位置创建拼图形状 154 | 155 | xRef.current = (0, _tool.getRandomNumberByRange)(L + 10, width - (L + 10)); 156 | yRef.current = (0, _tool.getRandomNumberByRange)( 157 | 10 + r * 2, 158 | height - (L + 10), 159 | ); 160 | drawPath(canvasCtx, xRef.current, yRef.current, 'fill'); 161 | drawPath(blockCtx, xRef.current, yRef.current, 'clip'); // 画入图片 162 | 163 | canvasCtx.drawImage(img, 0, 0, width, height); 164 | blockCtx.drawImage(img, 0, 0, width, height); // 提取滑块并放到最左边 165 | 166 | var y1 = yRef.current - r * 2 - 1; 167 | var ImageData = blockCtx.getImageData(xRef.current - 3, y1, L, L); 168 | blockRef.current.width = L; 169 | blockCtx.putImageData(ImageData, 0, y1); 170 | }; 171 | 172 | var initImg = function initImg() { 173 | var img = createImg(function () { 174 | setLoading(false); 175 | draw(img); 176 | }); 177 | imgRef.current = img; 178 | }; 179 | 180 | var reset = function reset() { 181 | var canvasCtx = canvasRef.current.getContext('2d'); 182 | var blockCtx = blockRef.current.getContext('2d'); // 重置样式 183 | 184 | setSliderLeft(0); 185 | setSliderClass('sliderContainer'); 186 | blockRef.current.width = width; 187 | blockRef.current.style.left = 0 + 'px'; // 清空画布 188 | 189 | canvasCtx.clearRect(0, 0, width, height); 190 | blockCtx.clearRect(0, 0, width, height); // 重新加载图片 191 | 192 | setLoading(true); 193 | imgRef.current.setSrc(getRandomImgSrc()); 194 | }; 195 | 196 | var handleRefresh = function handleRefresh() { 197 | reset(); 198 | typeof onRefresh === 'function' && onRefresh(); 199 | }; 200 | 201 | var verify = function verify() { 202 | var arr = trailRef.current; // 拖动时y轴的移动距离 203 | 204 | var average = arr.reduce(_tool.sum) / arr.length; 205 | var deviations = arr.map(function (x) { 206 | return x - average; 207 | }); 208 | var stddev = Math.sqrt( 209 | deviations.map(_tool.square).reduce(_tool.sum) / arr.length, 210 | ); 211 | var left = parseInt(blockRef.current.style.left); 212 | return { 213 | spliced: Math.abs(left - xRef.current) < 10, 214 | verified: stddev !== 0, 215 | left: left, 216 | destX: xRef.current, 217 | }; 218 | }; 219 | 220 | var handleDragStart = function handleDragStart(e) { 221 | originXRef.current = e.clientX || e.touches[0].clientX; 222 | originYRef.current = e.clientY || e.touches[0].clientY; 223 | isMouseDownRef.current = true; 224 | }; 225 | 226 | var handleDragMove = function handleDragMove(e) { 227 | if (!isMouseDownRef.current) return false; 228 | e.preventDefault(); 229 | var eventX = e.clientX || e.touches[0].clientX; 230 | var eventY = e.clientY || e.touches[0].clientY; 231 | var moveX = eventX - originXRef.current; 232 | var moveY = eventY - originYRef.current; 233 | if (moveX < 0 || moveX + 38 >= width) return false; 234 | setSliderLeft(moveX); 235 | var blockLeft = ((width - 40 - 20) / (width - 40)) * moveX; 236 | blockRef.current.style.left = blockLeft + 'px'; 237 | setSliderClass('sliderContainer sliderContainer_active'); 238 | trailRef.current.push(moveY); 239 | onDraw && onDraw(blockLeft); 240 | }; 241 | 242 | var handleDragEnd = function handleDragEnd(e) { 243 | if (!isMouseDownRef.current) return false; 244 | isMouseDownRef.current = false; 245 | var eventX = e.clientX || e.changedTouches[0].clientX; 246 | if (eventX === originXRef.current) return false; 247 | setSliderClass('sliderContainer'); 248 | 249 | var _ref2 = onCustomVertify ? onCustomVertify(verify()) : verify(), 250 | spliced = _ref2.spliced, 251 | verified = _ref2.verified; 252 | 253 | if (spliced) { 254 | if (verified) { 255 | setSliderClass('sliderContainer sliderContainer_success'); 256 | typeof onSuccess === 'function' && onSuccess(); 257 | } else { 258 | setSliderClass('sliderContainer sliderContainer_fail'); 259 | setTextTip('请再试一次'); 260 | reset(); 261 | } 262 | } else { 263 | setSliderClass('sliderContainer sliderContainer_fail'); 264 | typeof onFail === 'function' && onFail(); 265 | setTimeout(reset.bind(_this), 1000); 266 | } 267 | }; 268 | 269 | (0, _react.useEffect)( 270 | function () { 271 | if (visible) { 272 | imgRef.current ? reset() : initImg(); 273 | } 274 | }, 275 | [visible], 276 | ); 277 | return /*#__PURE__*/ _react.default.createElement( 278 | 'div', 279 | { 280 | className: 'vertifyWrap', 281 | style: { 282 | width: width + 'px', 283 | margin: '0 auto', 284 | display: visible ? '' : 'none', 285 | }, 286 | onMouseMove: handleDragMove, 287 | onMouseUp: handleDragEnd, 288 | onTouchMove: handleDragMove, 289 | onTouchEnd: handleDragEnd, 290 | }, 291 | /*#__PURE__*/ _react.default.createElement( 292 | 'div', 293 | { 294 | className: 'canvasArea', 295 | }, 296 | /*#__PURE__*/ _react.default.createElement('canvas', { 297 | ref: canvasRef, 298 | width: width, 299 | height: height, 300 | }), 301 | /*#__PURE__*/ _react.default.createElement('canvas', { 302 | ref: blockRef, 303 | className: 'block', 304 | width: width, 305 | height: height, 306 | onMouseDown: handleDragStart, 307 | onTouchStart: handleDragStart, 308 | }), 309 | ), 310 | /*#__PURE__*/ _react.default.createElement( 311 | 'div', 312 | { 313 | className: sliderClass, 314 | style: { 315 | pointerEvents: isLoading ? 'none' : 'auto', 316 | width: width + 'px', 317 | }, 318 | }, 319 | /*#__PURE__*/ _react.default.createElement( 320 | 'div', 321 | { 322 | className: 'sliderMask', 323 | style: { 324 | width: sliderLeft + 'px', 325 | }, 326 | }, 327 | /*#__PURE__*/ _react.default.createElement( 328 | 'div', 329 | { 330 | className: 'slider', 331 | style: { 332 | left: sliderLeft + 'px', 333 | }, 334 | onMouseDown: handleDragStart, 335 | onTouchStart: handleDragStart, 336 | }, 337 | /*#__PURE__*/ _react.default.createElement( 338 | 'div', 339 | { 340 | className: 'sliderIcon', 341 | }, 342 | '\u2192', 343 | ), 344 | ), 345 | ), 346 | /*#__PURE__*/ _react.default.createElement( 347 | 'div', 348 | { 349 | className: 'sliderText', 350 | }, 351 | textTip, 352 | ), 353 | ), 354 | /*#__PURE__*/ _react.default.createElement('div', { 355 | className: 'refreshIcon', 356 | onClick: handleRefresh, 357 | style: { 358 | backgroundImage: 'url('.concat(refreshIcon, ')'), 359 | }, 360 | }), 361 | /*#__PURE__*/ _react.default.createElement( 362 | 'div', 363 | { 364 | className: 'loadingContainer', 365 | style: { 366 | width: width + 'px', 367 | height: height + 'px', 368 | display: isLoading ? '' : 'none', 369 | }, 370 | }, 371 | /*#__PURE__*/ _react.default.createElement('div', { 372 | className: 'loadingIcon', 373 | }), 374 | /*#__PURE__*/ _react.default.createElement( 375 | 'span', 376 | null, 377 | '\u52A0\u8F7D\u4E2D...', 378 | ), 379 | ), 380 | ); 381 | }); 382 | 383 | exports.default = _default; 384 | -------------------------------------------------------------------------------- /dist/index.umd.min.js: -------------------------------------------------------------------------------- 1 | !(function (e, t) { 2 | 'object' == typeof exports && 'undefined' != typeof module 3 | ? t(exports, require('react')) 4 | : 'function' == typeof define && define.amd 5 | ? define(['exports', 'react'], t) 6 | : t( 7 | ((e = 8 | 'undefined' != typeof globalThis 9 | ? globalThis 10 | : e || self).reactSliderVertify = {}), 11 | e.React, 12 | ); 13 | })(this, function (e, t) { 14 | 'use strict'; 15 | function r(e) { 16 | return e && 'object' == typeof e && 'default' in e ? e : { default: e }; 17 | } 18 | var n = r(t); 19 | function o(e) { 20 | return e && 21 | e.__esModule && 22 | Object.prototype.hasOwnProperty.call(e, 'default') 23 | ? e.default 24 | : e; 25 | } 26 | function u(e, t, r) { 27 | return ( 28 | e( 29 | (r = { 30 | path: t, 31 | exports: {}, 32 | require: function (e, t) { 33 | return (function () { 34 | throw Error( 35 | 'Dynamic requires are not currently supported by @rollup/plugin-commonjs', 36 | ); 37 | })(); 38 | }, 39 | }), 40 | r.exports, 41 | ), 42 | r.exports 43 | ); 44 | } 45 | var i = u(function (e) { 46 | (e.exports = function (e) { 47 | if (Array.isArray(e)) return e; 48 | }), 49 | (e.exports.default = e.exports), 50 | (e.exports.__esModule = !0); 51 | }), 52 | a = u(function (e) { 53 | (e.exports = function (e, t) { 54 | var r = 55 | null == e 56 | ? null 57 | : ('undefined' != typeof Symbol && e[Symbol.iterator]) || 58 | e['@@iterator']; 59 | if (null != r) { 60 | var n, 61 | o, 62 | u = [], 63 | i = !0, 64 | a = !1; 65 | try { 66 | for ( 67 | r = r.call(e); 68 | !(i = (n = r.next()).done) && 69 | (u.push(n.value), !t || u.length !== t); 70 | i = !0 71 | ); 72 | } catch (e) { 73 | (a = !0), (o = e); 74 | } finally { 75 | try { 76 | i || null == r.return || r.return(); 77 | } finally { 78 | if (a) throw o; 79 | } 80 | } 81 | return u; 82 | } 83 | }), 84 | (e.exports.default = e.exports), 85 | (e.exports.__esModule = !0); 86 | }), 87 | c = u(function (e) { 88 | (e.exports = function (e, t) { 89 | (null == t || t > e.length) && (t = e.length); 90 | for (var r = 0, n = Array(t); t > r; r++) n[r] = e[r]; 91 | return n; 92 | }), 93 | (e.exports.default = e.exports), 94 | (e.exports.__esModule = !0); 95 | }), 96 | l = u(function (e) { 97 | (e.exports = function (e, t) { 98 | if (e) { 99 | if ('string' == typeof e) return c(e, t); 100 | var r = Object.prototype.toString.call(e).slice(8, -1); 101 | return ( 102 | 'Object' === r && e.constructor && (r = e.constructor.name), 103 | 'Map' === r || 'Set' === r 104 | ? Array.from(e) 105 | : 'Arguments' === r || 106 | /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r) 107 | ? c(e, t) 108 | : void 0 109 | ); 110 | } 111 | }), 112 | (e.exports.default = e.exports), 113 | (e.exports.__esModule = !0); 114 | }), 115 | s = u(function (e) { 116 | (e.exports = function () { 117 | throw new TypeError( 118 | 'Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.', 119 | ); 120 | }), 121 | (e.exports.default = e.exports), 122 | (e.exports.__esModule = !0); 123 | }), 124 | f = o( 125 | u(function (e) { 126 | (e.exports = function (e, t) { 127 | return i(e) || a(e, t) || l(e, t) || s(); 128 | }), 129 | (e.exports.default = e.exports), 130 | (e.exports.__esModule = !0); 131 | }), 132 | ); 133 | function d(e, t) { 134 | return Math.round(Math.random() * (t - e) + e); 135 | } 136 | function p(e, t) { 137 | return e + t; 138 | } 139 | function h(e) { 140 | return e * e; 141 | } 142 | (e.Vertify = t.memo(function (e) { 143 | var r = e.width, 144 | o = void 0 === r ? 320 : r, 145 | u = e.height, 146 | i = void 0 === u ? 160 : u, 147 | a = e.l, 148 | c = void 0 === a ? 42 : a, 149 | l = e.r, 150 | s = void 0 === l ? 9 : l, 151 | m = e.imgUrl, 152 | v = e.text, 153 | y = e.refreshIcon, 154 | x = void 0 === y ? 'http://cdn.dooring.cn/dr/icon12.png' : y, 155 | g = e.visible, 156 | b = void 0 === g || g, 157 | w = e.onDraw, 158 | M = e.onCustomVertify, 159 | C = e.onSuccess, 160 | _ = e.onFail, 161 | E = e.onRefresh, 162 | S = t.useState(!1), 163 | T = f(S, 2), 164 | R = T[0], 165 | I = T[1], 166 | N = t.useState(0), 167 | A = f(N, 2), 168 | j = A[0], 169 | D = A[1], 170 | O = t.useState('sliderContainer'), 171 | X = f(O, 2), 172 | k = X[0], 173 | q = X[1], 174 | P = t.useState(v), 175 | U = f(P, 2), 176 | Y = U[0], 177 | V = U[1], 178 | F = t.useRef(null), 179 | L = t.useRef(null), 180 | W = t.useRef(null), 181 | G = t.useRef(!1), 182 | H = t.useRef([]), 183 | $ = t.useRef(0), 184 | z = t.useRef(0), 185 | B = t.useRef(0), 186 | J = t.useRef(0), 187 | K = Math.PI, 188 | Q = c + 2 * s + 3, 189 | Z = function (e, t, r, n) { 190 | e.beginPath(), 191 | e.moveTo(t, r), 192 | e.arc(t + c / 2, r - s + 2, s, 0.72 * K, 2.26 * K), 193 | e.lineTo(t + c, r), 194 | e.arc(t + c + s - 2, r + c / 2, s, 1.21 * K, 2.78 * K), 195 | e.lineTo(t + c, r + c), 196 | e.lineTo(t, r + c), 197 | e.arc(t + s - 2, r + c / 2, s + 0.4, 2.76 * K, 1.24 * K, !0), 198 | e.lineTo(t, r), 199 | (e.lineWidth = 2), 200 | (e.fillStyle = 'rgba(255, 255, 255, 0.7)'), 201 | (e.strokeStyle = 'rgba(255, 255, 255, 0.7)'), 202 | e.stroke(), 203 | (e.globalCompositeOperation = 'destination-over'), 204 | 'fill' === n ? e.fill() : e.clip(); 205 | }, 206 | ee = function () { 207 | return ( 208 | m || 209 | 'https://picsum.photos/id/' 210 | .concat(d(0, 1084), '/') 211 | .concat(o, '/') 212 | .concat(i) 213 | ); 214 | }, 215 | te = function () { 216 | var e = (function (e) { 217 | var t = new Image(); 218 | return ( 219 | (t.crossOrigin = 'Anonymous'), 220 | (t.onload = e), 221 | (t.onerror = function () { 222 | t.setSrc(ee()); 223 | }), 224 | (t.setSrc = function (e) { 225 | if (window.navigator.userAgent.indexOf('Trident') > -1) { 226 | var r = new XMLHttpRequest(); 227 | (r.onloadend = function (e) { 228 | var r = new FileReader(); 229 | r.readAsDataURL(e.target.response), 230 | (r.onloadend = function (e) { 231 | var r; 232 | t.src = 233 | null == e || null === (r = e.target) || void 0 === r 234 | ? void 0 235 | : r.result; 236 | }); 237 | }), 238 | r.open('GET', e), 239 | (r.responseType = 'blob'), 240 | r.send(); 241 | } else t.src = e; 242 | }), 243 | t.setSrc(ee()), 244 | t 245 | ); 246 | })(function () { 247 | I(!1), 248 | (function (e) { 249 | var t = F.current.getContext('2d'), 250 | r = L.current.getContext('2d'); 251 | (B.current = d(Q + 10, o - (Q + 10))), 252 | (J.current = d(10 + 2 * s, i - (Q + 10))), 253 | Z(t, B.current, J.current, 'fill'), 254 | Z(r, B.current, J.current, 'clip'), 255 | t.drawImage(e, 0, 0, o, i), 256 | r.drawImage(e, 0, 0, o, i); 257 | var n = J.current - 2 * s - 1, 258 | u = r.getImageData(B.current - 3, n, Q, Q); 259 | (L.current.width = Q), r.putImageData(u, 0, n); 260 | })(e); 261 | }); 262 | W.current = e; 263 | }, 264 | re = function () { 265 | var e = F.current.getContext('2d'), 266 | t = L.current.getContext('2d'); 267 | D(0), 268 | q('sliderContainer'), 269 | (L.current.width = o), 270 | (L.current.style.left = '0px'), 271 | e.clearRect(0, 0, o, i), 272 | t.clearRect(0, 0, o, i), 273 | I(!0), 274 | W.current.setSrc(ee()); 275 | }, 276 | ne = function () { 277 | var e = H.current, 278 | t = e.reduce(p) / e.length, 279 | r = e.map(function (e) { 280 | return e - t; 281 | }), 282 | n = Math.sqrt(r.map(h).reduce(p) / e.length), 283 | o = parseInt(L.current.style.left); 284 | return { 285 | spliced: 10 > Math.abs(o - B.current), 286 | verified: 0 !== n, 287 | left: o, 288 | destX: B.current, 289 | }; 290 | }, 291 | oe = function (e) { 292 | ($.current = e.clientX || e.touches[0].clientX), 293 | (z.current = e.clientY || e.touches[0].clientY), 294 | (G.current = !0); 295 | }, 296 | ue = function (e) { 297 | if (!G.current) return !1; 298 | e.preventDefault(); 299 | var t = (e.clientX || e.touches[0].clientX) - $.current, 300 | r = (e.clientY || e.touches[0].clientY) - z.current; 301 | if (0 > t || t + 38 >= o) return !1; 302 | D(t); 303 | var n = ((o - 40 - 20) / (o - 40)) * t; 304 | (L.current.style.left = n + 'px'), 305 | q('sliderContainer sliderContainer_active'), 306 | H.current.push(r), 307 | w && w(n); 308 | }, 309 | ie = function (e) { 310 | if (!G.current) return !1; 311 | if ( 312 | ((G.current = !1), 313 | (e.clientX || e.changedTouches[0].clientX) === $.current) 314 | ) 315 | return !1; 316 | q('sliderContainer'); 317 | var t = M ? M(ne()) : ne(); 318 | t.spliced 319 | ? t.verified 320 | ? (q('sliderContainer sliderContainer_success'), 321 | 'function' == typeof C && C()) 322 | : (q('sliderContainer sliderContainer_fail'), V('请再试一次'), re()) 323 | : (q('sliderContainer sliderContainer_fail'), 324 | 'function' == typeof _ && _(), 325 | setTimeout(re.bind(undefined), 1e3)); 326 | }; 327 | return ( 328 | t.useEffect( 329 | function () { 330 | b && (W.current ? re() : te()); 331 | }, 332 | [b], 333 | ), 334 | n.default.createElement( 335 | 'div', 336 | { 337 | className: 'vertifyWrap', 338 | style: { 339 | width: o + 'px', 340 | margin: '0 auto', 341 | display: b ? '' : 'none', 342 | }, 343 | onMouseMove: ue, 344 | onMouseUp: ie, 345 | onTouchMove: ue, 346 | onTouchEnd: ie, 347 | }, 348 | n.default.createElement( 349 | 'div', 350 | { className: 'canvasArea' }, 351 | n.default.createElement('canvas', { ref: F, width: o, height: i }), 352 | n.default.createElement('canvas', { 353 | ref: L, 354 | className: 'block', 355 | width: o, 356 | height: i, 357 | onMouseDown: oe, 358 | onTouchStart: oe, 359 | }), 360 | ), 361 | n.default.createElement( 362 | 'div', 363 | { 364 | className: k, 365 | style: { pointerEvents: R ? 'none' : 'auto', width: o + 'px' }, 366 | }, 367 | n.default.createElement( 368 | 'div', 369 | { className: 'sliderMask', style: { width: j + 'px' } }, 370 | n.default.createElement( 371 | 'div', 372 | { 373 | className: 'slider', 374 | style: { left: j + 'px' }, 375 | onMouseDown: oe, 376 | onTouchStart: oe, 377 | }, 378 | n.default.createElement('div', { className: 'sliderIcon' }, '→'), 379 | ), 380 | ), 381 | n.default.createElement('div', { className: 'sliderText' }, Y), 382 | ), 383 | n.default.createElement('div', { 384 | className: 'refreshIcon', 385 | onClick: function () { 386 | re(), 'function' == typeof E && E(); 387 | }, 388 | style: { backgroundImage: 'url('.concat(x, ')') }, 389 | }), 390 | n.default.createElement( 391 | 'div', 392 | { 393 | className: 'loadingContainer', 394 | style: { 395 | width: o + 'px', 396 | height: i + 'px', 397 | display: R ? '' : 'none', 398 | }, 399 | }, 400 | n.default.createElement('div', { className: 'loadingIcon' }), 401 | n.default.createElement('span', null, '加载中...'), 402 | ), 403 | ) 404 | ); 405 | })), 406 | Object.defineProperty(e, '__esModule', { value: !0 }); 407 | }); 408 | -------------------------------------------------------------------------------- /dist/index.umd.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' 3 | ? factory(exports, require('react')) 4 | : typeof define === 'function' && define.amd 5 | ? define(['exports', 'react'], factory) 6 | : ((global = 7 | typeof globalThis !== 'undefined' ? globalThis : global || self), 8 | factory((global.reactSliderVertify = {}), global.React)); 9 | })(this, function (exports, React) { 10 | 'use strict'; 11 | 12 | function _interopDefaultLegacy(e) { 13 | return e && typeof e === 'object' && 'default' in e ? e : { default: e }; 14 | } 15 | 16 | var React__default = /*#__PURE__*/ _interopDefaultLegacy(React); 17 | 18 | function getDefaultExportFromCjs(x) { 19 | return x && 20 | x.__esModule && 21 | Object.prototype.hasOwnProperty.call(x, 'default') 22 | ? x['default'] 23 | : x; 24 | } 25 | 26 | function createCommonjsModule(fn, basedir, module) { 27 | return ( 28 | (module = { 29 | path: basedir, 30 | exports: {}, 31 | require: function (path, base) { 32 | return commonjsRequire( 33 | path, 34 | base === undefined || base === null ? module.path : base, 35 | ); 36 | }, 37 | }), 38 | fn(module, module.exports), 39 | module.exports 40 | ); 41 | } 42 | 43 | function commonjsRequire() { 44 | throw new Error( 45 | 'Dynamic requires are not currently supported by @rollup/plugin-commonjs', 46 | ); 47 | } 48 | 49 | var arrayWithHoles = createCommonjsModule(function (module) { 50 | function _arrayWithHoles(arr) { 51 | if (Array.isArray(arr)) return arr; 52 | } 53 | 54 | module.exports = _arrayWithHoles; 55 | (module.exports['default'] = module.exports), 56 | (module.exports.__esModule = true); 57 | }); 58 | 59 | var iterableToArrayLimit = createCommonjsModule(function (module) { 60 | function _iterableToArrayLimit(arr, i) { 61 | var _i = 62 | arr == null 63 | ? null 64 | : (typeof Symbol !== 'undefined' && arr[Symbol.iterator]) || 65 | arr['@@iterator']; 66 | 67 | if (_i == null) return; 68 | var _arr = []; 69 | var _n = true; 70 | var _d = false; 71 | 72 | var _s, _e; 73 | 74 | try { 75 | for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { 76 | _arr.push(_s.value); 77 | 78 | if (i && _arr.length === i) break; 79 | } 80 | } catch (err) { 81 | _d = true; 82 | _e = err; 83 | } finally { 84 | try { 85 | if (!_n && _i['return'] != null) _i['return'](); 86 | } finally { 87 | if (_d) throw _e; 88 | } 89 | } 90 | 91 | return _arr; 92 | } 93 | 94 | module.exports = _iterableToArrayLimit; 95 | (module.exports['default'] = module.exports), 96 | (module.exports.__esModule = true); 97 | }); 98 | 99 | var arrayLikeToArray = createCommonjsModule(function (module) { 100 | function _arrayLikeToArray(arr, len) { 101 | if (len == null || len > arr.length) len = arr.length; 102 | 103 | for (var i = 0, arr2 = new Array(len); i < len; i++) { 104 | arr2[i] = arr[i]; 105 | } 106 | 107 | return arr2; 108 | } 109 | 110 | module.exports = _arrayLikeToArray; 111 | (module.exports['default'] = module.exports), 112 | (module.exports.__esModule = true); 113 | }); 114 | 115 | var unsupportedIterableToArray = createCommonjsModule(function (module) { 116 | function _unsupportedIterableToArray(o, minLen) { 117 | if (!o) return; 118 | if (typeof o === 'string') return arrayLikeToArray(o, minLen); 119 | var n = Object.prototype.toString.call(o).slice(8, -1); 120 | if (n === 'Object' && o.constructor) n = o.constructor.name; 121 | if (n === 'Map' || n === 'Set') return Array.from(o); 122 | if ( 123 | n === 'Arguments' || 124 | /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n) 125 | ) 126 | return arrayLikeToArray(o, minLen); 127 | } 128 | 129 | module.exports = _unsupportedIterableToArray; 130 | (module.exports['default'] = module.exports), 131 | (module.exports.__esModule = true); 132 | }); 133 | 134 | var nonIterableRest = createCommonjsModule(function (module) { 135 | function _nonIterableRest() { 136 | throw new TypeError( 137 | 'Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.', 138 | ); 139 | } 140 | 141 | module.exports = _nonIterableRest; 142 | (module.exports['default'] = module.exports), 143 | (module.exports.__esModule = true); 144 | }); 145 | 146 | var slicedToArray = createCommonjsModule(function (module) { 147 | function _slicedToArray(arr, i) { 148 | return ( 149 | arrayWithHoles(arr) || 150 | iterableToArrayLimit(arr, i) || 151 | unsupportedIterableToArray(arr, i) || 152 | nonIterableRest() 153 | ); 154 | } 155 | 156 | module.exports = _slicedToArray; 157 | (module.exports['default'] = module.exports), 158 | (module.exports.__esModule = true); 159 | }); 160 | 161 | var _slicedToArray = /*@__PURE__*/ getDefaultExportFromCjs(slicedToArray); 162 | 163 | function getRandomNumberByRange(start, end) { 164 | return Math.round(Math.random() * (end - start) + start); 165 | } 166 | 167 | function sum(x, y) { 168 | return x + y; 169 | } 170 | 171 | function square(x) { 172 | return x * x; 173 | } 174 | 175 | var _this = undefined; 176 | var index = /*#__PURE__*/ React.memo(function (_ref) { 177 | var _ref$width = _ref.width, 178 | width = _ref$width === void 0 ? 320 : _ref$width, 179 | _ref$height = _ref.height, 180 | height = _ref$height === void 0 ? 160 : _ref$height, 181 | _ref$l = _ref.l, 182 | l = _ref$l === void 0 ? 42 : _ref$l, 183 | _ref$r = _ref.r, 184 | r = _ref$r === void 0 ? 9 : _ref$r, 185 | imgUrl = _ref.imgUrl, 186 | text = _ref.text, 187 | _ref$refreshIcon = _ref.refreshIcon, 188 | refreshIcon = 189 | _ref$refreshIcon === void 0 190 | ? 'http://cdn.dooring.cn/dr/icon12.png' 191 | : _ref$refreshIcon, 192 | _ref$visible = _ref.visible, 193 | visible = _ref$visible === void 0 ? true : _ref$visible, 194 | onDraw = _ref.onDraw, 195 | onCustomVertify = _ref.onCustomVertify, 196 | onSuccess = _ref.onSuccess, 197 | onFail = _ref.onFail, 198 | onRefresh = _ref.onRefresh; 199 | 200 | var _useState = React.useState(false), 201 | _useState2 = _slicedToArray(_useState, 2), 202 | isLoading = _useState2[0], 203 | setLoading = _useState2[1]; 204 | 205 | var _useState3 = React.useState(0), 206 | _useState4 = _slicedToArray(_useState3, 2), 207 | sliderLeft = _useState4[0], 208 | setSliderLeft = _useState4[1]; 209 | 210 | var _useState5 = React.useState('sliderContainer'), 211 | _useState6 = _slicedToArray(_useState5, 2), 212 | sliderClass = _useState6[0], 213 | setSliderClass = _useState6[1]; 214 | 215 | var _useState7 = React.useState(text), 216 | _useState8 = _slicedToArray(_useState7, 2), 217 | textTip = _useState8[0], 218 | setTextTip = _useState8[1]; 219 | 220 | var canvasRef = React.useRef(null); 221 | var blockRef = React.useRef(null); 222 | var imgRef = React.useRef(null); 223 | var isMouseDownRef = React.useRef(false); 224 | var trailRef = React.useRef([]); 225 | var originXRef = React.useRef(0); 226 | var originYRef = React.useRef(0); 227 | var xRef = React.useRef(0); 228 | var yRef = React.useRef(0); 229 | var PI = Math.PI; 230 | var L = l + r * 2 + 3; // 滑块实际边长 231 | 232 | var drawPath = function drawPath(ctx, x, y, operation) { 233 | ctx.beginPath(); 234 | ctx.moveTo(x, y); 235 | ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI); 236 | ctx.lineTo(x + l, y); 237 | ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI); 238 | ctx.lineTo(x + l, y + l); 239 | ctx.lineTo(x, y + l); 240 | ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true); 241 | ctx.lineTo(x, y); 242 | ctx.lineWidth = 2; 243 | ctx.fillStyle = 'rgba(255, 255, 255, 0.7)'; 244 | ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)'; 245 | ctx.stroke(); 246 | ctx.globalCompositeOperation = 'destination-over'; 247 | operation === 'fill' ? ctx.fill() : ctx.clip(); 248 | }; 249 | 250 | var getRandomImgSrc = function getRandomImgSrc() { 251 | return ( 252 | imgUrl || 253 | 'https://picsum.photos/id/' 254 | .concat(getRandomNumberByRange(0, 1084), '/') 255 | .concat(width, '/') 256 | .concat(height) 257 | ); 258 | }; 259 | 260 | var createImg = function createImg(onload) { 261 | var img = new Image(); 262 | img.crossOrigin = 'Anonymous'; 263 | img.onload = onload; 264 | 265 | img.onerror = function () { 266 | img.setSrc(getRandomImgSrc()); // 图片加载失败的时候重新加载其他图片 267 | }; 268 | 269 | img.setSrc = function (src) { 270 | var isIE = window.navigator.userAgent.indexOf('Trident') > -1; 271 | 272 | if (isIE) { 273 | // IE浏览器无法通过img.crossOrigin跨域,使用ajax获取图片blob然后转为dataURL显示 274 | var xhr = new XMLHttpRequest(); 275 | 276 | xhr.onloadend = function (e) { 277 | var file = new FileReader(); // FileReader仅支持IE10+ 278 | 279 | file.readAsDataURL(e.target.response); 280 | 281 | file.onloadend = function (e) { 282 | var _e$target; 283 | 284 | img.src = 285 | e === null || e === void 0 286 | ? void 0 287 | : (_e$target = e.target) === null || _e$target === void 0 288 | ? void 0 289 | : _e$target.result; 290 | }; 291 | }; 292 | 293 | xhr.open('GET', src); 294 | xhr.responseType = 'blob'; 295 | xhr.send(); 296 | } else img.src = src; 297 | }; 298 | 299 | img.setSrc(getRandomImgSrc()); 300 | return img; 301 | }; 302 | 303 | var draw = function draw(img) { 304 | var canvasCtx = canvasRef.current.getContext('2d'); 305 | var blockCtx = blockRef.current.getContext('2d'); // 随机位置创建拼图形状 306 | 307 | xRef.current = getRandomNumberByRange(L + 10, width - (L + 10)); 308 | yRef.current = getRandomNumberByRange(10 + r * 2, height - (L + 10)); 309 | drawPath(canvasCtx, xRef.current, yRef.current, 'fill'); 310 | drawPath(blockCtx, xRef.current, yRef.current, 'clip'); // 画入图片 311 | 312 | canvasCtx.drawImage(img, 0, 0, width, height); 313 | blockCtx.drawImage(img, 0, 0, width, height); // 提取滑块并放到最左边 314 | 315 | var y1 = yRef.current - r * 2 - 1; 316 | var ImageData = blockCtx.getImageData(xRef.current - 3, y1, L, L); 317 | blockRef.current.width = L; 318 | blockCtx.putImageData(ImageData, 0, y1); 319 | }; 320 | 321 | var initImg = function initImg() { 322 | var img = createImg(function () { 323 | setLoading(false); 324 | draw(img); 325 | }); 326 | imgRef.current = img; 327 | }; 328 | 329 | var reset = function reset() { 330 | var canvasCtx = canvasRef.current.getContext('2d'); 331 | var blockCtx = blockRef.current.getContext('2d'); // 重置样式 332 | 333 | setSliderLeft(0); 334 | setSliderClass('sliderContainer'); 335 | blockRef.current.width = width; 336 | blockRef.current.style.left = 0 + 'px'; // 清空画布 337 | 338 | canvasCtx.clearRect(0, 0, width, height); 339 | blockCtx.clearRect(0, 0, width, height); // 重新加载图片 340 | 341 | setLoading(true); 342 | imgRef.current.setSrc(getRandomImgSrc()); 343 | }; 344 | 345 | var handleRefresh = function handleRefresh() { 346 | reset(); 347 | typeof onRefresh === 'function' && onRefresh(); 348 | }; 349 | 350 | var verify = function verify() { 351 | var arr = trailRef.current; // 拖动时y轴的移动距离 352 | 353 | var average = arr.reduce(sum) / arr.length; 354 | var deviations = arr.map(function (x) { 355 | return x - average; 356 | }); 357 | var stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length); 358 | var left = parseInt(blockRef.current.style.left); 359 | return { 360 | spliced: Math.abs(left - xRef.current) < 10, 361 | verified: stddev !== 0, 362 | left: left, 363 | destX: xRef.current, 364 | }; 365 | }; 366 | 367 | var handleDragStart = function handleDragStart(e) { 368 | originXRef.current = e.clientX || e.touches[0].clientX; 369 | originYRef.current = e.clientY || e.touches[0].clientY; 370 | isMouseDownRef.current = true; 371 | }; 372 | 373 | var handleDragMove = function handleDragMove(e) { 374 | if (!isMouseDownRef.current) return false; 375 | e.preventDefault(); 376 | var eventX = e.clientX || e.touches[0].clientX; 377 | var eventY = e.clientY || e.touches[0].clientY; 378 | var moveX = eventX - originXRef.current; 379 | var moveY = eventY - originYRef.current; 380 | if (moveX < 0 || moveX + 38 >= width) return false; 381 | setSliderLeft(moveX); 382 | var blockLeft = ((width - 40 - 20) / (width - 40)) * moveX; 383 | blockRef.current.style.left = blockLeft + 'px'; 384 | setSliderClass('sliderContainer sliderContainer_active'); 385 | trailRef.current.push(moveY); 386 | onDraw && onDraw(blockLeft); 387 | }; 388 | 389 | var handleDragEnd = function handleDragEnd(e) { 390 | if (!isMouseDownRef.current) return false; 391 | isMouseDownRef.current = false; 392 | var eventX = e.clientX || e.changedTouches[0].clientX; 393 | if (eventX === originXRef.current) return false; 394 | setSliderClass('sliderContainer'); 395 | 396 | var _ref2 = onCustomVertify ? onCustomVertify(verify()) : verify(), 397 | spliced = _ref2.spliced, 398 | verified = _ref2.verified; 399 | 400 | if (spliced) { 401 | if (verified) { 402 | setSliderClass('sliderContainer sliderContainer_success'); 403 | typeof onSuccess === 'function' && onSuccess(); 404 | } else { 405 | setSliderClass('sliderContainer sliderContainer_fail'); 406 | setTextTip('请再试一次'); 407 | reset(); 408 | } 409 | } else { 410 | setSliderClass('sliderContainer sliderContainer_fail'); 411 | typeof onFail === 'function' && onFail(); 412 | setTimeout(reset.bind(_this), 1000); 413 | } 414 | }; 415 | 416 | React.useEffect( 417 | function () { 418 | if (visible) { 419 | imgRef.current ? reset() : initImg(); 420 | } 421 | }, 422 | [visible], 423 | ); 424 | return /*#__PURE__*/ React__default['default'].createElement( 425 | 'div', 426 | { 427 | className: 'vertifyWrap', 428 | style: { 429 | width: width + 'px', 430 | margin: '0 auto', 431 | display: visible ? '' : 'none', 432 | }, 433 | onMouseMove: handleDragMove, 434 | onMouseUp: handleDragEnd, 435 | onTouchMove: handleDragMove, 436 | onTouchEnd: handleDragEnd, 437 | }, 438 | /*#__PURE__*/ React__default['default'].createElement( 439 | 'div', 440 | { 441 | className: 'canvasArea', 442 | }, 443 | /*#__PURE__*/ React__default['default'].createElement('canvas', { 444 | ref: canvasRef, 445 | width: width, 446 | height: height, 447 | }), 448 | /*#__PURE__*/ React__default['default'].createElement('canvas', { 449 | ref: blockRef, 450 | className: 'block', 451 | width: width, 452 | height: height, 453 | onMouseDown: handleDragStart, 454 | onTouchStart: handleDragStart, 455 | }), 456 | ), 457 | /*#__PURE__*/ React__default['default'].createElement( 458 | 'div', 459 | { 460 | className: sliderClass, 461 | style: { 462 | pointerEvents: isLoading ? 'none' : 'auto', 463 | width: width + 'px', 464 | }, 465 | }, 466 | /*#__PURE__*/ React__default['default'].createElement( 467 | 'div', 468 | { 469 | className: 'sliderMask', 470 | style: { 471 | width: sliderLeft + 'px', 472 | }, 473 | }, 474 | /*#__PURE__*/ React__default['default'].createElement( 475 | 'div', 476 | { 477 | className: 'slider', 478 | style: { 479 | left: sliderLeft + 'px', 480 | }, 481 | onMouseDown: handleDragStart, 482 | onTouchStart: handleDragStart, 483 | }, 484 | /*#__PURE__*/ React__default['default'].createElement( 485 | 'div', 486 | { 487 | className: 'sliderIcon', 488 | }, 489 | '\u2192', 490 | ), 491 | ), 492 | ), 493 | /*#__PURE__*/ React__default['default'].createElement( 494 | 'div', 495 | { 496 | className: 'sliderText', 497 | }, 498 | textTip, 499 | ), 500 | ), 501 | /*#__PURE__*/ React__default['default'].createElement('div', { 502 | className: 'refreshIcon', 503 | onClick: handleRefresh, 504 | style: { 505 | backgroundImage: 'url('.concat(refreshIcon, ')'), 506 | }, 507 | }), 508 | /*#__PURE__*/ React__default['default'].createElement( 509 | 'div', 510 | { 511 | className: 'loadingContainer', 512 | style: { 513 | width: width + 'px', 514 | height: height + 'px', 515 | display: isLoading ? '' : 'none', 516 | }, 517 | }, 518 | /*#__PURE__*/ React__default['default'].createElement('div', { 519 | className: 'loadingIcon', 520 | }), 521 | /*#__PURE__*/ React__default['default'].createElement( 522 | 'span', 523 | null, 524 | '\u52A0\u8F7D\u4E2D...', 525 | ), 526 | ), 527 | ); 528 | }); 529 | 530 | exports.Vertify = index; 531 | 532 | Object.defineProperty(exports, '__esModule', { value: true }); 533 | }); 534 | --------------------------------------------------------------------------------