├── docs ├── .vitepress │ ├── cache │ │ └── deps │ │ │ ├── package.json │ │ │ ├── vue.js.map │ │ │ ├── vitepress___@vueuse_core.js.map │ │ │ ├── _metadata.json │ │ │ ├── vue.js │ │ │ └── vitepress___@vueuse_core.js │ ├── theme │ │ ├── index.ts │ │ └── style.less │ └── config.ts ├── changelog.md ├── guide │ ├── introduction.md │ ├── quick-start.md │ ├── install.md │ └── modules │ │ ├── color.md │ │ ├── speech.md │ │ ├── data.md │ │ ├── string.md │ │ ├── event.md │ │ ├── file.md │ │ ├── number.md │ │ ├── platform.md │ │ ├── common.md │ │ └── element.md └── index.md ├── README.md ├── examples ├── main.ts └── App.vue ├── .npmignore ├── tsconfig.node.json ├── .gitignore ├── index.html ├── lib ├── color.d.ts ├── number.d.ts ├── event.d.ts ├── string.d.ts ├── speech.d.ts ├── data.d.ts ├── file.d.ts ├── common.d.ts ├── platform.d.ts ├── element.d.ts ├── index.d.ts ├── dap-util.umd.js └── dap-util.es.js ├── tsconfig.json ├── src ├── index.ts ├── data.ts ├── string.ts ├── number.ts ├── speech.ts ├── color.ts ├── file.ts ├── event.ts ├── platform.ts ├── common.ts └── element.ts ├── vite.config.ts ├── package.json └── LICENSE /docs/.vitepress/cache/deps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### 一个提供大量便捷高效方法的 JS 库 2 | 3 | > 具体使用方法请参阅:[dap-util](https://www.so-better.cn/docs/dap-util/) 4 | -------------------------------------------------------------------------------- /examples/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/vue.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": [], 4 | "sourcesContent": [], 5 | "mappings": "", 6 | "names": [] 7 | } 8 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | import DefaultTheme from 'vitepress/theme' 2 | import './style.less' 3 | 4 | export default { 5 | extends: DefaultTheme 6 | } 7 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": [], 4 | "sourcesContent": [], 5 | "mappings": "", 6 | "names": [] 7 | } 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # 忽略目录 2 | examples/ 3 | public/ 4 | docs/ 5 | src/ 6 | 7 | # 忽略指定文件 8 | LICENSE 9 | index.html 10 | vite.config.ts 11 | tsconfig.json 12 | tsconfig.node.json 13 | package-lock.json 14 | vite-env.d.ts 15 | 16 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /examples/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 14 | 15 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | lastUpdated: false 3 | title: 更新日志 4 | --- 5 | 6 | # 更新日志 7 | 8 | ## v1.6.0 9 | 10 | - 优化 TS 类型 11 | - 部分方法优化 12 | - 文档完善 13 | 14 | ## v1.5.9 15 | 16 | - 优化 TS 类型 17 | - 修复 matchingText 方法中对十六进制颜色值判断错误的 bug 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | docs/dap-util 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | 24 | /package-lock.json 25 | -------------------------------------------------------------------------------- /docs/guide/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 简介 3 | --- 4 | 5 | # 简介 6 | 7 | `dap-util` 是一个不依赖于任何库的工具性质的 `JS` 库,它提供了大量的工具函数来方便我们的开发 8 | 9 | ## 模块 10 | 11 | `dap-util` 内的工具方法,是按照不同的模块加以归类和使用的,目前的模块有: 12 | 13 | - `color`:颜色模块 14 | - `common`:通用模块 15 | - `data`:数据模块 16 | - `element`:dom 模块 17 | - `event`:事件模块 18 | - `file`:文件模块 19 | - `number`:数字模块 20 | - `platform`:平台终端模块 21 | - `speech`:语音合成模块 22 | - `string`:字符串模块 23 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | dap-util JS工具库 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /lib/color.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 颜色相关方法 3 | */ 4 | export declare const color: { 5 | /** 6 | * rgb转hsv值 7 | * @param {Object} rgb rgb值,数组 8 | */ 9 | rgb2hsv(rgb: number[]): number[]; 10 | /** 11 | * hsv格式值转rgb值 12 | * @param {Object} hsv hsv值,数组 13 | */ 14 | hsv2rgb(hsv: number[]): number[]; 15 | /** 16 | * rgb值转十六进制 17 | * @param {Array} rgb rgb值,数组 18 | */ 19 | rgb2hex(rgb: number[]): string; 20 | /** 21 | * 十六进制颜色转rgb 22 | * @param {String} hex 十六进制颜色值 23 | */ 24 | hex2rgb(hex: string): number[]; 25 | }; 26 | -------------------------------------------------------------------------------- /lib/number.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 数字相关方法 3 | */ 4 | export declare const number: { 5 | /** 6 | * 数字格式化 7 | * @param {Number} num 8 | */ 9 | formatNumber(num: number): string; 10 | /** 11 | * 判断是否数字 12 | * @param {Object} num 13 | */ 14 | isNumber(num: any): boolean; 15 | /** 16 | * 多个数的加法运算 17 | */ 18 | add(...values: number[]): number; 19 | /** 20 | * 多个数的减法运算 21 | */ 22 | subtract(...values: number[]): number; 23 | /** 24 | * 多个数的乘法运算 25 | */ 26 | mutiply(...values: number[]): number; 27 | /** 28 | * 多个数的除法运算 29 | */ 30 | divide(...values: number[]): number; 31 | }; 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | "allowSyntheticDefaultImports": true, 9 | "types": ["node"], 10 | 11 | /* Bundler mode */ 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "preserve", 18 | 19 | /* Linting */ 20 | "strict": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "noFallthroughCasesInSwitch": true 24 | }, 25 | "include": ["src/**/*.ts"], 26 | "references": [{ "path": "./tsconfig.node.json" }] 27 | } 28 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { color } from './color' 2 | import { common } from './common' 3 | import { data } from './data' 4 | import { element } from './element' 5 | import { event } from './event' 6 | import { file } from './file' 7 | import { number } from './number' 8 | import { platform } from './platform' 9 | import { speech } from './speech' 10 | import { string } from './string' 11 | 12 | export * from './color' 13 | export * from './common' 14 | export * from './data' 15 | export * from './element' 16 | export * from './event' 17 | export * from './file' 18 | export * from './number' 19 | export * from './platform' 20 | export * from './speech' 21 | export * from './string' 22 | 23 | export default { number, data, element, event, common, color, file, string, platform, speech } 24 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import dts from 'vite-plugin-dts' 4 | import path from 'path' 5 | 6 | export default defineConfig({ 7 | plugins: [vue(), dts()], 8 | build: { 9 | //打包后的目录名称 10 | outDir: 'lib', 11 | minify: 'terser', 12 | lib: { 13 | entry: path.resolve(__dirname, 'src/index.ts'), 14 | name: 'Dap', 15 | fileName: format => `dap-util.${format}.js` 16 | }, 17 | rollupOptions: { 18 | // 确保外部化处理那些你不想打包进库的依赖 19 | external: ['vue'], 20 | output: { 21 | // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量 22 | globals: { 23 | vue: 'Vue' 24 | }, 25 | exports: 'named' 26 | } 27 | }, 28 | sourcemap: false //是否构建source map 文件 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /docs/guide/quick-start.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 快速上手 3 | --- 4 | 5 | # 快速上手 6 | 7 | ## 获取 dap-util 提供的对象 8 | 9 | - 如果你是通过 `cdn` 在线引入 `dap-util` 或者下载 `dap-util` 文件到本地使用,直接通过 `window` 获取 `dap-util` 提供的对象 10 | 11 | ```ts 12 | const Dap = window.Dap 13 | ``` 14 | 15 | - 如果你是通过 `npm/yarn/pnpm` 引入 16 | 17 | ```ts 18 | import Dap from 'dap-util' 19 | ``` 20 | 21 | ## 使用方法 22 | 23 | 获取到 `dap-util` 的 `Dap` 对象后,即可通过 `Dap` 来调用各个模块里的函数,如下: 24 | 25 | ```ts 26 | //调用platform模块的device方法获取运行设备的信息 27 | const result = Dap.platform.device() 28 | ``` 29 | 30 | ```ts 31 | //调用common模块的isObeject方法判断是否对象 32 | const isObj = Dap.common.isObject(val) 33 | ``` 34 | 35 | > 关于 dap-util 的模块会在后面文档中尽数列出,具体使用请看后续文档 36 | 37 | ## 按需引入 38 | 39 | `dap-util` 支持按需引入的方式,如下: 40 | 41 | ```ts 42 | //按需引入platform模块来使用 43 | import { platform } from 'dap-util' 44 | const result = platform.device() 45 | ``` 46 | 47 | ```ts 48 | //按需引入common模块来使用 49 | import { common } from 'dap-util' 50 | const isObj = common.isObject(val) 51 | ``` 52 | -------------------------------------------------------------------------------- /lib/event.d.ts: -------------------------------------------------------------------------------- 1 | export type EventNameObjType = { 2 | eventName?: string; 3 | guid?: string | number; 4 | }; 5 | export type EventObjType = { 6 | [key: string]: { 7 | type: string; 8 | fn: EventListenerOrEventListenerObject; 9 | options?: AddEventListenerOptions; 10 | } | undefined; 11 | }; 12 | export declare const event: { 13 | /** 14 | * 绑定事件 15 | * @param {Object} el 元素节点 16 | * @param {Object} eventName 事件名称 17 | * @param {Object} fn 函数 18 | * @param {Object} options 参数 19 | */ 20 | on(el: HTMLElement | Window | Document, eventName: string, fn: (e: Event) => void, options?: AddEventListenerOptions): void; 21 | /** 22 | * 事件解绑 23 | * @param {Object} el 元素节点 24 | * @param {Object} eventName 事件名称 25 | */ 26 | off(el: HTMLElement | Window | Document, eventName?: string): void; 27 | /** 28 | * 获取绑定的所有事件 29 | * @param {*} el 30 | */ 31 | get(el: HTMLElement | Window | Document): EventObjType | undefined; 32 | }; 33 | -------------------------------------------------------------------------------- /lib/string.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 字符串操作 3 | */ 4 | export declare const string: { 5 | /** 6 | * 向指定位置插入字符串 7 | * @param {Object} original 原始字符串 8 | * @param {Object} str 插入的字符串 9 | * @param {Object} index 插入的位置 10 | */ 11 | insert(original: string, str: string, index: number): string; 12 | /** 13 | * 删除指定位置的字符串 14 | * @param {Object} original 原始字符串 15 | * @param {Object} index 删除的位置序列 16 | * @param {Object} num 删除的字符串长度 17 | */ 18 | delete(original: string, index: number, num: number): string; 19 | /** 20 | * 替换指定位置的字符串 21 | * @param {Object} original 原始字符串 22 | * @param {Object} start 开始位置 23 | * @param {Object} end 结束位置 24 | * @param {Object} str 替换的字符串 25 | */ 26 | replace(original: string, start: number, end: number, str: string): string; 27 | /** 28 | * 去除字符串空格 29 | * @param {Object} str 原始字符串 30 | * @param {Object} global 为true时去除所有空格,否则只去除两边空格 31 | */ 32 | trim(str: string, global?: boolean): string; 33 | }; 34 | -------------------------------------------------------------------------------- /lib/speech.d.ts: -------------------------------------------------------------------------------- 1 | export type EventParamsType = { 2 | text: string; 3 | pitch: number; 4 | rate: number; 5 | volume: number; 6 | }; 7 | export type SpeechParamsType = { 8 | pitch?: number; 9 | rate?: number; 10 | volume?: number; 11 | start?: (e: Event, options: EventParamsType) => void; 12 | end?: (e: Event, options: EventParamsType) => void; 13 | pause?: (e: Event, options: EventParamsType) => void; 14 | resume?: (e: Event, options: EventParamsType) => void; 15 | error?: (e: Event, options: EventParamsType) => void; 16 | }; 17 | /** 18 | * 语音合成方法 19 | */ 20 | export declare const speech: { 21 | /** 22 | * 将文字加入语音播报队列 23 | * @param {Object} text 24 | * @param {Object} options 25 | */ 26 | start(text: string, options?: SpeechParamsType): void; 27 | /** 28 | * 停止播报,停止所有播报队列里面的语音 29 | */ 30 | stop(): void; 31 | /** 32 | * 暂停播报 33 | */ 34 | pause(): void; 35 | /** 36 | * 恢复暂停的播报 37 | */ 38 | resume(): void; 39 | }; 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dap-util", 3 | "version": "1.6.0", 4 | "private": false, 5 | "type": "module", 6 | "author": "so-better", 7 | "main": "lib/dap-util.umd.js", 8 | "module": "lib/dap-util.es.js", 9 | "types": "lib/index.d.ts", 10 | "license": "MIT", 11 | "description": "一个轻量的前端JavaScript工具库,专注于JavaScript,不关心UI", 12 | "scripts": { 13 | "dev": "vite --host", 14 | "lib": "tsc && vite build", 15 | "docs:dev": "vitepress dev docs", 16 | "docs:build": "vitepress build docs" 17 | }, 18 | "dependencies": { 19 | "vue": "^3.3.13" 20 | }, 21 | "devDependencies": { 22 | "@types/node": "^20.11.24", 23 | "terser": "^5.16.9", 24 | "vite": "^5.4.10", 25 | "less": "^3.0.4", 26 | "less-loader": "^5.0.0", 27 | "@vitejs/plugin-vue": "^5.0.4", 28 | "typescript": "^5.6.3", 29 | "vite-plugin-dts": "^4.3.0", 30 | "vitepress": "^1.5.0" 31 | }, 32 | "browserslist": [ 33 | "> 1%", 34 | "last 2 versions", 35 | "not dead" 36 | ], 37 | "repository": { 38 | "type": "git", 39 | "url": "https://github.com/so-better/dap-util" 40 | } 41 | } -------------------------------------------------------------------------------- /lib/data.d.ts: -------------------------------------------------------------------------------- 1 | export type DataHTMLElement = HTMLElement & { 2 | [key: string]: any; 3 | }; 4 | export type DataWindow = Window & { 5 | [key: string]: any; 6 | }; 7 | export type DataDocument = Document & { 8 | [key: string]: any; 9 | }; 10 | /** 11 | * 元素数据挂载方法 12 | */ 13 | export declare const data: { 14 | /** 15 | * 移除指定数据 16 | * @param {Object} el 17 | * @param {Object} key 18 | */ 19 | remove(el: DataHTMLElement | DataWindow | DataDocument, key?: string): void; 20 | /** 21 | * 判断是否含有指定数据 22 | * @param {Object} el 23 | * @param {Object} key 24 | */ 25 | has(el: DataHTMLElement | DataWindow | DataDocument, key: string): boolean; 26 | /** 27 | * 获取元素指定数据 28 | * @param {Object} el 29 | * @param {Object} key 30 | */ 31 | get(el: DataHTMLElement | DataWindow | DataDocument, key?: string): T; 32 | /** 33 | * 设置元素指定数据 34 | * @param {Object} el 35 | * @param {Object} key 36 | * @param {Object} value 37 | */ 38 | set(el: DataHTMLElement | DataWindow | DataDocument, key: string, value: any): void; 39 | }; 40 | -------------------------------------------------------------------------------- /lib/file.d.ts: -------------------------------------------------------------------------------- 1 | export type CompressOptionsType = { 2 | width?: number; 3 | quality?: number; 4 | mimeType?: 'jpeg' | 'webp'; 5 | maxSize?: number; 6 | minSize?: number; 7 | }; 8 | export type CompressResultType = { 9 | file?: File; 10 | url?: string; 11 | quality?: number; 12 | width?: number; 13 | height?: number; 14 | }; 15 | export declare const file: { 16 | /** 17 | * 根据文件获取本地可预览的图片路径 18 | * @param {Object} file 19 | */ 20 | getImageUrl(file: File): string; 21 | /** 22 | * 将JS的file对象转为BASE64位字符串,通过then方法回调,参数为base64字符串 23 | * @param {Object} file 24 | */ 25 | dataFileToBase64(file: File): Promise; 26 | /** 27 | * 将base64位格式文件转换为file对象 28 | * @param {Object} base64String base64位格式字符串 29 | * @param {Object} fileName 转换后的文件名字,包含后缀 30 | */ 31 | dataBase64toFile(base64String: string, fileName: string): File; 32 | /** 33 | * 图片压缩方法 34 | * @param {*} file 需要压缩的图片File文件 35 | * @param {*} options 压缩参数 36 | */ 37 | compressImage(file: File, options: CompressOptionsType): Promise; 38 | }; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 so-better 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/common.d.ts: -------------------------------------------------------------------------------- 1 | export type matchingParamType = 'Chinese' | 'chinese' | 'email' | 'username' | 'int+' | 'int-' | 'int' | 'pos' | 'neg' | 'number' | 'phone' | 'idCard' | 'url' | 'IPv4' | 'hex' | 'rgb' | 'rgba' | 'QQ' | 'weixin' | 'plate'; 2 | /** 3 | * 常用方法 4 | */ 5 | export declare const common: { 6 | /** 7 | * 常用判断 8 | * @param {Object} text 要判断的字符串 9 | * @param {Object} param 判断的类型字符串 10 | */ 11 | matchingText(text: string, param: matchingParamType): boolean; 12 | /** 13 | * 根据参数名获取地址栏参数值 14 | * @param {Object} name 15 | */ 16 | getUrlParams(name: string): string | undefined; 17 | /** 18 | * 判断是否空对象 19 | * @param {Object} obj 20 | */ 21 | isEmptyObject(obj: any): boolean; 22 | /** 23 | * 判断两个参数是否相等 24 | * @param {Object} a 25 | * @param {Object} b 26 | */ 27 | equal(a: any, b: any): boolean; 28 | /** 29 | * 是否对象 30 | * @param {Object} val 31 | */ 32 | isObject(val: any): boolean; 33 | /** 34 | * 文本复制 35 | * @param {Object} text 36 | */ 37 | copyText(text: string): Promise; 38 | /** 39 | * 深度克隆 40 | * @param {Object} data 41 | */ 42 | clone(data: T): T; 43 | }; 44 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/style.less: -------------------------------------------------------------------------------- 1 | :root { 2 | --vp-home-hero-name-background: linear-gradient(120deg, #8b1bbf 20%, #308af3); 3 | --vp-home-hero-name-color: transparent; 4 | --vp-c-indigo-1: #1269cd; 5 | --vp-c-indigo-2: #1e79e1; 6 | --vp-c-indigo-3: #308af3; 7 | 8 | --vp-code-block-bg: #f7f8fa; 9 | 10 | h5 > .VPBadge.danger { 11 | font-family: var(--vp-font-family-mono); 12 | } 13 | 14 | &.dark { 15 | --vp-code-block-bg: #1f1f22; 16 | } 17 | } 18 | 19 | .demo-button { 20 | display: inline-flex; 21 | justify-content: center; 22 | align-items: center; 23 | padding: 2px 6px; 24 | border: 1px solid fade(#308af3, 30%); 25 | background: fade(#308af3, 10%); 26 | border-radius: 3px; 27 | font-size: 14px; 28 | color: #308af3; 29 | transition: all 300ms; 30 | 31 | &:not(:disabled):focus-visible { 32 | outline: none; 33 | } 34 | 35 | &:not(:disabled):hover { 36 | cursor: pointer; 37 | background: fade(#308af3, 20%); 38 | } 39 | 40 | &:not(:disabled):active { 41 | border: 1px solid fade(#308af3, 50%); 42 | background: fade(#308af3, 30%); 43 | } 44 | 45 | & + .demo-button { 46 | margin-left: 10px; 47 | } 48 | 49 | &:disabled { 50 | opacity: 0.6; 51 | cursor: not-allowed; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/platform.d.ts: -------------------------------------------------------------------------------- 1 | export type OSResultType = { 2 | Windows: boolean; 3 | WindowsCPU?: 'x64' | 'x32'; 4 | WindowsVersion?: 'win7' | 'win8' | 'win10'; 5 | Mac: boolean; 6 | MacVersion: string; 7 | ios: boolean; 8 | iosVersion: string; 9 | Android: boolean; 10 | AndroidVersion: string; 11 | Linux: boolean; 12 | HarmonyOS: boolean; 13 | Ubuntu: boolean; 14 | }; 15 | export declare const platform: { 16 | language(): string; 17 | /** 18 | * 获取设备类型 19 | */ 20 | device(): { 21 | PC: boolean; 22 | Mobile: boolean; 23 | iPhone: boolean; 24 | Phone: boolean; 25 | iPad: boolean; 26 | Tablet: boolean; 27 | WindowsPhone: boolean; 28 | }; 29 | /** 30 | * 获取浏览器类型 31 | */ 32 | browser(): { 33 | Edge: boolean; 34 | Weixin: boolean; 35 | QQ: boolean; 36 | QQBrowser: boolean; 37 | UC: boolean; 38 | Chrome: boolean; 39 | Firefox: boolean; 40 | Sougou: boolean; 41 | Safari: boolean; 42 | }; 43 | /** 44 | * 获取浏览器内核 45 | */ 46 | browserKernel(): "opera" | "webkit" | "gecko" | undefined; 47 | /** 48 | * 获取操作系统数据 49 | */ 50 | os(): OSResultType; 51 | }; 52 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | lastUpdated: false 3 | aside: false 4 | layout: home 5 | title: 主页 6 | 7 | hero: 8 | name: dap-util 9 | text: 一个轻量的前端JavaScript工具库 10 | tagline: 专注于JavaScript,不关心UI... 11 | image: 12 | src: https://www.so-better.cn/logo.png 13 | width: 400 14 | actions: 15 | - theme: brand 16 | text: Get Started 17 | link: /guide/introduction 18 | - theme: alt 19 | text: View on GitHub 20 | link: https://github.com/so-better/dap-util 21 | 22 | features: 23 | - title: '@kaitify/core' 24 | details: 基于原生JS的富文本编辑器核心库 25 | link: https://www.so-better.cn/docs/kaitify-core/ 26 | - title: '@kaitify/vue' 27 | details: 基于kaitify开发的vue富文本编辑器核心库 28 | link: https://www.so-better.cn/docs/kaitify-vue/ 29 | - title: '@kaitify/react' 30 | details: 基于kaitify开发的react富文本编辑器核心库 31 | link: https://www.so-better.cn/docs/kaitify-react/ 32 | - title: animator-clip 33 | details: 一个基于 JavaScript 的 requestAnimationFrame API 封装的轻量级 JS 动画插件 34 | link: https://www.so-better.cn/docs/animator-clip/ 35 | - title: rem-fit 36 | details: 一款使用rem适配web页面的轻量级插件 37 | link: https://www.so-better.cn/docs/rem-fit/ 38 | - title: ruax 39 | details: 一个轻量级的Javascript异步数据请求库 40 | link: https://www.so-better.cn/docs/ruax/ 41 | --- 42 | -------------------------------------------------------------------------------- /docs/guide/install.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 安装 3 | --- 4 | 5 | # 安装 6 | 7 | ## 下载 dap-util 本地到使用 8 | 9 | - 下载地址:[dap-util](https://registry.npmmirror.com/dap-util/download/dap-util-1.6.0.tgz) 10 | - 下载完成后最终解压得到一个 package 文件夹,进入 package 文件夹后,将 package 目录下的整个 lib 目录拷贝到你的项目下 11 | - 在 html 页面中引入 js 12 | 13 | ```html 14 | 15 | 16 | ``` 17 | 18 | ```html 19 | 20 | 23 | ``` 24 | 25 | ## 通过 CDN 使用 dap-util 26 | 27 | 你可以借助 `script` 标签直接通过 CDN 来使用 `dap-util` 28 | 29 | ```html 30 | 31 | 32 | 33 | 34 | ``` 35 | 36 | ```html 37 | 38 | 41 | ``` 42 | 43 | ## 通过 npm/yarn/pnpm 安装 dap-util 44 | 45 | > 假设你已了解关于  html、css  和  javascript  的中级知识,并且对于 npm,es6,webpack 已经有了足够的了解,我们更推荐这类安装方式 46 | 47 | ::: code-group 48 | 49 | ```bash [npm] 50 | npm install dap-util 51 | 52 | # 安装指定版本 53 | npm install dap-util@1.6.0 54 | ``` 55 | 56 | ```bash [yarn] 57 | yarn install dap-util 58 | 59 | # 安装指定版本 60 | yarn install dap-util@1.6.0 61 | ``` 62 | 63 | ```bash [pnpm] 64 | pnpm install dap-util 65 | 66 | # 安装指定版本 67 | pnpm install dap-util@1.6.0 68 | ``` 69 | 70 | ::: 71 | -------------------------------------------------------------------------------- /src/data.ts: -------------------------------------------------------------------------------- 1 | const dataName: string = '_dap-datas' 2 | 3 | export type DataHTMLElement = HTMLElement & { 4 | [key: string]: any 5 | } 6 | 7 | export type DataWindow = Window & { 8 | [key: string]: any 9 | } 10 | 11 | export type DataDocument = Document & { 12 | [key: string]: any 13 | } 14 | 15 | /** 16 | * 元素数据挂载方法 17 | */ 18 | export const data = { 19 | /** 20 | * 移除指定数据 21 | * @param {Object} el 22 | * @param {Object} key 23 | */ 24 | remove(el: DataHTMLElement | DataWindow | DataDocument, key?: string) { 25 | const data = el[dataName] || {} 26 | if (key) { 27 | delete data[key] 28 | el[dataName] = data 29 | } else { 30 | el[dataName] = {} 31 | } 32 | }, 33 | 34 | /** 35 | * 判断是否含有指定数据 36 | * @param {Object} el 37 | * @param {Object} key 38 | */ 39 | has(el: DataHTMLElement | DataWindow | DataDocument, key: string): boolean { 40 | return (el[dataName] || {}).hasOwnProperty(key) 41 | }, 42 | 43 | /** 44 | * 获取元素指定数据 45 | * @param {Object} el 46 | * @param {Object} key 47 | */ 48 | get(el: DataHTMLElement | DataWindow | DataDocument, key?: string) { 49 | const data = el[dataName] || {} 50 | return (!!key ? data[key] : data) as T 51 | }, 52 | 53 | /** 54 | * 设置元素指定数据 55 | * @param {Object} el 56 | * @param {Object} key 57 | * @param {Object} value 58 | */ 59 | set(el: DataHTMLElement | DataWindow | DataDocument, key: string, value: any) { 60 | const data = el[dataName] || {} 61 | data[key] = value 62 | el[dataName] = data 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /docs/guide/modules/color.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: color 3 | --- 4 | 5 | # color 6 | 7 | 颜色模块 8 | 9 | ## rgb2hsv() 10 | 11 | RGB 颜色值转 HSV 颜色值 12 | 13 | - 类型 14 | 15 | ```ts 16 | rgb2hsv(rgb: number[]): number[] 17 | ``` 18 | 19 | - 详细信息 20 | 21 | 该方法接收一个长度为 3 的数字数组,如 `[255,0,0]`,表示 RGB 颜色值,最终返回一个长度为 3 的数字数组,表示 HSV 颜色值 22 | 23 | - 示例 24 | 25 | ```ts 26 | import { color } from 'dap-util' 27 | const hsv = color.rgb2hsv([255, 0, 0]) //[0, 100, 100] 28 | ``` 29 | 30 | ## hsv2rgb() 31 | 32 | HSV 颜色值转 RGB 颜色值 33 | 34 | - 类型 35 | 36 | ```ts 37 | hsv2rgb(hsv: number[]): number[] 38 | ``` 39 | 40 | - 详细信息 41 | 42 | 该方法接收一个长度为 3 的数字数组,如 `[0,100,100]`,表示 HSV 颜色值,最终返回一个长度为 3 的数字数组,表示 RGB 颜色值 43 | 44 | - 示例 45 | 46 | ```ts 47 | import { color } from 'dap-util' 48 | const rgb = color.hsv2rgb([0, 100, 100]) //[255, 0, 0] 49 | ``` 50 | 51 | ## rgb2hex() 52 | 53 | RGB 颜色值转 HEX 颜色值 54 | 55 | - 类型 56 | 57 | ```ts 58 | rgb2hex(rgb: number[]): string 59 | ``` 60 | 61 | - 详细信息 62 | 63 | 该方法接收一个长度为 3 的数字数组,如 `[255,0,0]`,表示 RGB 颜色值,最终返回一个字符串,表示 HEX 颜色值,即十六进制颜色值 64 | 65 | - 示例 66 | 67 | ```ts 68 | import { color } from 'dap-util' 69 | const hex = color.rgb2hex([255, 0, 0]) //#ff0000 70 | ``` 71 | 72 | ## hex2rgb() 73 | 74 | HEX 颜色值转 RGB 颜色值 75 | 76 | - 类型 77 | 78 | ```ts 79 | hex2rgb(hex: string): number[] 80 | ``` 81 | 82 | - 详细信息 83 | 84 | 该方法接收一个字符串,表示 HEX 颜色值,即十六进制颜色值,最终返回一个长度为 3 的数字数组,表示 RGB 颜色值 85 | 86 | - 示例 87 | 88 | ```ts 89 | import { color } from 'dap-util' 90 | const rgb = color.hex2rgb('#ff0000') //[255, 0, 0] 91 | ``` 92 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/_metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "8ac9bc4a", 3 | "configHash": "b656fdc5", 4 | "lockfileHash": "51eac44b", 5 | "browserHash": "c5c37062", 6 | "optimized": { 7 | "vue": { 8 | "src": "../../../../node_modules/vue/dist/vue.runtime.esm-bundler.js", 9 | "file": "vue.js", 10 | "fileHash": "3cfad5fa", 11 | "needsInterop": false 12 | }, 13 | "vitepress > @vue/devtools-api": { 14 | "src": "../../../../node_modules/@vue/devtools-api/dist/index.js", 15 | "file": "vitepress___@vue_devtools-api.js", 16 | "fileHash": "f7e4ff4b", 17 | "needsInterop": false 18 | }, 19 | "vitepress > @vueuse/core": { 20 | "src": "../../../../node_modules/@vueuse/core/index.mjs", 21 | "file": "vitepress___@vueuse_core.js", 22 | "fileHash": "b0e25ed2", 23 | "needsInterop": false 24 | }, 25 | "vitepress > @vueuse/integrations/useFocusTrap": { 26 | "src": "../../../../node_modules/@vueuse/integrations/useFocusTrap.mjs", 27 | "file": "vitepress___@vueuse_integrations_useFocusTrap.js", 28 | "fileHash": "b8598fa6", 29 | "needsInterop": false 30 | }, 31 | "vitepress > mark.js/src/vanilla.js": { 32 | "src": "../../../../node_modules/mark.js/src/vanilla.js", 33 | "file": "vitepress___mark__js_src_vanilla__js.js", 34 | "fileHash": "de967aaf", 35 | "needsInterop": false 36 | }, 37 | "vitepress > minisearch": { 38 | "src": "../../../../node_modules/minisearch/dist/es/index.js", 39 | "file": "vitepress___minisearch.js", 40 | "fileHash": "fd3d6556", 41 | "needsInterop": false 42 | } 43 | }, 44 | "chunks": { 45 | "chunk-YJ6QP2VR": { 46 | "file": "chunk-YJ6QP2VR.js" 47 | }, 48 | "chunk-LW4I4DCF": { 49 | "file": "chunk-LW4I4DCF.js" 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /docs/guide/modules/speech.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: speech 3 | --- 4 | 5 | # speech 6 | 7 | 语音合成模块 8 | 9 | ## start() 10 | 11 | 将文字加入语音播报队列 12 | 13 | - 类型 14 | 15 | ```ts 16 | start(text: string, options?: SpeechParamsType): void 17 | ``` 18 | 19 | - 详细信息 20 | 21 | 第一个入参表示要播报的文字内容,第二个入参表示播报相关的配置属性,包含如下属性: 22 | 23 | - pitch:话语的音调,类型为 `number` 24 | - rate:说话的速度,类型为 `number` 25 | - volume:说话的音量,,类型为 `number`,取值为 0-1 26 | - start:播放开始事件,类型为 `(e: Event, options: EventParamsType) => void` 27 | - end:播放结束事件,类型为 `(e: Event, options: EventParamsType) => void` 28 | - pause:播放暂停事件,类型为 `(e: Event, options: EventParamsType) => void` 29 | - resume:播放恢复事件,类型为 `(e: Event, options: EventParamsType) => void` 30 | - error:播放出错事件,类型为 `(e: Event, options: EventParamsType) => void` 31 | 32 | 上述几个播放事件的第二个参数都是表示当前播放的配置,包含 `text` `pitch` `rate` `volume` 四个属性 33 | 34 | - 示例 35 | 36 | ```ts 37 | import { speech } from 'dap-util' 38 | speech.start('hello') 39 | ``` 40 | 41 | ## stop() 42 | 43 | 停止所有播报队列里面的语音 44 | 45 | - 类型 46 | 47 | ```ts 48 | stop(): void 49 | ``` 50 | 51 | - 详细信息 52 | 53 | 该方法会停止播报队列里所有的语音 54 | 55 | - 示例 56 | 57 | ```ts 58 | import { speech } from 'dap-util' 59 | speech.stop() 60 | ``` 61 | 62 | ## pause() 63 | 64 | 暂停播报 65 | 66 | - 类型 67 | 68 | ```ts 69 | pause(): void 70 | ``` 71 | 72 | - 详细信息 73 | 74 | 该方法会暂停当前的语音播报 75 | 76 | - 示例 77 | 78 | ```ts 79 | import { speech } from 'dap-util' 80 | speech.pause() 81 | ``` 82 | 83 | ## resume() 84 | 85 | 恢复暂停的播报 86 | 87 | - 类型 88 | 89 | ```ts 90 | resume(): void 91 | ``` 92 | 93 | - 详细信息 94 | 95 | 该方法会恢复之前暂停的播报 96 | 97 | - 示例 98 | 99 | ```ts 100 | import { speech } from 'dap-util' 101 | speech.resume() 102 | ``` 103 | -------------------------------------------------------------------------------- /src/string.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 字符串操作 3 | */ 4 | export const string = { 5 | /** 6 | * 向指定位置插入字符串 7 | * @param {Object} original 原始字符串 8 | * @param {Object} str 插入的字符串 9 | * @param {Object} index 插入的位置 10 | */ 11 | insert(original: string, str: string, index: number) { 12 | if (index < 0) { 13 | throw new Error('The third argument cannot be less than 0') 14 | } 15 | return original.substring(0, index) + str + original.substring(index, original.length) 16 | }, 17 | 18 | /** 19 | * 删除指定位置的字符串 20 | * @param {Object} original 原始字符串 21 | * @param {Object} index 删除的位置序列 22 | * @param {Object} num 删除的字符串长度 23 | */ 24 | delete(original: string, index: number, num: number) { 25 | if (index < 0) { 26 | throw new Error('The second argument cannot be less than 0') 27 | } 28 | if (num < 0) { 29 | throw new Error('The third argument cannot be less than 0') 30 | } 31 | return original.substring(0, index) + original.substring(index + num, original.length) 32 | }, 33 | 34 | /** 35 | * 替换指定位置的字符串 36 | * @param {Object} original 原始字符串 37 | * @param {Object} start 开始位置 38 | * @param {Object} end 结束位置 39 | * @param {Object} str 替换的字符串 40 | */ 41 | replace(original: string, start: number, end: number, str: string) { 42 | if (start < 0) { 43 | throw new Error('The second argument cannot be less than 0') 44 | } 45 | if (end < 0) { 46 | throw new Error('The third argument cannot be less than 0') 47 | } 48 | return original.substring(0, start) + str + original.substring(end, original.length) 49 | }, 50 | 51 | /** 52 | * 去除字符串空格 53 | * @param {Object} str 原始字符串 54 | * @param {Object} global 为true时去除所有空格,否则只去除两边空格 55 | */ 56 | trim(str: string, global?: boolean) { 57 | let result = str.replace(/(^\s+)|(\s+$)/g, '') 58 | if (global) { 59 | result = result.replace(/\s/g, '') 60 | } 61 | return result 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /docs/guide/modules/data.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: data 3 | --- 4 | 5 | # data 6 | 7 | 数据模块 8 | 9 | ## set() 10 | 11 | 在 `dom` 元素或者 `window` 或者 `Document` 上绑定指定数据 12 | 13 | - 类型 14 | 15 | ```ts 16 | set(el: HTMLElement | Window | Document, key: string, value: any): void 17 | ``` 18 | 19 | - 详细信息 20 | 21 | 第一个入参表示`dom` 元素或者 `window` 或者 `Document`,第二个入参表示绑定数据的 `key` 值,第三个参数表示绑定的数据 22 | 23 | 该方法会将指定数据绑定到 `dom` 元素或者 `window` 或者 `Document` 上 24 | 25 | - 示例 26 | 27 | ```ts 28 | import { data } from 'dap-util' 29 | data.set(document.body, 'name', 'jack') 30 | ``` 31 | 32 | ## get() 33 | 34 | 从 `dom` 元素或者 `window` 或者 `Document` 上获取绑定的指定数据 35 | 36 | - 类型 37 | 38 | ```ts 39 | get(el: HTMLElement | Window | Document, key?: string): T 40 | ``` 41 | 42 | - 详细信息 43 | 44 | 第一个入参表示`dom` 元素或者 `window` 或者 `Document`,第二个入参表示需要获取的数据的 `key` 值,如果没有设置第二个参数,则默认返回绑定的全部数据 45 | 46 | - 示例 47 | 48 | ```ts 49 | import { data } from 'dap-util' 50 | const name = data.get(document.body, 'name') //jack 51 | ``` 52 | 53 | ## has() 54 | 55 | 判断 `dom` 元素或者 `window` 或者 `Document` 上是否存在绑定的数据 56 | 57 | - 类型 58 | 59 | ```ts 60 | has(el: HTMLElement | Window | Document, key: string): boolean 61 | ``` 62 | 63 | - 详细信息 64 | 65 | 第一个入参表示`dom` 元素或者 `window` 或者 `Document`,第二个入参表示需要获取的数据的 `key` 值 66 | 67 | 该方法会判断指定 `key` 值的数据是否在 `dom` 元素或者 `window` 或者 `Document` 上,返回 `boolean` 值 68 | 69 | - 示例 70 | 71 | ```ts 72 | import { data } from 'dap-util' 73 | data.has(document.body, 'name') //true 74 | ``` 75 | 76 | ## remove() 77 | 78 | 在 `dom` 元素或者 `window` 或者 `Document` 上移除指定数据 79 | 80 | - 类型 81 | 82 | ```ts 83 | remove(el: HTMLElement | Window | Document, key?: string): void 84 | ``` 85 | 86 | - 详细信息 87 | 88 | 第一个入参表示`dom` 元素或者 `window` 或者 `Document`,第二个入参表示需要移除的数据的 `key` 值,如果没有设置第二个参数,则默认移除绑定的全部数据 89 | 90 | - 示例 91 | 92 | ```ts 93 | import { data } from 'dap-util' 94 | data.remove(document.body, 'name') 95 | ``` 96 | -------------------------------------------------------------------------------- /docs/guide/modules/string.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: string 3 | --- 4 | 5 | # string 6 | 7 | 字符串模块 8 | 9 | ## insert() 10 | 11 | 向指定位置插入字符串 12 | 13 | - 类型 14 | 15 | ```ts 16 | insert(original: string, str: string, index: number): string 17 | ``` 18 | 19 | - 详细信息 20 | 21 | 第一个入参表示源字符串,第二个参数表示需要插入的字符串,第三个参数表示插入的位置 22 | 23 | 该方法会将指定字符串插入到源字符串中的指定位置,返回新的字符串 24 | 25 | - 示例 26 | 27 | ```ts 28 | import { string } from 'dap-util' 29 | string.insert('这是一个美好的世界', '且充满无限可能', 6) // 这是一个美好且充满无限可能的世界 30 | ``` 31 | 32 | ## delete() 33 | 34 | 删除指定位置的字符串 35 | 36 | - 类型 37 | 38 | ```ts 39 | delete(original: string, index: number, num: number): string 40 | ``` 41 | 42 | - 详细信息 43 | 44 | 第一个入参表示源字符串,第二个参数表示从哪个位置删除,第三个参数表示删除字符串的长度 45 | 46 | 该方法会在删除指定位置的字符串后返回新的字符串 47 | 48 | - 示例 49 | 50 | ```ts 51 | import { string } from 'dap-util' 52 | string.delete('这是一个美好且充满无限可能的世界', 6, 7) //这是一个美好的世界 53 | ``` 54 | 55 | ## replace() 56 | 57 | 替换指定位置的字符串 58 | 59 | - 类型 60 | 61 | ```ts 62 | replace(original: string, start: number, end: number, str: string): string 63 | ``` 64 | 65 | - 详细信息 66 | 67 | 第一个入参表示源字符串,第二个参数表示替换开始的位置,第三个参数表示替换截止的位置,第四个参数表示替换的字符串 68 | 69 | 该方法会将原字符串从开始位置到截止为止之间的字符串删除,然后将替换的字符串插入其中 70 | 71 | - 示例 72 | 73 | ```ts 74 | import { string } from 'dap-util' 75 | string.replace('这是一个美好且充满无限可能的世界', 4, 13, '糟糕') //这是一个糟糕的世界 76 | ``` 77 | 78 | ## trim() 79 | 80 | 去除字符串的空格 81 | 82 | - 类型 83 | 84 | ```ts 85 | trim(str: string, global?: boolean): string 86 | ``` 87 | 88 | - 详细信息 89 | 90 | 第一个入参表示需要去除空格的字符串,第二个入参表示是否去去除全部的空格,如果为 `false` 或者不设置则只去除字符串两侧的空格 91 | 92 | 该方法会返回去除空格后的新字符串 93 | 94 | - 示例 95 | 96 | ```ts 97 | import { string } from 'dap-util' 98 | //去除两边的空格 99 | const result = string.trim(' he llo ') // 'he llo' 100 | //去除所有空格 101 | const result = string.trim(' he llo ', true) // 'hello' 102 | ``` 103 | -------------------------------------------------------------------------------- /docs/guide/modules/event.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: event 3 | --- 4 | 5 | # event 6 | 7 | 事件模块 8 | 9 | ## on() 10 | 11 | 给元素绑定事件 12 | 13 | - 类型 14 | 15 | ```ts 16 | on(el: HTMLElement | Window | Document, eventName: string, fn: (e: Event) => void, options?: AddEventListenerOptions): void 17 | ``` 18 | 19 | - 详细信息 20 | 21 | 第一个入参表示绑定事件的元素,同时支持 `window` 和 `document`;第二个入参表示事件名称,如“click”;第三个参数是可选参数,表示事件修饰参数,等同于 `addEventListener` 函数的第三个参数 22 | 23 | 第二个参数在设置事件名称时,支持设置事件修饰名称,如“click.a”,后面的“a”就是修饰名称,同时也支持绑定多个事件,每个事件之间通过空格隔开 24 | 25 | - 示例 26 | 27 | ```ts 28 | import { event } from 'dap-util' 29 | //设置事件 30 | event.on(document.body, 'click', e => { 31 | console.log('click') 32 | }) 33 | //设置事件修饰名称 34 | event.on(document.body, 'mousedown.a', e => { 35 | console.log('mousedown.a') 36 | }) 37 | //绑定多个事件 38 | event.on(document.body, 'mousedown click.a', e => { 39 | console.log('mousedown.a') 40 | }) 41 | ``` 42 | 43 | ## off() 44 | 45 | 解绑元素事件 46 | 47 | - 类型 48 | 49 | ```ts 50 | off(el: HTMLElement | Window | Document, eventName?: string): void 51 | ``` 52 | 53 | - 详细信息 54 | 55 | 第一个入参表示解绑事件的元素,同时支持 `window` 和 `document`;第二个入参表示事件名称,如“click” 56 | 57 | 第二个参数支持设置事件修饰名称,如“click.a”,后面的“a”就是修饰名称,同时也支持解绑多个事件,每个事件之间通过空格隔开 58 | 59 | - 示例 60 | 61 | ```ts 62 | import { event } from 'dap-util' 63 | //解绑事件 64 | event.off(document.body, 'click') 65 | //解绑带事件修饰名称的事件 66 | event.off(document.body, 'mousedown.a') 67 | //同时解绑多个事件 68 | event.off(document.body, 'mousedown click.a') 69 | ``` 70 | 71 | ## get() 72 | 73 | 获取元素绑定的所有事件 74 | 75 | - 类型 76 | 77 | ```ts 78 | get(el: HTMLElement | Window | Document): EventObjType | undefined 79 | ``` 80 | 81 | - 详细信息 82 | 83 | 提供一个入参,表示获取事件的元素,同时支持 `window` 和 `document`,该方法返回一个对象,每个 key 表示带修饰名称的事件名称,每个值表示事件配置,包含 `type`(事件名,如“click”) `fn`(执行函数) `options`(在 on 方法中设置的第三个参数)三个属性 84 | 85 | - 示例 86 | 87 | ```ts 88 | import { event } from 'dap-util' 89 | const events = event.get(document.body) 90 | ``` 91 | -------------------------------------------------------------------------------- /docs/guide/modules/file.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: file 3 | --- 4 | 5 | # file 6 | 7 | 文件模块 8 | 9 | ## getImageUrl() 10 | 11 | 根据文件获取本地可预览的图片路径 12 | 13 | - 类型 14 | 15 | ```ts 16 | getImageUrl(file: File): string 17 | ``` 18 | 19 | - 详细信息 20 | 21 | 提供一个入参,类型为 `File`,该方法返回该文件的本地可预览地址 22 | 23 | - 示例 24 | 25 | ```ts 26 | import { file } from 'dap-util' 27 | const url = file.getImageUrl(imageFile) 28 | ``` 29 | 30 | ## dataFileToBase64() 31 | 32 | `file` 对象转 `base64` 字符串 33 | 34 | - 类型 35 | 36 | ```ts 37 | dataFileToBase64(file: File): Promise 38 | ``` 39 | 40 | - 详细信息 41 | 42 | 提供一个入参,类型为 `File`,表示需要转换的文件对象,该方法返回一个 `Promise` 对象,通过该对象回调可以获得 `base64` 字符串 43 | 44 | - 示例 45 | 46 | ```ts 47 | import { file } from 'dap-util' 48 | const base64String = await file.dataFileToBase64(imageFile) 49 | //或者 50 | file.dataFileToBase64(imageFile).then(res => { 51 | const base64String = res 52 | }) 53 | ``` 54 | 55 | ## dataBase64toFile() 56 | 57 | `base64` 字符串转 `file` 对象 58 | 59 | - 类型 60 | 61 | ```ts 62 | dataBase64toFile(base64String: string, fileName: string): File 63 | ``` 64 | 65 | - 详细信息 66 | 67 | 第一个入参表示需要转换的 `base64` 字符串,第二个入参表示转换成文件的文件名称,该方法返回一个 `File` 对象 68 | 69 | - 示例 70 | 71 | ```ts 72 | import { file } from 'dap-util' 73 | const image = file.dataBase64toFile(,'image.png') 74 | ``` 75 | 76 | ## compressImage() 77 | 78 | 图片压缩 79 | 80 | - 类型 81 | 82 | ```ts 83 | compressImage(file: File, options: CompressOptionsType): Promise 84 | ``` 85 | 86 | - 详细信息 87 | 88 | 第一个入参表示需要压缩的图片文件,第二个入参是压缩的相关配置,具体包含如下属性: 89 | 90 | - `width`:类型为 `number`,表示压缩图片的宽,单位 `px`,如果不设置默认为原图宽 91 | - `quality`:类型为 `number`,取值 0-1,表示压缩图片质量,默认为原图的 0.8 92 | - `mimeType`:压缩图片的类型,可取值 `jpeg` `webp`,默认为 `jpeg` 93 | - `maxSize`:类型为 `number`,表示压缩后的最大值,单位 `kb`,默认为 0 表示不设置此值 94 | - `minSize`:类型为 `number`,表示压缩后的最小值,小于该大小的图片不进行压缩,单位 `kb`,默认为 0 表示任何图片都要压缩 95 | 96 | - 示例 97 | 98 | ```ts 99 | import { file } from 'dap-util' 100 | const newImage = file.compressImage(image, { 101 | width: 800, 102 | quality: 0.5 103 | }) 104 | ``` 105 | -------------------------------------------------------------------------------- /docs/guide/modules/number.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: number 3 | --- 4 | 5 | # number 6 | 7 | 数字模块 8 | 9 | ## formatNumber() 10 | 11 | 数字格式化 12 | 13 | - 类型 14 | 15 | ```ts 16 | formatNumber(num: number): string 17 | ``` 18 | 19 | - 详细信息 20 | 21 | 提供一个入参,类型为 `number`,表示需要格式化的数字,该方法会返回该数字格式化后的字符串 22 | 23 | 所谓的格式化,即每隔 3 位数给数值加上逗号 24 | 25 | - 示例 26 | 27 | ```ts 28 | import { number } from 'dap-util' 29 | const formatNum = number.formatNumber(100000) //1000,00 30 | ``` 31 | 32 | ## isNumber() 33 | 34 | 判断是否数字 35 | 36 | - 类型 37 | 38 | ```ts 39 | isNumber(num: any): boolean 40 | ``` 41 | 42 | - 详细信息 43 | 44 | 提供一个任意类型的入参,判断是否数字类型,返回 `boolean` 值 45 | 46 | - 示例 47 | 48 | ```ts 49 | import { number } from 'dap-util' 50 | number.isNumber(100) //true 51 | number.isNumber('100') //false 52 | number.isNumber(NaN) //false 53 | ``` 54 | 55 | ## add() 56 | 57 | 多个数的加法运算 58 | 59 | - 类型 60 | 61 | ```ts 62 | add(...values: number[]): number 63 | ``` 64 | 65 | - 详细信息 66 | 67 | 将多个数进行累加,参数可以是任意多个,必须都是数字类型,该方法返回累加后的数值 68 | 69 | - 示例 70 | 71 | ```ts 72 | import { number } from 'dap-util' 73 | number.add(1, 1, 1) //3 74 | number.add(1, 10, 21) //32 75 | ``` 76 | 77 | ## subtract() 78 | 79 | 多个数的减法运算 80 | 81 | - 类型 82 | 83 | ```ts 84 | subtract(...values: number[]): number 85 | ``` 86 | 87 | - 详细信息 88 | 89 | 将多个数进行累减,参数可以是任意多个,必须都是数字类型,该方法返回累减后的数值 90 | 91 | - 示例 92 | 93 | ```ts 94 | import { number } from 'dap-util' 95 | number.subtract(1, 1, 1) //-1 96 | number.subtract(1, 10, 21) //-30 97 | ``` 98 | 99 | ## mutiply() 100 | 101 | 多个数的乘法运算 102 | 103 | - 类型 104 | 105 | ```ts 106 | mutiply(...values: number[]): number 107 | ``` 108 | 109 | - 详细信息 110 | 111 | 将多个数进行累乘,参数可以是任意多个,必须都是数字类型,该方法返回累乘后的数值 112 | 113 | - 示例 114 | 115 | ```ts 116 | import { number } from 'dap-util' 117 | number.mutiply(1, 1, 1) //1 118 | number.mutiply(1, 10, 21) //210 119 | ``` 120 | 121 | ## divide() 122 | 123 | 多个数的除法运算 124 | 125 | - 类型 126 | 127 | ```ts 128 | divide(...values: number[]): number 129 | ``` 130 | 131 | - 详细信息 132 | 133 | 将多个数进行累除,参数可以是任意多个,必须都是数字类型,该方法返回累除后的数值 134 | 135 | - 示例 136 | 137 | ```ts 138 | import { number } from 'dap-util' 139 | number.divide(9, 3, 2) //1.5 140 | number.divide(100, 1000, 2) //0.05 141 | ``` 142 | -------------------------------------------------------------------------------- /src/number.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 数字相关方法 3 | */ 4 | export const number = { 5 | /** 6 | * 数字格式化 7 | * @param {Number} num 8 | */ 9 | formatNumber(num: number) { 10 | if (this.isNumber(num)) { 11 | return num.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') 12 | } 13 | return num.toString() 14 | }, 15 | 16 | /** 17 | * 判断是否数字 18 | * @param {Object} num 19 | */ 20 | isNumber(num: any) { 21 | if (typeof num == 'number' && !isNaN(num)) { 22 | return true 23 | } 24 | return false 25 | }, 26 | 27 | /** 28 | * 多个数的加法运算 29 | */ 30 | add(...values: number[]) { 31 | return values.reduce((num, value) => { 32 | let r1 = 0 33 | let r2 = 0 34 | let m = 0 35 | try { 36 | r1 = num.toString().split('.')[1].length 37 | } catch (e) {} 38 | try { 39 | r2 = value.toString().split('.')[1].length 40 | } catch (e) {} 41 | m = Math.pow(10, Math.max(r1, r2)) 42 | return (num * m + value * m) / m 43 | }) 44 | }, 45 | 46 | /** 47 | * 多个数的减法运算 48 | */ 49 | subtract(...values: number[]) { 50 | return values.reduce((num, value) => { 51 | let r1 = 0 52 | let r2 = 0 53 | let m = 0 54 | try { 55 | r1 = num.toString().split('.')[1].length 56 | } catch (e) {} 57 | try { 58 | r2 = value.toString().split('.')[1].length 59 | } catch (e) {} 60 | m = Math.pow(10, Math.max(r1, r2)) 61 | return (num * m - value * m) / m 62 | }) 63 | }, 64 | 65 | /** 66 | * 多个数的乘法运算 67 | */ 68 | mutiply(...values: number[]) { 69 | return values.reduce((num, value) => { 70 | let m = 0 71 | let s1 = num.toString() 72 | let s2 = value.toString() 73 | try { 74 | m += s1.split('.')[1].length 75 | } catch (e) {} 76 | try { 77 | m += s2.split('.')[1].length 78 | } catch (e) {} 79 | return (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) / Math.pow(10, m) 80 | }) 81 | }, 82 | 83 | /** 84 | * 多个数的除法运算 85 | */ 86 | divide(...values: number[]) { 87 | return values.reduce((num, value) => { 88 | let t1 = 0 89 | let t2 = 0 90 | let s1 = num.toString() 91 | let s2 = value.toString() 92 | try { 93 | t1 = s1.split('.')[1].length 94 | } catch (e) {} 95 | try { 96 | t2 = s2.split('.')[1].length 97 | } catch (e) {} 98 | return (Number(s1.replace('.', '')) / Number(s2.replace('.', ''))) * Math.pow(10, t2 - t1) 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /docs/guide/modules/platform.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: platform 3 | --- 4 | 5 | # platform 6 | 7 | 平台终端模块 8 | 9 | ## language() 10 | 11 | 获取设备语言类型 12 | 13 | - 类型 14 | 15 | ```ts 16 | language(): string 17 | ``` 18 | 19 | - 详细信息 20 | 21 | 该方法返回当前浏览器的语言类型 22 | 23 | - 示例 24 | 25 | ```ts 26 | import { platform } from 'dap-util' 27 | platform.language() //zh-CN 28 | ``` 29 | 30 | ## device() 31 | 32 | 获取设备类型 33 | 34 | - 类型 35 | 36 | ```ts 37 | device(): { 38 | PC: boolean; 39 | Mobile: boolean; 40 | iPhone: boolean; 41 | Phone: boolean; 42 | iPad: boolean; 43 | Tablet: boolean; 44 | WindowsPhone: boolean; 45 | } 46 | ``` 47 | 48 | - 详细信息 49 | 50 | 该方法会判断当前浏览器所在的设备类型,并返回一个对象,该对象包含如下属性: 51 | 52 | - `PC`:是否 `PC` 电脑端 53 | - `Mobile`:是否移动端 54 | - `iPhone`:是否 `iPhone` 55 | - `Phone`:是否手机 56 | - `iPad`:是否 `iPad` 57 | - `Tablet`:是否平板电脑 58 | - `WindowsPhone`:是否 `s` 系统手机 59 | 60 | - 示例 61 | 62 | ```ts 63 | import { platform } from 'dap-util' 64 | platform.device() //{ PC: true, Mobile: false, iPhone: false, Phone: false, iPad: false, Tablet: false, WindowsPhone: false} 65 | ``` 66 | 67 | ## browser() 68 | 69 | 获取浏览器类型 70 | 71 | - 类型 72 | 73 | ```ts 74 | browser(): { 75 | Edge: boolean; 76 | Weixin: boolean; 77 | QQ: boolean; 78 | QQBrowser: boolean; 79 | UC: boolean; 80 | Chrome: boolean; 81 | Firefox: boolean; 82 | Sougou: boolean; 83 | Safari: boolean; 84 | } 85 | ``` 86 | 87 | - 详细信息 88 | 89 | 该方法会判断当前浏览器类型,并返回一个对象,该对象包含如下属性: 90 | 91 | - `Edge`:是否 `edge` 浏览器 92 | - `Weixin`:是否微信内置浏览器 93 | - `QQ`:是否 `QQ` 内置浏览器 94 | - `QQBrowser`:是否 `QQ` 浏览器 95 | - `UC`:是否 `UC` 浏览器 96 | - `Chrome`:是否谷歌浏览器 97 | - `Firefox`:是否火狐浏览器 98 | - `Sougou`:是否搜狗浏览器 99 | - `Safari`:是否 `safari` 浏览器 100 | 101 | - 示例 102 | 103 | ```ts 104 | import { platform } from 'dap-util' 105 | platform.browser() //{ Edge: false, Weixin: false, QQ: false, QQBrowser: false, UC: false, Chrome: true, Firefox: false, Sougou: false, Safari: false } 106 | ``` 107 | 108 | ## browserKernel() 109 | 110 | 获取浏览器内核 111 | 112 | - 类型 113 | 114 | ```ts 115 | browserKernel(): "opera" | "webkit" | "gecko" | undefined 116 | ``` 117 | 118 | - 详细信息 119 | 120 | 该方法用于获取当前浏览器内核,返回值可取值为 `opera` `webkit` `gecko`,如果内核都不是这三者,则返回 `undefined` 121 | 122 | - 示例 123 | 124 | ```ts 125 | import { platform } from 'dap-util' 126 | platform.browserKernel() //webkit 127 | ``` 128 | 129 | ## os() 130 | 131 | 获取操作系统数据 132 | 133 | - 类型 134 | 135 | ```ts 136 | os(): OSResultType 137 | ``` 138 | 139 | - 详细信息 140 | 141 | 该方法会返回当前浏览器所在系统的数据,包含如下属性: 142 | 143 | - `Windows`:是否 `windows` 系统 144 | - `WindowsCPU`:`windows` 系统的 cpu 类型,值为“x64”或者“x32” 145 | - `WindowsVersion`:`windows` 系统的版本,可取值 `win7` `win8` `win10` 146 | - Mac:是否 `Mac` 系统 147 | - MacVersion:`Mac` 系统版本号 148 | - ios:是否 `ios` 系统 149 | - iosVersion:`ios` 系统版本号 150 | - Android:是否安卓系统 151 | - AndroidVersion:安卓系统版本号 152 | - Linux:是否 `linux` 系统 153 | - HarmonyOS:是否鸿蒙系统 154 | - Ubuntu:是否 `unbuntu` 系统 155 | 156 | - 示例 157 | 158 | ```ts 159 | import { platform } from 'dap-util' 160 | const { Mac } = platform.os() //判断是否Mac系统 161 | ``` 162 | -------------------------------------------------------------------------------- /src/speech.ts: -------------------------------------------------------------------------------- 1 | export type EventParamsType = { 2 | text: string 3 | pitch: number 4 | rate: number 5 | volume: number 6 | } 7 | 8 | export type SpeechParamsType = { 9 | //话语的音调 10 | pitch?: number 11 | //说话的速度 12 | rate?: number 13 | //说话的音量:0-1 14 | volume?: number 15 | //播放开始事件 16 | start?: (e: Event, options: EventParamsType) => void 17 | //播放结束事件 18 | end?: (e: Event, options: EventParamsType) => void 19 | //播放暂停事件 20 | pause?: (e: Event, options: EventParamsType) => void 21 | //播放恢复事件 22 | resume?: (e: Event, options: EventParamsType) => void 23 | //播放出错事件 24 | error?: (e: Event, options: EventParamsType) => void 25 | } 26 | 27 | /** 28 | * 语音合成方法 29 | */ 30 | export const speech = { 31 | /** 32 | * 将文字加入语音播报队列 33 | * @param {Object} text 34 | * @param {Object} options 35 | */ 36 | start(text: string, options?: SpeechParamsType) { 37 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) { 38 | throw new Error('The current browser does not support this syntax') 39 | } 40 | const speech = new SpeechSynthesisUtterance() 41 | speech.text = text 42 | speech.pitch = options?.pitch ?? 0.8 43 | speech.rate = options?.rate ?? 1 44 | speech.volume = options?.volume ?? 1 45 | speech.lang = 'zh-CN' 46 | //播放开始后调用 47 | speech.onstart = event => { 48 | options?.start?.apply(speech, [ 49 | event, 50 | { 51 | text: text, 52 | pitch: options.pitch ?? 0.8, 53 | rate: options.rate ?? 1, 54 | volume: options.volume ?? 1 55 | } 56 | ]) 57 | } 58 | //播放结束后调用 59 | speech.onend = event => { 60 | options?.end?.apply(speech, [ 61 | event, 62 | { 63 | text: text, 64 | pitch: options.pitch ?? 0.8, 65 | rate: options.rate ?? 1, 66 | volume: options.volume ?? 1 67 | } 68 | ]) 69 | } 70 | //播放暂停后调用 71 | speech.onpause = event => { 72 | options?.pause?.apply(speech, [ 73 | event, 74 | { 75 | text: text, 76 | pitch: options.pitch ?? 0.8, 77 | rate: options.rate ?? 1, 78 | volume: options.volume ?? 1 79 | } 80 | ]) 81 | } 82 | //播放恢复后调用 83 | speech.onresume = event => { 84 | options?.resume?.apply(speech, [ 85 | event, 86 | { 87 | text: text, 88 | pitch: options.pitch ?? 0.8, 89 | rate: options.rate ?? 1, 90 | volume: options.volume ?? 1 91 | } 92 | ]) 93 | } 94 | //播放出错后调用 95 | speech.onerror = event => { 96 | options?.error?.apply(speech, [ 97 | event, 98 | { 99 | text: text, 100 | pitch: options.pitch ?? 0.8, 101 | rate: options.rate ?? 1, 102 | volume: options.volume ?? 1 103 | } 104 | ]) 105 | } 106 | //加入播放队列 107 | window.speechSynthesis.speak(speech) 108 | }, 109 | /** 110 | * 停止播报,停止所有播报队列里面的语音 111 | */ 112 | stop() { 113 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) { 114 | throw new Error('The current browser does not support this syntax') 115 | } 116 | window.speechSynthesis.cancel() 117 | }, 118 | 119 | /** 120 | * 暂停播报 121 | */ 122 | pause() { 123 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) { 124 | throw new Error('The current browser does not support this syntax') 125 | } 126 | window.speechSynthesis.pause() 127 | }, 128 | 129 | /** 130 | * 恢复暂停的播报 131 | */ 132 | resume() { 133 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) { 134 | throw new Error('The current browser does not support this syntax') 135 | } 136 | window.speechSynthesis.resume() 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/color.ts: -------------------------------------------------------------------------------- 1 | import { common as commonUtil } from './common' 2 | /** 3 | * 颜色相关方法 4 | */ 5 | export const color = { 6 | /** 7 | * rgb转hsv值 8 | * @param {Object} rgb rgb值,数组 9 | */ 10 | rgb2hsv(rgb: number[]) { 11 | if (rgb.length != 3) { 12 | throw new TypeError('Invalid argument') 13 | } 14 | let h = 0 15 | let s = 0 16 | let v = 0 17 | let r = rgb[0] >= 255 ? 255 : rgb[0] 18 | let g = rgb[1] >= 255 ? 255 : rgb[1] 19 | let b = rgb[2] >= 255 ? 255 : rgb[2] 20 | r = r <= 0 ? 0 : r 21 | g = g <= 0 ? 0 : g 22 | b = b <= 0 ? 0 : b 23 | let max = Math.max(r, g, b) 24 | let min = Math.min(r, g, b) 25 | v = max / 255 26 | if (max === 0) { 27 | s = 0 28 | } else { 29 | s = 1 - min / max 30 | } 31 | if (max === min) { 32 | h = 0 //事实上,max===min的时候,h无论为多少都无所谓 33 | } else if (max === r && g >= b) { 34 | h = 60 * ((g - b) / (max - min)) + 0 35 | } else if (max === r && g < b) { 36 | h = 60 * ((g - b) / (max - min)) + 360 37 | } else if (max === g) { 38 | h = 60 * ((b - r) / (max - min)) + 120 39 | } else if (max === b) { 40 | h = 60 * ((r - g) / (max - min)) + 240 41 | } 42 | return [h, s * 100, v * 100] 43 | }, 44 | 45 | /** 46 | * hsv格式值转rgb值 47 | * @param {Object} hsv hsv值,数组 48 | */ 49 | hsv2rgb(hsv: number[]) { 50 | if (hsv.length != 3) { 51 | throw new TypeError('Invalid argument') 52 | } 53 | let h = hsv[0] >= 360 || hsv[0] <= 0 ? 0 : hsv[0] 54 | let s = hsv[1] >= 100 ? 100 : hsv[1] 55 | s = s <= 0 ? 0 : s 56 | let v = hsv[2] >= 100 ? 100 : hsv[2] 57 | v = v <= 0 ? 0 : v 58 | s = s / 100 59 | v = v / 100 60 | let r = 0 61 | let g = 0 62 | let b = 0 63 | let i = parseInt(((h / 60) % 6) + '') 64 | let f = h / 60 - i 65 | let p = v * (1 - s) 66 | let q = v * (1 - f * s) 67 | let t = v * (1 - (1 - f) * s) 68 | switch (i) { 69 | case 0: 70 | r = v 71 | g = t 72 | b = p 73 | break 74 | case 1: 75 | r = q 76 | g = v 77 | b = p 78 | break 79 | case 2: 80 | r = p 81 | g = v 82 | b = t 83 | break 84 | case 3: 85 | r = p 86 | g = q 87 | b = v 88 | break 89 | case 4: 90 | r = t 91 | g = p 92 | b = v 93 | break 94 | case 5: 95 | r = v 96 | g = p 97 | b = q 98 | break 99 | default: 100 | break 101 | } 102 | r = parseInt(r * 255 + '') 103 | g = parseInt(g * 255 + '') 104 | b = parseInt(b * 255 + '') 105 | return [r, g, b] 106 | }, 107 | 108 | /** 109 | * rgb值转十六进制 110 | * @param {Array} rgb rgb值,数组 111 | */ 112 | rgb2hex(rgb: number[]) { 113 | if (rgb.length != 3) { 114 | throw new TypeError('Invalid argument') 115 | } 116 | let r = rgb[0] 117 | let g = rgb[1] 118 | let b = rgb[2] 119 | let hex = '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1) 120 | return hex 121 | }, 122 | 123 | /** 124 | * 十六进制颜色转rgb 125 | * @param {String} hex 十六进制颜色值 126 | */ 127 | hex2rgb(hex: string) { 128 | let color = hex.toLowerCase() 129 | if (!commonUtil.matchingText(color, 'hex')) { 130 | throw new TypeError('The argument must be a hexadecimal color value') 131 | } 132 | //4位数的十六进制颜色值 133 | if (color.length === 4) { 134 | let colorNew = '#' 135 | for (let i = 1; i < 4; i += 1) { 136 | colorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1)) 137 | } 138 | color = colorNew 139 | } 140 | //处理六位的颜色值 141 | let colorChange = [] 142 | for (let i = 1; i < 7; i += 2) { 143 | colorChange.push(parseInt('0x' + color.slice(i, i + 2))) 144 | } 145 | return colorChange 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitepress' 2 | 3 | export default defineConfig({ 4 | base: '/docs/dap-util/', 5 | title: 'dap-util', 6 | description: '一个轻量的前端JavaScript工具库,专注于JavaScript,不关心UI', 7 | lastUpdated: true, 8 | head: [ 9 | ['link', { rel: 'icon', type: 'image/png', href: 'https://www.so-better.cn/ico.png' }], 10 | ['meta', { name: 'viewport', content: 'width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no' }] 11 | ], 12 | outDir: 'dap-util', 13 | cleanUrls: true, 14 | themeConfig: { 15 | logo: { 16 | src: 'https://www.so-better.cn/logo.png' 17 | }, 18 | outline: { 19 | label: '本页目录', 20 | level: [2, 5] 21 | }, 22 | nav: [ 23 | { text: '指南', link: '/guide/introduction', activeMatch: '/guide' }, 24 | { text: '更新日志', link: '/changelog' } 25 | ], 26 | sidebar: { 27 | '/guide': [ 28 | { 29 | text: '开始使用', 30 | items: [ 31 | { 32 | text: '简介', 33 | link: '/guide/introduction' 34 | }, 35 | { 36 | text: '安装', 37 | link: '/guide/install' 38 | }, 39 | { 40 | text: '快速上手', 41 | link: '/guide/quick-start' 42 | } 43 | ] 44 | }, 45 | { 46 | text: '模块', 47 | items: [ 48 | { 49 | text: 'color', 50 | link: '/guide/modules/color' 51 | }, 52 | { 53 | text: 'common', 54 | link: '/guide/modules/common' 55 | }, 56 | { 57 | text: 'data', 58 | link: '/guide/modules/data' 59 | }, 60 | { 61 | text: 'element', 62 | link: '/guide/modules/element' 63 | }, 64 | { 65 | text: 'event', 66 | link: '/guide/modules/event' 67 | }, 68 | { 69 | text: 'file', 70 | link: '/guide/modules/file' 71 | }, 72 | { 73 | text: 'number', 74 | link: '/guide/modules/number' 75 | }, 76 | { 77 | text: 'platform', 78 | link: '/guide/modules/platform' 79 | }, 80 | { 81 | text: 'speech', 82 | link: '/guide/modules/speech' 83 | }, 84 | { 85 | text: 'string', 86 | link: '/guide/modules/string' 87 | } 88 | ] 89 | } 90 | ] 91 | }, 92 | socialLinks: [ 93 | { icon: 'npm', link: 'https://www.npmjs.com/package/dap-util' }, 94 | { icon: 'gitee', link: 'https://gitee.com/so-better/dap-util' }, 95 | { icon: 'github', link: 'https://github.com/so-better/dap-util' } 96 | ], 97 | search: { provider: 'local' }, 98 | lastUpdated: { 99 | text: '上次更新' 100 | }, 101 | docFooter: { 102 | prev: 'Prev', 103 | next: 'Next' 104 | }, 105 | darkModeSwitchTitle: '切换到深色模式', 106 | lightModeSwitchTitle: '切换到浅色模式', 107 | darkModeSwitchLabel: '主题风格切换', 108 | sidebarMenuLabel: '菜单目录', 109 | returnToTopLabel: '返回顶部', 110 | externalLinkIcon: true 111 | }, 112 | markdown: { 113 | image: { 114 | lazyLoading: true 115 | }, 116 | theme: { 117 | dark: 'github-dark', 118 | light: 'github-light' 119 | }, 120 | codeCopyButtonTitle: '复制代码' 121 | }, 122 | vite: { 123 | server: { 124 | port: 5402 125 | } 126 | } 127 | }) 128 | -------------------------------------------------------------------------------- /docs/guide/modules/common.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: common 3 | --- 4 | 5 | # common 6 | 7 | 通用模块 8 | 9 | ## matchingText() 10 | 11 | 常用正则匹配判断 12 | 13 | - 类型 14 | 15 | ```ts 16 | matchingText(text: string, param: matchingParamType): boolean 17 | ``` 18 | 19 | - 详细信息 20 | 21 | 第一个入参表示需要匹配的字符串,第二个入参表示匹配的类型,该方法返回一个 `boolean` 值 22 | 23 | 关于匹配的类型,取值范围如下: 24 | 25 | `Chinese`:判断字符串是否为中文 26 | 27 | `chinese`:判断字符串是否包含中文 28 | 29 | `email`:判断字符串是否为邮箱 30 | 31 | `username`:判断字符串是否为 4-16 位的用户名(字母数字下划线) 32 | 33 | `int+`:判断字符串是否为正整数 34 | 35 | `int`:判断字符串是否为整数 36 | 37 | `int-`:判断字符串是否为负整数 38 | 39 | `pos`:判断字符串是否为正数 40 | 41 | `neg`:判断字符串是否为负数 42 | 43 | `number`:判断字符串是否为数字 44 | 45 | `phone`:判断字符串是否为手机号 46 | 47 | `idCard`:判断字符串是否为身份证号码 48 | 49 | `url`:判断字符串是否为网址 50 | 51 | `IPv4`:判断字符串是否为 ip 地址 52 | 53 | `hex`:判断字符串是否为十六进制颜色值 54 | 55 | `rgb`:判断字符串是否为 rgb 值 56 | 57 | `rgba`:判断字符串是否为 rgba 值 58 | 59 | `QQ`:判断字符串是否为 QQ 号码 60 | 61 | `weixin`:判断字符串是否为微信号,6 至 20 位,以字母开头,字母,数字,减号,下划线 62 | 63 | `plate`:判断字符串是否为车牌号 64 | 65 | - 示例 66 | 67 | ```ts 68 | import { common } from 'dap-util' 69 | common.matchingText('rgb(255, 0, 0)', 'rgb') //true 70 | ``` 71 | 72 | ## getUrlParams() 73 | 74 | 根据参数名获取地址栏参数值 75 | 76 | - 类型 77 | 78 | ```ts 79 | getUrlParams(name: string): string | undefined 80 | ``` 81 | 82 | - 详细信息 83 | 84 | 提供一个入参,类型为 `string`,表示要获取的地址栏参数名称,该方法返回一个参数值,如果地址栏没有该参数则返回 `undefined` 85 | 86 | 该方法本质上可以兼容 `hash` 地址并且参数在 `hash` 后面的情况,但是如果 `hash` 前面也有参数,则不会考虑 `hash` 后面的参数 87 | 88 | - 示例 89 | 90 | ```ts 91 | import { common } from 'dap-util' 92 | const name = common.getUrlParams('name') 93 | ``` 94 | 95 | ## isEmptyObject() 96 | 97 | 判断是否空对象 98 | 99 | - 类型 100 | 101 | ```ts 102 | isEmptyObject(obj: any): boolean 103 | ``` 104 | 105 | - 详细信息 106 | 107 | 提供一个任意类型的入参,判断是否为空对象,返回 `boolean` 值 108 | 109 | 所谓的空对象,即该参数的 `typeof` 是 `object`,但是该参数没有任何属性 110 | 111 | - 示例 112 | 113 | ```ts 114 | import { common } from 'dap-util' 115 | common.isEmptyObject({}) //true 116 | common.isEmptyObject({ name: 'a' }) //false 117 | common.isEmptyObject('name') //false 118 | ``` 119 | 120 | ## equal() 121 | 122 | 判断两个参数是否相等 123 | 124 | - 类型 125 | 126 | ```ts 127 | equal(a: any, b: any): boolean 128 | ``` 129 | 130 | - 详细信息 131 | 132 | 提供两个任意类型的入参,该方法会对这两个入参进行比较,判断是否相等,返回 `boolean` 值 133 | 134 | - 示例 135 | 136 | ```ts 137 | import { common } from 'dap-util' 138 | common.equal(1, '1') //false 139 | common.equal(1, 1) //true 140 | common.equal({ name: 'a' }, { name: 'b' }) //false 141 | common.equal({ name: 'a' }, { name: 'a' }) //true 142 | ``` 143 | 144 | ## isObject() 145 | 146 | 判断是否对象 147 | 148 | - 类型 149 | 150 | ```ts 151 | isObject(val: any): boolean 152 | ``` 153 | 154 | - 详细信息 155 | 156 | 提供一个任意类型的入参,该方法会判断它是否为对象,返回 `boolean` 值 157 | 158 | - 示例 159 | 160 | ```ts 161 | import { common } from 'dap-util' 162 | common.isObject('a') //false 163 | common.isObject({}) //true 164 | common.isObject({ name: 'a' }) //true 165 | ``` 166 | 167 | ## copyText() 168 | 169 | 复制指定文本到剪切板 170 | 171 | - 类型 172 | 173 | ```ts 174 | copyText(text: string): Promise 175 | ``` 176 | 177 | - 详细信息 178 | 179 | 提供一个入参,类型为 `string`,表示需要复制的文字内容 180 | 181 | 该方法内部调用的是 `navigator.clipboard.writeText` 方法,因此需要 `https` 支持 182 | 183 | - 示例 184 | 185 | ```ts 186 | import { common } from 'dap-util' 187 | common.copyText('hello') 188 | ``` 189 | 190 | ## clone() 191 | 192 | 深度克隆 193 | 194 | - 类型 195 | 196 | ```ts 197 | clone(data: T): T 198 | ``` 199 | 200 | - 详细信息 201 | 202 | 提供一个任意类型的入参,该方法会对该参数进行克隆,返回克隆的结果 203 | 204 | - 示例 205 | 206 | ```ts 207 | import { common } from 'dap-util' 208 | const a = { 209 | name: 'a', 210 | age: 14 211 | } 212 | const b = common.clone(a) // { name: 'a', age: 14 } 213 | ``` 214 | -------------------------------------------------------------------------------- /src/file.ts: -------------------------------------------------------------------------------- 1 | export type CompressOptionsType = { 2 | //压缩图片的宽,单位px,如果不设置默认为原图宽 3 | width?: number 4 | //压缩图片质量,默认为原图的0.8 5 | quality?: number 6 | //图片类型,jpeg或者webp,默认为jpeg 7 | mimeType?: 'jpeg' | 'webp' 8 | //压缩后的最大值,单位kb,默认为0表示不设置此值 9 | maxSize?: number 10 | //小于该大小的图片不进行压缩,单位kb,默认为0表示任何图片都要压缩 11 | minSize?: number 12 | } 13 | 14 | export type CompressResultType = { 15 | file?: File 16 | url?: string 17 | quality?: number 18 | width?: number 19 | height?: number 20 | } 21 | 22 | export const file = { 23 | /** 24 | * 根据文件获取本地可预览的图片路径 25 | * @param {Object} file 26 | */ 27 | getImageUrl(file: File) { 28 | return window.URL.createObjectURL(file) 29 | }, 30 | 31 | /** 32 | * 将JS的file对象转为BASE64位字符串,通过then方法回调,参数为base64字符串 33 | * @param {Object} file 34 | */ 35 | dataFileToBase64(file: File) { 36 | return new Promise(resolve => { 37 | const reader = new FileReader() 38 | reader.readAsDataURL(file) // 读出 base64 39 | reader.onloadend = () => { 40 | // 图片的 base64 格式, 可以直接当成 img 的 src 属性值 41 | const dataURL = reader.result as string 42 | resolve(dataURL) 43 | } 44 | }) 45 | }, 46 | 47 | /** 48 | * 将base64位格式文件转换为file对象 49 | * @param {Object} base64String base64位格式字符串 50 | * @param {Object} fileName 转换后的文件名字,包含后缀 51 | */ 52 | dataBase64toFile(base64String: string, fileName: string) { 53 | const arr: string[] = base64String.split(',') 54 | const mime = arr[0].match(/:(.*?);/)![1] 55 | const bstr = atob(arr[1]) 56 | let n = bstr.length 57 | const u8arr = new Uint8Array(n) 58 | while (n--) { 59 | u8arr[n] = bstr.charCodeAt(n) 60 | } 61 | return new File([u8arr], fileName, { 62 | type: mime 63 | }) 64 | }, 65 | 66 | /** 67 | * 图片压缩方法 68 | * @param {*} file 需要压缩的图片File文件 69 | * @param {*} options 压缩参数 70 | */ 71 | compressImage(file: File, options: CompressOptionsType) { 72 | //压缩图片的具体实现方法 73 | const createFile = (canvas: HTMLCanvasElement, fileName: string, quality: number): CompressResultType => { 74 | //压缩后图片的base64 75 | let url = canvas.toDataURL('image/' + (options.mimeType ?? 'jpeg'), quality) 76 | //压缩后图片的file类型文件 77 | let file = this.dataBase64toFile(url, fileName) 78 | //比最大尺寸大,继续压缩,此时会降低质量 79 | if (options.maxSize && options.maxSize > 0 && file.size > options.maxSize * 1024) { 80 | quality = quality <= 0 ? 0 : Number((quality - 0.01).toFixed(2)) 81 | const res: CompressResultType = createFile(canvas, fileName, quality) 82 | url = res.url! 83 | file = res.file! 84 | quality = res.quality! 85 | } 86 | return { 87 | file, 88 | url, 89 | quality 90 | } 91 | } 92 | return new Promise((resolve, reject) => { 93 | //创建FileReader对象读取文件 94 | const reader = new FileReader() 95 | reader.readAsDataURL(file) 96 | reader.onload = () => { 97 | //获取文件的base64字符串 98 | const url = reader.result as string 99 | //创建图片对象 100 | const img = new Image() 101 | //设置图片链接地址 102 | img.src = url 103 | //图片加载完成事件触发 104 | img.onload = () => { 105 | //小于minSize的图片不压缩 106 | if (options.minSize && options.minSize > 0 && file.size <= options.minSize * 1024) { 107 | resolve({ 108 | file, 109 | url, 110 | quality: 1, 111 | width: img.width, 112 | height: img.height 113 | }) 114 | return 115 | } 116 | //创建画布 117 | const canvas = document.createElement('canvas') 118 | //创建2d上下文 119 | const context = canvas.getContext('2d')! 120 | //设置画布宽度 121 | canvas.width = options.width || img.width 122 | //设置画布高度 123 | canvas.height = options.width ? options.width / (img.width / img.height) : img.height 124 | //画图片 125 | context.drawImage(img, 0, 0, canvas.width, canvas.height) 126 | //设置压缩图片名称 127 | const index = file.name.lastIndexOf('.') 128 | const fileName = file.name.substring(0, index) + '.' + (options.mimeType ?? 'jpeg') 129 | //获取生成的文件和base64以及当前quality 130 | let res = createFile(canvas, fileName, options.quality ?? 0.8) 131 | resolve({ 132 | ...res, 133 | width: canvas.width, 134 | height: canvas.height 135 | }) 136 | } 137 | img.onerror = () => { 138 | reject(new Error('Failed to load image file')) 139 | } 140 | } 141 | 142 | reader.onerror = () => { 143 | reject(new Error('Failed to load image file')) 144 | } 145 | }) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/event.ts: -------------------------------------------------------------------------------- 1 | import { data as dataUtil } from './data' 2 | 3 | export type EventNameObjType = { 4 | eventName?: string 5 | guid?: string | number 6 | } 7 | 8 | export type EventObjType = { 9 | [key: string]: 10 | | { 11 | type: string 12 | fn: EventListenerOrEventListenerObject 13 | options?: AddEventListenerOptions 14 | } 15 | | undefined 16 | } 17 | 18 | //解析绑定事件名称字符串 19 | const parseEventName = (eventName: string) => { 20 | //先以空格划分 21 | const eventNames = eventName.split(/[\s]+/g) 22 | const result: EventNameObjType[] = [] 23 | eventNames.forEach(name => { 24 | const arr = name.split('.') 25 | const obj: EventNameObjType = { 26 | eventName: arr[0] 27 | } 28 | if (arr.length > 1) { 29 | obj.guid = arr[1] 30 | } 31 | result.push(obj) 32 | }) 33 | return result 34 | } 35 | 36 | //更新事件对象,移除空的元素 37 | const updateEvents = (events: EventObjType) => { 38 | const obj: EventObjType = {} 39 | const keys = Object.keys(events) 40 | keys.forEach(key => { 41 | if (events[key]) { 42 | obj[key] = events[key] 43 | } 44 | }) 45 | return obj 46 | } 47 | 48 | //给元素添加单个事件 49 | const bindSingleListener = (el: HTMLElement | Window | Document, eventName: string, guid: string | number | undefined, fn: (e: Event) => void, options?: AddEventListenerOptions) => { 50 | //获取该元素上的事件对象events:{click.0:{type:'click',fn:fn}} 51 | const events: EventObjType = dataUtil.get(el, 'dap-defined-events') || {} 52 | //如果没有设定guid 53 | if (!guid) { 54 | //从该元素上拿到记录的guid值 55 | guid = dataUtil.get(el, 'dap-event-guid') || 0 56 | //更新guid 57 | dataUtil.set(el, 'dap-event-guid', (guid as number) + 1) 58 | } 59 | //更改guid,结合事件名称作为存储的key值 60 | guid = eventName + '.' + guid 61 | //先判断是否已经含有同guid且同类型事件,有则移除 62 | if (events[guid] && events[guid]!.type == eventName) { 63 | el.removeEventListener(eventName, events[guid]!.fn, events[guid]!.options) 64 | } 65 | //添加事件 66 | el.addEventListener(eventName, fn, options) 67 | //添加到events对象里,并更新到节点上 68 | events[guid] = { 69 | type: eventName, 70 | fn: fn, 71 | options: options 72 | } 73 | dataUtil.set(el, 'dap-defined-events', events) 74 | } 75 | 76 | //移除元素的单个事件 77 | const unbindSingleListener = (el: HTMLElement | Window | Document, eventName: string, guid: string | number | undefined) => { 78 | const events: EventObjType = dataUtil.get(el, 'dap-defined-events') || {} 79 | const keys = Object.keys(events) 80 | const length = keys.length 81 | for (let i = 0; i < length; i++) { 82 | const key = keys[i] 83 | if (events[key] && events[key].type == eventName) { 84 | //如果guid存在则移除该修饰符指定的事件,否则移除全部该类型事件 85 | if (guid) { 86 | if (key == eventName + '.' + guid) { 87 | el.removeEventListener(events[key].type, events[key].fn, events[key].options) 88 | events[key] = undefined 89 | } 90 | } else { 91 | el.removeEventListener(events[key].type, events[key].fn, events[key].options) 92 | events[key] = undefined 93 | } 94 | } 95 | } 96 | //更新events 97 | dataUtil.set(el, 'dap-defined-events', updateEvents(events)) 98 | } 99 | 100 | export const event = { 101 | /** 102 | * 绑定事件 103 | * @param {Object} el 元素节点 104 | * @param {Object} eventName 事件名称 105 | * @param {Object} fn 函数 106 | * @param {Object} options 参数 107 | */ 108 | on(el: HTMLElement | Window | Document, eventName: string, fn: (e: Event) => void, options?: AddEventListenerOptions) { 109 | //解析eventName,获取事件数组以及guid标志 110 | const result = parseEventName(eventName) 111 | //批量添加事件 112 | result.forEach((res: EventNameObjType) => { 113 | bindSingleListener(el, res.eventName!, res.guid, fn.bind(el), options) 114 | }) 115 | }, 116 | 117 | /** 118 | * 事件解绑 119 | * @param {Object} el 元素节点 120 | * @param {Object} eventName 事件名称 121 | */ 122 | off(el: HTMLElement | Window | Document, eventName?: string) { 123 | const events: EventObjType = dataUtil.get(el, 'dap-defined-events') 124 | if (!events) { 125 | return 126 | } 127 | //事件名称不存在,则移除该元素的全部事件 128 | if (!eventName) { 129 | const keys = Object.keys(events) 130 | const length = keys.length 131 | for (let i = 0; i < length; i++) { 132 | const key = keys[i] 133 | el.removeEventListener(events[key]!.type, events[key]!.fn, events[key]!.options) 134 | } 135 | dataUtil.remove(el, 'dap-defined-events') 136 | dataUtil.remove(el, 'dap-event-guid') 137 | return 138 | } 139 | //解析eventName,获取事件数组以及guid标志 140 | const result = parseEventName(eventName) 141 | result.forEach((res: EventNameObjType) => { 142 | unbindSingleListener(el, res.eventName!, res.guid) 143 | }) 144 | }, 145 | 146 | /** 147 | * 获取绑定的所有事件 148 | * @param {*} el 149 | */ 150 | get(el: HTMLElement | Window | Document) { 151 | const events: EventObjType = dataUtil.get(el, 'dap-defined-events') 152 | if (!events) { 153 | return 154 | } 155 | return events 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /lib/element.d.ts: -------------------------------------------------------------------------------- 1 | export type ScrollOptionsType = { 2 | el?: HTMLElement | string | Window; 3 | time?: number; 4 | number?: number; 5 | }; 6 | export type PlacementType = { 7 | left: number; 8 | top: number; 9 | right: number; 10 | bottom: number; 11 | }; 12 | export type scrollTopBottomResultType = { 13 | state: 'top' | 'bottom'; 14 | target: HTMLElement | Window; 15 | }; 16 | /** 17 | * element相关工具方法 18 | */ 19 | export declare const element: { 20 | /** 21 | * 判断是否是Window对象 22 | * @param {Object} data 入参 23 | */ 24 | isWindow(data: any): boolean; 25 | /** 26 | * 获取元素距离某个定位祖先元素左侧/顶部/底部/右侧的距离 27 | * @param {Object} el 元素 28 | * @param {Object} root 定位父元素或者祖先元素,未指定则为document.body 29 | */ 30 | getElementPoint(el: HTMLElement, root?: HTMLElement): PlacementType; 31 | /** 32 | * 判断某个元素是否包含指定元素,包含相等关系和父子关系 33 | * @param {Object} parentNode 父元素或祖先元素 34 | * @param {Object} childNode 子元素 35 | */ 36 | isContains(parentNode: HTMLElement, childNode: HTMLElement): boolean; 37 | /** 38 | * 判断某个元素是否是指定元素的父元素 39 | * @param {Object} parentNode 父元素 40 | * @param {Object} childNode 子元素 41 | */ 42 | isParentNode(parentNode: HTMLElement, childNode: HTMLElement): boolean; 43 | /** 44 | * 查找某个元素下指定选择器的子元素 45 | * @param {Object} el 元素 46 | * @param {Object} selector 支持多选择器,等同于querySelectorAll的参数 47 | */ 48 | children(el: HTMLElement, selector?: string): HTMLElement[]; 49 | /** 50 | * 查找某个元素下指定选择器的兄弟元素 51 | * @param {Object} el 元素 52 | * @param {Object} selector 取值等同于queryselectorAll的参数,支持多选择器 53 | */ 54 | siblings(el: HTMLElement, selector?: string): HTMLElement[]; 55 | /** 56 | * rem与px单位转换 57 | * @param {Object} num rem数值 58 | */ 59 | rem2px(num: number): number; 60 | /** 61 | * rem与px单位转换 62 | * @param {Object} num px数值 63 | */ 64 | px2rem(num: number): number; 65 | /** 66 | * 获取元素的内容宽度,内容宽度不包括border和padding 67 | * @param {Object} el 支持css选择器字符串,未指定则表示document.body 68 | */ 69 | width(el?: HTMLElement | string): number; 70 | /** 71 | * 获取元素的内容高度,内容高度不包括border和padding 72 | * @param {Object} el 支持css选择器字符串 未指定则表示document.body 73 | */ 74 | height(el?: HTMLElement | string): number; 75 | /** 76 | * 移除class 77 | * @param {Object} el 元素 78 | * @param {Object} className 支持多类,以空格划分 79 | */ 80 | removeClass(el: HTMLElement, className: string): void; 81 | /** 82 | * 添加class 83 | * @param {Object} el 元素 84 | * @param {Object} className 支持多类,以空格划分 85 | */ 86 | addClass(el: HTMLElement, className: string): void; 87 | /** 88 | * 判断指定元素是否含有指定类名 89 | * @param {Object} el 元素 90 | * @param {Object} className 支持多类,以空格划分 91 | */ 92 | hasClass(el: HTMLElement, className: string): boolean; 93 | /** 94 | * 监听元素滚动到顶部或者底部 95 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动 96 | * @param {Object} callback 回调函数 97 | */ 98 | scrollTopBottomTrigger(el?: HTMLElement | string | Window, callback?: (options: scrollTopBottomResultType) => void): void; 99 | /** 100 | * 获取文档或元素的总宽度 101 | * @param {Object} el 支持css选择器字符串 未指定则表示整个页面文档 102 | */ 103 | getScrollWidth(el?: HTMLElement | string): number; 104 | /** 105 | * 获取文档或者元素的总高度 106 | * @param {Object} el 支持css选择器字符串 未指定则表示整个页面文档 107 | */ 108 | getScrollHeight(el?: HTMLElement | string): number; 109 | /** 110 | * 设置滚动条在Y轴上的距离 111 | * @param {Object} options {el,number,time} el支持css选择器字符串 未指定则为窗口滚动 112 | */ 113 | setScrollTop(options: ScrollOptionsType): Promise; 114 | /** 115 | * 获取滚动条在Y轴上滚动的距离 116 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动 117 | */ 118 | getScrollTop(el?: HTMLElement | string | Window): number; 119 | /** 120 | * 获取滚动条在X轴上滚动的距离 121 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动 122 | */ 123 | getScrollLeft(el?: HTMLElement | string | Window): number; 124 | /** 125 | * 设置滚动条在X轴上的距离 126 | * @param {Object} options {el,number,time} el支持css选择器字符串 未指定则为窗口滚动 127 | */ 128 | setScrollLeft(options: ScrollOptionsType): Promise; 129 | /** 130 | * 获取元素指定样式 131 | * @param {Object} el 元素 132 | * @param {Object} cssName 样式名称 133 | */ 134 | getCssStyle(el: HTMLElement, cssName: string): string; 135 | /** 136 | * 判断字符串属于哪种选择器 137 | * @param {Object} selector 138 | */ 139 | getCssSelector(selector: string): { 140 | type: "id" | "class" | "attribute" | "tag"; 141 | value: string | { 142 | attributeName: string; 143 | attributeValue: string; 144 | }; 145 | }; 146 | /** 147 | * 获取元素距离可视窗口的位置 148 | * @param {Object} el 支持css选择器字符串 未指定则为document.body 149 | */ 150 | getElementBounding(el?: HTMLElement | string): PlacementType; 151 | /** 152 | * 判断是否是元素 153 | * @param {Object} el 154 | */ 155 | isElement(el: any): boolean; 156 | /** 157 | * 字符串转dom 158 | * @param {Object} html 159 | */ 160 | string2dom(html: string): HTMLElement | HTMLElement[]; 161 | }; 162 | -------------------------------------------------------------------------------- /src/platform.ts: -------------------------------------------------------------------------------- 1 | export type OSResultType = { 2 | Windows: boolean 3 | WindowsCPU?: 'x64' | 'x32' 4 | WindowsVersion?: 'win7' | 'win8' | 'win10' 5 | Mac: boolean 6 | MacVersion: string 7 | ios: boolean 8 | iosVersion: string 9 | Android: boolean 10 | AndroidVersion: string 11 | Linux: boolean 12 | HarmonyOS: boolean 13 | Ubuntu: boolean 14 | } 15 | 16 | //设备信息相关方法 17 | export const platform = { 18 | //设备语言类型 19 | language() { 20 | return window.navigator.language 21 | }, 22 | 23 | /** 24 | * 获取设备类型 25 | */ 26 | device() { 27 | const userAgent = window.navigator.userAgent 28 | return { 29 | PC: !userAgent.match(/AppleWebKit.*Mobile.*/), 30 | //是否移动终端 31 | Mobile: !!userAgent.match(/AppleWebKit.*Mobile.*/), 32 | //是否iPhone 33 | iPhone: userAgent.includes('iPhone'), 34 | //是否手机 35 | Phone: (userAgent.includes('Android') && /(?:Mobile)/.test(userAgent)) || userAgent.includes('iPhone') || /(?:Windows Phone)/.test(userAgent), 36 | //是否iPad 37 | iPad: userAgent.includes('iPad'), 38 | //是否平板电脑 39 | Tablet: userAgent.includes('iPad') || (userAgent.includes('Android') && !/(?:Mobile)/.test(userAgent)) || (userAgent.includes('Firefox') && /(?:Tablet)/.test(userAgent)), 40 | //windows手机 41 | WindowsPhone: /(?:Windows Phone)/.test(userAgent) 42 | } 43 | }, 44 | 45 | /** 46 | * 获取浏览器类型 47 | */ 48 | browser() { 49 | const userAgent = window.navigator.userAgent 50 | return { 51 | //是否edge浏览器 52 | Edge: !!userAgent.match(/Edg\/([\d.]+)/), 53 | //是否微信内置浏览器 54 | Weixin: userAgent.includes('MicroMessenger'), 55 | //是否QQ内置浏览器 56 | QQ: userAgent.includes('QQ'), 57 | //是否QQ浏览器 58 | QQBrowser: userAgent.includes('MQQBrowser'), 59 | //是否UC浏览器 60 | UC: userAgent.includes('UCBrowser'), 61 | //是否谷歌浏览器 62 | Chrome: userAgent.includes('Chrome'), 63 | //是否火狐浏览器 64 | Firefox: userAgent.includes('Firefox'), 65 | //是否搜狗浏览器 66 | Sougou: userAgent.toLocaleLowerCase().includes('se 2.x') || userAgent.toLocaleLowerCase().includes('metasr') || userAgent.toLocaleLowerCase().includes('sogou'), 67 | //是否safari浏览器 68 | Safari: userAgent.includes('Safari') && !userAgent.includes('Chrome') 69 | } 70 | }, 71 | 72 | /** 73 | * 获取浏览器内核 74 | */ 75 | browserKernel() { 76 | const userAgent = window.navigator.userAgent 77 | if (userAgent.includes('Presto')) { 78 | return 'opera' 79 | } 80 | if (userAgent.includes('AppleWebKit')) { 81 | return 'webkit' 82 | } 83 | if (userAgent.includes('Gecko') && !userAgent.includes('KHTML')) { 84 | return 'gecko' 85 | } 86 | }, 87 | 88 | /** 89 | * 获取操作系统数据 90 | */ 91 | os(): OSResultType { 92 | const userAgent = window.navigator.userAgent 93 | return { 94 | //是否Windows系统 95 | Windows: userAgent.includes('Windows'), 96 | //x64/x32 97 | WindowsCPU: (function () { 98 | if (userAgent.toLocaleLowerCase().includes('win64') || userAgent.toLocaleLowerCase().includes('wow64')) { 99 | return 'x64' 100 | } else if (userAgent.toLocaleLowerCase().includes('win32') || userAgent.toLocaleLowerCase().includes('wow32')) { 101 | return 'x32' 102 | } 103 | })(), 104 | //Windows版本 105 | WindowsVersion: (function () { 106 | if (userAgent.includes('Windows NT 6.1') || userAgent.includes('Windows 7')) { 107 | return 'win7' 108 | } 109 | if (userAgent.includes('Windows NT 6.3') || userAgent.includes('Windows NT 6.2') || userAgent.includes('Windows NT 8')) { 110 | return 'win8' 111 | } 112 | if (userAgent.includes('Windows NT 10') || userAgent.includes('Windows NT 6.4')) { 113 | return 'win10' 114 | } 115 | })(), 116 | //是否Mac 117 | Mac: userAgent.includes('Macintosh'), 118 | //Mac版本 119 | MacVersion: (function () { 120 | if (userAgent.includes('Macintosh')) { 121 | const matches = userAgent.match(/Mac OS X ([^\s]+)\)/) 122 | if (matches && matches[1]) { 123 | return matches[1].split(/_|\./).join('.') 124 | } 125 | } 126 | return '' 127 | })(), 128 | //是否ios系统 129 | ios: !!userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), 130 | //ios系统版本 131 | iosVersion: (function () { 132 | if (!!userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)) { 133 | const matches = userAgent.match(/CPU.+OS ([^\s]+) like Mac OS X/) 134 | if (matches && matches[1]) { 135 | return matches[1].split(/_|\./).join('.') 136 | } 137 | } 138 | return '' 139 | })(), 140 | //是否Android系统 141 | Android: userAgent.includes('Android'), 142 | //Android系统版本 143 | AndroidVersion: (function () { 144 | const matches = userAgent.match(/Android ([^\s]+);/) 145 | if (matches && matches[1]) { 146 | return matches[1].split(/_|\./).join('.') 147 | } 148 | return '' 149 | })(), 150 | //是否Linux系统 151 | Linux: userAgent.includes('Linux'), 152 | //是否鸿蒙系统 153 | HarmonyOS: userAgent.includes('HarmonyOS'), 154 | //是否Ubuntu系统 155 | Ubuntu: userAgent.includes('Ubuntu') 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './color'; 2 | export * from './common'; 3 | export * from './data'; 4 | export * from './element'; 5 | export * from './event'; 6 | export * from './file'; 7 | export * from './number'; 8 | export * from './platform'; 9 | export * from './speech'; 10 | export * from './string'; 11 | declare const _default: { 12 | number: { 13 | formatNumber(num: number): string; 14 | isNumber(num: any): boolean; 15 | add(...values: number[]): number; 16 | subtract(...values: number[]): number; 17 | mutiply(...values: number[]): number; 18 | divide(...values: number[]): number; 19 | }; 20 | data: { 21 | remove(el: import('./data').DataHTMLElement | import('./data').DataWindow | import('./data').DataDocument, key?: string): void; 22 | has(el: import('./data').DataHTMLElement | import('./data').DataWindow | import('./data').DataDocument, key: string): boolean; 23 | get(el: import('./data').DataHTMLElement | import('./data').DataWindow | import('./data').DataDocument, key?: string): T; 24 | set(el: import('./data').DataHTMLElement | import('./data').DataWindow | import('./data').DataDocument, key: string, value: any): void; 25 | }; 26 | element: { 27 | isWindow(data: any): boolean; 28 | getElementPoint(el: HTMLElement, root?: HTMLElement): import('./element').PlacementType; 29 | isContains(parentNode: HTMLElement, childNode: HTMLElement): boolean; 30 | isParentNode(parentNode: HTMLElement, childNode: HTMLElement): boolean; 31 | children(el: HTMLElement, selector?: string): HTMLElement[]; 32 | siblings(el: HTMLElement, selector?: string): HTMLElement[]; 33 | rem2px(num: number): number; 34 | px2rem(num: number): number; 35 | width(el?: HTMLElement | string): number; 36 | height(el?: HTMLElement | string): number; 37 | removeClass(el: HTMLElement, className: string): void; 38 | addClass(el: HTMLElement, className: string): void; 39 | hasClass(el: HTMLElement, className: string): boolean; 40 | scrollTopBottomTrigger(el?: HTMLElement | string | Window, callback?: (options: import('./element').scrollTopBottomResultType) => void): void; 41 | getScrollWidth(el?: HTMLElement | string): number; 42 | getScrollHeight(el?: HTMLElement | string): number; 43 | setScrollTop(options: import('./element').ScrollOptionsType): Promise; 44 | getScrollTop(el?: HTMLElement | string | Window): number; 45 | getScrollLeft(el?: HTMLElement | string | Window): number; 46 | setScrollLeft(options: import('./element').ScrollOptionsType): Promise; 47 | getCssStyle(el: HTMLElement, cssName: string): string; 48 | getCssSelector(selector: string): { 49 | type: "id" | "class" | "attribute" | "tag"; 50 | value: string | { 51 | attributeName: string; 52 | attributeValue: string; 53 | }; 54 | }; 55 | getElementBounding(el?: HTMLElement | string): import('./element').PlacementType; 56 | isElement(el: any): boolean; 57 | string2dom(html: string): HTMLElement | HTMLElement[]; 58 | }; 59 | event: { 60 | on(el: HTMLElement | Window | Document, eventName: string, fn: (e: Event) => void, options?: AddEventListenerOptions): void; 61 | off(el: HTMLElement | Window | Document, eventName?: string): void; 62 | get(el: HTMLElement | Window | Document): import('./event').EventObjType | undefined; 63 | }; 64 | common: { 65 | matchingText(text: string, param: import('./common').matchingParamType): boolean; 66 | getUrlParams(name: string): string | undefined; 67 | isEmptyObject(obj: any): boolean; 68 | equal(a: any, b: any): boolean; 69 | isObject(val: any): boolean; 70 | copyText(text: string): Promise; 71 | clone(data: T): T; 72 | }; 73 | color: { 74 | rgb2hsv(rgb: number[]): number[]; 75 | hsv2rgb(hsv: number[]): number[]; 76 | rgb2hex(rgb: number[]): string; 77 | hex2rgb(hex: string): number[]; 78 | }; 79 | file: { 80 | getImageUrl(file: File): string; 81 | dataFileToBase64(file: File): Promise; 82 | dataBase64toFile(base64String: string, fileName: string): File; 83 | compressImage(file: File, options: import('./file').CompressOptionsType): Promise; 84 | }; 85 | string: { 86 | insert(original: string, str: string, index: number): string; 87 | delete(original: string, index: number, num: number): string; 88 | replace(original: string, start: number, end: number, str: string): string; 89 | trim(str: string, global?: boolean): string; 90 | }; 91 | platform: { 92 | language(): string; 93 | device(): { 94 | PC: boolean; 95 | Mobile: boolean; 96 | iPhone: boolean; 97 | Phone: boolean; 98 | iPad: boolean; 99 | Tablet: boolean; 100 | WindowsPhone: boolean; 101 | }; 102 | browser(): { 103 | Edge: boolean; 104 | Weixin: boolean; 105 | QQ: boolean; 106 | QQBrowser: boolean; 107 | UC: boolean; 108 | Chrome: boolean; 109 | Firefox: boolean; 110 | Sougou: boolean; 111 | Safari: boolean; 112 | }; 113 | browserKernel(): "opera" | "webkit" | "gecko" | undefined; 114 | os(): import('./platform').OSResultType; 115 | }; 116 | speech: { 117 | start(text: string, options?: import('./speech').SpeechParamsType): void; 118 | stop(): void; 119 | pause(): void; 120 | resume(): void; 121 | }; 122 | }; 123 | export default _default; 124 | -------------------------------------------------------------------------------- /src/common.ts: -------------------------------------------------------------------------------- 1 | export type matchingParamType = 'Chinese' | 'chinese' | 'email' | 'username' | 'int+' | 'int-' | 'int' | 'pos' | 'neg' | 'number' | 'phone' | 'idCard' | 'url' | 'IPv4' | 'hex' | 'rgb' | 'rgba' | 'QQ' | 'weixin' | 'plate' 2 | 3 | /** 4 | * 常用方法 5 | */ 6 | export const common = { 7 | /** 8 | * 常用判断 9 | * @param {Object} text 要判断的字符串 10 | * @param {Object} param 判断的类型字符串 11 | */ 12 | matchingText(text: string, param: matchingParamType) { 13 | //判断text是否为中文 14 | if (param == 'Chinese') { 15 | return /^[\u4e00-\u9fa5]+$/.test(text) 16 | } 17 | //判断text是否含有中文 18 | if (param == 'chinese') { 19 | return /[\u4e00-\u9fa5]/.test(text) 20 | } 21 | //判断text是否为邮箱 22 | if (param == 'email') { 23 | return /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(text) 24 | } 25 | //判断text是否为4-16位的用户名(字母数字下划线) 26 | if (param == 'username') { 27 | return /^[a-zA-Z0-9_]{4,16}$/.test(text) 28 | } 29 | //判断text是否为正整数 30 | if (param == 'int+') { 31 | return /^\d+$/.test(text) 32 | } 33 | //判断text是否为负整数 34 | if (param == 'int-') { 35 | return /^-\d+$/.test(text) 36 | } 37 | //判断text是否为整数 38 | if (param == 'int') { 39 | return /^-?\d+$/.test(text) 40 | } 41 | //判断text是否为正数 42 | if (param == 'pos') { 43 | return /^\d*\.?\d+$/.test(text) 44 | } 45 | //判断text是否为负数 46 | if (param == 'neg') { 47 | return /^-\d*\.?\d+$/.test(text) 48 | } 49 | //判断text是否为数字 50 | if (param == 'number') { 51 | return /^-?\d*\.?\d+$/.test(text) 52 | } 53 | //判断text是否为手机号 54 | if (param == 'phone') { 55 | return /^1[0-9]\d{9}$/.test(text) 56 | } 57 | //判断text是否为身份证号 58 | if (param == 'idCard') { 59 | return /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(text) 60 | } 61 | //判断text是否为网址 62 | if (param == 'url') { 63 | return /^(https?|ftp):\/\/(-\.)?([^\s\/?\.#-]+\.?)+(\/[^\s]*)?$/.test(text) 64 | } 65 | //判断是否为ip地址 66 | if (param == 'IPv4') { 67 | return /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(text) 68 | } 69 | //判断是否为十六进制颜色 70 | if (param == 'hex') { 71 | return /^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/.test(text) 72 | } 73 | //判断是否为rgb值 74 | if (param == 'rgb') { 75 | return /^rgb\((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\)$/.test(text) 76 | } 77 | //判断是否为rgba值 78 | if (param == 'rgba') { 79 | return /^rgba\((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(0?\.\d|1(\.0)?|0)\)$/.test(text) 80 | } 81 | //判断text是否为QQ号码 82 | if (param == 'QQ') { 83 | return /^[1-9][0-9]{4,10}$/.test(text) 84 | } 85 | //判断text是否为微信号,6至20位,以字母开头,字母,数字,减号,下划线 86 | if (param == 'weixin') { 87 | return /^[a-zA-Z]([-_a-zA-Z0-9]{5,19})+$/.test(text) 88 | } 89 | //判断text是否为车牌 90 | if (param == 'plate') { 91 | return /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$|^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[D|F]{1}[A-Z0-9]{5,6}$/.test(text) 92 | } 93 | return false 94 | }, 95 | 96 | /** 97 | * 根据参数名获取地址栏参数值 98 | * @param {Object} name 99 | */ 100 | getUrlParams(name: string) { 101 | const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)') 102 | let search = window.location.search.substring(1) 103 | if (!search) { 104 | const arr = window.location.hash.split('?') 105 | if (arr.length == 2) { 106 | search = arr[1] 107 | } 108 | } 109 | let r = search.match(reg) 110 | if (r) { 111 | return decodeURIComponent(r[2]) 112 | } 113 | }, 114 | 115 | /** 116 | * 判断是否空对象 117 | * @param {Object} obj 118 | */ 119 | isEmptyObject(obj: any) { 120 | return this.isObject(obj) && Object.keys(obj).length == 0 121 | }, 122 | 123 | /** 124 | * 判断两个参数是否相等 125 | * @param {Object} a 126 | * @param {Object} b 127 | */ 128 | equal(a: any, b: any) { 129 | if (typeof a !== typeof b) { 130 | return false 131 | } 132 | if (this.isObject(a) && this.isObject(b)) { 133 | const aProps = Object.getOwnPropertyNames(a) 134 | const bProps = Object.getOwnPropertyNames(b) 135 | if (aProps.length != bProps.length) { 136 | return false 137 | } 138 | const length = aProps.length 139 | let isEqual = true 140 | for (let i = 0; i < length; i++) { 141 | const propName = aProps[i] 142 | const propA = a[propName] 143 | const propB = b[propName] 144 | if (!this.equal(propA, propB)) { 145 | isEqual = false 146 | break 147 | } 148 | } 149 | return isEqual 150 | } 151 | return a === b 152 | }, 153 | 154 | /** 155 | * 是否对象 156 | * @param {Object} val 157 | */ 158 | isObject(val: any) { 159 | return typeof val === 'object' && !!val 160 | }, 161 | 162 | /** 163 | * 文本复制 164 | * @param {Object} text 165 | */ 166 | copyText(text: string) { 167 | if (!navigator.clipboard) { 168 | throw new Error("navigator.clipboard must be obtained in a secure environment, such as localhost, 127.0.0.1, or https, so the method won't work") 169 | } 170 | return navigator.clipboard.writeText(text) 171 | }, 172 | 173 | /** 174 | * 深度克隆 175 | * @param {Object} data 176 | */ 177 | clone(data: T) { 178 | if (this.isObject(data)) { 179 | if (Array.isArray(data)) { 180 | return data.map((item: any): any => { 181 | return this.clone(item) 182 | }) as T 183 | } 184 | const newData: any = {} 185 | for (let key in data) { 186 | newData[key] = this.clone(data[key]) 187 | } 188 | return newData as T 189 | } 190 | return data as T 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/vue.js: -------------------------------------------------------------------------------- 1 | import { 2 | BaseTransition, 3 | BaseTransitionPropsValidators, 4 | Comment, 5 | DeprecationTypes, 6 | EffectScope, 7 | ErrorCodes, 8 | ErrorTypeStrings, 9 | Fragment, 10 | KeepAlive, 11 | ReactiveEffect, 12 | Static, 13 | Suspense, 14 | Teleport, 15 | Text, 16 | TrackOpTypes, 17 | Transition, 18 | TransitionGroup, 19 | TriggerOpTypes, 20 | VueElement, 21 | assertNumber, 22 | callWithAsyncErrorHandling, 23 | callWithErrorHandling, 24 | camelize, 25 | capitalize, 26 | cloneVNode, 27 | compatUtils, 28 | compile, 29 | computed, 30 | createApp, 31 | createBaseVNode, 32 | createBlock, 33 | createCommentVNode, 34 | createElementBlock, 35 | createHydrationRenderer, 36 | createPropsRestProxy, 37 | createRenderer, 38 | createSSRApp, 39 | createSlots, 40 | createStaticVNode, 41 | createTextVNode, 42 | createVNode, 43 | customRef, 44 | defineAsyncComponent, 45 | defineComponent, 46 | defineCustomElement, 47 | defineEmits, 48 | defineExpose, 49 | defineModel, 50 | defineOptions, 51 | defineProps, 52 | defineSSRCustomElement, 53 | defineSlots, 54 | devtools, 55 | effect, 56 | effectScope, 57 | getCurrentInstance, 58 | getCurrentScope, 59 | getCurrentWatcher, 60 | getTransitionRawChildren, 61 | guardReactiveProps, 62 | h, 63 | handleError, 64 | hasInjectionContext, 65 | hydrate, 66 | hydrateOnIdle, 67 | hydrateOnInteraction, 68 | hydrateOnMediaQuery, 69 | hydrateOnVisible, 70 | initCustomFormatter, 71 | initDirectivesForSSR, 72 | inject, 73 | isMemoSame, 74 | isProxy, 75 | isReactive, 76 | isReadonly, 77 | isRef, 78 | isRuntimeOnly, 79 | isShallow, 80 | isVNode, 81 | markRaw, 82 | mergeDefaults, 83 | mergeModels, 84 | mergeProps, 85 | nextTick, 86 | normalizeClass, 87 | normalizeProps, 88 | normalizeStyle, 89 | onActivated, 90 | onBeforeMount, 91 | onBeforeUnmount, 92 | onBeforeUpdate, 93 | onDeactivated, 94 | onErrorCaptured, 95 | onMounted, 96 | onRenderTracked, 97 | onRenderTriggered, 98 | onScopeDispose, 99 | onServerPrefetch, 100 | onUnmounted, 101 | onUpdated, 102 | onWatcherCleanup, 103 | openBlock, 104 | popScopeId, 105 | provide, 106 | proxyRefs, 107 | pushScopeId, 108 | queuePostFlushCb, 109 | reactive, 110 | readonly, 111 | ref, 112 | registerRuntimeCompiler, 113 | render, 114 | renderList, 115 | renderSlot, 116 | resolveComponent, 117 | resolveDirective, 118 | resolveDynamicComponent, 119 | resolveFilter, 120 | resolveTransitionHooks, 121 | setBlockTracking, 122 | setDevtoolsHook, 123 | setTransitionHooks, 124 | shallowReactive, 125 | shallowReadonly, 126 | shallowRef, 127 | ssrContextKey, 128 | ssrUtils, 129 | stop, 130 | toDisplayString, 131 | toHandlerKey, 132 | toHandlers, 133 | toRaw, 134 | toRef, 135 | toRefs, 136 | toValue, 137 | transformVNodeArgs, 138 | triggerRef, 139 | unref, 140 | useAttrs, 141 | useCssModule, 142 | useCssVars, 143 | useHost, 144 | useId, 145 | useModel, 146 | useSSRContext, 147 | useShadowRoot, 148 | useSlots, 149 | useTemplateRef, 150 | useTransitionState, 151 | vModelCheckbox, 152 | vModelDynamic, 153 | vModelRadio, 154 | vModelSelect, 155 | vModelText, 156 | vShow, 157 | version, 158 | warn, 159 | watch, 160 | watchEffect, 161 | watchPostEffect, 162 | watchSyncEffect, 163 | withAsyncContext, 164 | withCtx, 165 | withDefaults, 166 | withDirectives, 167 | withKeys, 168 | withMemo, 169 | withModifiers, 170 | withScopeId 171 | } from "./chunk-LW4I4DCF.js"; 172 | export { 173 | BaseTransition, 174 | BaseTransitionPropsValidators, 175 | Comment, 176 | DeprecationTypes, 177 | EffectScope, 178 | ErrorCodes, 179 | ErrorTypeStrings, 180 | Fragment, 181 | KeepAlive, 182 | ReactiveEffect, 183 | Static, 184 | Suspense, 185 | Teleport, 186 | Text, 187 | TrackOpTypes, 188 | Transition, 189 | TransitionGroup, 190 | TriggerOpTypes, 191 | VueElement, 192 | assertNumber, 193 | callWithAsyncErrorHandling, 194 | callWithErrorHandling, 195 | camelize, 196 | capitalize, 197 | cloneVNode, 198 | compatUtils, 199 | compile, 200 | computed, 201 | createApp, 202 | createBlock, 203 | createCommentVNode, 204 | createElementBlock, 205 | createBaseVNode as createElementVNode, 206 | createHydrationRenderer, 207 | createPropsRestProxy, 208 | createRenderer, 209 | createSSRApp, 210 | createSlots, 211 | createStaticVNode, 212 | createTextVNode, 213 | createVNode, 214 | customRef, 215 | defineAsyncComponent, 216 | defineComponent, 217 | defineCustomElement, 218 | defineEmits, 219 | defineExpose, 220 | defineModel, 221 | defineOptions, 222 | defineProps, 223 | defineSSRCustomElement, 224 | defineSlots, 225 | devtools, 226 | effect, 227 | effectScope, 228 | getCurrentInstance, 229 | getCurrentScope, 230 | getCurrentWatcher, 231 | getTransitionRawChildren, 232 | guardReactiveProps, 233 | h, 234 | handleError, 235 | hasInjectionContext, 236 | hydrate, 237 | hydrateOnIdle, 238 | hydrateOnInteraction, 239 | hydrateOnMediaQuery, 240 | hydrateOnVisible, 241 | initCustomFormatter, 242 | initDirectivesForSSR, 243 | inject, 244 | isMemoSame, 245 | isProxy, 246 | isReactive, 247 | isReadonly, 248 | isRef, 249 | isRuntimeOnly, 250 | isShallow, 251 | isVNode, 252 | markRaw, 253 | mergeDefaults, 254 | mergeModels, 255 | mergeProps, 256 | nextTick, 257 | normalizeClass, 258 | normalizeProps, 259 | normalizeStyle, 260 | onActivated, 261 | onBeforeMount, 262 | onBeforeUnmount, 263 | onBeforeUpdate, 264 | onDeactivated, 265 | onErrorCaptured, 266 | onMounted, 267 | onRenderTracked, 268 | onRenderTriggered, 269 | onScopeDispose, 270 | onServerPrefetch, 271 | onUnmounted, 272 | onUpdated, 273 | onWatcherCleanup, 274 | openBlock, 275 | popScopeId, 276 | provide, 277 | proxyRefs, 278 | pushScopeId, 279 | queuePostFlushCb, 280 | reactive, 281 | readonly, 282 | ref, 283 | registerRuntimeCompiler, 284 | render, 285 | renderList, 286 | renderSlot, 287 | resolveComponent, 288 | resolveDirective, 289 | resolveDynamicComponent, 290 | resolveFilter, 291 | resolveTransitionHooks, 292 | setBlockTracking, 293 | setDevtoolsHook, 294 | setTransitionHooks, 295 | shallowReactive, 296 | shallowReadonly, 297 | shallowRef, 298 | ssrContextKey, 299 | ssrUtils, 300 | stop, 301 | toDisplayString, 302 | toHandlerKey, 303 | toHandlers, 304 | toRaw, 305 | toRef, 306 | toRefs, 307 | toValue, 308 | transformVNodeArgs, 309 | triggerRef, 310 | unref, 311 | useAttrs, 312 | useCssModule, 313 | useCssVars, 314 | useHost, 315 | useId, 316 | useModel, 317 | useSSRContext, 318 | useShadowRoot, 319 | useSlots, 320 | useTemplateRef, 321 | useTransitionState, 322 | vModelCheckbox, 323 | vModelDynamic, 324 | vModelRadio, 325 | vModelSelect, 326 | vModelText, 327 | vShow, 328 | version, 329 | warn, 330 | watch, 331 | watchEffect, 332 | watchPostEffect, 333 | watchSyncEffect, 334 | withAsyncContext, 335 | withCtx, 336 | withDefaults, 337 | withDirectives, 338 | withKeys, 339 | withMemo, 340 | withModifiers, 341 | withScopeId 342 | }; 343 | //# sourceMappingURL=vue.js.map 344 | -------------------------------------------------------------------------------- /docs/guide/modules/element.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: element 3 | --- 4 | 5 | # element 6 | 7 | 元素模块 8 | 9 | ## isWindow() 10 | 11 | 判断是否 `Window` 对象 12 | 13 | - 类型 14 | 15 | ```ts 16 | isWindow(data: any): boolean 17 | ``` 18 | 19 | - 详细信息 20 | 21 | 提供一个任意类型的入参,该方法会判断它是否为 `Window` 对象,返回 `boolean` 值 22 | 23 | - 示例 24 | 25 | ```ts 26 | import { element } from 'dap-util' 27 | element.isWindow(window) //true 28 | element.isWindow('a') //false 29 | ``` 30 | 31 | ## getElementPoint() 32 | 33 | 获取元素距离指定祖先元素的位置 34 | 35 | - 类型 36 | 37 | ```ts 38 | getElementPoint(el: HTMLElement, root?: HTMLElement): PlacementType 39 | ``` 40 | 41 | - 详细信息 42 | 43 | 第一个入参表示指定的元素,第二个入参表示目标元素,即指定的元素的定位父元素或者祖先元素,未设置则默认为 `document.body` 44 | 45 | 该方法会计算指定元素到目标元素之间的距离,返回值包含如下属性: 46 | 47 | - `top`:类型为 `number`,表示指定元素顶部到目标元素顶部的距离 48 | - `bottom`:类型为 `number`,表示指定元素底部到目标元素底部的距离 49 | - `left`:类型为 `number`,表示指定元素左侧到目标元素左侧的距离 50 | - `right`:类型为 `number`,表示指定元素右侧到目标元素右侧的距离 51 | 52 | - 示例 53 | 54 | ```ts 55 | import { element } from 'dap-util' 56 | //假定el是target的子元素 57 | const placement = element.getElementPoint(el, target) 58 | ``` 59 | 60 | ## isContains() 61 | 62 | 判断某个元素是否包含另一个元素 63 | 64 | - 类型 65 | 66 | ```ts 67 | isContains(parentNode: HTMLElement, childNode: HTMLElement): boolean 68 | ``` 69 | 70 | - 详细信息 71 | 72 | 第一个入参表示指定元素,第二个入参表示目标元素,该方法会判断目标元素是不是指定元素的子孙元素,即指定元素是包含目标元素的,如果二者是同一个元素,也视为包含关系,该方法返回 `boolean` 值 73 | 74 | - 示例 75 | 76 | ```ts 77 | import { element } from 'dap-util' 78 | const isContains = element.isContains(el, target) 79 | ``` 80 | 81 | ## isParentNode() 82 | 83 | 判断某个元素是否另一个元素的父元素 84 | 85 | - 类型 86 | 87 | ```ts 88 | isParentNode(parentNode: HTMLElement, childNode: HTMLElement): boolean 89 | ``` 90 | 91 | - 详细信息 92 | 93 | 第一个入参表示指定的元素,第二个入参表示目标元素,该方法会判断指定元素是否目标元素的父元素,返回 `boolean` 值 94 | 95 | - 示例 96 | 97 | ```ts 98 | import { element } from 'dap-util' 99 | const isParentNode = element.isParentNode(el, target) 100 | ``` 101 | 102 | ## children() 103 | 104 | 获取某个元素的子元素 105 | 106 | - 类型 107 | 108 | ```ts 109 | children(el: HTMLElement, selector?: string): HTMLElement[] 110 | ``` 111 | 112 | - 详细信息 113 | 114 | 第一个入参表示指定的元素,第二个入参表示指定的选择器字符串,等同于 `querySelectorAll` 的参数,如果不设置则默认查询所有的子元素,如果设置了,则只查找符合选择器的子元素 115 | 116 | - 示例 117 | 118 | ```ts 119 | import { element } from 'dap-util' 120 | const children = element.children(document.body, '.a') // 查找body下的 class 是 a 的元素 121 | ``` 122 | 123 | ## siblings() 124 | 125 | 获取某个元素的兄弟元素 126 | 127 | - 类型 128 | 129 | ```ts 130 | siblings(el: HTMLElement, selector?: string): HTMLElement[] 131 | ``` 132 | 133 | - 详细信息 134 | 135 | 第一个入参表示指定的元素,第二个入参表示指定的选择器字符串,等同于 `querySelectorAll` 的参数,如果不设置则默认查询所有的兄弟元素,如果设置了,则只查找符合选择器的兄弟元素 136 | 137 | - 示例 138 | 139 | ```ts 140 | import { element } from 'dap-util' 141 | const elements = element.siblings(el, '.a') // 查找el的兄弟元素中 class 是 a 的元素 142 | ``` 143 | 144 | ## rem2px() 145 | 146 | `rem` 的值转为 `px` 的值 147 | 148 | - 类型 149 | 150 | ```ts 151 | rem2px(num: number): number 152 | ``` 153 | 154 | - 详细信息 155 | 156 | 提供一个入参,类型为 `number`,表示 `rem` 单位的值,该方法会返回一个值,表示 `px` 单位的值 157 | 158 | - 示例 159 | 160 | ```ts 161 | import { element } from 'dap-util' 162 | const pxValue = element.rem2px(1) //默认情况下1rem=16px,则输出16 163 | ``` 164 | 165 | ## px2rem() 166 | 167 | `px` 的值转为 `rem` 的值 168 | 169 | - 类型 170 | 171 | ```ts 172 | px2rem(num: number): number 173 | ``` 174 | 175 | - 详细信息 176 | 177 | 提供一个入参,类型为 `number`,表示 `px` 单位的值,该方法会返回一个值,表示 `rem` 单位的值 178 | 179 | - 示例 180 | 181 | ```ts 182 | import { element } from 'dap-util' 183 | const remValue = element.px2rem(32) //默认情况下1rem=16px,则输出2 184 | ``` 185 | 186 | ## width() 187 | 188 | 获取元素的内容宽度 189 | 190 | - 类型 191 | 192 | ```ts 193 | width(el?: HTMLElement | string): number 194 | ``` 195 | 196 | - 详细信息 197 | 198 | 提供一个入参,类型为 `HTMLElement | string`,表示指定的元素,支持 css 选择器字符串,如果不设置则默认为 `document.body` 199 | 200 | 该方法会返回指定元素的内容宽度,所谓内容宽度指的是不包括边框宽度和内边距大小 201 | 202 | - 示例 203 | 204 | ```ts 205 | import { element } from 'dap-util' 206 | const width = element.width(document.body) // 获取body的内容宽度 207 | ``` 208 | 209 | ## height() 210 | 211 | 获取元素的内容高度 212 | 213 | - 类型 214 | 215 | ```ts 216 | height(el?: HTMLElement | string): number 217 | ``` 218 | 219 | - 详细信息 220 | 221 | 提供一个入参,类型为 `HTMLElement | string`,表示指定的元素,支持 css 选择器字符串,如果不设置则默认为 `document.body` 222 | 223 | 该方法会返回指定元素的内容高度,所谓内容高度指的是不包括边框宽度和内边距大小 224 | 225 | - 示例 226 | 227 | ```ts 228 | import { element } from 'dap-util' 229 | const height = element.height(document.body) // 获取body的内容高度 230 | ``` 231 | 232 | ## removeClass() 233 | 234 | 移除 `class` 235 | 236 | - 类型 237 | 238 | ```ts 239 | removeClass(el: HTMLElement, className: string): void 240 | ``` 241 | 242 | - 详细信息 243 | 244 | 第一个入参表示需要移除 `class` 的元素,第二个参数表示移除的 `class` 值,支持移除多个,以空格划分 245 | 246 | - 示例 247 | 248 | ```ts 249 | import { element } from 'dap-util' 250 | element.removeClass(document.body, 'a b') //移除body上的a和b两个class 251 | ``` 252 | 253 | ## addClass() 254 | 255 | 添加 `class` 256 | 257 | - 类型 258 | 259 | ```ts 260 | addClass(el: HTMLElement, className: string): void 261 | ``` 262 | 263 | - 详细信息 264 | 265 | 第一个入参表示需要添加 `class` 的元素,第二个参数表示添加的 `class` 值,支持添加多个,以空格划分 266 | 267 | - 示例 268 | 269 | ```ts 270 | import { element } from 'dap-util' 271 | element.addClass(document.body, 'a b') //给body添加a和b两个class 272 | ``` 273 | 274 | ## hasClass() 275 | 276 | 判断是否拥有指定的 `class` 277 | 278 | - 类型 279 | 280 | ```ts 281 | hasClass(el: HTMLElement, className: string): boolean 282 | ``` 283 | 284 | - 详细信息 285 | 286 | 第一个入参表示需要判断的元素,第二个参数表示指定的 `class` 值,支持多个,以空格划分 287 | 288 | - 示例 289 | 290 | ```ts 291 | import { element } from 'dap-util' 292 | element.hasClass(document.body, 'a b') //判断body是否有a和b两个class 293 | ``` 294 | 295 | ## scrollTopBottomTrigger() 296 | 297 | 监听元素滚动到顶部或者底部 298 | 299 | - 类型 300 | 301 | ```ts 302 | scrollTopBottomTrigger(el?: HTMLElement | string | Window, callback?: (options: scrollTopBottomResultType) => void): void 303 | ``` 304 | 305 | - 详细信息 306 | 307 | 第一个入参表示监听的元素,支持 css 选择器字符串,如果未设置则默认为窗口滚动;第二个入参表示回调函数,该函数的回调参数包含 2 个属性: 308 | 309 | - `state`:取值为 `top` | `bottom`,表示滚动到顶部还是底部 310 | - `target`:滚动的元素 311 | 312 | 回调函数会在元素到达顶部或者底部时触发 313 | 314 | - 示例 315 | 316 | ```ts 317 | import { element } from 'dap-util' 318 | element.scrollTopBottomTrigger(document.body, options => { 319 | console.log(options.state) //通过state判断是滚动到顶部还是底部 320 | }) 321 | ``` 322 | 323 | ## getScrollWidth() 324 | 325 | 获取文档或元素的总宽度 326 | 327 | - 类型 328 | 329 | ```ts 330 | getScrollWidth(el?: HTMLElement | string): number 331 | ``` 332 | 333 | - 详细信息 334 | 335 | 提供一个入参,表示指定的元素,支持 `css` 字符串选择器,如果不设置则默认为文档元素,该方法会返回该元素或者文档的总宽度,即如果有滚动条,则会包含滚动内容在内的宽度 336 | 337 | - 示例 338 | 339 | ```ts 340 | import { element } from 'dap-util' 341 | const width = element.getScrollWidth() //获取页面的总宽度 342 | ``` 343 | 344 | ## getScrollHeight() 345 | 346 | 获取文档或元素的总高度 347 | 348 | - 类型 349 | 350 | ```ts 351 | getScrollHeight(el?: HTMLElement | string): number 352 | ``` 353 | 354 | - 详细信息 355 | 356 | 提供一个入参,表示指定的元素,支持 `css` 字符串选择器,如果不设置则默认为文档元素,该方法会返回该元素或者文档的总高度,即如果有滚动条,则会包含滚动内容在内的高度 357 | 358 | - 示例 359 | 360 | ```ts 361 | import { element } from 'dap-util' 362 | const height = element.getScrollHeight() //获取页面的总高度 363 | ``` 364 | 365 | ## setScrollTop() 366 | 367 | 设置滚动条在 Y 轴上的距离 368 | 369 | - 类型 370 | 371 | ```ts 372 | setScrollTop(options: ScrollOptionsType): Promise 373 | ``` 374 | 375 | - 详细信息 376 | 377 | 提供一个入参,类型为 `ScrollOptionsType`,包含如下属性: 378 | 379 | - `el`:指定的元素,支持 `css` 选择器字符串,支持 `window` 380 | - `time`:滚动到指定距离的时间,单位 `ms`,不设置或者 `<=0` 则直接设置到指定距离 381 | - `number`:滚动到的指定距离,单位 `px` 382 | 383 | 该方法会将指定元素的垂直滚动条设置到指定的距离,如果设置了 `time`,则会有一个滚动的效果 384 | 385 | - 示例 386 | 387 | ```ts 388 | import { element } from 'dap-util' 389 | //将窗口的垂直滚动条滚动到120px的位置,用时500ms 390 | setScrollTop({ 391 | el: window, 392 | time: 500, 393 | number: 120 394 | }) 395 | ``` 396 | 397 | ## getScrollTop() 398 | 399 | 获取滚动条在 Y 轴上滚动的距离 400 | 401 | - 类型 402 | 403 | ```ts 404 | getScrollTop(el?: HTMLElement | string | Window): number 405 | ``` 406 | 407 | - 详细信息 408 | 409 | 提供一个入参,类型为 `HTMLElement | string | Window`,表示指定的元素,支持 `css` 选择器字符串,也支持 `window`,如果未指定,则默认为 `window`,该方法返回该元素垂直方向的滚动条滚动的距离,单位 `px` 410 | 411 | - 示例 412 | 413 | ```ts 414 | import { element } from 'dap-util' 415 | const scrollTop = element.getScrollTop() 416 | ``` 417 | 418 | ## getScrollLeft() 419 | 420 | 获取滚动条在 X 轴上滚动的距离 421 | 422 | - 类型 423 | 424 | ```ts 425 | getScrollLeft(el?: HTMLElement | string | Window): number 426 | ``` 427 | 428 | - 详细信息 429 | 430 | 提供一个入参,类型为 `HTMLElement | string | Window`,表示指定的元素,支持 `css` 选择器字符串,也支持 `window`,如果未指定,则默认为 `window`,该方法返回该元素水平方向的滚动条滚动的距离,单位 `px` 431 | 432 | - 示例 433 | 434 | ```ts 435 | import { element } from 'dap-util' 436 | const scrollLeft = element.getScrollLeft() 437 | ``` 438 | 439 | ## setScrollLeft() 440 | 441 | 设置滚动条在 X 轴上的距离 442 | 443 | - 类型 444 | 445 | ```ts 446 | setScrollLeft(options: ScrollOptionsType): Promise 447 | ``` 448 | 449 | - 详细信息 450 | 451 | 提供一个入参,类型为 `ScrollOptionsType`,包含如下属性: 452 | 453 | - `el`:指定的元素,支持 `css` 选择器字符串,支持 `window` 454 | - `time`:滚动到指定距离的时间,单位 `ms`,不设置或者 `<=0` 则直接设置到指定距离 455 | - `number`:滚动到的指定距离,单位 `px` 456 | 457 | 该方法会将指定元素的横向滚动条设置到指定的距离,如果设置了 `time`,则会有一个滚动的效果 458 | 459 | - 示例 460 | 461 | ```ts 462 | import { element } from 'dap-util' 463 | //将窗口的横向滚动条滚动到120px的位置,用时500ms 464 | setScrollLeft({ 465 | el: window, 466 | time: 500, 467 | number: 120 468 | }) 469 | ``` 470 | 471 | ## getCssStyle() 472 | 473 | 获取元素指定样式 474 | 475 | - 类型 476 | 477 | ```ts 478 | getCssStyle(el: HTMLElement, cssName: string): string 479 | ``` 480 | 481 | - 详细信息 482 | 483 | 第一个入参表示指定的元素,第二个入参表示 `css` 样式名称,该方法返回该 css 样式名称的值 484 | 485 | - 示例 486 | 487 | ```ts 488 | import { element } from 'dap-util' 489 | const color = element.getCssStyle(el, 'color') //获取el元素的color值 490 | ``` 491 | 492 | ## getCssSelector() 493 | 494 | 判断字符串属于哪种 css 选择器 495 | 496 | - 类型 497 | 498 | ```ts 499 | getCssSelector(selector: string): { 500 | type: "id" | "class" | "attribute" | "tag"; 501 | value: string | { 502 | attributeName: string; 503 | attributeValue: string; 504 | }; 505 | } 506 | ``` 507 | 508 | - 详细信息 509 | 510 | 提供一个入参,类型为 `string`,表示需要判断的 `css` 选择器字符串 511 | 512 | 该方法返回的值包含 2 个属性: 513 | 514 | - `type`:选择器的类型,可取值为 `id` `class` `attribute` `tag`,分别表示 ID 选择器、类名选择器、属性选择器、标签选择器 515 | - `value`:选择器的值,如果是属性选择器,可存在属性值的情况下,该属性包含 attributeName 和 attributeValue 两个字段,分别表示属性名称和属性值,如 `[data-id="a"]`,其他情况下返回值都是字符串 516 | 517 | - 示例 518 | 519 | ```ts 520 | import { element } from 'dap-util' 521 | const val = element.getCssSelector('#a') //a 522 | const val = element.getCssSelector('.red') //red 523 | const val = element.getCssSelector('[data-id="a"') //{ attributeName: "data-id", attributeValue: "a" } 524 | ``` 525 | 526 | ## getElementBounding() 527 | 528 | 获取元素距离可视窗口的位置 529 | 530 | - 类型 531 | 532 | ```ts 533 | getElementBounding(el?: HTMLElement | string): PlacementType 534 | ``` 535 | 536 | - 详细信息 537 | 538 | 提供一个入参,表示指定的元素,该方法会获取指定元素到可视窗口之间的距离,返回值包含如下属性: 539 | 540 | - `top`:类型为 `number`,表示指定元素顶部到可视窗口顶部的距离 541 | - `bottom`:类型为 `number`,表示指定元素底部到可视窗口底部的距离 542 | - `left`:类型为 `number`,表示指定元素左侧到可视窗口左侧的距离 543 | - `right`:类型为 `number`,表示指定元素右侧到可视窗口右侧的距离 544 | 545 | - 示例 546 | 547 | ```ts 548 | import { element } from 'dap-util' 549 | const placement = element.getElementBounding(el) 550 | ``` 551 | 552 | ## isElement() 553 | 554 | 判断是否是元素 555 | 556 | - 类型 557 | 558 | ```ts 559 | isElement(el: any): boolean 560 | ``` 561 | 562 | - 详细信息 563 | 564 | 提供一个任意类型的入参,该方法会判断入参是否为元素类型,所谓元素类型指的是类型为 `Node` 且 `nodeType` 为 565 | 566 | - 示例 567 | 568 | ```ts 569 | import { element } from 'dap-util' 570 | element.isElement('a') //false 571 | element.isElement(document.body) //true 572 | ``` 573 | 574 | ## string2dom() 575 | 576 | 字符串转 dom 577 | 578 | - 类型 579 | 580 | ```ts 581 | string2dom(html: string): HTMLElement | HTMLElement[] 582 | ``` 583 | 584 | - 详细信息 585 | 586 | 提供一个入参,类型为 `string`,表示需要转换的字符串,该方法返回一个元素或者元素数组 587 | 588 | - 示例 589 | 590 | ```ts 591 | import { element } from 'dap-util' 592 | const dom = element.string2dom('

