├── .editorconfig
├── .fatherrc.ts
├── .gitignore
├── .redbudrc.base.ts
├── .umirc.ts
├── LICENSE
├── README.md
├── build.sh
├── docs
├── guide
│ └── getting-started.md
└── index.md
├── lerna.json
├── now.json
├── package.json
├── packages
├── aliplayer
│ ├── .redbudrc.ts
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── docs
│ │ ├── aliplayer.md
│ │ └── demos
│ │ │ ├── demo-01.tsx
│ │ │ ├── demo-02.tsx
│ │ │ ├── demo-03.tsx
│ │ │ └── demo-04.tsx
│ ├── package.json
│ └── src
│ │ ├── index.less
│ │ ├── index.tsx
│ │ ├── player.tsx
│ │ ├── types.ts
│ │ └── typings.d.ts
├── responsive-card
│ ├── .redbudrc.ts
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── demo
│ │ ├── demo01.tsx
│ │ ├── demo02.tsx
│ │ └── demo03.tsx
│ ├── docs
│ │ └── responsive-card.md
│ ├── package.json
│ └── src
│ │ ├── index.tsx
│ │ └── utils.ts
└── split-screen
│ ├── .redbudrc.ts
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── docs
│ ├── demo
│ │ ├── demo-01.tsx
│ │ ├── demo-02.tsx
│ │ ├── demo-03.less
│ │ └── demo-03.tsx
│ └── split-screen.md
│ ├── package.json
│ └── src
│ ├── index.tsx
│ ├── types.ts
│ └── utils.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── renovate.json
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
15 | [Makefile]
16 | indent_style = tab
17 |
--------------------------------------------------------------------------------
/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | import { readdirSync } from 'fs';
2 | import { join } from 'path';
3 |
4 | // utils must build before core
5 | // runtime must build before renderer-react
6 | const headPkgs: string[] = [];
7 | const tailPkgs = readdirSync(join(__dirname, 'packages')).filter(
8 | (pkg) => pkg.charAt(0) !== '.' && !headPkgs.includes(pkg),
9 | );
10 |
11 | export default {
12 | cjs: {
13 | type: 'babel',
14 | lazy: true
15 | },
16 | esm: {
17 | type: 'babel',
18 | importLibToEs: true,
19 | },
20 | pkgs: [...headPkgs, ...tailPkgs],
21 | extraBabelPlugins: [
22 | ['babel-plugin-import', { libraryName: 'antd', libraryDirectory: 'es', style: true }, 'antd'],
23 | ],
24 | };
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Log
2 | npm-debug.log*
3 | yarn-debug.log*
4 | yarn-error.log*
5 |
6 | # Test
7 | coverage
8 |
9 | # Dependency directories
10 | node_modules
11 |
12 | package-lock.json
13 |
14 | # build output
15 | es
16 | lib
17 | dist
18 |
19 | # IDE
20 | .idea
21 | .vscode
22 |
23 | .DS_Store
24 |
25 | # Umi
26 | .umi
27 | .umi-production
28 |
29 | # Local
30 | local.config.ts
31 | yarn.lock
32 | install-state.gz
33 |
--------------------------------------------------------------------------------
/.redbudrc.base.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | esm: {
3 | output: 'es',
4 | },
5 | cjs: {
6 | output: 'lib',
7 | },
8 | platform: 'browser'
9 | };
10 |
--------------------------------------------------------------------------------
/.umirc.ts:
--------------------------------------------------------------------------------
1 | import { readdirSync } from 'fs';
2 | import chalk from 'chalk';
3 | import { join } from 'path';
4 |
5 | const headPkgList = [];
6 | // utils must build before core
7 | // runtime must build before renderer-react
8 | const pkgList = readdirSync(join(__dirname, 'packages')).filter(
9 | (pkg) => pkg.charAt(0) !== '.' && !headPkgList.includes(pkg),
10 | );
11 |
12 | const alias = pkgList.reduce((pre, pkg) => {
13 | pre[`@pansy/react-${pkg}`] = join(__dirname, 'packages', pkg, 'src');
14 | return {
15 | ...pre,
16 | };
17 | }, {});
18 |
19 | console.log(`🌼 alias list \n${chalk.blue(Object.keys(alias).join('\n'))}`);
20 |
21 | const tailPkgList = pkgList
22 | .map((path) => [join('packages', path, 'docs')])
23 | .reduce((acc, val) => acc.concat(val), []);
24 |
25 | const logo = 'https://cdn.jsdelivr.net/gh/wangxingkang/pictures@latest/imgs/react.svg';
26 |
27 | export default {
28 | title: 'React Components',
29 | logo,
30 | favicon: logo,
31 | mode: 'site',
32 | alias,
33 | resolve: { includes: [...tailPkgList, 'docs'] },
34 | navs: [
35 | null,
36 | {
37 | title: 'AMap',
38 | path: 'https://react-amap-pansyjs.vercel.app/',
39 | },
40 | {
41 | title: 'GitHub',
42 | path: 'https://github.com/pansyjs/react-components',
43 | },
44 | ],
45 | hash: true,
46 | dynamicImport: {}
47 | };
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Pansy
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
React Components
2 |
3 | React components library.
4 |
5 | ## 📦 组件看板
6 |
7 | | 组件 | 下载量 | 版本 |
8 | | --- | --- | --- |
9 | |[@pansy/react-watermark][npm-watermark-web]|[![npm download][npm-watermark-dw]][npm-watermark-url]|[![npm version][npm-watermark-v]][npm-watermark-url]|
10 | |[@pansy/react-aliplayer][npm-aliplayer-web]|[![npm download][npm-aliplayer-dw]][npm-aliplayer-url]|[![npm version][npm-aliplayer-v]][npm-aliplayer-url]|
11 | |[@pansy/react-split-screen][npm-ss-web]|[![npm download][npm-ss-dw]][npm-ss-url]|[![npm version][npm-ss-v]][npm-ss-url]|
12 | |[@pansy/react-responsive-card][npm-rc-web]|[![npm download][npm-rc-dw]][npm-rc-url]|[![npm version][npm-rc-v]][npm-rc-url]|
13 |
14 | ## 🌟 社区互助
15 |
16 | | Github Issue | 钉钉群 | 微信群 |
17 | | ------------------------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
18 | | [issues](https://github.com/pansyjs/react-components/issues) |
|
|
19 |
20 |
21 | [npm-watermark-dw]: https://img.shields.io/npm/dw/@pansy/react-watermark.svg
22 | [npm-watermark-v]: https://img.shields.io/npm/v/@pansy/react-watermark.svg?style=flat-square?style=flat-square
23 | [npm-watermark-url]: https://www.npmjs.com/package/@pansy/react-watermark
24 | [npm-watermark-web]: https://watermark-eosin.vercel.app/packages/frames
25 |
26 | [npm-aliplayer-dw]: https://img.shields.io/npm/dw/@pansy/react-aliplayer.svg
27 | [npm-aliplayer-v]: https://img.shields.io/npm/v/@pansy/react-aliplayer.svg?style=flat-square?style=flat-square
28 | [npm-aliplayer-url]: https://www.npmjs.com/package/@pansy/react-aliplayer
29 | [npm-aliplayer-web]: https://react-components-vert.vercel.app/components/video/aliplayer
30 |
31 | [npm-ss-dw]: https://img.shields.io/npm/dw/@pansy/react-split-screen.svg
32 | [npm-ss-v]: https://img.shields.io/npm/v/@pansy/react-split-screen.svg?style=flat-square?style=flat-square
33 | [npm-ss-url]: https://www.npmjs.com/package/@pansy/react-split-screen
34 | [npm-ss-web]: https://react-components-vert.vercel.app/components/basic/split-screen
35 |
36 | [npm-rc-dw]: https://img.shields.io/npm/dw/@pansy/react-responsive-card.svg
37 | [npm-rc-v]: https://img.shields.io/npm/v/@pansy/react-responsive-card.svg?style=flat-square?style=flat-square
38 | [npm-rc-url]: https://www.npmjs.com/package/@pansy/react-responsive-card
39 | [npm-rc-web]: https://react-components-vert.vercel.app/components/basic/responsive-card
40 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | yarn
3 | yarn site
4 |
--------------------------------------------------------------------------------
/docs/guide/getting-started.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 快速开始
3 | order: 2
4 | group:
5 | path: /
6 | nav:
7 | title: 文档
8 | path: /docs
9 | ---
10 |
11 | ## 安装
12 |
13 | 每一个组件都是一个独立的包,你需要在你的项目中安装对应的 npm 包并使用。
14 |
15 | ```sh
16 | $ npm i @pansy/react-watermark --save
17 | ```
18 |
19 | 包含的组件:
20 |
21 | - @pansy/react-aliplayer
22 | - @pansy/react-split-screen
23 | - @pansy/react-watermark
24 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 介绍 - ProComponents
3 | order: 10
4 | sidebar: false
5 | hero:
6 | title: React Components
7 | desc: React components library.
8 | actions:
9 | - text: 快速开始 →
10 | link: /docs/getting-started-table
11 |
12 | features:
13 | - icon: https://gw.alipayobjects.com/os/q/cms/images/k9ziitmp/13668549-b393-42a2-97c3-a6365ba87ac2_w96_h96.png
14 | title: 简单易用
15 | desc: 简洁的API,开箱即用
16 | - icon: https://gw.alipayobjects.com/os/q/cms/images/k9ziip85/89434dcf-5f1d-4362-9ce0-ab8012a85924_w96_h96.png
17 | title: 国际化
18 | desc: 提供完备的国际化语言支持
19 | - icon: https://gw.alipayobjects.com/os/q/cms/images/k9zij2bh/67f75d56-0d62-47d6-a8a5-dbd0cb79a401_w96_h96.png
20 | title: TypeScript
21 | desc: 使用 TypeScript 开发,提供完整的类型定义文件
22 |
23 | footer: Open-source MIT Licensed | Copyright © 2017-present
24 | ---
25 |
26 | ## 📦 组件看板
27 |
28 | | 组件 | 下载量 | 版本 |
29 | | --- | --- | --- |
30 | | watermark | [](https://www.npmjs.com/package/@pansy/react-watermark) | [](https://www.npmjs.com/package/@pansy/react-watermark) |
31 | | aliplayer | [](https://www.npmjs.com/package/@pansy/react-aliplayer) | [](https://www.npmjs.com/package/@pansy/react-aliplayer) |
32 | | split-screen | [](https://www.npmjs.com/package/@pansy/react-split-screen) | [](https://www.npmjs.com/package/@pansy/react-split-screen) |
33 |
34 | ## 🌟 社区互助
35 |
36 | | Github Issue | 钉钉群 | 微信群 |
37 | | ------------------------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
38 | | [issues](https://github.com/pansyjs/react-components/issues) |
|
|
39 |
40 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "npmClient": "yarn",
3 | "version": "independent",
4 | "useWorkspaces": true,
5 | "packages": [
6 | "packages/*"
7 | ],
8 | "command": {
9 | "publish": {
10 | "npmClient": "npm",
11 | "verifyAccess": false,
12 | "ignoreChanges": ["*.md", "**/test/**"],
13 | "message": "chore(release): publish"
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/now.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-components",
3 | "version": 2,
4 | "builds": [
5 | {
6 | "src": "build.sh",
7 | "use": "@now/static-build",
8 | "config": {
9 | "distDir": "dist"
10 | }
11 | }
12 | ],
13 | "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
14 | }
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pansy/react-components",
3 | "repository": "git@github.com:pansyjs/react-components.git",
4 | "author": "wangxingkang <156148958@qq.com>",
5 | "license": "MIT",
6 | "private": true,
7 | "scripts": {
8 | "start": "dumi dev",
9 | "build": "pnpm run -r build",
10 | "site": "dumi build",
11 | "test": "walrus test",
12 | "release": "walrus release"
13 | },
14 | "devDependencies": {
15 | "@pansy/react-aliplayer": "workspace:*",
16 | "@ant-design/pro-card": "^2.1.10",
17 | "@types/react": "^18.0.27",
18 | "@types/react-dom": "^18.0.10",
19 | "@pansy/shared": "1.9.0",
20 | "@walrus/cli": "1.3.4",
21 | "@walrus/plugin-release": "1.14.3",
22 | "@walrus/plugin-test": "1.1.0",
23 | "antd": "5.1.7",
24 | "commitizen": "4.3.0",
25 | "cz-conventional-changelog": "3.3.0",
26 | "dumi": "1.1.38",
27 | "redbud": "1.5.0",
28 | "lerna": "6.4.1",
29 | "react": "18.2.0",
30 | "react-dom": "18.2.0",
31 | "typescript": "4.9.5"
32 | },
33 | "config": {
34 | "commitizen": {
35 | "path": "cz-conventional-changelog"
36 | }
37 | },
38 | "workspaces": [
39 | "packages/*"
40 | ]
41 | }
42 |
--------------------------------------------------------------------------------
/packages/aliplayer/.redbudrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'redbud';
2 |
3 | export default defineConfig({
4 | extends: '../../.redbudrc.base.ts'
5 | });
6 |
--------------------------------------------------------------------------------
/packages/aliplayer/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | ## [2.1.1](https://github.com/pansyjs/react-components/compare/@pansy/react-aliplayer@2.1.0...@pansy/react-aliplayer@2.1.1) (2023-03-02)
7 |
8 |
9 | ### Bug Fixes
10 |
11 | * 修复播放器绑定事件逻辑错误 ([d7c0112](https://github.com/pansyjs/react-components/commit/d7c01126d8964dd851d0ca11ce12c9790080569a))
12 | * 修复偶发 aliplayer 报 没有为播放器指定容器 ([56eab0c](https://github.com/pansyjs/react-components/commit/56eab0c0a1ed4d2f5d30f75f65af3bee0e8faa25))
13 |
14 |
15 |
16 |
17 |
18 | # [2.1.0](https://github.com/pansyjs/react-components/compare/@pansy/react-aliplayer@1.3.0...@pansy/react-aliplayer@2.1.0) (2023-02-02)
19 |
20 |
21 | ### Features
22 |
23 | * **aliplayer:** 代码重构 ([cc512af](https://github.com/pansyjs/react-components/commit/cc512af63644dc1baf1622dd6ad9766465ca5eb9))
24 |
25 |
26 |
27 |
28 |
29 | # [1.3.0](https://github.com/pansyjs/react-components/compare/@pansy/react-aliplayer@1.2.1...@pansy/react-aliplayer@1.3.0) (2021-07-13)
30 |
31 |
32 | ### Features
33 |
34 | * **react-aliplayer:** update default version ([873bec5](https://github.com/pansyjs/react-components/commit/873bec5c19bc60032f248c3c9377625e11bedc8a))
35 |
36 |
37 |
38 |
39 |
40 | ## [1.2.1](https://github.com/pansyjs/react-components/compare/@pansy/react-aliplayer@1.2.0...@pansy/react-aliplayer@1.2.1) (2021-07-13)
41 |
42 |
43 | ### Bug Fixes
44 |
45 | * **aliplayer:** 修复乾坤使用问题 ([eadd047](https://github.com/pansyjs/react-components/commit/eadd047ff98e7cdd67d4089ce85e26a25cd71325))
46 |
47 |
48 |
49 |
50 |
51 | # [1.2.0](https://github.com/pansyjs/react-components/compare/@pansy/react-aliplayer@1.1.0...@pansy/react-aliplayer@1.2.0) (2021-07-12)
52 |
53 |
54 | ### Features
55 |
56 | * **aliplayer:** 支持乾坤沙盒 ([dd0c55a](https://github.com/pansyjs/react-components/commit/dd0c55afe7c5662dfc36d203adf667647ab98c4c))
57 |
58 |
59 |
60 |
61 |
62 | # 1.1.0 (2021-04-13)
63 |
64 |
65 | ### Features
66 |
67 | * **aliplayer:** update aliplayer sdk version ([c1a8fe2](https://github.com/pansyjs/react-components/commit/c1a8fe2adae92f146df7151abab0a84d7b64b3b1))
68 | * **aliplayer:** 添加flv配置类型定义 ([b3e93fa](https://github.com/pansyjs/react-components/commit/b3e93fa12c0d450f503786baca4d414233939f9f))
69 | * **amap:** add InfoWindow component ([db0e9ae](https://github.com/pansyjs/react-components/commit/db0e9ae551b7d08b21b312f2534c2fa3a232b2f5))
70 |
--------------------------------------------------------------------------------
/packages/aliplayer/README.md:
--------------------------------------------------------------------------------
1 | React AliPlayer
2 |
3 | React component wrapper for aliplayer.
4 |
5 | ## 🏗 安装
6 |
7 | ```sh
8 | # npm 安装
9 | npm install --save @pansy/react-aliplayer
10 |
11 | # yarn 安装
12 | yarn add @pansy/react-aliplayer
13 | ```
14 |
15 | ## 🔨 使用
16 |
17 | ```tsx
18 | import React, { FC } from 'react';
19 | import Player from '@pansy/react-aliplayer';
20 |
21 | const Example: FC = () => {
22 | return (
23 |
28 | );
29 | };
30 |
31 | export default Example;
32 | ```
33 |
--------------------------------------------------------------------------------
/packages/aliplayer/docs/aliplayer.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: AliPlayer 阿里播放器
3 | nav:
4 | title: 组件
5 | path: /components
6 | order: 2
7 | group:
8 | path: /video
9 | title: '音视频组件'
10 | ---
11 |
12 | # AliPlayer 阿里播放器
13 |
14 | ## 何时使用
15 |
16 | - 播放视频。
17 | - 播放直播流(flv、hls)。
18 |
19 | ## 代码示例
20 |
21 | ### 简单使用
22 |
23 |
24 |
25 | ### 隐藏控制栏
26 |
27 |
28 |
29 | ### 直播
30 |
31 |
32 |
33 | ### 手动控制
34 |
35 |
36 |
37 | ## API
38 |
39 | | 参数 | 说明 | 类型 | 默认值 |
40 | | --------- | ------------------ | ------- | ------------------ |
41 | | className | 额外的样式类 | string | -- |
42 | | style | 额外的样式 | CSSProperties | -- |
43 | | source | 播放源 | string | -- |
44 | | isLive | 是否是直播 | boolean | `false` |
45 | | options | 配置参数 | object | {} |
46 | | version | 阿里播放器版本 | string | 2.8.2 |
47 | |changeSourceMode|修改播放源处理模式 loadByUrl(加载播放源)、init(销毁重新创建实例) |`loadByUrl` \| `init`| `init` |
48 | | hideControlbar | 是否隐藏控制栏 | boolean | false |
49 | | cssLinkTemplate | 阿里播放器CSS模板 | string | //g.alicdn.com/de/prismplayer/{%version}/skins/default/aliplayer-min.css |
50 | | scriptSrcTemplate | 阿里播放器JS模板 | string | //g.alicdn.com/de/prismplayer/{%version}/aliplayer-min.js |
51 | | loading | 用于在播放器加载成功前渲染 | ReactNode | -- |
52 | | onReady | 播放器视频初始化按钮渲染完毕 | Function | -- |
53 | | onPlay | 视频暂停时触发 | Function | -- |
54 | | onPause | 播放器视频初始化按钮渲染完毕 | Function | -- |
55 | | onCanplay | 能够开始播放音频/视频时发生,会多次触发,仅H5播放器 | Function | -- |
56 | | onPlaying | 播放中,会触发多次 | Function | -- |
57 | | onLiveStreamStop | 直播流中断时触发 | Function | -- |
58 | | onM3u8Retry | m3u8直播流中断后重试事件 | Function | -- |
59 | | onHideBar | 控制栏自动隐藏事件 | Function | -- |
60 | | onShowBar | 控制栏自动显示事件 | Function | -- |
61 | | onWaiting | 数据缓冲事件 | Function | -- |
62 | | onSnapshoted | 截图完成事件 | Function | -- |
63 | | onRequestFullScreen | 全屏事件,仅H5支持 | Function | -- |
64 | | onCancelFullScreen | 取消全屏事件,iOS下不会触发,仅H5支持 | Function | -- |
65 | | onError | 错误事件 | Function | -- |
66 |
67 | **注意**
68 |
69 | cssLinkTemplate、scriptSrcTemplate 中的 `{%version}` 会使用version进行替换
70 |
71 | ## options
72 |
73 | | 参数 | 说明 | 类型 | 默认值 |
74 | | --------- | ------------------ | ------- | ------------------ |
75 | | vid | 媒体转码服务的媒体Id | string | -- |
76 | | playauth | 播放权证 | string | -- |
77 | | height | 播放器高度 | string | `100%` |
78 | | width | 播放器宽度 | string | `100%` |
79 | | videoWidth | 视频宽度,仅h5支持 | string | -- |
80 | | videoHeight | 视频高度,仅h5支持 | string | -- |
81 | | preload | 播放器自动加载,目前仅h5可用 | boolean | -- |
82 | | cover | 播放器默认封面图片 | string | -- |
83 | | autoplay | 播放器是否自动播放 | boolean | -- |
84 | | rePlay | 播放器自动循环播放 | boolean | -- |
85 | | useH5Prism | 指定使用H5播放器 | boolean | -- |
86 | | useFlashPrism | 指定使用Flash播放器 | boolean | -- |
87 | | playsinline | H5是否内置播放,有的Android浏览器不起作用 | boolean | -- |
88 | | showBuffer | 显示播放时缓冲图标 | boolean | -- |
89 | | skinRes | 皮肤图片,不建议随意修改该字段,如要修改,请参照皮肤定制 | any | -- |
90 | | skinLayout | 功能组件布局配置,不传该字段使用默认布局。传false隐藏所有功能组件,请参照皮肤定制 | any | -- |
91 | | controlBarVisibility | 控制面板的实现 | 'click' \| 'hover' \| 'always' | -- |
92 | | showBarTime | 控制栏自动隐藏时间 | number | -- |
93 | | extraInfo | JSON串用于定制性接口参数 | string | -- |
94 | | enableSystemMenu | 是否允许系统右键菜单显示 | boolean | -- |
95 | | format | 指定播放地址格式,只有使用vid的播放方式时支持 | 'mp4'\| 'm3u8' \| 'flv' \| 'mp3' | -- |
96 | | mediaType | 指定返回音频还是视频,只有使用vid的播放方式时支持 | 'video' \| 'audio' | -- |
97 | | qualitySort | 指定排序方式,只有使用vid + plauth播放方式时支持 | 'desc' \| 'asc' | -- |
98 | | definition | 显示视频清晰度,多个用逗号分隔 | string | -- |
99 | | defaultDefinition | 默认视频清晰度 |'FD' \| 'LD' \| 'SD' \| 'HD' \| 'OD' \| '2K' \| '4K' | -- |
100 | | x5_type | 声明启用同层H5播放器,启用时设置的值为‘h5’ | string | `auto` |
101 | | x5_fullscreen | 声明视频播放时是否进入到TBS的全屏模式 | boolean | false |
102 | | x5_video_position | 声明视频播在界面上的位置 | 'top' \| 'center' | `center` |
103 | | x5_orientation | 声明视频播放时是否进入到TBS的全屏模式 | 'landscape' \| 'landscape' | -- |
104 | | x5LandscapeAsFullScreen | 声明TBS全屏播放是否横屏 | boolean | -- |
105 | | autoPlayDelay | 延迟播放时间,单位为秒 | number | -- |
106 | | autoPlayDelayDisplayText | 延迟播放提示文本 | string | -- |
107 | | language | 指定语言 | string | -- |
108 | | languageTexts | 国际化 | string | -- |
109 | | snapshot | flash启用截图功能 | boolean | -- |
110 | | snapshotWatermark | H5设置截图水印 | object | -- |
111 | | useHlsPluginForSafari | Safari浏览器可以启用Hls插件播放,Safari 11除外。 | boolean | -- |
112 | | enableStashBufferForFlv | H5播放flv时,设置是否启用播放缓存,只在直播下起作用 | boolean | -- |
113 | | stashInitialSizeForFlv | H5播放flv时,初始缓存大小,只在直播下起作用 | number | -- |
114 | | loadDataTimeout | 缓冲多长时间后,提示用户切换低清晰度 | number | 20 |
115 | | waitingTimeout | 大缓冲超时时间 | number | 60 |
116 | | liveStartTime | 直播开始时间,直播时移功能使用,格式为:“2018/01/04 12:00:00” | string | -- |
117 | | liveOverTime | 直播结束时间,直播时移功能使用,格式为:“2018/01/04 12:00:00” | string | -- |
118 | | liveTimeShiftUrl | 直播可用时移查询地址 | string | -- |
119 | | liveShiftSource | flv直播地址播放时,hls的流地址 | string | -- |
120 | | recreatePlayer | flv直播和hls时移切换是,重新创建播放器方法 | function | -- |
121 | | diagnosisButtonVisible | 是否显示检测按钮 | boolean | true |
122 | | disableSeek | 禁用进度条的Seek,默认为false,仅Flash支持 | boolean | false |
123 | | encryptType | 加密类型 | 0 /| 1 | 0 |
124 | | progressMarkers | 进度条打点内容数组 | object[] | -- |
125 | | vodRetry | 点播失败重试次数 | number | 3 |
126 | | liveRetry | 直播播放失败重试次数 | number | 5 |
127 |
128 | ## 错误代码
129 |
130 | |代码|含义|
131 | |--|--|
132 | |4001|参数不合理|
133 | |4002|鉴权过期|
134 | |4003|无效地址|
135 | |4004|地址不存在|
136 | |4005|开始下载数据错误,检测网络情况或播放地址是否可以访问。|
137 | |4006|开始下载元数据数据错误|
138 | |4007|播放时错误|
139 | |4008|加载超时,检测网络情况或播放地址是否可以访问|
140 | |4009|请求数据错误,测网络情况或播放地址是否可以访问|
141 | |4010|不支持加密视频播放|
142 | |4011|播放格式不支持|
143 | |4012|playauth解析错误|
144 | |4013|播放数据解码错误MEDIA_ERR_DECODE,检测浏览器是否支持视频格式。|
145 | |4014|网络不可用|
146 | |4015|获取数据过程被中止MEDIA_ERR_ABORTED|
147 | |4016|播网络错误加载数据失败MEDIA_ERR_NETWORK|
148 | |4017|返回的播放地址为空|
149 | |4400|未知错误MEDIA_ERR_SRC_NOT_SUPPORTED(由于服务器或网络原因不能加载资源,或者格式不支持)|
150 | |4500|服务端请求错误,查看Network里点播服务的请求的具体错误|
151 |
--------------------------------------------------------------------------------
/packages/aliplayer/docs/demos/demo-01.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Player } from '@pansy/react-aliplayer';
3 |
4 | export default () => {
5 | return (
6 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/packages/aliplayer/docs/demos/demo-02.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Player } from '@pansy/react-aliplayer';
3 |
4 | export default () => {
5 | return (
6 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/packages/aliplayer/docs/demos/demo-03.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Player } from '@pansy/react-aliplayer';
3 |
4 | export default () => {
5 | return (
6 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/packages/aliplayer/docs/demos/demo-04.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef } from 'react';
2 | import { Button, Space } from 'antd'
3 | import { Player } from '@pansy/react-aliplayer';
4 |
5 | import type { PlayerRef } from '@pansy/react-aliplayer';
6 |
7 | export default () => {
8 | const playerRef = useRef(null);
9 |
10 | const handlePlay = () => {
11 | if (!playerRef.current) return;
12 | const player = playerRef.current.getInstance();
13 | player && player.play()
14 | }
15 |
16 | const handlePause = () => {
17 | if (!playerRef.current) return;
18 | const player = playerRef.current.getInstance();
19 | player && player.pause()
20 | }
21 |
22 | const handleReplay = () => {
23 | if (!playerRef.current) return;
24 | const player = playerRef.current.getInstance();
25 | player && player.replay()
26 | }
27 |
28 | const handleSeek = () => {
29 | if (!playerRef.current) return;
30 | const player = playerRef.current.getInstance();
31 | player && player.seek(10)
32 | }
33 |
34 | const handleLoadByUrl = () => {
35 | if (!playerRef.current) return;
36 | const player = playerRef.current.getInstance();
37 | player && player.loadByUrl('//stream7.iqilu.com/10339/upload_transcode/202002/18/202002181038474liyNnnSzz.mp4')
38 | }
39 |
40 | return (
41 | <>
42 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | >
59 | );
60 | };
61 |
--------------------------------------------------------------------------------
/packages/aliplayer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pansy/react-aliplayer",
3 | "version": "2.1.1",
4 | "main": "lib/index.js",
5 | "module": "es/index.js",
6 | "types": "lib/index.d.ts",
7 | "files": [
8 | "es",
9 | "lib"
10 | ],
11 | "scripts": {
12 | "build": "redbud build"
13 | },
14 | "license": "MIT",
15 | "peerDependencies": {
16 | "react": ">=16.9.0",
17 | "react-dom": ">=16.9.0"
18 | },
19 | "browserslist": [
20 | "last 2 versions",
21 | "Firefox ESR",
22 | "> 1%",
23 | "ie >= 11"
24 | ],
25 | "publishConfig": {
26 | "registry": "https://registry.npmjs.org",
27 | "access": "public"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/aliplayer/src/index.less:
--------------------------------------------------------------------------------
1 | @player-prefix-cls: ~'pansy-aliplayer';
2 |
3 | .@{player-prefix-cls} {
4 |
5 | &.hide-controlbar.prism-player {
6 | .prism-controlbar {
7 | display: none !important;
8 | }
9 | }
10 |
11 | .prism-info-left-bottom {
12 | display: none !important;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/aliplayer/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { Player } from './player';
2 |
3 | export type { PlayerErrorEvent, PlayerConfig, PlayerProps, PlayerRef } from './types';
4 |
5 | export { Player };
6 | export default Player;
7 |
--------------------------------------------------------------------------------
/packages/aliplayer/src/player.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { useState, useEffect, useRef, useImperativeHandle } from 'react'
3 |
4 | import type { PlayerProps, PlayerConfig, PlayerInstance, PlayerRef } from './types';
5 |
6 | const playerScriptId = 'ali-player-js';
7 | const playerLinkId = 'ali-player-css';
8 | const tempPrefix = '//g.alicdn.com/de/prismplayer/{%version}/';
9 | const eventNames = [
10 | 'ready',
11 | 'play',
12 | 'pause',
13 | 'canplay',
14 | 'playing',
15 | 'ended',
16 | 'hideBar',
17 | 'showBar',
18 | 'waiting',
19 | 'snapshoted',
20 | 'timeupdate',
21 | 'onM3u8Retry',
22 | 'liveStreamStop',
23 | 'cancelFullScreen',
24 | 'requestFullScreen',
25 | 'error',
26 | ];
27 |
28 | const InternalPlayer: React.ForwardRefRenderFunction = (props, ref) => {
29 | const {
30 | prefixCls = 'pansy-aliplayer',
31 | className,
32 | style,
33 | hideControlbar = false,
34 | options = {},
35 | loading,
36 | source,
37 | isLive,
38 | version = '2.13.4',
39 | // version = '2.9.22',
40 | cssLinkTemplate = `${tempPrefix}skins/default/aliplayer-min.css`,
41 | scriptSrcTemplate = `${tempPrefix}aliplayer-min.js`,
42 | } = props;
43 | const playerInstance = useRef();
44 | const playerId = useRef(`aliplayer-${Math.floor(Math.random() * 1000000)}`);
45 | const [isLoading, setIsLoading] = useState(true);
46 |
47 | useEffect(
48 | () => {
49 | insertLinkTag();
50 | init();
51 |
52 | return () => {
53 | playerInstance.current && playerInstance.current.dispose();
54 | }
55 | },
56 | []
57 | )
58 |
59 | useEffect(
60 | () => {
61 | if (playerInstance.current && props.source) {
62 | playerInstance.current.loadByUrl(props.source);
63 | }
64 | },
65 | [props.source, playerInstance.current]
66 | )
67 |
68 | useImperativeHandle(
69 | ref,
70 | () => {
71 | return {
72 | getInstance: () => playerInstance.current!
73 | }
74 | },
75 | [playerInstance.current]
76 | )
77 |
78 | const classes = [
79 | prefixCls,
80 | className,
81 | 'prism-player',
82 | hideControlbar ? 'hide-controlbar' : null
83 | ].filter(item => item).join(' ').trim();
84 |
85 | const getPath = (path: string, version: string): string => {
86 | return path.replace(/\{([^{]*?)%version(.*?)\}/g, version.toString());
87 | }
88 |
89 | const insertLinkTag = () => {
90 | const playerLinkTag = document.getElementById(playerLinkId);
91 |
92 | if (!playerLinkTag && version && cssLinkTemplate) {
93 | const link = document.createElement('link');
94 | link.type = 'text/css';
95 | link.rel = 'stylesheet';
96 | link.href = getPath(cssLinkTemplate, version);
97 | link.id = playerLinkId;
98 | document.head.appendChild(link);
99 | }
100 | }
101 |
102 | const insertScriptTag = () => {
103 | let playerScriptTag = document.getElementById(playerScriptId) as HTMLScriptElement;
104 |
105 | if (!playerScriptTag) {
106 | playerScriptTag = document.createElement('script');
107 | playerScriptTag.type = 'text/javascript';
108 | playerScriptTag.charset = 'utf-8';
109 | playerScriptTag.src = getPath(scriptSrcTemplate, version);
110 | playerScriptTag.id = playerScriptId;
111 |
112 | document.body.appendChild(playerScriptTag);
113 | }
114 |
115 | playerScriptTag.addEventListener('load', () => {
116 | setIsLoading(false);
117 | initAliPlayer();
118 | });
119 | }
120 |
121 | const init = () => {
122 | if (window['Aliplayer']) {
123 | setIsLoading(false);
124 | initAliPlayer();
125 | } else {
126 | insertScriptTag();
127 | }
128 | }
129 |
130 | const initAliPlayer = () => {
131 | const config: Partial = {
132 | ...options,
133 | useH5Prism: true,
134 | id: playerId.current,
135 | source,
136 | isLive: !!isLive
137 | };
138 |
139 | if (!config.width) {
140 | config.width = '100%';
141 | }
142 |
143 | if (!config.height) {
144 | config.height = '100%';
145 | }
146 |
147 | if (config.autoplay === undefined) {
148 | config.autoplay = false;
149 | }
150 |
151 | if (playerInstance.current) {
152 | playerInstance.current.dispose();
153 | }
154 |
155 | const Aliplayer = window['Aliplayer'] || window['proxy']?.Aliplayer;
156 |
157 | playerInstance.current = new Aliplayer(config);
158 | playerInstance.current!.setVolume(0);
159 | initEvents(playerInstance.current);
160 | }
161 |
162 | const initEvents = (player?: PlayerInstance) => {
163 | if (!player) return;
164 | eventNames.forEach((eventName) => {
165 | let propsEventName = eventName;
166 | if (!eventName.startsWith('on')) {
167 | propsEventName = `on${eventName.charAt(0).toUpperCase()}${eventName.slice(1, eventName.length)}`
168 | }
169 |
170 | if (propsEventName in props && props[propsEventName]) {
171 | player.on(eventName, props[propsEventName]);
172 | }
173 | })
174 | }
175 |
176 | return (
177 |
178 | {isLoading ? loading : null}
179 |
180 | )
181 | }
182 |
183 | export const Player = React.forwardRef(InternalPlayer);
184 |
--------------------------------------------------------------------------------
/packages/aliplayer/src/types.ts:
--------------------------------------------------------------------------------
1 | export interface PlayerErrorEvent {
2 | target: HTMLDivElement;
3 | type: 'error';
4 | paramData: {
5 | display_msg: string;
6 | error_code: number;
7 | }
8 | }
9 |
10 | export interface PlayerConfig {
11 | // 播放器外层容器的dom元素id
12 | id: string;
13 | // 视频播放地址url
14 | source: string;
15 | // 媒体转码服务的媒体Id
16 | vid: string;
17 | // 播放权证
18 | playauth: string;
19 | // 播放器高度,可形如‘100%’或者‘100px’
20 | height: string;
21 | // 播放器宽度,可形如‘100%’或者‘100px’
22 | width: string;
23 | // 视频宽度,仅h5支持
24 | videoWidth: string;
25 | // 视频高度,仅h5支持
26 | videoHeight: string;
27 | // 播放器自动加载,目前仅h5可用
28 | preload: boolean;
29 | // 播放器默认封面图片
30 | cover: string;
31 | // 播放内容是否为直播
32 | isLive: boolean;
33 | // flv配置
34 | flvOption: {
35 | hasAudio: boolean;
36 | }
37 | // 播放器是否自动播放
38 | autoplay: boolean;
39 | // 播放器自动循环播放
40 | rePlay: boolean;
41 | // 指定使用H5播放器
42 | useH5Prism: boolean;
43 | // 指定使用Flash播放器
44 | useFlashPrism: boolean;
45 | // H5是否内置播放,有的Android浏览器不起作用
46 | playsinline: boolean;
47 | // 显示播放时缓冲图标
48 | showBuffer: boolean;
49 | // 皮肤图片,不建议随意修改该字段,如要修改,请参照皮肤定制
50 | skinRes: any;
51 | // 功能组件布局配置,不传该字段使用默认布局。传false隐藏所有功能组件,请参照皮肤定制。
52 | skinLayout: any[] | boolean;
53 | // 控制面板的实现
54 | controlBarVisibility: 'click' | 'hover' | 'always';
55 | // 控制栏自动隐藏时间
56 | showBarTime: number;
57 | // JSON串用于定制性接口参数
58 | extraInfo: string;
59 | // 是否允许系统右键菜单显示
60 | enableSystemMenu: boolean;
61 | // 指定播放地址格式,只有使用vid的播放方式时支持
62 | format: 'mp4'| 'm3u8' | 'flv' | 'mp3';
63 | // 指定返回音频还是视频,只有使用vid的播放方式时支持。
64 | mediaType: 'video' | 'audio';
65 | // 指定排序方式,只有使用vid + plauth播放方式时支持。
66 | qualitySort: 'desc' | 'asc';
67 | // 显示视频清晰度,多个用逗号分隔
68 | // 取值范围:FD(流畅)LD(标清)SD(高清)HD(超清)OD(原画)2K(2K)4K(4K),仅H5支持。
69 | definition: string;
70 | // 默认视频清晰度,此值是vid对应流的一个清晰度
71 | defaultDefinition: 'FD' | 'LD' | 'SD' | 'HD' | 'OD' | '2K' | '4K';
72 | // 声明启用同层H5播放器,启用时设置的值为‘h5’
73 | x5_type: string;
74 | // 声明视频播放时是否进入到TBS的全屏模式
75 | x5_fullscreen: boolean;
76 | // 声明视频播在界面上的位置
77 | x5_video_position: 'top' | 'center';
78 | // 声明TBS播放器支持的方向
79 | x5_orientation: 'landscape' | 'landscape';
80 | // 声明TBS全屏播放是否横屏
81 | x5LandscapeAsFullScreen: boolean;
82 | // 延迟播放时间,单位为秒。
83 | autoPlayDelay: number;
84 | // 延迟播放提示文本
85 | autoPlayDelayDisplayText: string;
86 | // 国际化
87 | language: string;
88 | languageTexts: string;
89 | // flash启用截图功能
90 | snapshot: boolean;
91 | // H5设置截图水印
92 | snapshotWatermark: object;
93 | // Safari浏览器可以启用Hls插件播放,Safari 11除外。
94 | useHlsPluginForSafari: boolean;
95 | // H5播放flv时,设置是否启用播放缓存,只在直播下起作用
96 | enableStashBufferForFlv: boolean;
97 | // H5播放flv时,初始缓存大小,只在直播下起作用
98 | stashInitialSizeForFlv: number;
99 | // 缓冲多长时间后,提示用户切换低清晰度,默认:20秒
100 | loadDataTimeout: number;
101 | // 最大缓冲超时时间,超过这个时间会有错误提示,默认:60秒
102 | waitingTimeout: number;
103 | // 直播开始时间,直播时移功能使用,格式为:“2018/01/04 12:00:00”
104 | liveStartTime: string;
105 | // 直播结束时间,直播时移功能使用,格式为:“2018/01/04 12:00:00”
106 | liveOverTime: string;
107 | // 直播可用时移查询地址
108 | liveTimeShiftUrl: string;
109 | // flv直播地址播放时,hls的流地址
110 | liveShiftSource: string;
111 | // flv直播和hls时移切换是,重新创建播放器方法
112 | recreatePlayer: Function;
113 | // 是否显示检测按钮,默认为true
114 | diagnosisButtonVisible: boolean;
115 | // 禁用进度条的Seek,默认为false,仅Flash支持
116 | disableSeek: boolean;
117 | // 加密类型,播放点播私有加密视频时,设置值为1,默认值为0
118 | encryptType: 0 | 1;
119 | // 进度条打点内容数组
120 | progressMarkers: {
121 | offset: number;
122 | text: string;
123 | isCustomized: boolean;
124 | }[];
125 | // 点播失败重试次数,默认3次
126 | vodRetry: number;
127 | // 直播播放失败重试次数,默认5次
128 | liveRetry: number;
129 | }
130 |
131 | export type PlayerStatus =
132 | 'init' |
133 | 'ready' |
134 | 'loading' |
135 | 'play' |
136 | 'pause' |
137 | 'playing' |
138 | 'waiting' |
139 | 'error' |
140 | 'ended';
141 |
142 | export interface PlayerProps {
143 | /** 样式类前缀 */
144 | prefixCls?: string;
145 | /** 自定义样式类 */
146 | className?: string;
147 | /** 自定义样式 */
148 | style?: React.CSSProperties;
149 | // 播放源
150 | source: string;
151 | // 是否是直播
152 | isLive?: boolean;
153 | // 用于在播放器加载成功前渲染
154 | loading?: React.ReactNode;
155 | /** 播放器配置 */
156 | options?: Partial>;
157 | // aliplayer版本
158 | version?: string;
159 | // 是否隐藏控制栏
160 | hideControlbar?: boolean;
161 | changeSourceMode?: 'loadByUrl' | 'init',
162 | // 播放器CSS模板
163 | cssLinkTemplate?: string;
164 | // 播放器JS模板
165 | scriptSrcTemplate?: string;
166 | // 播放器视频初始化按钮渲染完毕
167 | onReady?: () => void;
168 | // 视频由暂停恢复为播放时触发
169 | onPlay?: () => void;
170 | // 视频暂停时触发
171 | onPause?: () => void;
172 | // 能够开始播放音频/视频时发生,会多次触发,仅H5播放器
173 | onCanplay?: () => void;
174 | // 播放中,会触发多次
175 | onPlaying?: () => void;
176 | // 当前视频播放完毕时触发
177 | onEnded?: () => void;
178 | // 直播流中断时触发
179 | onLiveStreamStop?: () => void;
180 | // m3u8直播流中断后重试事件
181 | onM3u8Retry?: () => void;
182 | // 控制栏自动隐藏事件
183 | onHideBar?: () => void;
184 | // 控制栏自动显示事件
185 | onShowBar?: () => void;
186 | // 数据缓冲事件
187 | onWaiting?: () => void;
188 | // 截图完成事件
189 | onSnapshoted?: () => void;
190 | // 播放位置发生改变时触发,仅H5播放器。
191 | // 可通过getCurrentTime方法,得到当前播放时间。
192 | onTimeupdate?: () => void;
193 | // 全屏事件,仅H5支持
194 | onRequestFullScreen?: () => void;
195 | // 取消全屏事件,iOS下不会触发,仅H5支持
196 | onCancelFullScreen?: () => void;
197 | // 错误事件
198 | onError?: (e: PlayerErrorEvent) => void;
199 | }
200 |
201 | export interface PlayerRef {
202 | getInstance: () => PlayerInstance;
203 | }
204 |
205 | export interface PlayerInstance {
206 | /** 播放视频 */
207 | play: () => void;
208 | /** 暂停视频 */
209 | pause: () => void;
210 | /** 重播视频 */
211 | replay: () => void;
212 | /** 跳转到某个已加载的时刻进行播放,时间单位:秒 */
213 | seek: (time: number) => void;
214 | /** 获取当前的播放时刻,返回的时间单位:秒 */
215 | getCurrentTime: () => number;
216 | /** 获取视频总时长,返回的时间单位:秒 */
217 | getDuration: () => number;
218 | /** 获取当前的音量 */
219 | getVolume: () => number;
220 | /** 设置当前的音量 */
221 | setVolume: (vol: number) => void;
222 | /** 设置当前的音量 */
223 | loadByUrl: (url: string, time?: number) => void;
224 | /** 设置播放器大小 */
225 | setPlayerSize: (w: number, h: number) => void;
226 | /** 手动设置播放的倍速,支持0.5~2倍速播放 */
227 | setSpeed: (speed: number) => void;
228 | /** 设置截图参数 */
229 | setSanpshotProperties: (w: number, h: number, rate: number) => void;
230 | fullscreenService: {
231 | /** 播放器全屏 */
232 | requestFullScreen: () => void;
233 | /** 播放器退出全屏 */
234 | cancelFullScreen: () => void;
235 | /** 获取播放器全屏状态 */
236 | getIsFullScreen: () => boolean;
237 | },
238 | /** 获取播放器状态 */
239 | getStatus: () => PlayerStatus;
240 | /** 设置旋转角度 */
241 | setRotate: (rotate: number) => void;
242 | /** 获取旋转角度 */
243 | getRotate: () => number;
244 | /** 设置镜像 */
245 | setImage: (image: 'horizon' | 'vertical') => number;
246 | /** 播放器销毁 */
247 | dispose: () => void;
248 | /** 设置封面 */
249 | setCover: (cover: string) => void;
250 | /** 设置打点数据 */
251 | setProgressMarkers: (markers: any) => void;
252 | /** 设置试看时间 */
253 | setPreviewTime: (time: number) => void;
254 | /** 获取试看时间 */
255 | getPreviewTime: () => number;
256 | /** 是否试看 */
257 | isPreview: () => boolean;
258 | /** 绑定事件 */
259 | on: (eventName: string, callback: any) => void;
260 | replayByVidAndPlayAuth: (vid: string, playAuth: string) => void;
261 | }
262 |
--------------------------------------------------------------------------------
/packages/aliplayer/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.css';
2 | declare module '*.less';
3 |
4 | interface Window {
5 | Aliplayer: any;
6 | globalThis: Window;
7 | }
8 |
--------------------------------------------------------------------------------
/packages/responsive-card/.redbudrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'redbud';
2 |
3 | export default defineConfig({
4 | extends: '../../.redbudrc.base.ts'
5 | });
6 |
--------------------------------------------------------------------------------
/packages/responsive-card/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | ## [0.2.1](https://github.com/pansyjs/react-components/compare/@pansy/react-responsive-card@0.2.0...@pansy/react-responsive-card@0.2.1) (2023-02-02)
7 |
8 | **Note:** Version bump only for package @pansy/react-responsive-card
9 |
10 |
11 |
12 |
13 |
14 | # [0.2.0](https://github.com/pansyjs/react-components/compare/@pansy/react-responsive-card@0.1.2...@pansy/react-responsive-card@0.2.0) (2022-02-17)
15 |
16 |
17 | ### Features
18 |
19 | * **responsive-card:** 代码优化 ([6d40fd6](https://github.com/pansyjs/react-components/commit/6d40fd63756f66add6070649fcb53da9260b306b))
20 |
21 |
22 |
23 |
24 |
25 | ## [0.1.2](https://github.com/pansyjs/react-components/compare/@pansy/react-responsive-card@0.1.1...@pansy/react-responsive-card@0.1.2) (2022-01-24)
26 |
27 | **Note:** Version bump only for package @pansy/react-responsive-card
28 |
29 |
30 |
31 |
32 |
33 | ## 0.1.1 (2022-01-24)
34 |
35 | **Note:** Version bump only for package @pansy/react-responsive-card
36 |
--------------------------------------------------------------------------------
/packages/responsive-card/README.md:
--------------------------------------------------------------------------------
1 | @pansy/react-responsive-card
2 |
3 | 卡片自适应间距
4 |
5 | ## 🏗 安装
6 |
7 | ```sh
8 | # npm 安装
9 | npm install --save @pansy/react-responsive-card
10 |
11 | # yarn 安装
12 | yarn add @pansy/react-responsive-card
13 |
14 | # pnpm 安装
15 | pnpm install @pansy/react-responsive-card
16 | ```
17 |
18 | ## 🔨 使用
19 |
20 | ```tsx
21 | import React from 'react';
22 | import { CheckCard } from '@ant-design/pro-card';
23 | import { ResponsiveCard } from '@pansy/react-responsive-card';
24 |
25 | export default () => {
26 | return (
27 |
28 |
29 |
35 |
41 |
47 |
48 |
49 | );
50 | };
51 | ```
52 |
--------------------------------------------------------------------------------
/packages/responsive-card/demo/demo01.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { CheckCard } from '@ant-design/pro-card';
3 | import { ResponsiveCard } from '@pansy/react-responsive-card';
4 |
5 | const options = [
6 | { title: '🍊 Orange', description: '🍊 Orange', value: 'option1' },
7 | { title: '🍐 Pear', description: '🍐 Pear', value: 'option2' },
8 | { title: '🍎 Apple', description: '🍎 Apple', value: 'option3' },
9 | { title: '🍎 Apple1', description: '🍎 Apple1', value: 'option4' },
10 | ]
11 |
12 | export default () => {
13 | return (
14 |
15 |
16 | {(config) => {
17 | return options.map((item, index) => {
18 | const style = {
19 | width: config.width,
20 | marginRight:
21 | (index + 1) % config.span != 0
22 | ? config.gutter
23 | : 0,
24 | }
25 |
26 | return (
27 |
32 | )
33 | })
34 | }}
35 |
36 |
37 | );
38 | };
39 |
--------------------------------------------------------------------------------
/packages/responsive-card/demo/demo02.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { CheckCard } from '@ant-design/pro-card';
3 | import { ResponsiveCard } from '@pansy/react-responsive-card';
4 |
5 | export default () => {
6 | return (
7 |
8 |
9 |
15 |
21 |
27 |
28 |
29 | );
30 | };
31 |
--------------------------------------------------------------------------------
/packages/responsive-card/demo/demo03.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { CheckCard } from '@ant-design/pro-card';
3 | import { ResponsiveCard } from '@pansy/react-responsive-card';
4 |
5 | export default () => {
6 | return (
7 |
8 |
9 |
15 |
21 |
27 |
28 |
29 | );
30 | };
31 |
--------------------------------------------------------------------------------
/packages/responsive-card/docs/responsive-card.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: ResponsiveCard 自适应间距卡片
3 | nav:
4 | title: 组件
5 | path: /components
6 | order: 1
7 | group:
8 | path: /basic
9 | title: '基础组件'
10 | order: 0
11 | ---
12 |
13 | # ResponsiveCard 卡片自适应间距
14 |
15 | ## 何时使用
16 |
17 | - 需要将卡片进行自适应间距时
18 |
19 | ## 代码演示
20 |
21 | ### 基础示例
22 |
23 |
24 |
25 | ### 自定义渲染
26 |
27 |
28 |
29 |
30 | ### 自定义间距
31 |
32 |
33 |
34 | ## API
35 |
36 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
37 | | ------------ | --------------| ------------------- | ------ | ---- |
38 | | className | 额外的样式类 | `string` | -- | -- |
39 | | style | 额外的样式 | `React.CSSProperties` | -- | -- |
40 | | defaultWidth | 卡片的默认宽度 | `number` | `260` | -- |
41 | | gutter | 分屏的间隔 | `number` \| `[number, number]` | `16` | -- |
42 | | children | 需要展示的业务内容 | `((config: AdaptiveConfig) => React.ReactNode) | React.ReactNode` |-- | -- |
43 |
44 |
45 | ```ts
46 | interface AdaptiveConfig {
47 | /** 每项的宽度 */
48 | width: number;
49 | /** 左右间距 */
50 | gutter: number;
51 | /** 每行的数目 */
52 | span: number;
53 | }
54 | ```
55 |
--------------------------------------------------------------------------------
/packages/responsive-card/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pansy/react-responsive-card",
3 | "version": "0.2.1",
4 | "main": "lib/index.js",
5 | "module": "es/index.js",
6 | "types": "lib/index.d.ts",
7 | "files": [
8 | "es",
9 | "lib"
10 | ],
11 | "scripts": {
12 | "build": "redbud build"
13 | },
14 | "peerDependencies": {
15 | "react": ">=16.9.0",
16 | "react-dom": ">=16.9.0"
17 | },
18 | "dependencies": {
19 | "@pansy/shared": "^1.9.0",
20 | "@pansy/use-size": "^0.3.1"
21 | },
22 | "publishConfig": {
23 | "registry": "https://registry.npmjs.org",
24 | "access": "public"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/responsive-card/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useState, useEffect } from 'react';
2 | import { useSize } from '@pansy/use-size';
3 | import { getAdaptiveConfig, getGutter } from './utils';
4 |
5 | import type { AdaptiveConfig } from './utils';
6 |
7 | export interface ResponsiveCardProps {
8 | /** 额外的样式类 */
9 | className?: string;
10 | /** 额外的样式 */
11 | style?: React.CSSProperties;
12 | /**
13 | * 卡片的默认宽度
14 | * @default 260
15 | */
16 | defaultWidth?: number;
17 | /**
18 | * 间距设置
19 | * @default 16
20 | */
21 | gutter?: [number, number];
22 | children?: ((config: AdaptiveConfig) => React.ReactNode) | React.ReactNode;
23 | }
24 |
25 | const rootStyle: React.CSSProperties = {
26 | position: 'relative',
27 | display: 'flex',
28 | flexDirection: 'row',
29 | flexWrap: 'wrap',
30 | }
31 |
32 | const defaultGutter = 16;
33 |
34 | export const ResponsiveCard: React.FC = ({
35 | className,
36 | style,
37 | defaultWidth = 260,
38 | gutter = defaultGutter,
39 | children,
40 | }) => {
41 | const rootRef = useRef(null);
42 | const [adaptiveConfig, setAdaptiveConfig] = useState({} as AdaptiveConfig);
43 |
44 | /** 水平间隔 */
45 | const horizontalGutter = getGutter(gutter, 0) ?? defaultGutter;
46 | /** 垂直间隔 */
47 | const verticalGutter = getGutter(gutter, 1) ?? defaultGutter;
48 | const { width } = useSize(rootRef) ?? {};
49 |
50 | useEffect(
51 | () => {
52 | setAdaptiveConfig(getAdaptiveConfig(width, {
53 | defaultWidth,
54 | defaultGutter: horizontalGutter
55 | }));
56 | },
57 | [width, horizontalGutter, verticalGutter, defaultGutter]
58 | );
59 |
60 | const renderChildren = () => {
61 | if (typeof children === 'function') {
62 | return children(adaptiveConfig);
63 | }
64 |
65 | /** 优化体验 */
66 | if (adaptiveConfig.width === 0) {
67 | return null;
68 | }
69 |
70 | const { width = defaultWidth, gutter, span } = adaptiveConfig;
71 |
72 | return React.Children.map(children, (child: any, index: number) => {
73 | const style = {
74 | width,
75 | marginRight:
76 | (index + 1) % span != 0
77 | ? gutter
78 | : 0,
79 | }
80 |
81 | return React.cloneElement(child, {
82 | style
83 | })
84 | });
85 | }
86 |
87 | return (
88 |
97 | {renderChildren()}
98 |
99 | )
100 | }
101 |
--------------------------------------------------------------------------------
/packages/responsive-card/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { isNumber } from '@pansy/shared';
2 |
3 | export interface Opts {
4 | /** 默认宽度 */
5 | defaultWidth?: number;
6 | /** 默认间距 */
7 | defaultGutter?: number;
8 | }
9 |
10 | export interface AdaptiveConfig {
11 | /** 每项的宽度 */
12 | width: number;
13 | /** 左右间距 */
14 | gutter: number;
15 | /** 每行的数目 */
16 | span: number;
17 | }
18 |
19 | /**
20 | * 获取适配结果
21 | * @param width
22 | * @param opts
23 | */
24 | export const getAdaptiveConfig = (
25 | width?: number,
26 | { defaultWidth = 260, defaultGutter = 16 }: Opts = {},
27 | ) => {
28 | const adaptiveConfig: AdaptiveConfig = {
29 | width: 0,
30 | gutter: 0,
31 | span: 0,
32 | };
33 |
34 | if (!isNumber(width) || width < defaultWidth) return adaptiveConfig;
35 |
36 | // 只能放置一个卡片
37 | if (width <= defaultWidth * 2) {
38 | adaptiveConfig.width = width;
39 | adaptiveConfig.gutter = 0;
40 | adaptiveConfig.span = 1;
41 | }
42 |
43 | const num = Math.floor(width / defaultWidth);
44 | const defaultGutterTotal = (num - 1) * defaultGutter;
45 |
46 | if (width > defaultGutterTotal + defaultWidth * num) {
47 | adaptiveConfig.width = (width - defaultGutterTotal) / num;
48 | adaptiveConfig.gutter = defaultGutter;
49 | adaptiveConfig.span = num;
50 | } else {
51 | adaptiveConfig.width = (width - (num - 2) * defaultGutter) / (num - 1);
52 | adaptiveConfig.gutter = defaultGutter;
53 | adaptiveConfig.span = num - 1;
54 | }
55 |
56 | return adaptiveConfig;
57 | };
58 |
59 | export function getGutter(value: number | number[], index: 0 | 1): number | undefined {
60 | if (Array.isArray(value)) {
61 | return value[index];
62 | }
63 |
64 | return value;
65 | }
66 |
--------------------------------------------------------------------------------
/packages/split-screen/.redbudrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'redbud';
2 |
3 | export default defineConfig({
4 | extends: '../../.redbudrc.base.ts'
5 | });
6 |
--------------------------------------------------------------------------------
/packages/split-screen/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | ## [1.2.1](https://github.com/pansyjs/react-components/compare/@pansy/react-split-screen@1.2.0...@pansy/react-split-screen@1.2.1) (2023-02-02)
7 |
8 | **Note:** Version bump only for package @pansy/react-split-screen
9 |
10 |
11 |
12 |
13 |
14 | # [1.2.0](https://github.com/pansyjs/react-components/compare/@pansy/react-split-screen@1.1.1...@pansy/react-split-screen@1.2.0) (2021-11-04)
15 |
16 |
17 | ### Features
18 |
19 | * **split-screen:** 添加插入视频场景 ([f70886b](https://github.com/pansyjs/react-components/commit/f70886b4388bd58f1c8ceb1732b7367ef45d5811))
20 |
21 |
22 |
23 |
24 |
25 | ## [1.1.1](https://github.com/pansyjs/react-components/compare/@pansy/react-split-screen@1.1.0...@pansy/react-split-screen@1.1.1) (2021-07-12)
26 |
27 | **Note:** Version bump only for package @pansy/react-split-screen
28 |
29 |
30 |
31 |
32 |
33 | # 1.1.0 (2021-04-13)
34 |
35 |
36 | ### Features
37 |
38 | * add @pansy/react-split-screen ([1527319](https://github.com/pansyjs/react-components/commit/15273193de7c5d22998cd4596d83d2b38604a08d))
39 |
--------------------------------------------------------------------------------
/packages/split-screen/README.md:
--------------------------------------------------------------------------------
1 | @pansy/react-split-screen
2 |
3 | 分屏组件
4 |
5 | ## 🏗 安装
6 |
7 | ```sh
8 | # npm 安装
9 | npm install --save @pansy/react-split-screen
10 |
11 | # yarn 安装
12 | yarn add @pansy/react-split-screen
13 | ```
14 |
15 | ## 🔨 使用
16 |
17 | ```tsx
18 | import React from 'react';
19 | import SplitScreen from '@pansy/react-split-screen';
20 |
21 | export default () => {
22 | return (
23 |
24 | >
25 |
26 | );
27 | };
28 | ```
29 |
--------------------------------------------------------------------------------
/packages/split-screen/docs/demo/demo-01.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Select, Space } from 'antd';
3 | import SplitScreen from '@pansy/react-split-screen';
4 |
5 | const list = [1, 4, 6, 8, 9, 13 , 16];
6 |
7 | const options = list.map(item => ({ value: item, label: item }))
8 |
9 | export default () => {
10 | const [current, setCurrent] = useState(4);
11 |
12 | return (
13 |
14 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/packages/split-screen/docs/demo/demo-02.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Select, Space } from 'antd';
3 | import SplitScreen from '@pansy/react-split-screen';
4 | import type { SplitScreenAmount } from '@pansy/react-split-screen/es/types';
5 | import Player from '@pansy/react-aliplayer';
6 |
7 | const list: SplitScreenAmount[] = [1, 4, 6, 8, 9, 13, 16];
8 |
9 | const options = list.map(item => ({ value: item, label: item }))
10 |
11 | export default () => {
12 | const [current, setCurrent] = useState(4);
13 |
14 | return (
15 |
16 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/packages/split-screen/docs/demo/demo-03.less:
--------------------------------------------------------------------------------
1 | .item {
2 | position: relative;
3 | width: 100%;
4 | height: 100%;
5 | }
6 |
7 | .itemActive {
8 | border: 1px solid red;
9 | }
10 |
--------------------------------------------------------------------------------
/packages/split-screen/docs/demo/demo-03.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useMemo } from 'react';
2 | import { classNames } from '@pansy/shared'
3 | import { Select, Space, Input, Button, message } from 'antd';
4 | import SplitScreen from '@pansy/react-split-screen';
5 | import Player from '@pansy/react-aliplayer';
6 | import type { SplitScreenAmount } from '@pansy/react-split-screen/es/types';
7 | import styles from './demo-03.less';
8 |
9 | const list: SplitScreenAmount[] = [1, 4, 6, 8, 9, 13, 16];
10 |
11 | const options = list.map(item => ({ value: item, label: item }));
12 |
13 | interface PlayerItemProps {
14 | url?: string;
15 | active?: boolean;
16 | onClick?: () => void;
17 | }
18 |
19 | const PlayerItem: React.FC = ({
20 | url,
21 | active,
22 | onClick,
23 | }) => {
24 | const player = useMemo(
25 | () => {
26 | return (
27 |
28 | )
29 | },
30 | [url]
31 | );
32 |
33 | return (
34 |
40 | {url && player}
41 |
42 | )
43 | }
44 |
45 | export default () => {
46 | const [currentAmount, setCurrentAmount] = useState(list[1]);
47 | const [videos, setVideos] = useState([]);
48 | const [inputValue, setInputValue] = useState('');
49 | const [currentWindowIndex, setCurrentWindowIndex] = useState(0);
50 |
51 | useEffect(
52 | () => {
53 | setVideos(prev => {
54 | if (prev.length > currentAmount) {
55 | return prev.splice(0, currentAmount);
56 | }
57 |
58 | if (prev.length < currentAmount) {
59 | for (let i = 0; i < currentAmount- prev.length; i++) {
60 | prev.push('');
61 | }
62 | }
63 |
64 | return prev;
65 | })
66 | },
67 | [currentAmount]
68 | );
69 |
70 | const handleSelect = (index: number) => {
71 | setCurrentWindowIndex(index);
72 | }
73 |
74 | const handleAdd = () => {
75 | if (!inputValue) {
76 | message.warning('视频地址不能为空');
77 | return;
78 | }
79 | setVideos(prev => {
80 | const next = [...prev];
81 | next[currentWindowIndex] = inputValue;
82 | console.log(next);
83 | return next;
84 | });
85 | }
86 |
87 | return (
88 |
89 |
114 |
115 |
116 |
amount={currentAmount} list={videos}>
117 | {(index: number, url) => (
118 | {
121 | handleSelect(index);
122 | }}
123 | url={url}
124 | />
125 | )}
126 |
127 |
128 |
129 | )
130 | }
131 |
--------------------------------------------------------------------------------
/packages/split-screen/docs/split-screen.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: SplitScreen 分屏组件
3 | nav:
4 | title: 组件
5 | path: /components
6 | order: 1
7 | group:
8 | path: /basic
9 | title: '基础组件'
10 | order: 0
11 | ---
12 |
13 | # SplitScreen 分屏组件
14 |
15 | ## 何时使用
16 |
17 | - 需要将页面切割成多份展示时使用。
18 |
19 | ## 代码演示
20 |
21 | ### 基础示例
22 |
23 |
24 |
25 | ### 场景一
26 |
27 |
28 |
29 | ## API
30 |
31 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
32 | | ------------ | --------------| ------------------- | ------ | ---- |
33 | | className | 额外的样式类 | `string` | -- | -- |
34 | | style | 额外的样式 | `React.CSSProperties` | -- | -- |
35 | | amount | 当前的分屏数量 | `number` | `4` | -- |
36 | | gutter | 分屏的间隔 | `number` | `8` | -- |
37 | | background | 分屏的背景色 | `string` | `#000` | -- |
38 | | children | 需要展示的业务内容 | `(index: number) => React.ReactNode` |-- | -- |
39 |
--------------------------------------------------------------------------------
/packages/split-screen/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pansy/react-split-screen",
3 | "version": "1.2.1",
4 | "license": "MIT",
5 | "main": "lib/index.js",
6 | "module": "es/index.js",
7 | "types": "es/index.d.ts",
8 | "files": [
9 | "es",
10 | "lib"
11 | ],
12 | "scripts": {
13 | "build": "redbud build"
14 | },
15 | "peerDependencies": {
16 | "react": ">=16.9.0",
17 | "react-dom": ">=16.9.0"
18 | },
19 | "publishConfig": {
20 | "registry": "https://registry.npmjs.org",
21 | "access": "public"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/split-screen/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { getVideoWindowStyle } from './utils';
3 | import type { SplitScreenAmount } from './types';
4 |
5 | export interface SplitScreenProps {
6 | /** 额外的样式类 */
7 | className?: string;
8 | /** 额外的样式 */
9 | style?: React.CSSProperties;
10 | /** 当前的分屏数量 */
11 | amount: SplitScreenAmount;
12 | /** 需要用到的数据池 */
13 | list?: D[];
14 | /** 分屏的间隔 */
15 | gutter?: number;
16 | /** 分屏的背景色 */
17 | background?: string;
18 | /** 业务相关 */
19 | children: (index: number, data: D) => React.ReactNode;
20 | }
21 |
22 | const containerStyle: React.CSSProperties = {
23 | position: 'relative',
24 | width: '100%',
25 | height: '100%',
26 | }
27 |
28 | export function SplitScreen({
29 | className,
30 | style,
31 | amount = 4,
32 | list = [],
33 | gutter = 8,
34 | background = "#000",
35 | children,
36 | }: SplitScreenProps) {
37 | const [items, setItems] = useState(new Array(amount).fill(undefined));
38 |
39 | useEffect(
40 | () => {
41 | if (amount) {
42 | setItems(new Array(amount).fill(undefined));
43 | }
44 | },
45 | [amount]
46 | )
47 |
48 | const renderChildren = (index: number) => {
49 | if (typeof children === 'function') {
50 | return children(index, list[index]);
51 | }
52 |
53 | return null
54 | }
55 |
56 | const contentStyle: React.CSSProperties = {
57 | position: 'absolute',
58 | width: `calc(100% + ${gutter}px)`,
59 | height: `calc(100% + ${gutter}px)`,
60 | margin: `-${gutter / 2}px -${gutter / 2}px`,
61 | }
62 |
63 | const itemStyle: React.CSSProperties = {
64 | position: 'relative',
65 | display: 'inline-block',
66 | padding: `${gutter / 2}px`,
67 | overflow: 'hidden',
68 | verticalAlign: 'top'
69 | }
70 |
71 | const itemContentStyle: React.CSSProperties = {
72 | width: '100%',
73 | height: '100%',
74 | background
75 | }
76 |
77 | return (
78 |
79 |
80 | {items.map((_, index) => {
81 | const style = getVideoWindowStyle(amount, index);
82 | return (
83 |
84 |
85 | {renderChildren(index)}
86 |
87 |
88 | )
89 | })}
90 |
91 |
92 | )
93 | }
94 |
95 | export default SplitScreen;
96 |
--------------------------------------------------------------------------------
/packages/split-screen/src/types.ts:
--------------------------------------------------------------------------------
1 | /** 目前支持的分屏数 */
2 | export type SplitScreenAmount =
3 | 1 |
4 | 4 |
5 | 6 |
6 | 8 |
7 | 9 |
8 | 13 |
9 | 16;
10 |
--------------------------------------------------------------------------------
/packages/split-screen/src/utils.ts:
--------------------------------------------------------------------------------
1 | import type { CSSProperties } from 'react';
2 |
3 | interface Config {
4 | rows: number;
5 | different: number;
6 | differentRows: number;
7 | }
8 |
9 | const weirdSplitScreenConfig: Record = {
10 | 6: {
11 | rows: 3,
12 | different: 1,
13 | differentRows: 2,
14 | },
15 | 8: {
16 | rows: 4,
17 | different: 1,
18 | differentRows: 3,
19 | }
20 | }
21 |
22 | /**
23 | * 获取窗口的样式
24 | * @param amount 分屏数
25 | * @param amount 分屏的间隔
26 | * @param index 分屏的索引
27 | * @returns
28 | */
29 | export const getVideoWindowStyle = (
30 | amount: number,
31 | index: number
32 | ) => {
33 | let style: CSSProperties = {};
34 |
35 | const sqrt = Math.sqrt(amount);
36 |
37 | // n*n >> 1、4、6、8
38 | if (Number.isInteger(sqrt)) {
39 | style = {
40 | width: `calc(100% / ${sqrt})`,
41 | height: `calc(100% / ${sqrt})`,
42 | }
43 | return style;
44 | }
45 |
46 | // 6、8
47 | const config = weirdSplitScreenConfig[amount];
48 |
49 | if (config) {
50 | style = {
51 | float: 'left',
52 | width: `${100 /config.rows}%`,
53 | height: `${100 /config.rows}%`,
54 | }
55 |
56 | if (index + 1 === config.different) {
57 | style.width = `calc(${(100 / config.rows) * config.differentRows}%)`;
58 | style.height = `calc(${(100 / config.rows) * config.differentRows}% - 1px)`;
59 | }
60 |
61 | return style;
62 | };
63 |
64 | // 13
65 | const splitScreenConfig = {
66 | rows: 4,
67 | different: 6,
68 | differentRows: 2
69 | }
70 |
71 | style = {
72 | position: 'absolute',
73 | width: `25%`,
74 | height: `25%`
75 | }
76 |
77 | if (index + 1 === splitScreenConfig.different) {
78 | style.width = `calc(${(100 / splitScreenConfig.rows) * splitScreenConfig.differentRows}%)`;
79 | style.height = `calc(${(100 / splitScreenConfig.rows) * splitScreenConfig.differentRows}%)`;
80 | }
81 |
82 | if (index >= 0 && index <= 3) {
83 | style.top = 0;
84 | style.left = `${index * 25}%`;
85 | }
86 |
87 | if (index === 4) {
88 | style.top = '25%';
89 | style.left = 0;
90 | }
91 |
92 | if (splitScreenConfig.different === index + 1) {
93 | style.top = '25%';
94 | style.left = '25%';
95 | }
96 |
97 | if (index === 6) {
98 | style.top = '25%';
99 | style.left = '75%';
100 | }
101 |
102 | if (index === 7) {
103 | style.top = `calc(50%)`;
104 | style.left = 0;
105 | }
106 |
107 | if (index === 8) {
108 | style.top = `calc(50%)`;
109 | style.left = '75%';
110 | }
111 |
112 | if (index >= 9 && index <= 12) {
113 | style.top = `calc(75%)`;
114 | style.left = `${(index - 9) * 25}%`;
115 | }
116 |
117 | return style;
118 | }
119 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'packages/*'
3 | - 'scripts'
4 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ],
5 | "ignoreDeps": ["@types/react", "@types/react-dom"]
6 | }
7 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "target": "esnext",
5 | "moduleResolution": "node",
6 | "jsx": "preserve",
7 | "esModuleInterop": true,
8 | "experimentalDecorators": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "noImplicitReturns": true,
12 | "suppressImplicitAnyIndexErrors": true,
13 | "declaration": true,
14 | "skipLibCheck": true,
15 | "paths": {}
16 | },
17 | "include": [
18 | "**/src/**/*",
19 | "**/docs/**/*",
20 | "scripts/**/*",
21 | "**/demos",
22 | ".eslintrc.js",
23 | "tests",
24 | "jest.config.js",
25 | "**/fixtures",
26 | "./tests/no-duplicated.ts"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------