commit
179858deb2
|
@ -2925,7 +2925,7 @@ Action 是一种特殊的渲染器,它本身是一个按钮,同时它能触
|
|||
"link": "/docs/index"
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
* `url` 当按钮点击后,新窗口打开指定页面。
|
||||
|
||||
```schema:height="200"
|
||||
|
@ -2938,7 +2938,7 @@ Action 是一种特殊的渲染器,它本身是一个按钮,同时它能触
|
|||
"url": "raw:http://www.baidu.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
* `dialog` 当按钮点击后,弹出一个对话框。 关于 dialog 配置,请查看 [Dialog 模型](#dialog)。
|
||||
|
||||
```schema:height="200"
|
||||
|
@ -2964,7 +2964,7 @@ Action 是一种特殊的渲染器,它本身是一个按钮,同时它能触
|
|||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
* `drawer` 当按钮点击后,弹出一个抽出式对话框。 关于 drawer 配置,请查看 [Drawer 模型](#drawer)。
|
||||
|
||||
|
@ -2991,7 +2991,7 @@ Action 是一种特殊的渲染器,它本身是一个按钮,同时它能触
|
|||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
## Dialog
|
||||
|
||||
|
@ -3465,6 +3465,28 @@ CRUD 支持三种模式:`table`、`cards`、`list`,默认为 `table`。
|
|||
}
|
||||
```
|
||||
|
||||
## Audio
|
||||
|
||||
音频播放器
|
||||
|
||||
|属性名 | 类型 | 默认值 | 说明 |
|
||||
|---|---|---|---|
|
||||
| type | `string` | `"audio"` | 指定为 audio 渲染器 |
|
||||
| className | `string` | | 外层 Dom 的类名 |
|
||||
| inline | `boolean` | true | 是否是内联模式 |
|
||||
| src | `string` | | 音频地址 |
|
||||
| loop | `boolean` | false | 是否循环播放 |
|
||||
| autoPlay | `boolean` | false | 是否自动播放 |
|
||||
| rates | `array` | `[1.0, 2.0, 4.0]` | 加速播放 |
|
||||
|
||||
```schema:height="500" scope="body"
|
||||
{
|
||||
"type": "audio",
|
||||
"autoPlay": false,
|
||||
"src": ""
|
||||
}
|
||||
```
|
||||
|
||||
## Video
|
||||
|
||||
视频播放器。
|
||||
|
|
|
@ -1178,15 +1178,63 @@ $Tree-folderIconContent: "\f07b" !default;
|
|||
$Tree-itemText--onChecked-color: $Form-selectValue-color !default;
|
||||
|
||||
// IconPicker
|
||||
$IconPicker-tabs-bg: #F0F3F4;
|
||||
$IconPicker-tab-padding: 0 px2rem(5px);
|
||||
$IconPicker-tab-height: px2rem(30px);
|
||||
$IconPicker-tab-lineHeight: px2rem(30px);
|
||||
$IconPicker-tab-onActive-bg: $white;
|
||||
$IconPicker-content-maxHeight: px2rem(350px);
|
||||
$IconPicker-singleVendor-padding: px2rem(5px) 0 px2rem(5px) px2rem(13px);
|
||||
$IconPicker-multiVendor-padding: px2rem(35px) 0 px2rem(5px) px2rem(13px);
|
||||
$IconPicker-sugItem-width: px2rem(28px);
|
||||
$IconPicker-sugItem-height: px2rem(28px);
|
||||
$IconPicker-sugItem-lineHeight: px2rem(28px);
|
||||
$IconPicker-selectedIcon-marginRight: px2rem(5px);
|
||||
$IconPicker-tabs-bg: #F0F3F4 !default;
|
||||
$IconPicker-tab-padding: 0 px2rem(5px) !default;
|
||||
$IconPicker-tab-height: px2rem(30px) !default;
|
||||
$IconPicker-tab-lineHeight: px2rem(30px) !default;
|
||||
$IconPicker-tab-onActive-bg: $white !default;
|
||||
$IconPicker-content-maxHeight: px2rem(350px) !default;
|
||||
$IconPicker-singleVendor-padding: px2rem(5px) 0 px2rem(5px) px2rem(13px) !default;
|
||||
$IconPicker-multiVendor-padding: px2rem(35px) 0 px2rem(5px) px2rem(13px) !default;
|
||||
$IconPicker-sugItem-width: px2rem(28px) !default;
|
||||
$IconPicker-sugItem-height: px2rem(28px) !default;
|
||||
$IconPicker-sugItem-lineHeight: px2rem(28px) !default;
|
||||
$IconPicker-selectedIcon-marginRight: px2rem(5px) !default;
|
||||
|
||||
// Audio
|
||||
$Audio-width: px2rem(300px) !default;
|
||||
$Audio-height: px2rem(50px) !default;
|
||||
$Audio-lineHeight: px2rem(50px) !default;
|
||||
$Audio-border: px2rem(1px) solid #dee2e6 !default;
|
||||
$Audio-rate-padding: 0 px2rem(5px) !default;
|
||||
$Audio-rate-width: px2rem(40px) !default;
|
||||
$Audio-rate-height: px2rem(50px) !default;
|
||||
$Audio-rate-lineHeight: px2rem(50px) !default;
|
||||
$Audio-rate-bg: #dee2e6 !default;
|
||||
$Audio-rateControlItem-padding: px2rem(16px) px2rem(7px) !default;
|
||||
$Audio-rateControlItem-bg: #dee2e6 !default;
|
||||
$Audio-rateControlItem-borderRight: px2rem(1px) solid #d3dae0 !default;
|
||||
$Audio-play-width: px2rem(25px) !default;
|
||||
$Audio-play-top: px2rem(5px) !default;
|
||||
$Audio-play-marginLeft: px2rem(10px) !default;
|
||||
$Audio-times-width: px2rem(80px) !default;
|
||||
$Audio-times-margin: 0 px2rem(10px) !default;
|
||||
$Audio-process-width: px2rem(80px) !default;
|
||||
$Audio-volume-width: px2rem(22px) !default;
|
||||
$Audio-volume-height: px2rem(22px) !default;
|
||||
$Audio-volume-lineHeight: px2rem(30px) !default;
|
||||
$Audio-volume-borderRadius: px2rem(15px) !default;
|
||||
$Audio-volume-top: px2rem(5px) !default;
|
||||
$Audio-volume-left: px2rem(10px) !default;
|
||||
$Audio-volumeControl-width: px2rem(130px) !default;
|
||||
$Audio-volumeControl-height: px2rem(30px) !default;
|
||||
$Audio-volumeControl-lineHeight: px2rem(30px) !default;
|
||||
$Audio-volumeControl-right: px2rem(-6px) !default;
|
||||
$Audio-volumeControl-top: px2rem(-4px) !default;
|
||||
$Audio-volumeControl-paddingLeft: px2rem(10px) !default;
|
||||
$Audio-volumeControl-bg: #E6E7E9 !default;
|
||||
$Audio-volumeControl-border: px2rem(2px) solid transparent !default;
|
||||
$Audio-volumeControl-borderRadius: px2rem(10px) !default;
|
||||
$Audio-input-width: px2rem(80px) !default;
|
||||
$Audio-volumeControl-input-margin: px2rem(-3px) px2rem(10px) 0 0 !default;
|
||||
$Audio-track-height: px2rem(6px) !default;
|
||||
$Audio-track-bg: #d7dbdd !default;
|
||||
$Audio-track-borderRadius: px2rem(3px) !default;
|
||||
$Audio-track-border: px2rem(1px) solid transparent !default;
|
||||
$Audio-thumb-width: px2rem(14px) !default;
|
||||
$Audio-thumb-height: px2rem(14px) !default;
|
||||
$Audio-thumb-bg: #606670 !default;
|
||||
$Audio-thumb-marginTop: px2rem(-5px) !default;
|
||||
$Audio-svg-width: px2rem(20px) !default;
|
||||
$Audio-svg-height: px2rem(20px) !default;
|
||||
$Audio-svg-top: px2rem(4px) !default;
|
|
@ -0,0 +1,148 @@
|
|||
@mixin input-range {
|
||||
width: $Audio-input-width;
|
||||
display: inline-block;
|
||||
-webkit-appearance: none;
|
||||
vertical-align: middle;
|
||||
outline: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: none;
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
background-color: $Audio-track-bg;
|
||||
height: $Audio-track-height;
|
||||
border-radius: $Audio-track-borderRadius;
|
||||
border: $Audio-track-border;
|
||||
}
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
-webkit-appearance: none !important;
|
||||
border-radius: 100%;
|
||||
cursor: pointer;
|
||||
background: $Audio-thumb-bg;
|
||||
height: $Audio-thumb-width;
|
||||
width: $Audio-thumb-height;
|
||||
margin-top: $Audio-thumb-marginTop;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin svg {
|
||||
width: $Audio-svg-width;
|
||||
height: $Audio-svg-height;
|
||||
}
|
||||
|
||||
.#{$ns}Audio-original {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.#{$ns}Audio--inline {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.#{$ns}Audio {
|
||||
width: $Audio-width;
|
||||
height: $Audio-height;
|
||||
line-height: $Audio-lineHeight;
|
||||
border: $Audio-border;
|
||||
display: inline-block;
|
||||
&-rates {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
||||
.#{$ns}Audio-rate {
|
||||
display: block;
|
||||
padding: $Audio-rate-padding;
|
||||
width: $Audio-rate-width;
|
||||
height: $Audio-rate-height;
|
||||
line-height: $Audio-rate-lineHeight;
|
||||
text-align: center;
|
||||
background: $Audio-rate-bg;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.#{$ns}Audio-rateControl {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
|
||||
.#{$ns}Audio-rateControlItem {
|
||||
padding: $Audio-rateControlItem-padding;
|
||||
background: $Audio-rateControlItem-bg;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border-right: $Audio-rateControlItem-borderRight;
|
||||
}
|
||||
}
|
||||
}
|
||||
&-play {
|
||||
display: inline-block;
|
||||
width: $Audio-play-width;
|
||||
position: relative;
|
||||
top: $Audio-play-top;
|
||||
cursor: pointer;
|
||||
margin-left: $Audio-play-marginLeft;
|
||||
|
||||
svg {
|
||||
@include svg();
|
||||
}
|
||||
}
|
||||
&-times {
|
||||
display: inline-block;
|
||||
width: $Audio-times-width;
|
||||
margin: $Audio-times-margin;
|
||||
cursor: default;
|
||||
}
|
||||
&-process {
|
||||
display: inline-block;
|
||||
width: $Audio-process-width;
|
||||
cursor: pointer;
|
||||
|
||||
input[type=range] {
|
||||
@include input-range();
|
||||
}
|
||||
}
|
||||
&-volume {
|
||||
display: inline-block;
|
||||
width: $Audio-volume-width;
|
||||
height: $Audio-volume-height;
|
||||
line-height: $Audio-volume-lineHeight;
|
||||
border-radius: $Audio-volume-borderRadius;
|
||||
position: relative;
|
||||
top: $Audio-volume-top;
|
||||
left: $Audio-volume-left;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
@include svg();
|
||||
}
|
||||
|
||||
.#{$ns}Audio-volumeControl {
|
||||
position: absolute;
|
||||
right: $Audio-volumeControl-right;
|
||||
top: $Audio-volumeControl-top;
|
||||
padding-left: $Audio-volumeControl-paddingLeft;
|
||||
height: $Audio-volumeControl-height;
|
||||
line-height: $Audio-volumeControl-lineHeight;
|
||||
border-radius: $Audio-volumeControl-borderRadius;
|
||||
background: $Audio-volumeControl-bg;
|
||||
border: $Audio-volumeControl-border;
|
||||
width: $Audio-volumeControl-width;
|
||||
input[type=range] {
|
||||
margin: $Audio-volumeControl-input-margin;
|
||||
@include input-range();
|
||||
}
|
||||
|
||||
.#{$ns}Audio-volumeControlIcon {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
svg {
|
||||
position: relative;
|
||||
top: $Audio-svg-top;
|
||||
@include svg();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -413,6 +413,7 @@ $TagControl-sugTip-color: $primary;
|
|||
@import "../components/remark";
|
||||
@import "../components/chart";
|
||||
@import "../components/video";
|
||||
@import "../components/audio";
|
||||
@import "../components/panel";
|
||||
@import "../components/service";
|
||||
@import "../components/spinner";
|
||||
|
|
|
@ -39,6 +39,7 @@ $Form-input-borderColor: #cfdadd;
|
|||
@import "../components/remark";
|
||||
@import "../components/chart";
|
||||
@import "../components/video";
|
||||
@import "../components/audio";
|
||||
@import "../components/panel";
|
||||
@import "../components/service";
|
||||
@import "../components/spinner";
|
||||
|
|
|
@ -3,3 +3,7 @@ export const closeIcon = (<svg className="icon" viewBox="0 0 1024 1024" version=
|
|||
export const unDoIcon = (<svg className="icon" viewBox="0 0 1024 1024" version="1.1"><path d="M661.333333 341.333333H167.04l183.253333-183.253333L320 128 85.333333 362.666667l234.666667 234.666666 30.08-30.08L167.04 384H661.333333a234.666667 234.666667 0 0 1 0 469.333333H448v42.666667h213.333333a277.333333 277.333333 0 0 0 0-554.666667z"></path></svg>);
|
||||
export const reDoIcon = (<svg className="icon" viewBox="0 0 1024 1024" version="1.1"><path d="M704 128l-30.08 30.08L856.96 341.333333H362.666667a277.333333 277.333333 0 0 0 0 554.666667h213.333333v-42.666667H362.666667a234.666667 234.666667 0 0 1 0-469.333333h494.293333l-183.253333 183.253333L704 597.333333l234.666667-234.666666z"></path></svg>)
|
||||
export const enterIcon = (<svg className="icon" viewBox="0 0 1024 1024" version="1.1"><path d="M864 192c-19.2 0-32 12.8-32 32v224c0 89.6-70.4 160-160 160H236.8l105.6-105.6c12.8-12.8 12.8-32 0-44.8s-32-12.8-44.8 0l-160 160c-3.2 3.2-6.4 6.4-6.4 9.6-3.2 6.4-3.2 16 0 25.6 3.2 3.2 3.2 6.4 6.4 9.6l160 160c6.4 6.4 12.8 9.6 22.4 9.6s16-3.2 22.4-9.6c12.8-12.8 12.8-32 0-44.8L236.8 672H672c124.8 0 224-99.2 224-224V224c0-19.2-12.8-32-32-32z"></path></svg>)
|
||||
export const volumeIcon = (<svg className="icon" viewBox="0 0 1024 1024" version="1.1"><path d="M536.319574 5.11991a63.99888 63.99888 0 0 0-69.758779 13.439765L229.764939 255.99552H64.00784a63.99888 63.99888 0 0 0-63.99888 63.99888v383.99328a63.99888 63.99888 0 0 0 63.99888 63.99888h165.757099l236.795856 237.435845A63.99888 63.99888 0 0 0 512 1023.98208a53.759059 53.759059 0 0 0 24.319574-5.11991A63.99888 63.99888 0 0 0 575.99888 959.9832V63.99888a63.99888 63.99888 0 0 0-39.679306-58.87897zM192.0056 639.9888H128.00672V383.99328h63.99888z m255.99552 165.757099l-127.99776-127.99776V346.233941l127.99776-127.99776zM879.353571 148.477402a63.99888 63.99888 0 0 0-94.718342 87.038476 402.552955 402.552955 0 0 1 0 552.950324A63.99888 63.99888 0 0 0 831.9944 895.98432a63.99888 63.99888 0 0 0 46.719183-20.479641 531.830693 531.830693 0 0 0 0-727.027277z" fill="#606670" p-id="3605"></path><path d="M751.9958 277.11515a63.99888 63.99888 0 0 0-95.99832 85.7585A218.236181 218.236181 0 0 1 703.99664 511.99104a221.436125 221.436125 0 0 1-47.359171 149.117391 63.99888 63.99888 0 0 0 4.479921 90.23842A63.99888 63.99888 0 0 0 703.99664 767.98656a63.99888 63.99888 0 0 0 47.359171-21.11963A349.433885 349.433885 0 0 0 831.9944 511.99104a353.273818 353.273818 0 0 0-79.9986-234.87589z" fill="#606670" p-id="3606"></path></svg>)
|
||||
export const muteIcon = (<svg className="icon" viewBox="0 0 1024 1024" version="1.1"><path d="M536.310615 5.11991a63.99888 63.99888 0 0 0-69.75878 13.439765L229.755979 255.99552H63.99888a63.99888 63.99888 0 0 0-63.99888 63.99888v383.99328a63.99888 63.99888 0 0 0 63.99888 63.99888h165.757099l236.795856 237.435845A63.99888 63.99888 0 0 0 511.99104 1023.98208a53.759059 53.759059 0 0 0 24.319575-5.11991A63.99888 63.99888 0 0 0 575.98992 959.9832V63.99888a63.99888 63.99888 0 0 0-39.679305-58.87897zM191.99664 639.9888H127.99776V383.99328h63.99888z m255.99552 165.757099l-127.99776-127.99776V346.233941l127.99776-127.99776zM914.543995 511.99104l90.87841-90.238421a63.99888 63.99888 0 1 0-90.87841-90.878409l-90.23842 90.878409-90.238421-90.878409a63.99888 63.99888 0 0 0-90.87841 90.878409L734.067154 511.99104l-90.87841 90.238421a63.99888 63.99888 0 0 0 90.87841 90.87841l90.238421-90.87841 90.23842 90.87841a63.99888 63.99888 0 1 0 90.87841-90.87841z" fill="#606670" p-id="2312"></path></svg>)
|
||||
export const playIcon = (<svg className="icon" viewBox="0 0 1024 1024" version="1.1"><path d="M852.727563 392.447107C956.997809 458.473635 956.941389 565.559517 852.727563 631.55032L281.888889 993.019655C177.618644 1059.046186 93.090909 1016.054114 93.090909 897.137364L93.090909 126.860063C93.090909 7.879206 177.675064-35.013033 281.888889 30.977769L852.727563 392.447107 852.727563 392.447107Z" p-id="4494" fill="#606670"></path></svg>)
|
||||
export const pauseIcon = (<svg className="icon" viewBox="0 0 1024 1024" version="1.1"><path d="M757.52 73.107h-62.493c-34.526 0-62.498 27.984-62.498 62.511v749.948c0 34.526 27.974 62.493 62.498 62.493h62.493c34.516 0 62.502-27.968 62.502-62.493v-749.953c-0.001-34.524-27.984-62.509-62.502-62.509z" p-id="7567" fill="#606670"></path><path d="M320.054 73.107h-62.502c-34.526 0-62.498 27.984-62.498 62.511v749.948c0 34.526 27.974 62.493 62.498 62.493h62.502c34.505 0 62.493-27.968 62.493-62.493v-749.953c-0.001-34.524-27.984-62.509-62.493-62.509z" p-id="7568" fill="#606670"></path></svg>)
|
|
@ -141,6 +141,7 @@ import './renderers/Chart';
|
|||
import './renderers/Container';
|
||||
import './renderers/Service';
|
||||
import './renderers/Video';
|
||||
import './renderers/Audio';
|
||||
import './renderers/Nav';
|
||||
import './renderers/Tasks';
|
||||
import './renderers/Drawer';
|
||||
|
|
|
@ -0,0 +1,304 @@
|
|||
import * as React from 'react';
|
||||
import {
|
||||
Renderer,
|
||||
RendererProps
|
||||
} from '../factory';
|
||||
import { autobind } from '../utils/helper';
|
||||
import { volumeIcon, muteIcon, playIcon, pauseIcon} from '../components/icons';
|
||||
|
||||
export interface AudioProps extends RendererProps {
|
||||
className?: string;
|
||||
inline?: boolean,
|
||||
src?: string,
|
||||
autoPlay?: boolean,
|
||||
loop?: boolean,
|
||||
rates?: number[]
|
||||
}
|
||||
|
||||
export interface AudioState {
|
||||
isReady?: boolean,
|
||||
muted?: boolean,
|
||||
playing?: boolean,
|
||||
played: number,
|
||||
seeking?: boolean,
|
||||
volume: number,
|
||||
prevVolume: number,
|
||||
loaded?: number,
|
||||
playbackRate: number,
|
||||
showHandlePlaybackRate: boolean,
|
||||
showHandleVolume: boolean
|
||||
}
|
||||
|
||||
export class Audio extends React.Component<AudioProps, AudioState> {
|
||||
audio: any;
|
||||
progressTimeout: any;
|
||||
|
||||
static defaultProps:Pick<AudioProps, 'inline' | 'autoPlay' | 'playbackRate' | 'loop' | 'rates' | 'progressInterval'> = {
|
||||
inline: true,
|
||||
autoPlay: false,
|
||||
playbackRate: 1,
|
||||
loop: false,
|
||||
rates: [1.0, 2.0, 4.0],
|
||||
progressInterval: 1000
|
||||
};
|
||||
|
||||
state:AudioState = {
|
||||
isReady: false,
|
||||
muted: false,
|
||||
playing: false,
|
||||
played: 0,
|
||||
seeking: false,
|
||||
volume: 0.8,
|
||||
prevVolume: 0.8,
|
||||
loaded: 0,
|
||||
playbackRate: 1.0,
|
||||
showHandlePlaybackRate: false,
|
||||
showHandleVolume: false
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
clearTimeout(this.progressTimeout);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const autoPlay = this.props.autoPlay;
|
||||
const playing = autoPlay ? true : false;
|
||||
this.setState({
|
||||
playing: playing
|
||||
}, this.progress);
|
||||
}
|
||||
|
||||
@autobind
|
||||
progress() {
|
||||
clearTimeout(this.progressTimeout);
|
||||
if (this.props.src && this.audio) {
|
||||
const currentTime = this.audio.currentTime || 0;
|
||||
const duration = this.audio.duration;
|
||||
const played = currentTime / duration;
|
||||
let playing = this.state.playing;
|
||||
playing = (played != 1 && playing) ? true : false;
|
||||
this.setState({
|
||||
played,
|
||||
playing
|
||||
});
|
||||
this.progressTimeout = setTimeout(this.progress, (this.props.progressInterval / this.state.playbackRate))
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
audioRef(audio:any) {
|
||||
this.audio = audio;
|
||||
}
|
||||
|
||||
@autobind
|
||||
load() {
|
||||
this.setState({
|
||||
isReady: true
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handlePlaybackRate(rate:number) {
|
||||
this.audio.playbackRate = rate;
|
||||
this.setState({
|
||||
playbackRate: rate,
|
||||
showHandlePlaybackRate: false
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleMute() {
|
||||
const {muted, prevVolume} = this.state;
|
||||
const curVolume = !muted ? 0 : prevVolume;
|
||||
this.audio.muted = !muted;
|
||||
this.setState({
|
||||
muted: !muted,
|
||||
volume: curVolume
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handlePlaying() {
|
||||
let playing = this.state.playing;
|
||||
playing ? this.audio.pause() : this.audio.play();
|
||||
this.setState({
|
||||
playing: !playing
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
getCurrentTime() {
|
||||
if (!this.audio || !this.state.isReady) return 0;
|
||||
const duration = this.audio.duration;
|
||||
const played = this.state.played;
|
||||
return this.formatTime(duration * (played || 0));
|
||||
}
|
||||
|
||||
@autobind
|
||||
getDuration() {
|
||||
if (!this.audio || !this.state.isReady) return 0;
|
||||
const { duration, seekable } = this.audio;
|
||||
// on iOS, live streams return Infinity for the duration
|
||||
// so instead we use the end of the seekable timerange
|
||||
if (duration === Infinity && seekable.length > 0) {
|
||||
return seekable.end(seekable.length - 1)
|
||||
}
|
||||
return this.formatTime(duration);
|
||||
}
|
||||
|
||||
@autobind
|
||||
onSeekChange(e:any) {
|
||||
const played = e.target.value;
|
||||
this.setState({ played: played });
|
||||
}
|
||||
|
||||
@autobind
|
||||
onSeekMouseDown() {
|
||||
this.setState({ seeking: true });
|
||||
}
|
||||
|
||||
@autobind
|
||||
onSeekMouseUp(e:any) {
|
||||
if (!this.state.seeking) return;
|
||||
const played = e.target.value;
|
||||
const duration = this.audio.duration;
|
||||
this.audio.currentTime = duration * played;
|
||||
|
||||
const loop = this.props.loop;
|
||||
let playing = this.state.playing;
|
||||
playing = (played < 1 || loop) ? playing : false;
|
||||
this.setState({
|
||||
playing: playing,
|
||||
seeking: false
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
setVolume(e:any) {
|
||||
const volume = e.target.value;
|
||||
this.audio.volume = volume;
|
||||
this.setState({
|
||||
volume: volume,
|
||||
prevVolume: volume
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
formatTime(seconds:number) {
|
||||
const date = new Date(seconds * 1000);
|
||||
const hh = date.getUTCHours();
|
||||
const mm = date.getUTCMinutes();
|
||||
const ss = this.pad(date.getUTCSeconds());
|
||||
if (hh) {
|
||||
return `${hh}:${this.pad(mm)}:${ss}`;
|
||||
}
|
||||
return `${mm}:${ss}`;
|
||||
}
|
||||
|
||||
@autobind
|
||||
pad(string:number) {
|
||||
return ('0' + string).slice(-2)
|
||||
}
|
||||
|
||||
@autobind
|
||||
toggleHandlePlaybackRate() {
|
||||
this.setState({
|
||||
showHandlePlaybackRate: !this.state.showHandlePlaybackRate
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
toggleHandleVolume(type:boolean) {
|
||||
this.setState({
|
||||
showHandleVolume: type
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
inline,
|
||||
src,
|
||||
autoPlay,
|
||||
loop,
|
||||
rates,
|
||||
classnames: cx
|
||||
} = this.props;
|
||||
const {
|
||||
playing,
|
||||
played,
|
||||
volume,
|
||||
muted,
|
||||
isReady,
|
||||
playbackRate,
|
||||
showHandlePlaybackRate,
|
||||
showHandleVolume
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<div className={cx(inline ? 'Audio--inline' : '')}>
|
||||
<audio
|
||||
className={cx('Audio-original')}
|
||||
ref={this.audioRef}
|
||||
onCanPlay={this.load}
|
||||
autoPlay={autoPlay}
|
||||
controls
|
||||
muted={muted}
|
||||
loop={loop}>
|
||||
<source src={src}/>
|
||||
</audio>
|
||||
{isReady ? (<div className={cx('Audio', className)}>
|
||||
{rates ? (<div className={cx('Audio-rates')}>
|
||||
<div className={cx('Audio-rate')}
|
||||
onClick={this.toggleHandlePlaybackRate}>
|
||||
x{playbackRate.toFixed(1)}
|
||||
</div>
|
||||
{showHandlePlaybackRate ? (<div className={cx('Audio-rateControl')}>
|
||||
{rates.map((rate, index) =>
|
||||
<span className={cx('Audio-rateControlItem')}
|
||||
key={index}
|
||||
onClick={() => this.handlePlaybackRate(rate)}>
|
||||
x{rate.toFixed(1)}
|
||||
</span>
|
||||
)} </div>) : null}
|
||||
</div>)
|
||||
: null }
|
||||
<div className={cx('Audio-play')} onClick={this.handlePlaying}>
|
||||
{playing ? pauseIcon : playIcon}
|
||||
</div>
|
||||
<div className={cx('Audio-times')}>{this.getCurrentTime()} / {this.getDuration()}</div>
|
||||
<div className={cx('Audio-process')}>
|
||||
<input
|
||||
type="range"
|
||||
min={0} max={1} step="any"
|
||||
value={played || 0}
|
||||
onMouseDown={this.onSeekMouseDown}
|
||||
onChange={this.onSeekChange}
|
||||
onMouseUp={this.onSeekMouseUp}/>
|
||||
</div>
|
||||
<div className={cx('Audio-volume')}
|
||||
onMouseEnter={() => this.toggleHandleVolume(true)}
|
||||
onMouseLeave={() => this.toggleHandleVolume(false)}>
|
||||
{showHandleVolume ?
|
||||
(<div className={cx('Audio-volumeControl')}>
|
||||
<input
|
||||
type='range' min={0} max={1} step='any'
|
||||
value={volume}
|
||||
onChange={this.setVolume} />
|
||||
<div className={cx('Audio-volumeControlIcon')}
|
||||
onClick={this.handleMute}>
|
||||
{volume > 0 ? volumeIcon : muteIcon}
|
||||
</div></div>)
|
||||
: volume > 0 ? volumeIcon : muteIcon}
|
||||
</div>
|
||||
</div>) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Renderer({
|
||||
test: /(^|\/)audio/,
|
||||
name: 'audio'
|
||||
})
|
||||
export class AudioRenderer extends Audio {};
|
Loading…
Reference in New Issue