hello

') 593 | ``` 594 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/vitepress___@vueuse_core.js: -------------------------------------------------------------------------------- 1 | import { 2 | DefaultMagicKeysAliasMap, 3 | StorageSerializers, 4 | TransitionPresets, 5 | assert, 6 | breakpointsAntDesign, 7 | breakpointsBootstrapV5, 8 | breakpointsElement, 9 | breakpointsMasterCss, 10 | breakpointsPrimeFlex, 11 | breakpointsQuasar, 12 | breakpointsSematic, 13 | breakpointsTailwind, 14 | breakpointsVuetify, 15 | breakpointsVuetifyV2, 16 | breakpointsVuetifyV3, 17 | bypassFilter, 18 | camelize, 19 | clamp, 20 | cloneFnJSON, 21 | computedAsync, 22 | computedEager, 23 | computedInject, 24 | computedWithControl, 25 | containsProp, 26 | controlledRef, 27 | createEventHook, 28 | createFetch, 29 | createFilterWrapper, 30 | createGlobalState, 31 | createInjectionState, 32 | createReusableTemplate, 33 | createSharedComposable, 34 | createSingletonPromise, 35 | createTemplatePromise, 36 | createUnrefFn, 37 | customStorageEventName, 38 | debounceFilter, 39 | defaultDocument, 40 | defaultLocation, 41 | defaultNavigator, 42 | defaultWindow, 43 | directiveHooks, 44 | executeTransition, 45 | extendRef, 46 | formatDate, 47 | formatTimeAgo, 48 | get, 49 | getLifeCycleTarget, 50 | getSSRHandler, 51 | hasOwn, 52 | hyphenate, 53 | identity, 54 | increaseWithUnit, 55 | injectLocal, 56 | invoke, 57 | isClient, 58 | isDef, 59 | isDefined, 60 | isIOS, 61 | isObject, 62 | isWorker, 63 | makeDestructurable, 64 | mapGamepadToXbox360Controller, 65 | noop, 66 | normalizeDate, 67 | notNullish, 68 | now, 69 | objectEntries, 70 | objectOmit, 71 | objectPick, 72 | onClickOutside, 73 | onKeyDown, 74 | onKeyPressed, 75 | onKeyStroke, 76 | onKeyUp, 77 | onLongPress, 78 | onStartTyping, 79 | pausableFilter, 80 | promiseTimeout, 81 | provideLocal, 82 | rand, 83 | reactify, 84 | reactifyObject, 85 | reactiveComputed, 86 | reactiveOmit, 87 | reactivePick, 88 | refAutoReset, 89 | refDebounced, 90 | refDefault, 91 | refThrottled, 92 | refWithControl, 93 | resolveRef, 94 | resolveUnref, 95 | set, 96 | setSSRHandler, 97 | syncRef, 98 | syncRefs, 99 | templateRef, 100 | throttleFilter, 101 | timestamp, 102 | toReactive, 103 | toRef, 104 | toRefs, 105 | toValue, 106 | tryOnBeforeMount, 107 | tryOnBeforeUnmount, 108 | tryOnMounted, 109 | tryOnScopeDispose, 110 | tryOnUnmounted, 111 | unrefElement, 112 | until, 113 | useActiveElement, 114 | useAnimate, 115 | useArrayDifference, 116 | useArrayEvery, 117 | useArrayFilter, 118 | useArrayFind, 119 | useArrayFindIndex, 120 | useArrayFindLast, 121 | useArrayIncludes, 122 | useArrayJoin, 123 | useArrayMap, 124 | useArrayReduce, 125 | useArraySome, 126 | useArrayUnique, 127 | useAsyncQueue, 128 | useAsyncState, 129 | useBase64, 130 | useBattery, 131 | useBluetooth, 132 | useBreakpoints, 133 | useBroadcastChannel, 134 | useBrowserLocation, 135 | useCached, 136 | useClipboard, 137 | useClipboardItems, 138 | useCloned, 139 | useColorMode, 140 | useConfirmDialog, 141 | useCounter, 142 | useCssVar, 143 | useCurrentElement, 144 | useCycleList, 145 | useDark, 146 | useDateFormat, 147 | useDebounceFn, 148 | useDebouncedRefHistory, 149 | useDeviceMotion, 150 | useDeviceOrientation, 151 | useDevicePixelRatio, 152 | useDevicesList, 153 | useDisplayMedia, 154 | useDocumentVisibility, 155 | useDraggable, 156 | useDropZone, 157 | useElementBounding, 158 | useElementByPoint, 159 | useElementHover, 160 | useElementSize, 161 | useElementVisibility, 162 | useEventBus, 163 | useEventListener, 164 | useEventSource, 165 | useEyeDropper, 166 | useFavicon, 167 | useFetch, 168 | useFileDialog, 169 | useFileSystemAccess, 170 | useFocus, 171 | useFocusWithin, 172 | useFps, 173 | useFullscreen, 174 | useGamepad, 175 | useGeolocation, 176 | useIdle, 177 | useImage, 178 | useInfiniteScroll, 179 | useIntersectionObserver, 180 | useInterval, 181 | useIntervalFn, 182 | useKeyModifier, 183 | useLastChanged, 184 | useLocalStorage, 185 | useMagicKeys, 186 | useManualRefHistory, 187 | useMediaControls, 188 | useMediaQuery, 189 | useMemoize, 190 | useMemory, 191 | useMounted, 192 | useMouse, 193 | useMouseInElement, 194 | useMousePressed, 195 | useMutationObserver, 196 | useNavigatorLanguage, 197 | useNetwork, 198 | useNow, 199 | useObjectUrl, 200 | useOffsetPagination, 201 | useOnline, 202 | usePageLeave, 203 | useParallax, 204 | useParentElement, 205 | usePerformanceObserver, 206 | usePermission, 207 | usePointer, 208 | usePointerLock, 209 | usePointerSwipe, 210 | usePreferredColorScheme, 211 | usePreferredContrast, 212 | usePreferredDark, 213 | usePreferredLanguages, 214 | usePreferredReducedMotion, 215 | usePrevious, 216 | useRafFn, 217 | useRefHistory, 218 | useResizeObserver, 219 | useScreenOrientation, 220 | useScreenSafeArea, 221 | useScriptTag, 222 | useScroll, 223 | useScrollLock, 224 | useSessionStorage, 225 | useShare, 226 | useSorted, 227 | useSpeechRecognition, 228 | useSpeechSynthesis, 229 | useStepper, 230 | useStorage, 231 | useStorageAsync, 232 | useStyleTag, 233 | useSupported, 234 | useSwipe, 235 | useTemplateRefsList, 236 | useTextDirection, 237 | useTextSelection, 238 | useTextareaAutosize, 239 | useThrottleFn, 240 | useThrottledRefHistory, 241 | useTimeAgo, 242 | useTimeout, 243 | useTimeoutFn, 244 | useTimeoutPoll, 245 | useTimestamp, 246 | useTitle, 247 | useToNumber, 248 | useToString, 249 | useToggle, 250 | useTransition, 251 | useUrlSearchParams, 252 | useUserMedia, 253 | useVModel, 254 | useVModels, 255 | useVibrate, 256 | useVirtualList, 257 | useWakeLock, 258 | useWebNotification, 259 | useWebSocket, 260 | useWebWorker, 261 | useWebWorkerFn, 262 | useWindowFocus, 263 | useWindowScroll, 264 | useWindowSize, 265 | watchArray, 266 | watchAtMost, 267 | watchDebounced, 268 | watchDeep, 269 | watchIgnorable, 270 | watchImmediate, 271 | watchOnce, 272 | watchPausable, 273 | watchThrottled, 274 | watchTriggerable, 275 | watchWithFilter, 276 | whenever 277 | } from "./chunk-YJ6QP2VR.js"; 278 | import "./chunk-LW4I4DCF.js"; 279 | export { 280 | DefaultMagicKeysAliasMap, 281 | StorageSerializers, 282 | TransitionPresets, 283 | assert, 284 | computedAsync as asyncComputed, 285 | refAutoReset as autoResetRef, 286 | breakpointsAntDesign, 287 | breakpointsBootstrapV5, 288 | breakpointsElement, 289 | breakpointsMasterCss, 290 | breakpointsPrimeFlex, 291 | breakpointsQuasar, 292 | breakpointsSematic, 293 | breakpointsTailwind, 294 | breakpointsVuetify, 295 | breakpointsVuetifyV2, 296 | breakpointsVuetifyV3, 297 | bypassFilter, 298 | camelize, 299 | clamp, 300 | cloneFnJSON, 301 | computedAsync, 302 | computedEager, 303 | computedInject, 304 | computedWithControl, 305 | containsProp, 306 | computedWithControl as controlledComputed, 307 | controlledRef, 308 | createEventHook, 309 | createFetch, 310 | createFilterWrapper, 311 | createGlobalState, 312 | createInjectionState, 313 | reactify as createReactiveFn, 314 | createReusableTemplate, 315 | createSharedComposable, 316 | createSingletonPromise, 317 | createTemplatePromise, 318 | createUnrefFn, 319 | customStorageEventName, 320 | debounceFilter, 321 | refDebounced as debouncedRef, 322 | watchDebounced as debouncedWatch, 323 | defaultDocument, 324 | defaultLocation, 325 | defaultNavigator, 326 | defaultWindow, 327 | directiveHooks, 328 | computedEager as eagerComputed, 329 | executeTransition, 330 | extendRef, 331 | formatDate, 332 | formatTimeAgo, 333 | get, 334 | getLifeCycleTarget, 335 | getSSRHandler, 336 | hasOwn, 337 | hyphenate, 338 | identity, 339 | watchIgnorable as ignorableWatch, 340 | increaseWithUnit, 341 | injectLocal, 342 | invoke, 343 | isClient, 344 | isDef, 345 | isDefined, 346 | isIOS, 347 | isObject, 348 | isWorker, 349 | makeDestructurable, 350 | mapGamepadToXbox360Controller, 351 | noop, 352 | normalizeDate, 353 | notNullish, 354 | now, 355 | objectEntries, 356 | objectOmit, 357 | objectPick, 358 | onClickOutside, 359 | onKeyDown, 360 | onKeyPressed, 361 | onKeyStroke, 362 | onKeyUp, 363 | onLongPress, 364 | onStartTyping, 365 | pausableFilter, 366 | watchPausable as pausableWatch, 367 | promiseTimeout, 368 | provideLocal, 369 | rand, 370 | reactify, 371 | reactifyObject, 372 | reactiveComputed, 373 | reactiveOmit, 374 | reactivePick, 375 | refAutoReset, 376 | refDebounced, 377 | refDefault, 378 | refThrottled, 379 | refWithControl, 380 | resolveRef, 381 | resolveUnref, 382 | set, 383 | setSSRHandler, 384 | syncRef, 385 | syncRefs, 386 | templateRef, 387 | throttleFilter, 388 | refThrottled as throttledRef, 389 | watchThrottled as throttledWatch, 390 | timestamp, 391 | toReactive, 392 | toRef, 393 | toRefs, 394 | toValue, 395 | tryOnBeforeMount, 396 | tryOnBeforeUnmount, 397 | tryOnMounted, 398 | tryOnScopeDispose, 399 | tryOnUnmounted, 400 | unrefElement, 401 | until, 402 | useActiveElement, 403 | useAnimate, 404 | useArrayDifference, 405 | useArrayEvery, 406 | useArrayFilter, 407 | useArrayFind, 408 | useArrayFindIndex, 409 | useArrayFindLast, 410 | useArrayIncludes, 411 | useArrayJoin, 412 | useArrayMap, 413 | useArrayReduce, 414 | useArraySome, 415 | useArrayUnique, 416 | useAsyncQueue, 417 | useAsyncState, 418 | useBase64, 419 | useBattery, 420 | useBluetooth, 421 | useBreakpoints, 422 | useBroadcastChannel, 423 | useBrowserLocation, 424 | useCached, 425 | useClipboard, 426 | useClipboardItems, 427 | useCloned, 428 | useColorMode, 429 | useConfirmDialog, 430 | useCounter, 431 | useCssVar, 432 | useCurrentElement, 433 | useCycleList, 434 | useDark, 435 | useDateFormat, 436 | refDebounced as useDebounce, 437 | useDebounceFn, 438 | useDebouncedRefHistory, 439 | useDeviceMotion, 440 | useDeviceOrientation, 441 | useDevicePixelRatio, 442 | useDevicesList, 443 | useDisplayMedia, 444 | useDocumentVisibility, 445 | useDraggable, 446 | useDropZone, 447 | useElementBounding, 448 | useElementByPoint, 449 | useElementHover, 450 | useElementSize, 451 | useElementVisibility, 452 | useEventBus, 453 | useEventListener, 454 | useEventSource, 455 | useEyeDropper, 456 | useFavicon, 457 | useFetch, 458 | useFileDialog, 459 | useFileSystemAccess, 460 | useFocus, 461 | useFocusWithin, 462 | useFps, 463 | useFullscreen, 464 | useGamepad, 465 | useGeolocation, 466 | useIdle, 467 | useImage, 468 | useInfiniteScroll, 469 | useIntersectionObserver, 470 | useInterval, 471 | useIntervalFn, 472 | useKeyModifier, 473 | useLastChanged, 474 | useLocalStorage, 475 | useMagicKeys, 476 | useManualRefHistory, 477 | useMediaControls, 478 | useMediaQuery, 479 | useMemoize, 480 | useMemory, 481 | useMounted, 482 | useMouse, 483 | useMouseInElement, 484 | useMousePressed, 485 | useMutationObserver, 486 | useNavigatorLanguage, 487 | useNetwork, 488 | useNow, 489 | useObjectUrl, 490 | useOffsetPagination, 491 | useOnline, 492 | usePageLeave, 493 | useParallax, 494 | useParentElement, 495 | usePerformanceObserver, 496 | usePermission, 497 | usePointer, 498 | usePointerLock, 499 | usePointerSwipe, 500 | usePreferredColorScheme, 501 | usePreferredContrast, 502 | usePreferredDark, 503 | usePreferredLanguages, 504 | usePreferredReducedMotion, 505 | usePrevious, 506 | useRafFn, 507 | useRefHistory, 508 | useResizeObserver, 509 | useScreenOrientation, 510 | useScreenSafeArea, 511 | useScriptTag, 512 | useScroll, 513 | useScrollLock, 514 | useSessionStorage, 515 | useShare, 516 | useSorted, 517 | useSpeechRecognition, 518 | useSpeechSynthesis, 519 | useStepper, 520 | useStorage, 521 | useStorageAsync, 522 | useStyleTag, 523 | useSupported, 524 | useSwipe, 525 | useTemplateRefsList, 526 | useTextDirection, 527 | useTextSelection, 528 | useTextareaAutosize, 529 | refThrottled as useThrottle, 530 | useThrottleFn, 531 | useThrottledRefHistory, 532 | useTimeAgo, 533 | useTimeout, 534 | useTimeoutFn, 535 | useTimeoutPoll, 536 | useTimestamp, 537 | useTitle, 538 | useToNumber, 539 | useToString, 540 | useToggle, 541 | useTransition, 542 | useUrlSearchParams, 543 | useUserMedia, 544 | useVModel, 545 | useVModels, 546 | useVibrate, 547 | useVirtualList, 548 | useWakeLock, 549 | useWebNotification, 550 | useWebSocket, 551 | useWebWorker, 552 | useWebWorkerFn, 553 | useWindowFocus, 554 | useWindowScroll, 555 | useWindowSize, 556 | watchArray, 557 | watchAtMost, 558 | watchDebounced, 559 | watchDeep, 560 | watchIgnorable, 561 | watchImmediate, 562 | watchOnce, 563 | watchPausable, 564 | watchThrottled, 565 | watchTriggerable, 566 | watchWithFilter, 567 | whenever 568 | }; 569 | //# sourceMappingURL=vitepress___@vueuse_core.js.map 570 | -------------------------------------------------------------------------------- /lib/dap-util.umd.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Dap={})}(this,(function(e){"use strict";const t={matchingText:(e,t)=>"Chinese"==t?/^[\u4e00-\u9fa5]+$/.test(e):"chinese"==t?/[\u4e00-\u9fa5]/.test(e):"email"==t?/^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(e):"username"==t?/^[a-zA-Z0-9_]{4,16}$/.test(e):"int+"==t?/^\d+$/.test(e):"int-"==t?/^-\d+$/.test(e):"int"==t?/^-?\d+$/.test(e):"pos"==t?/^\d*\.?\d+$/.test(e):"neg"==t?/^-\d*\.?\d+$/.test(e):"number"==t?/^-?\d*\.?\d+$/.test(e):"phone"==t?/^1[0-9]\d{9}$/.test(e):"idCard"==t?/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(e):"url"==t?/^(https?|ftp):\/\/(-\.)?([^\s\/?\.#-]+\.?)+(\/[^\s]*)?$/.test(e):"IPv4"==t?/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(e):"hex"==t?/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/.test(e):"rgb"==t?/^rgb\((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\)$/.test(e):"rgba"==t?/^rgba\((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(0?\.\d|1(\.0)?|0)\)$/.test(e):"QQ"==t?/^[1-9][0-9]{4,10}$/.test(e):"weixin"==t?/^[a-zA-Z]([-_a-zA-Z0-9]{5,19})+$/.test(e):"plate"==t&&/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$|^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[D|F]{1}[A-Z0-9]{5,6}$/.test(e),getUrlParams(e){const t=new RegExp("(^|&)"+e+"=([^&]*)(&|$)");let n=window.location.search.substring(1);if(!n){const e=window.location.hash.split("?");2==e.length&&(n=e[1])}let o=n.match(t);if(o)return decodeURIComponent(o[2])},isEmptyObject(e){return this.isObject(e)&&0==Object.keys(e).length},equal(e,t){if(typeof e!=typeof t)return!1;if(this.isObject(e)&&this.isObject(t)){const n=Object.getOwnPropertyNames(e),o=Object.getOwnPropertyNames(t);if(n.length!=o.length)return!1;const r=n.length;let s=!0;for(let i=0;i"object"==typeof e&&!!e,copyText(e){if(!navigator.clipboard)throw new Error("navigator.clipboard must be obtained in a secure environment, such as localhost, 127.0.0.1, or https, so the method won't work");return navigator.clipboard.writeText(e)},clone(e){if(this.isObject(e)){if(Array.isArray(e))return e.map((e=>this.clone(e)));const t={};for(let n in e)t[n]=this.clone(e[n]);return t}return e}},n={rgb2hsv(e){if(3!=e.length)throw new TypeError("Invalid argument");let t=0,n=0,o=0,r=e[0]>=255?255:e[0],s=e[1]>=255?255:e[1],i=e[2]>=255?255:e[2];r=r<=0?0:r,s=s<=0?0:s,i=i<=0?0:i;let l=Math.max(r,s,i),d=Math.min(r,s,i);return o=l/255,n=0===l?0:1-d/l,l===d?t=0:l===r&&s>=i?t=(s-i)/(l-d)*60+0:l===r&&s=360||e[0]<=0?0:e[0],n=e[1]>=100?100:e[1];n=n<=0?0:n;let o=e[2]>=100?100:e[2];o=o<=0?0:o,n/=100,o/=100;let r=0,s=0,i=0,l=parseInt(t/60%6+""),d=t/60-l,c=o*(1-n),a=o*(1-d*n),u=o*(1-(1-d)*n);switch(l){case 0:r=o,s=u,i=c;break;case 1:r=a,s=o,i=c;break;case 2:r=c,s=o,i=u;break;case 3:r=c,s=a,i=o;break;case 4:r=u,s=c,i=o;break;case 5:r=o,s=c,i=a}return r=parseInt(255*r+""),s=parseInt(255*s+""),i=parseInt(255*i+""),[r,s,i]},rgb2hex(e){if(3!=e.length)throw new TypeError("Invalid argument");return"#"+((1<<24)+(e[0]<<16)+(e[1]<<8)+e[2]).toString(16).slice(1)},hex2rgb(e){let n=e.toLowerCase();if(!t.matchingText(n,"hex"))throw new TypeError("The argument must be a hexadecimal color value");if(4===n.length){let e="#";for(let t=1;t<4;t+=1)e+=n.slice(t,t+1).concat(n.slice(t,t+1));n=e}let o=[];for(let t=1;t<7;t+=2)o.push(parseInt("0x"+n.slice(t,t+2)));return o}},o="_dap-datas",r={remove(e,t){const n=e[o]||{};t?(delete n[t],e[o]=n):e[o]={}},has:(e,t)=>(e[o]||{}).hasOwnProperty(t),get(e,t){const n=e[o]||{};return t?n[t]:n},set(e,t,n){const r=e[o]||{};r[t]=n,e[o]=r}},s={insert(e,t,n){if(n<0)throw new Error("The third argument cannot be less than 0");return e.substring(0,n)+t+e.substring(n,e.length)},delete(e,t,n){if(t<0)throw new Error("The second argument cannot be less than 0");if(n<0)throw new Error("The third argument cannot be less than 0");return e.substring(0,t)+e.substring(t+n,e.length)},replace(e,t,n,o){if(t<0)throw new Error("The second argument cannot be less than 0");if(n<0)throw new Error("The third argument cannot be less than 0");return e.substring(0,t)+o+e.substring(n,e.length)},trim(e,t){let n=e.replace(/(^\s+)|(\s+$)/g,"");return t&&(n=n.replace(/\s/g,"")),n}},i={formatNumber(e){return this.isNumber(e)?e.toString().replace(/(\d)(?=(?:\d{3})+$)/g,"$1,"):e.toString()},isNumber:e=>"number"==typeof e&&!isNaN(e),add:(...e)=>e.reduce(((e,t)=>{let n=0,o=0,r=0;try{n=e.toString().split(".")[1].length}catch(s){}try{o=t.toString().split(".")[1].length}catch(s){}return r=Math.pow(10,Math.max(n,o)),(e*r+t*r)/r})),subtract:(...e)=>e.reduce(((e,t)=>{let n=0,o=0,r=0;try{n=e.toString().split(".")[1].length}catch(s){}try{o=t.toString().split(".")[1].length}catch(s){}return r=Math.pow(10,Math.max(n,o)),(e*r-t*r)/r})),mutiply:(...e)=>e.reduce(((e,t)=>{let n=0,o=e.toString(),r=t.toString();try{n+=o.split(".")[1].length}catch(s){}try{n+=r.split(".")[1].length}catch(s){}return Number(o.replace(".",""))*Number(r.replace(".",""))/Math.pow(10,n)})),divide:(...e)=>e.reduce(((e,t)=>{let n=0,o=0,r=e.toString(),s=t.toString();try{n=r.split(".")[1].length}catch(i){}try{o=s.split(".")[1].length}catch(i){}return Number(r.replace(".",""))/Number(s.replace(".",""))*Math.pow(10,o-n)}))},l={isWindow:e=>e&&e instanceof Window,getElementPoint(e,t){if(this.isElement(t)||(t=document.body),!this.isContains(t,e))throw new Error("The second argument and the first argument have no hierarchical relationship");const n=e;let o=0,r=0;for(;this.isElement(e)&&this.isContains(t,e)&&t!==e;)o+=e.offsetTop,r+=e.offsetLeft,e=e.offsetParent;return{top:o,left:r,right:t.offsetWidth-r-n.offsetWidth,bottom:t.offsetHeight-o-n.offsetHeight}},isContains:(e,t)=>e===t||(e.contains?e.contains(t):!!e.compareDocumentPosition&&!!(16&e.compareDocumentPosition(t))),isParentNode:(e,t)=>e!==t&&t.parentNode===e,children(e,t){const n=Array.from(e.querySelectorAll(t??"*"));return Array.from(n).filter((t=>this.isParentNode(e,t)))},siblings(e,t){if(!e.parentNode)return[];return Array.from(e.parentNode.querySelectorAll(t??"*")).filter((t=>t.parentNode===e.parentNode&&t!=e))},rem2px(e){const t=this.getCssStyle(document.documentElement,"font-size");return i.mutiply(e,parseFloat(t))},px2rem(e){const t=this.getCssStyle(document.documentElement,"font-size");return i.divide(e,parseFloat(t))},width(e){"string"==typeof e&&e&&(e=document.body.querySelector(e)),this.isElement(e)||(e=document.body);const t=e.clientWidth,n=parseFloat(this.getCssStyle(e,"padding-left")),o=parseFloat(this.getCssStyle(e,"padding-right"));return i.subtract(t,n,o)},height(e){"string"==typeof e&&e&&(e=document.body.querySelector(e)),this.isElement(e)||(e=document.body);const t=e.clientHeight,n=parseFloat(this.getCssStyle(e,"padding-top")),o=parseFloat(this.getCssStyle(e,"padding-bottom"));return i.subtract(t,n,o)},removeClass(e,t){s.trim(t).split(/\s+/).forEach((t=>{e.classList.remove(t)}))},addClass(e,t){s.trim(t).split(/\s+/).forEach((t=>{e.classList.add(t)}))},hasClass:(e,t)=>s.trim(t).split(/\s+/).every((t=>e.classList.contains(t))),scrollTopBottomTrigger(e,t){"string"==typeof e&&e&&(e=document.body.querySelector(e));let n=window;this.isElement(e)&&e!=document.body&&e!=document.documentElement&&(n=e),"function"==typeof e&&(t=e);let o=!0;n.addEventListener("scroll",(()=>{if(this.getScrollTop(n)<=0){if(!o)return;"function"==typeof t&&(o=!1,t({state:"top",target:n}))}else{const e={state:"bottom",target:n};let r=0;if(r=n==window?window.innerHeight:n.clientHeight,i.add(this.getScrollTop(n),r)+1>=this.getScrollHeight(n)&&r!=this.getScrollHeight(n)){if(!o)return;"function"==typeof t&&(o=!1,t(e))}else o=!0}}))},getScrollWidth(e){"string"==typeof e&&e&&(e=document.body.querySelector(e));let t=0;return t=this.isElement(e)&&e!=document.documentElement&&e!=document.body?e.scrollWidth:0==document.documentElement.scrollWidth||0==document.body.scrollWidth?document.documentElement.scrollWidth||document.body.scrollWidth:document.documentElement.scrollWidth>document.body.scrollWidth?document.documentElement.scrollWidth:document.body.scrollWidth,t},getScrollHeight(e){"string"==typeof e&&e&&(e=document.body.querySelector(e));let t=0;return t=this.isElement(e)&&e!=document.documentElement&&e!=document.body?e.scrollHeight:0==document.documentElement.scrollHeight||0==document.body.scrollHeight?document.documentElement.scrollHeight||document.body.scrollHeight:document.documentElement.scrollHeight>document.body.scrollHeight?document.documentElement.scrollHeight:document.body.scrollHeight,t},setScrollTop(e){let t=!1,n=e.el;"string"==typeof n&&n&&(n=document.body.querySelector(n));const o=e.number||0,r=e.time||0;return this.isElement(n)&&n!=document.body&&n!=document.documentElement&&n!=window||(t=!0),new Promise((e=>{if(r<=0)t?document.documentElement.scrollTop=document.body.scrollTop=o:n.scrollTop=o,e();else{const s=10;let l=i.divide(r,s),d=this.getScrollTop(n);const c=i.divide(i.subtract(o,d),l),a=setInterval((()=>{l>0?(l--,t?document.documentElement.scrollTop=document.body.scrollTop=d=i.add(d,c):n.scrollTop=d=i.add(d,c)):(clearInterval(a),e())}),s)}}))},getScrollTop(e){"string"==typeof e&&e&&(e=document.body.querySelector(e));let t=0;return t=this.isElement(e)&&e!=document.body&&e!=document.documentElement&&e!=window?e.scrollTop:0==document.documentElement.scrollTop||0==document.body.scrollTop?document.documentElement.scrollTop||document.body.scrollTop:document.documentElement.scrollTop>document.body.scrollTop?document.documentElement.scrollTop:document.body.scrollTop,t},getScrollLeft(e){"string"==typeof e&&e&&(e=document.body.querySelector(e));let t=0;return t=this.isElement(e)&&e!=document.body&&e!=document.documentElement&&e!=window?e.scrollLeft:0==document.documentElement.scrollLeft||0==document.body.scrollLeft?document.documentElement.scrollLeft||document.body.scrollLeft:document.documentElement.scrollLeft>document.body.scrollLeft?document.documentElement.scrollLeft:document.body.scrollLeft,t},setScrollLeft(e){let t=!1,n=e.el;"string"==typeof n&&n&&(n=document.body.querySelector(n));const o=e.number||0,r=e.time||0;return this.isElement(n)&&n!=document.body&&n!=document.documentElement&&n!=window||(t=!0),new Promise((e=>{if(r<=0)t?document.documentElement.scrollLeft=document.body.scrollLeft=o:n.scrollLeft=o,e();else{const s=10;let l=i.divide(r,s),d=this.getScrollLeft(n);const c=i.divide(i.subtract(o,d),l);let a=setInterval((()=>{l>0?(l--,t?document.documentElement.scrollLeft=document.body.scrollLeft=d=i.add(d,c):n.scrollLeft=d=i.add(d,c)):(clearInterval(a),e())}),s)}}))},getCssStyle(e,t){let n="";return n=document.defaultView&&document.defaultView.getComputedStyle?document.defaultView.getComputedStyle(e)[t]:e.currentStyle[t],n},getCssSelector(e){if(/^#{1}/.test(e))return{type:"id",value:e.substring(1)};if(/^\./.test(e))return{type:"class",value:e.substring(1)};if(/^\[(.+)\]$/.test(e)){let t="";const n=s.trim(e,!0).substring(1,s.trim(e,!0).length-1).split("=");return 1==n.length&&(t=n[0]),2==n.length&&(t={attributeName:n[0],attributeValue:n[1].replace(/\'/g,"").replace(/\"/g,"")}),{type:"attribute",value:t}}return{type:"tag",value:e}},getElementBounding(e){"string"==typeof e&&e&&(e=document.body.querySelector(e)),this.isElement(e)||(e=document.body);const t=e.getBoundingClientRect();return{top:t.top,bottom:i.subtract(document.documentElement.clientHeight||window.innerHeight,t.bottom),left:t.left,right:i.subtract(document.documentElement.clientWidth||window.innerWidth,t.right)}},isElement:e=>!!e&&e instanceof Node&&1===e.nodeType,string2dom(e){const t=document.createElement("template");return t.innerHTML=e,1==t.content.children.length?t.content.children[0]:Array.from(t.content.children)}},d=e=>{const t=e.split(/[\s]+/g),n=[];return t.forEach((e=>{const t=e.split("."),o={eventName:t[0]};t.length>1&&(o.guid=t[1]),n.push(o)})),n},c=(e,t,n)=>{const o=r.get(e,"dap-defined-events")||{},s=Object.keys(o),i=s.length;for(let r=0;r{const t={};return Object.keys(e).forEach((n=>{e[n]&&(t[n]=e[n])})),t})(o))},a={on(e,t,n,o){d(t).forEach((t=>{((e,t,n,o,s)=>{const i=r.get(e,"dap-defined-events")||{};n||(n=r.get(e,"dap-event-guid")||0,r.set(e,"dap-event-guid",n+1)),i[n=t+"."+n]&&i[n].type==t&&e.removeEventListener(t,i[n].fn,i[n].options),e.addEventListener(t,o,s),i[n]={type:t,fn:o,options:s},r.set(e,"dap-defined-events",i)})(e,t.eventName,t.guid,n.bind(e),o)}))},off(e,t){const n=r.get(e,"dap-defined-events");if(!n)return;if(!t){const t=Object.keys(n),o=t.length;for(let r=0;r{c(e,t.eventName,t.guid)}))},get(e){const t=r.get(e,"dap-defined-events");if(t)return t}},u={getImageUrl:e=>window.URL.createObjectURL(e),dataFileToBase64:e=>new Promise((t=>{const n=new FileReader;n.readAsDataURL(e),n.onloadend=()=>{const e=n.result;t(e)}})),dataBase64toFile(e,t){const n=e.split(","),o=n[0].match(/:(.*?);/)[1],r=atob(n[1]);let s=r.length;const i=new Uint8Array(s);for(;s--;)i[s]=r.charCodeAt(s);return new File([i],t,{type:o})},compressImage(e,t){const n=(e,o,r)=>{let s=e.toDataURL("image/"+(t.mimeType??"jpeg"),r),i=this.dataBase64toFile(s,o);if(t.maxSize&&t.maxSize>0&&i.size>1024*t.maxSize){r=r<=0?0:Number((r-.01).toFixed(2));const t=n(e,o,r);s=t.url,i=t.file,r=t.quality}return{file:i,url:s,quality:r}};return new Promise(((o,r)=>{const s=new FileReader;s.readAsDataURL(e),s.onload=()=>{const i=s.result,l=new Image;l.src=i,l.onload=()=>{if(t.minSize&&t.minSize>0&&e.size<=1024*t.minSize)return void o({file:e,url:i,quality:1,width:l.width,height:l.height});const r=document.createElement("canvas"),s=r.getContext("2d");r.width=t.width||l.width,r.height=t.width?t.width/(l.width/l.height):l.height,s.drawImage(l,0,0,r.width,r.height);const d=e.name.lastIndexOf("."),c=e.name.substring(0,d)+"."+(t.mimeType??"jpeg");let a=n(r,c,t.quality??.8);o({...a,width:r.width,height:r.height})},l.onerror=()=>{r(new Error("Failed to load image file"))}},s.onerror=()=>{r(new Error("Failed to load image file"))}}))}},m={language:()=>window.navigator.language,device(){const e=window.navigator.userAgent;return{PC:!e.match(/AppleWebKit.*Mobile.*/),Mobile:!!e.match(/AppleWebKit.*Mobile.*/),iPhone:e.includes("iPhone"),Phone:e.includes("Android")&&/(?:Mobile)/.test(e)||e.includes("iPhone")||/(?:Windows Phone)/.test(e),iPad:e.includes("iPad"),Tablet:e.includes("iPad")||e.includes("Android")&&!/(?:Mobile)/.test(e)||e.includes("Firefox")&&/(?:Tablet)/.test(e),WindowsPhone:/(?:Windows Phone)/.test(e)}},browser(){const e=window.navigator.userAgent;return{Edge:!!e.match(/Edg\/([\d.]+)/),Weixin:e.includes("MicroMessenger"),QQ:e.includes("QQ"),QQBrowser:e.includes("MQQBrowser"),UC:e.includes("UCBrowser"),Chrome:e.includes("Chrome"),Firefox:e.includes("Firefox"),Sougou:e.toLocaleLowerCase().includes("se 2.x")||e.toLocaleLowerCase().includes("metasr")||e.toLocaleLowerCase().includes("sogou"),Safari:e.includes("Safari")&&!e.includes("Chrome")}},browserKernel(){const e=window.navigator.userAgent;return e.includes("Presto")?"opera":e.includes("AppleWebKit")?"webkit":e.includes("Gecko")&&!e.includes("KHTML")?"gecko":void 0},os(){const e=window.navigator.userAgent;return{Windows:e.includes("Windows"),WindowsCPU:e.toLocaleLowerCase().includes("win64")||e.toLocaleLowerCase().includes("wow64")?"x64":e.toLocaleLowerCase().includes("win32")||e.toLocaleLowerCase().includes("wow32")?"x32":void 0,WindowsVersion:e.includes("Windows NT 6.1")||e.includes("Windows 7")?"win7":e.includes("Windows NT 6.3")||e.includes("Windows NT 6.2")||e.includes("Windows NT 8")?"win8":e.includes("Windows NT 10")||e.includes("Windows NT 6.4")?"win10":void 0,Mac:e.includes("Macintosh"),MacVersion:function(){if(e.includes("Macintosh")){const t=e.match(/Mac OS X ([^\s]+)\)/);if(t&&t[1])return t[1].split(/_|\./).join(".")}return""}(),ios:!!e.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),iosVersion:function(){if(e.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)){const t=e.match(/CPU.+OS ([^\s]+) like Mac OS X/);if(t&&t[1])return t[1].split(/_|\./).join(".")}return""}(),Android:e.includes("Android"),AndroidVersion:function(){const t=e.match(/Android ([^\s]+);/);return t&&t[1]?t[1].split(/_|\./).join("."):""}(),Linux:e.includes("Linux"),HarmonyOS:e.includes("HarmonyOS"),Ubuntu:e.includes("Ubuntu")}}},h={start(e,t){if(!window.SpeechSynthesisUtterance||!window.speechSynthesis)throw new Error("The current browser does not support this syntax");const n=new SpeechSynthesisUtterance;n.text=e,n.pitch=(null==t?void 0:t.pitch)??.8,n.rate=(null==t?void 0:t.rate)??1,n.volume=(null==t?void 0:t.volume)??1,n.lang="zh-CN",n.onstart=o=>{var r;null==(r=null==t?void 0:t.start)||r.apply(n,[o,{text:e,pitch:t.pitch??.8,rate:t.rate??1,volume:t.volume??1}])},n.onend=o=>{var r;null==(r=null==t?void 0:t.end)||r.apply(n,[o,{text:e,pitch:t.pitch??.8,rate:t.rate??1,volume:t.volume??1}])},n.onpause=o=>{var r;null==(r=null==t?void 0:t.pause)||r.apply(n,[o,{text:e,pitch:t.pitch??.8,rate:t.rate??1,volume:t.volume??1}])},n.onresume=o=>{var r;null==(r=null==t?void 0:t.resume)||r.apply(n,[o,{text:e,pitch:t.pitch??.8,rate:t.rate??1,volume:t.volume??1}])},n.onerror=o=>{var r;null==(r=null==t?void 0:t.error)||r.apply(n,[o,{text:e,pitch:t.pitch??.8,rate:t.rate??1,volume:t.volume??1}])},window.speechSynthesis.speak(n)},stop(){if(!window.SpeechSynthesisUtterance||!window.speechSynthesis)throw new Error("The current browser does not support this syntax");window.speechSynthesis.cancel()},pause(){if(!window.SpeechSynthesisUtterance||!window.speechSynthesis)throw new Error("The current browser does not support this syntax");window.speechSynthesis.pause()},resume(){if(!window.SpeechSynthesisUtterance||!window.speechSynthesis)throw new Error("The current browser does not support this syntax");window.speechSynthesis.resume()}},p={number:i,data:r,element:l,event:a,common:t,color:n,file:u,string:s,platform:m,speech:h};e.color=n,e.common=t,e.data=r,e.default=p,e.element=l,e.event=a,e.file=u,e.number=i,e.platform=m,e.speech=h,e.string=s,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})})); 2 | -------------------------------------------------------------------------------- /src/element.ts: -------------------------------------------------------------------------------- 1 | import { string as stringUtil } from './string' 2 | import { number as numberUtil } from './number' 3 | 4 | export type ScrollOptionsType = { 5 | el?: HTMLElement | string | Window 6 | time?: number 7 | number?: number 8 | } 9 | 10 | export type PlacementType = { 11 | left: number 12 | top: number 13 | right: number 14 | bottom: number 15 | } 16 | 17 | export type scrollTopBottomResultType = { 18 | state: 'top' | 'bottom' 19 | target: HTMLElement | Window 20 | } 21 | 22 | /** 23 | * element相关工具方法 24 | */ 25 | export const element = { 26 | /** 27 | * 判断是否是Window对象 28 | * @param {Object} data 入参 29 | */ 30 | isWindow(data: any) { 31 | return (data && data instanceof Window) as boolean 32 | }, 33 | /** 34 | * 获取元素距离某个定位祖先元素左侧/顶部/底部/右侧的距离 35 | * @param {Object} el 元素 36 | * @param {Object} root 定位父元素或者祖先元素,未指定则为document.body 37 | */ 38 | getElementPoint(el: HTMLElement, root?: HTMLElement): PlacementType { 39 | if (!this.isElement(root)) { 40 | root = document.body 41 | } 42 | if (!this.isContains(root!, el)) { 43 | throw new Error('The second argument and the first argument have no hierarchical relationship') 44 | } 45 | 46 | const obj = el 47 | let offsetTop = 0 48 | let offsetLeft = 0 49 | //判断是否有定位父容器,如果存在则累加其边距 50 | while (this.isElement(el) && this.isContains(root, el) && root !== el) { 51 | offsetTop += el.offsetTop //叠加父容器的上边距 52 | offsetLeft += el.offsetLeft //叠加父容器的左边距 53 | el = el.offsetParent as HTMLElement 54 | } 55 | let offsetRight = root!.offsetWidth - offsetLeft - obj.offsetWidth 56 | let offsetBottom = root!.offsetHeight - offsetTop - obj.offsetHeight 57 | return { 58 | top: offsetTop, 59 | left: offsetLeft, 60 | right: offsetRight, 61 | bottom: offsetBottom 62 | } 63 | }, 64 | 65 | /** 66 | * 判断某个元素是否包含指定元素,包含相等关系和父子关系 67 | * @param {Object} parentNode 父元素或祖先元素 68 | * @param {Object} childNode 子元素 69 | */ 70 | isContains(parentNode: HTMLElement, childNode: HTMLElement) { 71 | if (parentNode === childNode) { 72 | return true 73 | } 74 | //如果浏览器支持contains 75 | if (parentNode.contains) { 76 | return parentNode.contains(childNode) 77 | } 78 | //火狐支持 79 | if (parentNode.compareDocumentPosition) { 80 | return !!(parentNode.compareDocumentPosition(childNode) & 16) 81 | } 82 | return false 83 | }, 84 | 85 | /** 86 | * 判断某个元素是否是指定元素的父元素 87 | * @param {Object} parentNode 父元素 88 | * @param {Object} childNode 子元素 89 | */ 90 | isParentNode(parentNode: HTMLElement, childNode: HTMLElement) { 91 | if (parentNode === childNode) { 92 | return false 93 | } 94 | return childNode.parentNode === parentNode 95 | }, 96 | 97 | /** 98 | * 查找某个元素下指定选择器的子元素 99 | * @param {Object} el 元素 100 | * @param {Object} selector 支持多选择器,等同于querySelectorAll的参数 101 | */ 102 | children(el: HTMLElement, selector?: string) { 103 | const elements: HTMLElement[] = Array.from(el.querySelectorAll(selector ?? '*')) 104 | return Array.from(elements).filter(elm => this.isParentNode(el, elm)) 105 | }, 106 | 107 | /** 108 | * 查找某个元素下指定选择器的兄弟元素 109 | * @param {Object} el 元素 110 | * @param {Object} selector 取值等同于queryselectorAll的参数,支持多选择器 111 | */ 112 | siblings(el: HTMLElement, selector?: string) { 113 | if (!el.parentNode) { 114 | return [] as HTMLElement[] 115 | } 116 | const elements: HTMLElement[] = Array.from(el.parentNode.querySelectorAll(selector ?? '*')) 117 | return elements.filter(elm => elm.parentNode === el.parentNode && elm != el) 118 | }, 119 | 120 | /** 121 | * rem与px单位转换 122 | * @param {Object} num rem数值 123 | */ 124 | rem2px(num: number) { 125 | const fs = this.getCssStyle(document.documentElement, 'font-size') 126 | return numberUtil.mutiply(num, parseFloat(fs)) 127 | }, 128 | 129 | /** 130 | * rem与px单位转换 131 | * @param {Object} num px数值 132 | */ 133 | px2rem(num: number) { 134 | const fs = this.getCssStyle(document.documentElement, 'font-size') 135 | return numberUtil.divide(num, parseFloat(fs)) 136 | }, 137 | 138 | /** 139 | * 获取元素的内容宽度,内容宽度不包括border和padding 140 | * @param {Object} el 支持css选择器字符串,未指定则表示document.body 141 | */ 142 | width(el?: HTMLElement | string) { 143 | if (typeof el == 'string' && el) { 144 | el = document.body.querySelector(el) as HTMLElement 145 | } 146 | if (!this.isElement(el)) { 147 | el = document.body 148 | } 149 | const clientWidth = (el as HTMLElement).clientWidth //获取元素包括内边距在内的宽度 150 | const paddingLeft_width = parseFloat(this.getCssStyle(el as HTMLElement, 'padding-left')) //左内边距 151 | const paddingRight_width = parseFloat(this.getCssStyle(el as HTMLElement, 'padding-right')) //右内边距宽度 152 | return numberUtil.subtract(clientWidth, paddingLeft_width, paddingRight_width) 153 | }, 154 | 155 | /** 156 | * 获取元素的内容高度,内容高度不包括border和padding 157 | * @param {Object} el 支持css选择器字符串 未指定则表示document.body 158 | */ 159 | height(el?: HTMLElement | string) { 160 | if (typeof el == 'string' && el) { 161 | el = document.body.querySelector(el) as HTMLElement 162 | } 163 | if (!this.isElement(el)) { 164 | el = document.body 165 | } 166 | const clientHeight = (el as HTMLElement).clientHeight //获取元素包括内边距在内的高度 167 | const paddingTop_height = parseFloat(this.getCssStyle(el as HTMLElement, 'padding-top')) //上内边距 168 | const paddingBottom_height = parseFloat(this.getCssStyle(el as HTMLElement, 'padding-bottom')) //下内边距宽度 169 | return numberUtil.subtract(clientHeight, paddingTop_height, paddingBottom_height) 170 | }, 171 | 172 | /** 173 | * 移除class 174 | * @param {Object} el 元素 175 | * @param {Object} className 支持多类,以空格划分 176 | */ 177 | removeClass(el: HTMLElement, className: string) { 178 | const classArray = stringUtil.trim(className).split(/\s+/) //按照空格划分 179 | classArray.forEach(item => { 180 | el.classList.remove(item) 181 | }) 182 | }, 183 | 184 | /** 185 | * 添加class 186 | * @param {Object} el 元素 187 | * @param {Object} className 支持多类,以空格划分 188 | */ 189 | addClass(el: HTMLElement, className: string) { 190 | const classArray = stringUtil.trim(className).split(/\s+/) //按照空格划分 191 | classArray.forEach(item => { 192 | el.classList.add(item) 193 | }) 194 | }, 195 | 196 | /** 197 | * 判断指定元素是否含有指定类名 198 | * @param {Object} el 元素 199 | * @param {Object} className 支持多类,以空格划分 200 | */ 201 | hasClass(el: HTMLElement, className: string) { 202 | const classArray = stringUtil.trim(className).split(/\s+/) //按照空格划分 203 | return classArray.every(item => { 204 | return el.classList.contains(item) 205 | }) 206 | }, 207 | 208 | /** 209 | * 监听元素滚动到顶部或者底部 210 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动 211 | * @param {Object} callback 回调函数 212 | */ 213 | scrollTopBottomTrigger(el?: HTMLElement | string | Window, callback?: (options: scrollTopBottomResultType) => void) { 214 | if (typeof el == 'string' && el) { 215 | el = document.body.querySelector(el) as HTMLElement 216 | } 217 | let scrollEle: Window | HTMLElement = window 218 | if (this.isElement(el) && el != document.body && el != document.documentElement) { 219 | scrollEle = el as HTMLElement 220 | } 221 | if (typeof el == 'function') { 222 | callback = el 223 | } 224 | //滑动到底部时是否触发回调函数的标识,解决ios系统下多次触发回调的bug 225 | let flag = true 226 | scrollEle.addEventListener('scroll', () => { 227 | if (this.getScrollTop(scrollEle) <= 0) { 228 | //滑动到顶部 229 | const options: scrollTopBottomResultType = { 230 | state: 'top', 231 | target: scrollEle 232 | } 233 | if (!flag) { 234 | return 235 | } 236 | if (typeof callback == 'function') { 237 | flag = false 238 | callback(options) 239 | } 240 | } else { 241 | //滑动到底部 242 | const options: scrollTopBottomResultType = { 243 | state: 'bottom', 244 | target: scrollEle 245 | } 246 | let height = 0 247 | if (scrollEle == window) { 248 | height = window.innerHeight 249 | } else { 250 | height = (scrollEle as HTMLElement).clientHeight 251 | } 252 | //+1是为了防止出现小数点误差 253 | if (numberUtil.add(this.getScrollTop(scrollEle), height) + 1 >= this.getScrollHeight(scrollEle as HTMLElement) && height != this.getScrollHeight(scrollEle as HTMLElement)) { 254 | if (!flag) { 255 | return 256 | } 257 | if (typeof callback == 'function') { 258 | flag = false 259 | callback(options) 260 | } 261 | } else { 262 | flag = true 263 | } 264 | } 265 | }) 266 | }, 267 | 268 | /** 269 | * 获取文档或元素的总宽度 270 | * @param {Object} el 支持css选择器字符串 未指定则表示整个页面文档 271 | */ 272 | getScrollWidth(el?: HTMLElement | string) { 273 | if (typeof el == 'string' && el) { 274 | el = document.body.querySelector(el) as HTMLElement 275 | } 276 | let scrollWidth = 0 277 | if (this.isElement(el) && el != document.documentElement && el != document.body) { 278 | scrollWidth = (el as HTMLElement).scrollWidth 279 | } else { 280 | if (document.documentElement.scrollWidth == 0 || document.body.scrollWidth == 0) { 281 | scrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth 282 | } else { 283 | scrollWidth = document.documentElement.scrollWidth > document.body.scrollWidth ? document.documentElement.scrollWidth : document.body.scrollWidth 284 | } 285 | } 286 | return scrollWidth 287 | }, 288 | 289 | /** 290 | * 获取文档或者元素的总高度 291 | * @param {Object} el 支持css选择器字符串 未指定则表示整个页面文档 292 | */ 293 | getScrollHeight(el?: HTMLElement | string) { 294 | if (typeof el == 'string' && el) { 295 | el = document.body.querySelector(el) as HTMLElement 296 | } 297 | let scrollHeight = 0 298 | if (this.isElement(el) && el != document.documentElement && el != document.body) { 299 | scrollHeight = (el as HTMLElement).scrollHeight 300 | } else { 301 | if (document.documentElement.scrollHeight == 0 || document.body.scrollHeight == 0) { 302 | scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight 303 | } else { 304 | scrollHeight = document.documentElement.scrollHeight > document.body.scrollHeight ? document.documentElement.scrollHeight : document.body.scrollHeight 305 | } 306 | } 307 | return scrollHeight 308 | }, 309 | 310 | /** 311 | * 设置滚动条在Y轴上的距离 312 | * @param {Object} options {el,number,time} el支持css选择器字符串 未指定则为窗口滚动 313 | */ 314 | setScrollTop(options: ScrollOptionsType) { 315 | let isWindow = false 316 | let el = options.el 317 | if (typeof el == 'string' && el) { 318 | el = document.body.querySelector(el) as HTMLElement 319 | } 320 | const number = options.number || 0 321 | const time = options.time || 0 322 | if (!this.isElement(el) || el == document.body || el == document.documentElement || el == window) { 323 | isWindow = true 324 | } 325 | return new Promise(resolve => { 326 | if (time <= 0) { 327 | if (isWindow) { 328 | document.documentElement.scrollTop = document.body.scrollTop = number 329 | } else { 330 | ;(el as HTMLElement).scrollTop = number 331 | } 332 | resolve() 333 | } else { 334 | const spacingTime = 10 // 设置循环的间隔时间 值越小消耗性能越高 335 | let spacingIndex = numberUtil.divide(time, spacingTime) // 计算循环的次数 336 | // 获取当前滚动条位置 337 | let nowTop = this.getScrollTop(el!) 338 | const everTop = numberUtil.divide(numberUtil.subtract(number, nowTop), spacingIndex) // 计算每次滑动的距离 339 | const scrollTimer = setInterval(() => { 340 | if (spacingIndex > 0) { 341 | spacingIndex-- 342 | if (isWindow) { 343 | document.documentElement.scrollTop = document.body.scrollTop = nowTop = numberUtil.add(nowTop, everTop) 344 | } else { 345 | ;(el as HTMLElement).scrollTop = nowTop = numberUtil.add(nowTop, everTop) 346 | } 347 | } else { 348 | clearInterval(scrollTimer) // 清除计时器 349 | resolve() 350 | } 351 | }, spacingTime) 352 | } 353 | }) 354 | }, 355 | 356 | /** 357 | * 获取滚动条在Y轴上滚动的距离 358 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动 359 | */ 360 | getScrollTop(el?: HTMLElement | string | Window) { 361 | if (typeof el == 'string' && el) { 362 | el = document.body.querySelector(el) as HTMLElement 363 | } 364 | let scrollTop = 0 365 | //如果是元素 366 | if (this.isElement(el) && el != document.body && el != document.documentElement && el != window) { 367 | scrollTop = (el as HTMLElement).scrollTop 368 | } else { 369 | if (document.documentElement.scrollTop == 0 || document.body.scrollTop == 0) { 370 | scrollTop = document.documentElement.scrollTop || document.body.scrollTop 371 | } else { 372 | scrollTop = document.documentElement.scrollTop > document.body.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop 373 | } 374 | } 375 | return scrollTop 376 | }, 377 | 378 | /** 379 | * 获取滚动条在X轴上滚动的距离 380 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动 381 | */ 382 | getScrollLeft(el?: HTMLElement | string | Window) { 383 | if (typeof el == 'string' && el) { 384 | el = document.body.querySelector(el) as HTMLElement 385 | } 386 | let scrollLeft = 0 387 | //如果是元素 388 | if (this.isElement(el) && el != document.body && el != document.documentElement && el != window) { 389 | scrollLeft = (el as HTMLElement).scrollLeft 390 | } else { 391 | if (document.documentElement.scrollLeft == 0 || document.body.scrollLeft == 0) { 392 | scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft 393 | } else { 394 | scrollLeft = document.documentElement.scrollLeft > document.body.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft 395 | } 396 | } 397 | return scrollLeft 398 | }, 399 | 400 | /** 401 | * 设置滚动条在X轴上的距离 402 | * @param {Object} options {el,number,time} el支持css选择器字符串 未指定则为窗口滚动 403 | */ 404 | setScrollLeft(options: ScrollOptionsType) { 405 | let isWindow = false 406 | let el = options.el 407 | if (typeof el == 'string' && el) { 408 | el = document.body.querySelector(el) as HTMLElement 409 | } 410 | const number = options.number || 0 411 | const time = options.time || 0 412 | if (!this.isElement(el) || el == document.body || el == document.documentElement || el == window) { 413 | isWindow = true 414 | } 415 | return new Promise(resolve => { 416 | if (time <= 0) { 417 | if (isWindow) { 418 | document.documentElement.scrollLeft = document.body.scrollLeft = number 419 | } else { 420 | ;(el as HTMLElement).scrollLeft = number 421 | } 422 | resolve() 423 | } else { 424 | const spacingTime = 10 // 设置循环的间隔时间 值越小消耗性能越高 425 | let spacingIndex = numberUtil.divide(time, spacingTime) // 计算循环的次数 426 | // 获取当前滚动条位置 427 | let nowLeft = this.getScrollLeft(el) 428 | const everLeft = numberUtil.divide(numberUtil.subtract(number, nowLeft), spacingIndex) // 计算每次滑动的距离 429 | let scrollTimer = setInterval(() => { 430 | if (spacingIndex > 0) { 431 | spacingIndex-- 432 | if (isWindow) { 433 | document.documentElement.scrollLeft = document.body.scrollLeft = nowLeft = numberUtil.add(nowLeft, everLeft) 434 | } else { 435 | ;(el as HTMLElement).scrollLeft = nowLeft = numberUtil.add(nowLeft, everLeft) 436 | } 437 | } else { 438 | clearInterval(scrollTimer) // 清除计时器 439 | resolve() 440 | } 441 | }, spacingTime) 442 | } 443 | }) 444 | }, 445 | 446 | /** 447 | * 获取元素指定样式 448 | * @param {Object} el 元素 449 | * @param {Object} cssName 样式名称 450 | */ 451 | getCssStyle(el: HTMLElement, cssName: string) { 452 | let cssText = '' 453 | //兼容IE9-IE11、chrome、firefox、safari、opera;不兼容IE7-IE8 454 | if (document.defaultView && document.defaultView.getComputedStyle) { 455 | cssText = (document.defaultView.getComputedStyle(el) as any)[cssName] 456 | } 457 | //兼容IE7-IE11;不兼容chrome、firefox、safari、opera 458 | else { 459 | cssText = (el as any).currentStyle[cssName] 460 | } 461 | return cssText 462 | }, 463 | 464 | /** 465 | * 判断字符串属于哪种选择器 466 | * @param {Object} selector 467 | */ 468 | getCssSelector(selector: string): { type: 'id' | 'class' | 'attribute' | 'tag'; value: string | { attributeName: string; attributeValue: string } } { 469 | //id选择器,以#开头的字符串 470 | if (/^#{1}/.test(selector)) { 471 | return { 472 | type: 'id', 473 | value: selector.substring(1) 474 | } 475 | } 476 | //类名选择器,以.开头的字符串 477 | if (/^\./.test(selector)) { 478 | return { 479 | type: 'class', 480 | value: selector.substring(1) 481 | } 482 | } 483 | //属性选择器,以[]包裹的字符串 484 | if (/^\[(.+)\]$/.test(selector)) { 485 | let value: string | { attributeName: string; attributeValue: string } = '' 486 | const attribute = stringUtil.trim(selector, true).substring(1, stringUtil.trim(selector, true).length - 1) 487 | const arry = attribute.split('=') 488 | if (arry.length == 1) { 489 | value = arry[0] 490 | } 491 | if (arry.length == 2) { 492 | value = { 493 | attributeName: arry[0], 494 | attributeValue: arry[1].replace(/\'/g, '').replace(/\"/g, '') //去除属性值的单引号或者双引号 495 | } 496 | } 497 | return { 498 | type: 'attribute', 499 | value 500 | } 501 | } 502 | 503 | //默认为标签名选择器 504 | return { 505 | type: 'tag', 506 | value: selector 507 | } 508 | }, 509 | 510 | /** 511 | * 获取元素距离可视窗口的位置 512 | * @param {Object} el 支持css选择器字符串 未指定则为document.body 513 | */ 514 | getElementBounding(el?: HTMLElement | string): PlacementType { 515 | if (typeof el == 'string' && el) { 516 | el = document.body.querySelector(el) as HTMLElement 517 | } 518 | if (!this.isElement(el)) { 519 | el = document.body 520 | } 521 | const point = (el as HTMLElement).getBoundingClientRect() 522 | const top = point.top //元素顶部距离可视窗口上边的距离 523 | const bottom = numberUtil.subtract(document.documentElement.clientHeight || window.innerHeight, point.bottom) //元素底部距离可视窗口底部的距离 524 | const left = point.left //元素左侧距离可视窗口左边的距离 525 | const right = numberUtil.subtract(document.documentElement.clientWidth || window.innerWidth, point.right) //元素右侧距离可视窗口右边的距离 526 | return { 527 | top, 528 | bottom, 529 | left, 530 | right 531 | } 532 | }, 533 | 534 | /** 535 | * 判断是否是元素 536 | * @param {Object} el 537 | */ 538 | isElement(el: any) { 539 | return !!el && el instanceof Node && el.nodeType === 1 540 | }, 541 | 542 | /** 543 | * 字符串转dom 544 | * @param {Object} html 545 | */ 546 | string2dom(html: string) { 547 | const template = document.createElement('template') 548 | template.innerHTML = html 549 | if (template.content.children.length == 1) { 550 | return template.content.children[0] as HTMLElement 551 | } 552 | return Array.from(template.content.children) as HTMLElement[] 553 | } 554 | } 555 | -------------------------------------------------------------------------------- /lib/dap-util.es.js: -------------------------------------------------------------------------------- 1 | const common = { 2 | /** 3 | * 常用判断 4 | * @param {Object} text 要判断的字符串 5 | * @param {Object} param 判断的类型字符串 6 | */ 7 | matchingText(text, param) { 8 | if (param == "Chinese") { 9 | return /^[\u4e00-\u9fa5]+$/.test(text); 10 | } 11 | if (param == "chinese") { 12 | return /[\u4e00-\u9fa5]/.test(text); 13 | } 14 | if (param == "email") { 15 | return /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(text); 16 | } 17 | if (param == "username") { 18 | return /^[a-zA-Z0-9_]{4,16}$/.test(text); 19 | } 20 | if (param == "int+") { 21 | return /^\d+$/.test(text); 22 | } 23 | if (param == "int-") { 24 | return /^-\d+$/.test(text); 25 | } 26 | if (param == "int") { 27 | return /^-?\d+$/.test(text); 28 | } 29 | if (param == "pos") { 30 | return /^\d*\.?\d+$/.test(text); 31 | } 32 | if (param == "neg") { 33 | return /^-\d*\.?\d+$/.test(text); 34 | } 35 | if (param == "number") { 36 | return /^-?\d*\.?\d+$/.test(text); 37 | } 38 | if (param == "phone") { 39 | return /^1[0-9]\d{9}$/.test(text); 40 | } 41 | if (param == "idCard") { 42 | return /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(text); 43 | } 44 | if (param == "url") { 45 | return /^(https?|ftp):\/\/(-\.)?([^\s\/?\.#-]+\.?)+(\/[^\s]*)?$/.test(text); 46 | } 47 | if (param == "IPv4") { 48 | return /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(text); 49 | } 50 | if (param == "hex") { 51 | return /^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/.test(text); 52 | } 53 | if (param == "rgb") { 54 | return /^rgb\((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\)$/.test(text); 55 | } 56 | if (param == "rgba") { 57 | return /^rgba\((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(0?\.\d|1(\.0)?|0)\)$/.test(text); 58 | } 59 | if (param == "QQ") { 60 | return /^[1-9][0-9]{4,10}$/.test(text); 61 | } 62 | if (param == "weixin") { 63 | return /^[a-zA-Z]([-_a-zA-Z0-9]{5,19})+$/.test(text); 64 | } 65 | if (param == "plate") { 66 | return /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$|^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[D|F]{1}[A-Z0-9]{5,6}$/.test(text); 67 | } 68 | return false; 69 | }, 70 | /** 71 | * 根据参数名获取地址栏参数值 72 | * @param {Object} name 73 | */ 74 | getUrlParams(name) { 75 | const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); 76 | let search = window.location.search.substring(1); 77 | if (!search) { 78 | const arr = window.location.hash.split("?"); 79 | if (arr.length == 2) { 80 | search = arr[1]; 81 | } 82 | } 83 | let r = search.match(reg); 84 | if (r) { 85 | return decodeURIComponent(r[2]); 86 | } 87 | }, 88 | /** 89 | * 判断是否空对象 90 | * @param {Object} obj 91 | */ 92 | isEmptyObject(obj) { 93 | return this.isObject(obj) && Object.keys(obj).length == 0; 94 | }, 95 | /** 96 | * 判断两个参数是否相等 97 | * @param {Object} a 98 | * @param {Object} b 99 | */ 100 | equal(a, b) { 101 | if (typeof a !== typeof b) { 102 | return false; 103 | } 104 | if (this.isObject(a) && this.isObject(b)) { 105 | const aProps = Object.getOwnPropertyNames(a); 106 | const bProps = Object.getOwnPropertyNames(b); 107 | if (aProps.length != bProps.length) { 108 | return false; 109 | } 110 | const length = aProps.length; 111 | let isEqual = true; 112 | for (let i = 0; i < length; i++) { 113 | const propName = aProps[i]; 114 | const propA = a[propName]; 115 | const propB = b[propName]; 116 | if (!this.equal(propA, propB)) { 117 | isEqual = false; 118 | break; 119 | } 120 | } 121 | return isEqual; 122 | } 123 | return a === b; 124 | }, 125 | /** 126 | * 是否对象 127 | * @param {Object} val 128 | */ 129 | isObject(val) { 130 | return typeof val === "object" && !!val; 131 | }, 132 | /** 133 | * 文本复制 134 | * @param {Object} text 135 | */ 136 | copyText(text) { 137 | if (!navigator.clipboard) { 138 | throw new Error("navigator.clipboard must be obtained in a secure environment, such as localhost, 127.0.0.1, or https, so the method won't work"); 139 | } 140 | return navigator.clipboard.writeText(text); 141 | }, 142 | /** 143 | * 深度克隆 144 | * @param {Object} data 145 | */ 146 | clone(data2) { 147 | if (this.isObject(data2)) { 148 | if (Array.isArray(data2)) { 149 | return data2.map((item) => { 150 | return this.clone(item); 151 | }); 152 | } 153 | const newData = {}; 154 | for (let key in data2) { 155 | newData[key] = this.clone(data2[key]); 156 | } 157 | return newData; 158 | } 159 | return data2; 160 | } 161 | }; 162 | const color = { 163 | /** 164 | * rgb转hsv值 165 | * @param {Object} rgb rgb值,数组 166 | */ 167 | rgb2hsv(rgb) { 168 | if (rgb.length != 3) { 169 | throw new TypeError("Invalid argument"); 170 | } 171 | let h = 0; 172 | let s = 0; 173 | let v = 0; 174 | let r = rgb[0] >= 255 ? 255 : rgb[0]; 175 | let g = rgb[1] >= 255 ? 255 : rgb[1]; 176 | let b = rgb[2] >= 255 ? 255 : rgb[2]; 177 | r = r <= 0 ? 0 : r; 178 | g = g <= 0 ? 0 : g; 179 | b = b <= 0 ? 0 : b; 180 | let max = Math.max(r, g, b); 181 | let min = Math.min(r, g, b); 182 | v = max / 255; 183 | if (max === 0) { 184 | s = 0; 185 | } else { 186 | s = 1 - min / max; 187 | } 188 | if (max === min) { 189 | h = 0; 190 | } else if (max === r && g >= b) { 191 | h = 60 * ((g - b) / (max - min)) + 0; 192 | } else if (max === r && g < b) { 193 | h = 60 * ((g - b) / (max - min)) + 360; 194 | } else if (max === g) { 195 | h = 60 * ((b - r) / (max - min)) + 120; 196 | } else if (max === b) { 197 | h = 60 * ((r - g) / (max - min)) + 240; 198 | } 199 | return [h, s * 100, v * 100]; 200 | }, 201 | /** 202 | * hsv格式值转rgb值 203 | * @param {Object} hsv hsv值,数组 204 | */ 205 | hsv2rgb(hsv) { 206 | if (hsv.length != 3) { 207 | throw new TypeError("Invalid argument"); 208 | } 209 | let h = hsv[0] >= 360 || hsv[0] <= 0 ? 0 : hsv[0]; 210 | let s = hsv[1] >= 100 ? 100 : hsv[1]; 211 | s = s <= 0 ? 0 : s; 212 | let v = hsv[2] >= 100 ? 100 : hsv[2]; 213 | v = v <= 0 ? 0 : v; 214 | s = s / 100; 215 | v = v / 100; 216 | let r = 0; 217 | let g = 0; 218 | let b = 0; 219 | let i = parseInt(h / 60 % 6 + ""); 220 | let f = h / 60 - i; 221 | let p = v * (1 - s); 222 | let q = v * (1 - f * s); 223 | let t = v * (1 - (1 - f) * s); 224 | switch (i) { 225 | case 0: 226 | r = v; 227 | g = t; 228 | b = p; 229 | break; 230 | case 1: 231 | r = q; 232 | g = v; 233 | b = p; 234 | break; 235 | case 2: 236 | r = p; 237 | g = v; 238 | b = t; 239 | break; 240 | case 3: 241 | r = p; 242 | g = q; 243 | b = v; 244 | break; 245 | case 4: 246 | r = t; 247 | g = p; 248 | b = v; 249 | break; 250 | case 5: 251 | r = v; 252 | g = p; 253 | b = q; 254 | break; 255 | } 256 | r = parseInt(r * 255 + ""); 257 | g = parseInt(g * 255 + ""); 258 | b = parseInt(b * 255 + ""); 259 | return [r, g, b]; 260 | }, 261 | /** 262 | * rgb值转十六进制 263 | * @param {Array} rgb rgb值,数组 264 | */ 265 | rgb2hex(rgb) { 266 | if (rgb.length != 3) { 267 | throw new TypeError("Invalid argument"); 268 | } 269 | let r = rgb[0]; 270 | let g = rgb[1]; 271 | let b = rgb[2]; 272 | let hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); 273 | return hex; 274 | }, 275 | /** 276 | * 十六进制颜色转rgb 277 | * @param {String} hex 十六进制颜色值 278 | */ 279 | hex2rgb(hex) { 280 | let color2 = hex.toLowerCase(); 281 | if (!common.matchingText(color2, "hex")) { 282 | throw new TypeError("The argument must be a hexadecimal color value"); 283 | } 284 | if (color2.length === 4) { 285 | let colorNew = "#"; 286 | for (let i = 1; i < 4; i += 1) { 287 | colorNew += color2.slice(i, i + 1).concat(color2.slice(i, i + 1)); 288 | } 289 | color2 = colorNew; 290 | } 291 | let colorChange = []; 292 | for (let i = 1; i < 7; i += 2) { 293 | colorChange.push(parseInt("0x" + color2.slice(i, i + 2))); 294 | } 295 | return colorChange; 296 | } 297 | }; 298 | const dataName = "_dap-datas"; 299 | const data = { 300 | /** 301 | * 移除指定数据 302 | * @param {Object} el 303 | * @param {Object} key 304 | */ 305 | remove(el, key) { 306 | const data2 = el[dataName] || {}; 307 | if (key) { 308 | delete data2[key]; 309 | el[dataName] = data2; 310 | } else { 311 | el[dataName] = {}; 312 | } 313 | }, 314 | /** 315 | * 判断是否含有指定数据 316 | * @param {Object} el 317 | * @param {Object} key 318 | */ 319 | has(el, key) { 320 | return (el[dataName] || {}).hasOwnProperty(key); 321 | }, 322 | /** 323 | * 获取元素指定数据 324 | * @param {Object} el 325 | * @param {Object} key 326 | */ 327 | get(el, key) { 328 | const data2 = el[dataName] || {}; 329 | return !!key ? data2[key] : data2; 330 | }, 331 | /** 332 | * 设置元素指定数据 333 | * @param {Object} el 334 | * @param {Object} key 335 | * @param {Object} value 336 | */ 337 | set(el, key, value) { 338 | const data2 = el[dataName] || {}; 339 | data2[key] = value; 340 | el[dataName] = data2; 341 | } 342 | }; 343 | const string = { 344 | /** 345 | * 向指定位置插入字符串 346 | * @param {Object} original 原始字符串 347 | * @param {Object} str 插入的字符串 348 | * @param {Object} index 插入的位置 349 | */ 350 | insert(original, str, index2) { 351 | if (index2 < 0) { 352 | throw new Error("The third argument cannot be less than 0"); 353 | } 354 | return original.substring(0, index2) + str + original.substring(index2, original.length); 355 | }, 356 | /** 357 | * 删除指定位置的字符串 358 | * @param {Object} original 原始字符串 359 | * @param {Object} index 删除的位置序列 360 | * @param {Object} num 删除的字符串长度 361 | */ 362 | delete(original, index2, num) { 363 | if (index2 < 0) { 364 | throw new Error("The second argument cannot be less than 0"); 365 | } 366 | if (num < 0) { 367 | throw new Error("The third argument cannot be less than 0"); 368 | } 369 | return original.substring(0, index2) + original.substring(index2 + num, original.length); 370 | }, 371 | /** 372 | * 替换指定位置的字符串 373 | * @param {Object} original 原始字符串 374 | * @param {Object} start 开始位置 375 | * @param {Object} end 结束位置 376 | * @param {Object} str 替换的字符串 377 | */ 378 | replace(original, start, end, str) { 379 | if (start < 0) { 380 | throw new Error("The second argument cannot be less than 0"); 381 | } 382 | if (end < 0) { 383 | throw new Error("The third argument cannot be less than 0"); 384 | } 385 | return original.substring(0, start) + str + original.substring(end, original.length); 386 | }, 387 | /** 388 | * 去除字符串空格 389 | * @param {Object} str 原始字符串 390 | * @param {Object} global 为true时去除所有空格,否则只去除两边空格 391 | */ 392 | trim(str, global) { 393 | let result = str.replace(/(^\s+)|(\s+$)/g, ""); 394 | if (global) { 395 | result = result.replace(/\s/g, ""); 396 | } 397 | return result; 398 | } 399 | }; 400 | const number = { 401 | /** 402 | * 数字格式化 403 | * @param {Number} num 404 | */ 405 | formatNumber(num) { 406 | if (this.isNumber(num)) { 407 | return num.toString().replace(/(\d)(?=(?:\d{3})+$)/g, "$1,"); 408 | } 409 | return num.toString(); 410 | }, 411 | /** 412 | * 判断是否数字 413 | * @param {Object} num 414 | */ 415 | isNumber(num) { 416 | if (typeof num == "number" && !isNaN(num)) { 417 | return true; 418 | } 419 | return false; 420 | }, 421 | /** 422 | * 多个数的加法运算 423 | */ 424 | add(...values) { 425 | return values.reduce((num, value) => { 426 | let r1 = 0; 427 | let r2 = 0; 428 | let m = 0; 429 | try { 430 | r1 = num.toString().split(".")[1].length; 431 | } catch (e) { 432 | } 433 | try { 434 | r2 = value.toString().split(".")[1].length; 435 | } catch (e) { 436 | } 437 | m = Math.pow(10, Math.max(r1, r2)); 438 | return (num * m + value * m) / m; 439 | }); 440 | }, 441 | /** 442 | * 多个数的减法运算 443 | */ 444 | subtract(...values) { 445 | return values.reduce((num, value) => { 446 | let r1 = 0; 447 | let r2 = 0; 448 | let m = 0; 449 | try { 450 | r1 = num.toString().split(".")[1].length; 451 | } catch (e) { 452 | } 453 | try { 454 | r2 = value.toString().split(".")[1].length; 455 | } catch (e) { 456 | } 457 | m = Math.pow(10, Math.max(r1, r2)); 458 | return (num * m - value * m) / m; 459 | }); 460 | }, 461 | /** 462 | * 多个数的乘法运算 463 | */ 464 | mutiply(...values) { 465 | return values.reduce((num, value) => { 466 | let m = 0; 467 | let s1 = num.toString(); 468 | let s2 = value.toString(); 469 | try { 470 | m += s1.split(".")[1].length; 471 | } catch (e) { 472 | } 473 | try { 474 | m += s2.split(".")[1].length; 475 | } catch (e) { 476 | } 477 | return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m); 478 | }); 479 | }, 480 | /** 481 | * 多个数的除法运算 482 | */ 483 | divide(...values) { 484 | return values.reduce((num, value) => { 485 | let t1 = 0; 486 | let t2 = 0; 487 | let s1 = num.toString(); 488 | let s2 = value.toString(); 489 | try { 490 | t1 = s1.split(".")[1].length; 491 | } catch (e) { 492 | } 493 | try { 494 | t2 = s2.split(".")[1].length; 495 | } catch (e) { 496 | } 497 | return Number(s1.replace(".", "")) / Number(s2.replace(".", "")) * Math.pow(10, t2 - t1); 498 | }); 499 | } 500 | }; 501 | const element = { 502 | /** 503 | * 判断是否是Window对象 504 | * @param {Object} data 入参 505 | */ 506 | isWindow(data2) { 507 | return data2 && data2 instanceof Window; 508 | }, 509 | /** 510 | * 获取元素距离某个定位祖先元素左侧/顶部/底部/右侧的距离 511 | * @param {Object} el 元素 512 | * @param {Object} root 定位父元素或者祖先元素,未指定则为document.body 513 | */ 514 | getElementPoint(el, root) { 515 | if (!this.isElement(root)) { 516 | root = document.body; 517 | } 518 | if (!this.isContains(root, el)) { 519 | throw new Error("The second argument and the first argument have no hierarchical relationship"); 520 | } 521 | const obj = el; 522 | let offsetTop = 0; 523 | let offsetLeft = 0; 524 | while (this.isElement(el) && this.isContains(root, el) && root !== el) { 525 | offsetTop += el.offsetTop; 526 | offsetLeft += el.offsetLeft; 527 | el = el.offsetParent; 528 | } 529 | let offsetRight = root.offsetWidth - offsetLeft - obj.offsetWidth; 530 | let offsetBottom = root.offsetHeight - offsetTop - obj.offsetHeight; 531 | return { 532 | top: offsetTop, 533 | left: offsetLeft, 534 | right: offsetRight, 535 | bottom: offsetBottom 536 | }; 537 | }, 538 | /** 539 | * 判断某个元素是否包含指定元素,包含相等关系和父子关系 540 | * @param {Object} parentNode 父元素或祖先元素 541 | * @param {Object} childNode 子元素 542 | */ 543 | isContains(parentNode, childNode) { 544 | if (parentNode === childNode) { 545 | return true; 546 | } 547 | if (parentNode.contains) { 548 | return parentNode.contains(childNode); 549 | } 550 | if (parentNode.compareDocumentPosition) { 551 | return !!(parentNode.compareDocumentPosition(childNode) & 16); 552 | } 553 | return false; 554 | }, 555 | /** 556 | * 判断某个元素是否是指定元素的父元素 557 | * @param {Object} parentNode 父元素 558 | * @param {Object} childNode 子元素 559 | */ 560 | isParentNode(parentNode, childNode) { 561 | if (parentNode === childNode) { 562 | return false; 563 | } 564 | return childNode.parentNode === parentNode; 565 | }, 566 | /** 567 | * 查找某个元素下指定选择器的子元素 568 | * @param {Object} el 元素 569 | * @param {Object} selector 支持多选择器,等同于querySelectorAll的参数 570 | */ 571 | children(el, selector) { 572 | const elements = Array.from(el.querySelectorAll(selector ?? "*")); 573 | return Array.from(elements).filter((elm) => this.isParentNode(el, elm)); 574 | }, 575 | /** 576 | * 查找某个元素下指定选择器的兄弟元素 577 | * @param {Object} el 元素 578 | * @param {Object} selector 取值等同于queryselectorAll的参数,支持多选择器 579 | */ 580 | siblings(el, selector) { 581 | if (!el.parentNode) { 582 | return []; 583 | } 584 | const elements = Array.from(el.parentNode.querySelectorAll(selector ?? "*")); 585 | return elements.filter((elm) => elm.parentNode === el.parentNode && elm != el); 586 | }, 587 | /** 588 | * rem与px单位转换 589 | * @param {Object} num rem数值 590 | */ 591 | rem2px(num) { 592 | const fs = this.getCssStyle(document.documentElement, "font-size"); 593 | return number.mutiply(num, parseFloat(fs)); 594 | }, 595 | /** 596 | * rem与px单位转换 597 | * @param {Object} num px数值 598 | */ 599 | px2rem(num) { 600 | const fs = this.getCssStyle(document.documentElement, "font-size"); 601 | return number.divide(num, parseFloat(fs)); 602 | }, 603 | /** 604 | * 获取元素的内容宽度,内容宽度不包括border和padding 605 | * @param {Object} el 支持css选择器字符串,未指定则表示document.body 606 | */ 607 | width(el) { 608 | if (typeof el == "string" && el) { 609 | el = document.body.querySelector(el); 610 | } 611 | if (!this.isElement(el)) { 612 | el = document.body; 613 | } 614 | const clientWidth = el.clientWidth; 615 | const paddingLeft_width = parseFloat(this.getCssStyle(el, "padding-left")); 616 | const paddingRight_width = parseFloat(this.getCssStyle(el, "padding-right")); 617 | return number.subtract(clientWidth, paddingLeft_width, paddingRight_width); 618 | }, 619 | /** 620 | * 获取元素的内容高度,内容高度不包括border和padding 621 | * @param {Object} el 支持css选择器字符串 未指定则表示document.body 622 | */ 623 | height(el) { 624 | if (typeof el == "string" && el) { 625 | el = document.body.querySelector(el); 626 | } 627 | if (!this.isElement(el)) { 628 | el = document.body; 629 | } 630 | const clientHeight = el.clientHeight; 631 | const paddingTop_height = parseFloat(this.getCssStyle(el, "padding-top")); 632 | const paddingBottom_height = parseFloat(this.getCssStyle(el, "padding-bottom")); 633 | return number.subtract(clientHeight, paddingTop_height, paddingBottom_height); 634 | }, 635 | /** 636 | * 移除class 637 | * @param {Object} el 元素 638 | * @param {Object} className 支持多类,以空格划分 639 | */ 640 | removeClass(el, className) { 641 | const classArray = string.trim(className).split(/\s+/); 642 | classArray.forEach((item) => { 643 | el.classList.remove(item); 644 | }); 645 | }, 646 | /** 647 | * 添加class 648 | * @param {Object} el 元素 649 | * @param {Object} className 支持多类,以空格划分 650 | */ 651 | addClass(el, className) { 652 | const classArray = string.trim(className).split(/\s+/); 653 | classArray.forEach((item) => { 654 | el.classList.add(item); 655 | }); 656 | }, 657 | /** 658 | * 判断指定元素是否含有指定类名 659 | * @param {Object} el 元素 660 | * @param {Object} className 支持多类,以空格划分 661 | */ 662 | hasClass(el, className) { 663 | const classArray = string.trim(className).split(/\s+/); 664 | return classArray.every((item) => { 665 | return el.classList.contains(item); 666 | }); 667 | }, 668 | /** 669 | * 监听元素滚动到顶部或者底部 670 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动 671 | * @param {Object} callback 回调函数 672 | */ 673 | scrollTopBottomTrigger(el, callback) { 674 | if (typeof el == "string" && el) { 675 | el = document.body.querySelector(el); 676 | } 677 | let scrollEle = window; 678 | if (this.isElement(el) && el != document.body && el != document.documentElement) { 679 | scrollEle = el; 680 | } 681 | if (typeof el == "function") { 682 | callback = el; 683 | } 684 | let flag = true; 685 | scrollEle.addEventListener("scroll", () => { 686 | if (this.getScrollTop(scrollEle) <= 0) { 687 | const options = { 688 | state: "top", 689 | target: scrollEle 690 | }; 691 | if (!flag) { 692 | return; 693 | } 694 | if (typeof callback == "function") { 695 | flag = false; 696 | callback(options); 697 | } 698 | } else { 699 | const options = { 700 | state: "bottom", 701 | target: scrollEle 702 | }; 703 | let height = 0; 704 | if (scrollEle == window) { 705 | height = window.innerHeight; 706 | } else { 707 | height = scrollEle.clientHeight; 708 | } 709 | if (number.add(this.getScrollTop(scrollEle), height) + 1 >= this.getScrollHeight(scrollEle) && height != this.getScrollHeight(scrollEle)) { 710 | if (!flag) { 711 | return; 712 | } 713 | if (typeof callback == "function") { 714 | flag = false; 715 | callback(options); 716 | } 717 | } else { 718 | flag = true; 719 | } 720 | } 721 | }); 722 | }, 723 | /** 724 | * 获取文档或元素的总宽度 725 | * @param {Object} el 支持css选择器字符串 未指定则表示整个页面文档 726 | */ 727 | getScrollWidth(el) { 728 | if (typeof el == "string" && el) { 729 | el = document.body.querySelector(el); 730 | } 731 | let scrollWidth = 0; 732 | if (this.isElement(el) && el != document.documentElement && el != document.body) { 733 | scrollWidth = el.scrollWidth; 734 | } else { 735 | if (document.documentElement.scrollWidth == 0 || document.body.scrollWidth == 0) { 736 | scrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth; 737 | } else { 738 | scrollWidth = document.documentElement.scrollWidth > document.body.scrollWidth ? document.documentElement.scrollWidth : document.body.scrollWidth; 739 | } 740 | } 741 | return scrollWidth; 742 | }, 743 | /** 744 | * 获取文档或者元素的总高度 745 | * @param {Object} el 支持css选择器字符串 未指定则表示整个页面文档 746 | */ 747 | getScrollHeight(el) { 748 | if (typeof el == "string" && el) { 749 | el = document.body.querySelector(el); 750 | } 751 | let scrollHeight = 0; 752 | if (this.isElement(el) && el != document.documentElement && el != document.body) { 753 | scrollHeight = el.scrollHeight; 754 | } else { 755 | if (document.documentElement.scrollHeight == 0 || document.body.scrollHeight == 0) { 756 | scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight; 757 | } else { 758 | scrollHeight = document.documentElement.scrollHeight > document.body.scrollHeight ? document.documentElement.scrollHeight : document.body.scrollHeight; 759 | } 760 | } 761 | return scrollHeight; 762 | }, 763 | /** 764 | * 设置滚动条在Y轴上的距离 765 | * @param {Object} options {el,number,time} el支持css选择器字符串 未指定则为窗口滚动 766 | */ 767 | setScrollTop(options) { 768 | let isWindow = false; 769 | let el = options.el; 770 | if (typeof el == "string" && el) { 771 | el = document.body.querySelector(el); 772 | } 773 | const number$1 = options.number || 0; 774 | const time = options.time || 0; 775 | if (!this.isElement(el) || el == document.body || el == document.documentElement || el == window) { 776 | isWindow = true; 777 | } 778 | return new Promise((resolve) => { 779 | if (time <= 0) { 780 | if (isWindow) { 781 | document.documentElement.scrollTop = document.body.scrollTop = number$1; 782 | } else { 783 | el.scrollTop = number$1; 784 | } 785 | resolve(); 786 | } else { 787 | const spacingTime = 10; 788 | let spacingIndex = number.divide(time, spacingTime); 789 | let nowTop = this.getScrollTop(el); 790 | const everTop = number.divide(number.subtract(number$1, nowTop), spacingIndex); 791 | const scrollTimer = setInterval(() => { 792 | if (spacingIndex > 0) { 793 | spacingIndex--; 794 | if (isWindow) { 795 | document.documentElement.scrollTop = document.body.scrollTop = nowTop = number.add(nowTop, everTop); 796 | } else { 797 | el.scrollTop = nowTop = number.add(nowTop, everTop); 798 | } 799 | } else { 800 | clearInterval(scrollTimer); 801 | resolve(); 802 | } 803 | }, spacingTime); 804 | } 805 | }); 806 | }, 807 | /** 808 | * 获取滚动条在Y轴上滚动的距离 809 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动 810 | */ 811 | getScrollTop(el) { 812 | if (typeof el == "string" && el) { 813 | el = document.body.querySelector(el); 814 | } 815 | let scrollTop = 0; 816 | if (this.isElement(el) && el != document.body && el != document.documentElement && el != window) { 817 | scrollTop = el.scrollTop; 818 | } else { 819 | if (document.documentElement.scrollTop == 0 || document.body.scrollTop == 0) { 820 | scrollTop = document.documentElement.scrollTop || document.body.scrollTop; 821 | } else { 822 | scrollTop = document.documentElement.scrollTop > document.body.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop; 823 | } 824 | } 825 | return scrollTop; 826 | }, 827 | /** 828 | * 获取滚动条在X轴上滚动的距离 829 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动 830 | */ 831 | getScrollLeft(el) { 832 | if (typeof el == "string" && el) { 833 | el = document.body.querySelector(el); 834 | } 835 | let scrollLeft = 0; 836 | if (this.isElement(el) && el != document.body && el != document.documentElement && el != window) { 837 | scrollLeft = el.scrollLeft; 838 | } else { 839 | if (document.documentElement.scrollLeft == 0 || document.body.scrollLeft == 0) { 840 | scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; 841 | } else { 842 | scrollLeft = document.documentElement.scrollLeft > document.body.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft; 843 | } 844 | } 845 | return scrollLeft; 846 | }, 847 | /** 848 | * 设置滚动条在X轴上的距离 849 | * @param {Object} options {el,number,time} el支持css选择器字符串 未指定则为窗口滚动 850 | */ 851 | setScrollLeft(options) { 852 | let isWindow = false; 853 | let el = options.el; 854 | if (typeof el == "string" && el) { 855 | el = document.body.querySelector(el); 856 | } 857 | const number$1 = options.number || 0; 858 | const time = options.time || 0; 859 | if (!this.isElement(el) || el == document.body || el == document.documentElement || el == window) { 860 | isWindow = true; 861 | } 862 | return new Promise((resolve) => { 863 | if (time <= 0) { 864 | if (isWindow) { 865 | document.documentElement.scrollLeft = document.body.scrollLeft = number$1; 866 | } else { 867 | el.scrollLeft = number$1; 868 | } 869 | resolve(); 870 | } else { 871 | const spacingTime = 10; 872 | let spacingIndex = number.divide(time, spacingTime); 873 | let nowLeft = this.getScrollLeft(el); 874 | const everLeft = number.divide(number.subtract(number$1, nowLeft), spacingIndex); 875 | let scrollTimer = setInterval(() => { 876 | if (spacingIndex > 0) { 877 | spacingIndex--; 878 | if (isWindow) { 879 | document.documentElement.scrollLeft = document.body.scrollLeft = nowLeft = number.add(nowLeft, everLeft); 880 | } else { 881 | el.scrollLeft = nowLeft = number.add(nowLeft, everLeft); 882 | } 883 | } else { 884 | clearInterval(scrollTimer); 885 | resolve(); 886 | } 887 | }, spacingTime); 888 | } 889 | }); 890 | }, 891 | /** 892 | * 获取元素指定样式 893 | * @param {Object} el 元素 894 | * @param {Object} cssName 样式名称 895 | */ 896 | getCssStyle(el, cssName) { 897 | let cssText = ""; 898 | if (document.defaultView && document.defaultView.getComputedStyle) { 899 | cssText = document.defaultView.getComputedStyle(el)[cssName]; 900 | } else { 901 | cssText = el.currentStyle[cssName]; 902 | } 903 | return cssText; 904 | }, 905 | /** 906 | * 判断字符串属于哪种选择器 907 | * @param {Object} selector 908 | */ 909 | getCssSelector(selector) { 910 | if (/^#{1}/.test(selector)) { 911 | return { 912 | type: "id", 913 | value: selector.substring(1) 914 | }; 915 | } 916 | if (/^\./.test(selector)) { 917 | return { 918 | type: "class", 919 | value: selector.substring(1) 920 | }; 921 | } 922 | if (/^\[(.+)\]$/.test(selector)) { 923 | let value = ""; 924 | const attribute = string.trim(selector, true).substring(1, string.trim(selector, true).length - 1); 925 | const arry = attribute.split("="); 926 | if (arry.length == 1) { 927 | value = arry[0]; 928 | } 929 | if (arry.length == 2) { 930 | value = { 931 | attributeName: arry[0], 932 | attributeValue: arry[1].replace(/\'/g, "").replace(/\"/g, "") 933 | //去除属性值的单引号或者双引号 934 | }; 935 | } 936 | return { 937 | type: "attribute", 938 | value 939 | }; 940 | } 941 | return { 942 | type: "tag", 943 | value: selector 944 | }; 945 | }, 946 | /** 947 | * 获取元素距离可视窗口的位置 948 | * @param {Object} el 支持css选择器字符串 未指定则为document.body 949 | */ 950 | getElementBounding(el) { 951 | if (typeof el == "string" && el) { 952 | el = document.body.querySelector(el); 953 | } 954 | if (!this.isElement(el)) { 955 | el = document.body; 956 | } 957 | const point = el.getBoundingClientRect(); 958 | const top = point.top; 959 | const bottom = number.subtract(document.documentElement.clientHeight || window.innerHeight, point.bottom); 960 | const left = point.left; 961 | const right = number.subtract(document.documentElement.clientWidth || window.innerWidth, point.right); 962 | return { 963 | top, 964 | bottom, 965 | left, 966 | right 967 | }; 968 | }, 969 | /** 970 | * 判断是否是元素 971 | * @param {Object} el 972 | */ 973 | isElement(el) { 974 | return !!el && el instanceof Node && el.nodeType === 1; 975 | }, 976 | /** 977 | * 字符串转dom 978 | * @param {Object} html 979 | */ 980 | string2dom(html) { 981 | const template = document.createElement("template"); 982 | template.innerHTML = html; 983 | if (template.content.children.length == 1) { 984 | return template.content.children[0]; 985 | } 986 | return Array.from(template.content.children); 987 | } 988 | }; 989 | const parseEventName = (eventName) => { 990 | const eventNames = eventName.split(/[\s]+/g); 991 | const result = []; 992 | eventNames.forEach((name) => { 993 | const arr = name.split("."); 994 | const obj = { 995 | eventName: arr[0] 996 | }; 997 | if (arr.length > 1) { 998 | obj.guid = arr[1]; 999 | } 1000 | result.push(obj); 1001 | }); 1002 | return result; 1003 | }; 1004 | const updateEvents = (events) => { 1005 | const obj = {}; 1006 | const keys = Object.keys(events); 1007 | keys.forEach((key) => { 1008 | if (events[key]) { 1009 | obj[key] = events[key]; 1010 | } 1011 | }); 1012 | return obj; 1013 | }; 1014 | const bindSingleListener = (el, eventName, guid, fn, options) => { 1015 | const events = data.get(el, "dap-defined-events") || {}; 1016 | if (!guid) { 1017 | guid = data.get(el, "dap-event-guid") || 0; 1018 | data.set(el, "dap-event-guid", guid + 1); 1019 | } 1020 | guid = eventName + "." + guid; 1021 | if (events[guid] && events[guid].type == eventName) { 1022 | el.removeEventListener(eventName, events[guid].fn, events[guid].options); 1023 | } 1024 | el.addEventListener(eventName, fn, options); 1025 | events[guid] = { 1026 | type: eventName, 1027 | fn, 1028 | options 1029 | }; 1030 | data.set(el, "dap-defined-events", events); 1031 | }; 1032 | const unbindSingleListener = (el, eventName, guid) => { 1033 | const events = data.get(el, "dap-defined-events") || {}; 1034 | const keys = Object.keys(events); 1035 | const length = keys.length; 1036 | for (let i = 0; i < length; i++) { 1037 | const key = keys[i]; 1038 | if (events[key] && events[key].type == eventName) { 1039 | if (guid) { 1040 | if (key == eventName + "." + guid) { 1041 | el.removeEventListener(events[key].type, events[key].fn, events[key].options); 1042 | events[key] = void 0; 1043 | } 1044 | } else { 1045 | el.removeEventListener(events[key].type, events[key].fn, events[key].options); 1046 | events[key] = void 0; 1047 | } 1048 | } 1049 | } 1050 | data.set(el, "dap-defined-events", updateEvents(events)); 1051 | }; 1052 | const event = { 1053 | /** 1054 | * 绑定事件 1055 | * @param {Object} el 元素节点 1056 | * @param {Object} eventName 事件名称 1057 | * @param {Object} fn 函数 1058 | * @param {Object} options 参数 1059 | */ 1060 | on(el, eventName, fn, options) { 1061 | const result = parseEventName(eventName); 1062 | result.forEach((res) => { 1063 | bindSingleListener(el, res.eventName, res.guid, fn.bind(el), options); 1064 | }); 1065 | }, 1066 | /** 1067 | * 事件解绑 1068 | * @param {Object} el 元素节点 1069 | * @param {Object} eventName 事件名称 1070 | */ 1071 | off(el, eventName) { 1072 | const events = data.get(el, "dap-defined-events"); 1073 | if (!events) { 1074 | return; 1075 | } 1076 | if (!eventName) { 1077 | const keys = Object.keys(events); 1078 | const length = keys.length; 1079 | for (let i = 0; i < length; i++) { 1080 | const key = keys[i]; 1081 | el.removeEventListener(events[key].type, events[key].fn, events[key].options); 1082 | } 1083 | data.remove(el, "dap-defined-events"); 1084 | data.remove(el, "dap-event-guid"); 1085 | return; 1086 | } 1087 | const result = parseEventName(eventName); 1088 | result.forEach((res) => { 1089 | unbindSingleListener(el, res.eventName, res.guid); 1090 | }); 1091 | }, 1092 | /** 1093 | * 获取绑定的所有事件 1094 | * @param {*} el 1095 | */ 1096 | get(el) { 1097 | const events = data.get(el, "dap-defined-events"); 1098 | if (!events) { 1099 | return; 1100 | } 1101 | return events; 1102 | } 1103 | }; 1104 | const file = { 1105 | /** 1106 | * 根据文件获取本地可预览的图片路径 1107 | * @param {Object} file 1108 | */ 1109 | getImageUrl(file2) { 1110 | return window.URL.createObjectURL(file2); 1111 | }, 1112 | /** 1113 | * 将JS的file对象转为BASE64位字符串,通过then方法回调,参数为base64字符串 1114 | * @param {Object} file 1115 | */ 1116 | dataFileToBase64(file2) { 1117 | return new Promise((resolve) => { 1118 | const reader = new FileReader(); 1119 | reader.readAsDataURL(file2); 1120 | reader.onloadend = () => { 1121 | const dataURL = reader.result; 1122 | resolve(dataURL); 1123 | }; 1124 | }); 1125 | }, 1126 | /** 1127 | * 将base64位格式文件转换为file对象 1128 | * @param {Object} base64String base64位格式字符串 1129 | * @param {Object} fileName 转换后的文件名字,包含后缀 1130 | */ 1131 | dataBase64toFile(base64String, fileName) { 1132 | const arr = base64String.split(","); 1133 | const mime = arr[0].match(/:(.*?);/)[1]; 1134 | const bstr = atob(arr[1]); 1135 | let n = bstr.length; 1136 | const u8arr = new Uint8Array(n); 1137 | while (n--) { 1138 | u8arr[n] = bstr.charCodeAt(n); 1139 | } 1140 | return new File([u8arr], fileName, { 1141 | type: mime 1142 | }); 1143 | }, 1144 | /** 1145 | * 图片压缩方法 1146 | * @param {*} file 需要压缩的图片File文件 1147 | * @param {*} options 压缩参数 1148 | */ 1149 | compressImage(file2, options) { 1150 | const createFile = (canvas, fileName, quality) => { 1151 | let url = canvas.toDataURL("image/" + (options.mimeType ?? "jpeg"), quality); 1152 | let file3 = this.dataBase64toFile(url, fileName); 1153 | if (options.maxSize && options.maxSize > 0 && file3.size > options.maxSize * 1024) { 1154 | quality = quality <= 0 ? 0 : Number((quality - 0.01).toFixed(2)); 1155 | const res = createFile(canvas, fileName, quality); 1156 | url = res.url; 1157 | file3 = res.file; 1158 | quality = res.quality; 1159 | } 1160 | return { 1161 | file: file3, 1162 | url, 1163 | quality 1164 | }; 1165 | }; 1166 | return new Promise((resolve, reject) => { 1167 | const reader = new FileReader(); 1168 | reader.readAsDataURL(file2); 1169 | reader.onload = () => { 1170 | const url = reader.result; 1171 | const img = new Image(); 1172 | img.src = url; 1173 | img.onload = () => { 1174 | if (options.minSize && options.minSize > 0 && file2.size <= options.minSize * 1024) { 1175 | resolve({ 1176 | file: file2, 1177 | url, 1178 | quality: 1, 1179 | width: img.width, 1180 | height: img.height 1181 | }); 1182 | return; 1183 | } 1184 | const canvas = document.createElement("canvas"); 1185 | const context = canvas.getContext("2d"); 1186 | canvas.width = options.width || img.width; 1187 | canvas.height = options.width ? options.width / (img.width / img.height) : img.height; 1188 | context.drawImage(img, 0, 0, canvas.width, canvas.height); 1189 | const index2 = file2.name.lastIndexOf("."); 1190 | const fileName = file2.name.substring(0, index2) + "." + (options.mimeType ?? "jpeg"); 1191 | let res = createFile(canvas, fileName, options.quality ?? 0.8); 1192 | resolve({ 1193 | ...res, 1194 | width: canvas.width, 1195 | height: canvas.height 1196 | }); 1197 | }; 1198 | img.onerror = () => { 1199 | reject(new Error("Failed to load image file")); 1200 | }; 1201 | }; 1202 | reader.onerror = () => { 1203 | reject(new Error("Failed to load image file")); 1204 | }; 1205 | }); 1206 | } 1207 | }; 1208 | const platform = { 1209 | //设备语言类型 1210 | language() { 1211 | return window.navigator.language; 1212 | }, 1213 | /** 1214 | * 获取设备类型 1215 | */ 1216 | device() { 1217 | const userAgent = window.navigator.userAgent; 1218 | return { 1219 | PC: !userAgent.match(/AppleWebKit.*Mobile.*/), 1220 | //是否移动终端 1221 | Mobile: !!userAgent.match(/AppleWebKit.*Mobile.*/), 1222 | //是否iPhone 1223 | iPhone: userAgent.includes("iPhone"), 1224 | //是否手机 1225 | Phone: userAgent.includes("Android") && /(?:Mobile)/.test(userAgent) || userAgent.includes("iPhone") || /(?:Windows Phone)/.test(userAgent), 1226 | //是否iPad 1227 | iPad: userAgent.includes("iPad"), 1228 | //是否平板电脑 1229 | Tablet: userAgent.includes("iPad") || userAgent.includes("Android") && !/(?:Mobile)/.test(userAgent) || userAgent.includes("Firefox") && /(?:Tablet)/.test(userAgent), 1230 | //windows手机 1231 | WindowsPhone: /(?:Windows Phone)/.test(userAgent) 1232 | }; 1233 | }, 1234 | /** 1235 | * 获取浏览器类型 1236 | */ 1237 | browser() { 1238 | const userAgent = window.navigator.userAgent; 1239 | return { 1240 | //是否edge浏览器 1241 | Edge: !!userAgent.match(/Edg\/([\d.]+)/), 1242 | //是否微信内置浏览器 1243 | Weixin: userAgent.includes("MicroMessenger"), 1244 | //是否QQ内置浏览器 1245 | QQ: userAgent.includes("QQ"), 1246 | //是否QQ浏览器 1247 | QQBrowser: userAgent.includes("MQQBrowser"), 1248 | //是否UC浏览器 1249 | UC: userAgent.includes("UCBrowser"), 1250 | //是否谷歌浏览器 1251 | Chrome: userAgent.includes("Chrome"), 1252 | //是否火狐浏览器 1253 | Firefox: userAgent.includes("Firefox"), 1254 | //是否搜狗浏览器 1255 | Sougou: userAgent.toLocaleLowerCase().includes("se 2.x") || userAgent.toLocaleLowerCase().includes("metasr") || userAgent.toLocaleLowerCase().includes("sogou"), 1256 | //是否safari浏览器 1257 | Safari: userAgent.includes("Safari") && !userAgent.includes("Chrome") 1258 | }; 1259 | }, 1260 | /** 1261 | * 获取浏览器内核 1262 | */ 1263 | browserKernel() { 1264 | const userAgent = window.navigator.userAgent; 1265 | if (userAgent.includes("Presto")) { 1266 | return "opera"; 1267 | } 1268 | if (userAgent.includes("AppleWebKit")) { 1269 | return "webkit"; 1270 | } 1271 | if (userAgent.includes("Gecko") && !userAgent.includes("KHTML")) { 1272 | return "gecko"; 1273 | } 1274 | }, 1275 | /** 1276 | * 获取操作系统数据 1277 | */ 1278 | os() { 1279 | const userAgent = window.navigator.userAgent; 1280 | return { 1281 | //是否Windows系统 1282 | Windows: userAgent.includes("Windows"), 1283 | //x64/x32 1284 | WindowsCPU: function() { 1285 | if (userAgent.toLocaleLowerCase().includes("win64") || userAgent.toLocaleLowerCase().includes("wow64")) { 1286 | return "x64"; 1287 | } else if (userAgent.toLocaleLowerCase().includes("win32") || userAgent.toLocaleLowerCase().includes("wow32")) { 1288 | return "x32"; 1289 | } 1290 | }(), 1291 | //Windows版本 1292 | WindowsVersion: function() { 1293 | if (userAgent.includes("Windows NT 6.1") || userAgent.includes("Windows 7")) { 1294 | return "win7"; 1295 | } 1296 | if (userAgent.includes("Windows NT 6.3") || userAgent.includes("Windows NT 6.2") || userAgent.includes("Windows NT 8")) { 1297 | return "win8"; 1298 | } 1299 | if (userAgent.includes("Windows NT 10") || userAgent.includes("Windows NT 6.4")) { 1300 | return "win10"; 1301 | } 1302 | }(), 1303 | //是否Mac 1304 | Mac: userAgent.includes("Macintosh"), 1305 | //Mac版本 1306 | MacVersion: function() { 1307 | if (userAgent.includes("Macintosh")) { 1308 | const matches = userAgent.match(/Mac OS X ([^\s]+)\)/); 1309 | if (matches && matches[1]) { 1310 | return matches[1].split(/_|\./).join("."); 1311 | } 1312 | } 1313 | return ""; 1314 | }(), 1315 | //是否ios系统 1316 | ios: !!userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), 1317 | //ios系统版本 1318 | iosVersion: function() { 1319 | if (!!userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)) { 1320 | const matches = userAgent.match(/CPU.+OS ([^\s]+) like Mac OS X/); 1321 | if (matches && matches[1]) { 1322 | return matches[1].split(/_|\./).join("."); 1323 | } 1324 | } 1325 | return ""; 1326 | }(), 1327 | //是否Android系统 1328 | Android: userAgent.includes("Android"), 1329 | //Android系统版本 1330 | AndroidVersion: function() { 1331 | const matches = userAgent.match(/Android ([^\s]+);/); 1332 | if (matches && matches[1]) { 1333 | return matches[1].split(/_|\./).join("."); 1334 | } 1335 | return ""; 1336 | }(), 1337 | //是否Linux系统 1338 | Linux: userAgent.includes("Linux"), 1339 | //是否鸿蒙系统 1340 | HarmonyOS: userAgent.includes("HarmonyOS"), 1341 | //是否Ubuntu系统 1342 | Ubuntu: userAgent.includes("Ubuntu") 1343 | }; 1344 | } 1345 | }; 1346 | const speech = { 1347 | /** 1348 | * 将文字加入语音播报队列 1349 | * @param {Object} text 1350 | * @param {Object} options 1351 | */ 1352 | start(text, options) { 1353 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) { 1354 | throw new Error("The current browser does not support this syntax"); 1355 | } 1356 | const speech2 = new SpeechSynthesisUtterance(); 1357 | speech2.text = text; 1358 | speech2.pitch = (options == null ? void 0 : options.pitch) ?? 0.8; 1359 | speech2.rate = (options == null ? void 0 : options.rate) ?? 1; 1360 | speech2.volume = (options == null ? void 0 : options.volume) ?? 1; 1361 | speech2.lang = "zh-CN"; 1362 | speech2.onstart = (event2) => { 1363 | var _a; 1364 | (_a = options == null ? void 0 : options.start) == null ? void 0 : _a.apply(speech2, [ 1365 | event2, 1366 | { 1367 | text, 1368 | pitch: options.pitch ?? 0.8, 1369 | rate: options.rate ?? 1, 1370 | volume: options.volume ?? 1 1371 | } 1372 | ]); 1373 | }; 1374 | speech2.onend = (event2) => { 1375 | var _a; 1376 | (_a = options == null ? void 0 : options.end) == null ? void 0 : _a.apply(speech2, [ 1377 | event2, 1378 | { 1379 | text, 1380 | pitch: options.pitch ?? 0.8, 1381 | rate: options.rate ?? 1, 1382 | volume: options.volume ?? 1 1383 | } 1384 | ]); 1385 | }; 1386 | speech2.onpause = (event2) => { 1387 | var _a; 1388 | (_a = options == null ? void 0 : options.pause) == null ? void 0 : _a.apply(speech2, [ 1389 | event2, 1390 | { 1391 | text, 1392 | pitch: options.pitch ?? 0.8, 1393 | rate: options.rate ?? 1, 1394 | volume: options.volume ?? 1 1395 | } 1396 | ]); 1397 | }; 1398 | speech2.onresume = (event2) => { 1399 | var _a; 1400 | (_a = options == null ? void 0 : options.resume) == null ? void 0 : _a.apply(speech2, [ 1401 | event2, 1402 | { 1403 | text, 1404 | pitch: options.pitch ?? 0.8, 1405 | rate: options.rate ?? 1, 1406 | volume: options.volume ?? 1 1407 | } 1408 | ]); 1409 | }; 1410 | speech2.onerror = (event2) => { 1411 | var _a; 1412 | (_a = options == null ? void 0 : options.error) == null ? void 0 : _a.apply(speech2, [ 1413 | event2, 1414 | { 1415 | text, 1416 | pitch: options.pitch ?? 0.8, 1417 | rate: options.rate ?? 1, 1418 | volume: options.volume ?? 1 1419 | } 1420 | ]); 1421 | }; 1422 | window.speechSynthesis.speak(speech2); 1423 | }, 1424 | /** 1425 | * 停止播报,停止所有播报队列里面的语音 1426 | */ 1427 | stop() { 1428 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) { 1429 | throw new Error("The current browser does not support this syntax"); 1430 | } 1431 | window.speechSynthesis.cancel(); 1432 | }, 1433 | /** 1434 | * 暂停播报 1435 | */ 1436 | pause() { 1437 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) { 1438 | throw new Error("The current browser does not support this syntax"); 1439 | } 1440 | window.speechSynthesis.pause(); 1441 | }, 1442 | /** 1443 | * 恢复暂停的播报 1444 | */ 1445 | resume() { 1446 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) { 1447 | throw new Error("The current browser does not support this syntax"); 1448 | } 1449 | window.speechSynthesis.resume(); 1450 | } 1451 | }; 1452 | const index = { number, data, element, event, common, color, file, string, platform, speech }; 1453 | export { 1454 | color, 1455 | common, 1456 | data, 1457 | index as default, 1458 | element, 1459 | event, 1460 | file, 1461 | number, 1462 | platform, 1463 | speech, 1464 | string 1465 | }; 1466 | --------------------------------------------------------------------------------