commit
179858deb2
|
@ -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
|
## Video
|
||||||
|
|
||||||
视频播放器。
|
视频播放器。
|
||||||
|
|
|
@ -1178,15 +1178,63 @@ $Tree-folderIconContent: "\f07b" !default;
|
||||||
$Tree-itemText--onChecked-color: $Form-selectValue-color !default;
|
$Tree-itemText--onChecked-color: $Form-selectValue-color !default;
|
||||||
|
|
||||||
// IconPicker
|
// IconPicker
|
||||||
$IconPicker-tabs-bg: #F0F3F4;
|
$IconPicker-tabs-bg: #F0F3F4 !default;
|
||||||
$IconPicker-tab-padding: 0 px2rem(5px);
|
$IconPicker-tab-padding: 0 px2rem(5px) !default;
|
||||||
$IconPicker-tab-height: px2rem(30px);
|
$IconPicker-tab-height: px2rem(30px) !default;
|
||||||
$IconPicker-tab-lineHeight: px2rem(30px);
|
$IconPicker-tab-lineHeight: px2rem(30px) !default;
|
||||||
$IconPicker-tab-onActive-bg: $white;
|
$IconPicker-tab-onActive-bg: $white !default;
|
||||||
$IconPicker-content-maxHeight: px2rem(350px);
|
$IconPicker-content-maxHeight: px2rem(350px) !default;
|
||||||
$IconPicker-singleVendor-padding: px2rem(5px) 0 px2rem(5px) px2rem(13px);
|
$IconPicker-singleVendor-padding: px2rem(5px) 0 px2rem(5px) px2rem(13px) !default;
|
||||||
$IconPicker-multiVendor-padding: px2rem(35px) 0 px2rem(5px) px2rem(13px);
|
$IconPicker-multiVendor-padding: px2rem(35px) 0 px2rem(5px) px2rem(13px) !default;
|
||||||
$IconPicker-sugItem-width: px2rem(28px);
|
$IconPicker-sugItem-width: px2rem(28px) !default;
|
||||||
$IconPicker-sugItem-height: px2rem(28px);
|
$IconPicker-sugItem-height: px2rem(28px) !default;
|
||||||
$IconPicker-sugItem-lineHeight: px2rem(28px);
|
$IconPicker-sugItem-lineHeight: px2rem(28px) !default;
|
||||||
$IconPicker-selectedIcon-marginRight: px2rem(5px);
|
$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/remark";
|
||||||
@import "../components/chart";
|
@import "../components/chart";
|
||||||
@import "../components/video";
|
@import "../components/video";
|
||||||
|
@import "../components/audio";
|
||||||
@import "../components/panel";
|
@import "../components/panel";
|
||||||
@import "../components/service";
|
@import "../components/service";
|
||||||
@import "../components/spinner";
|
@import "../components/spinner";
|
||||||
|
|
|
@ -39,6 +39,7 @@ $Form-input-borderColor: #cfdadd;
|
||||||
@import "../components/remark";
|
@import "../components/remark";
|
||||||
@import "../components/chart";
|
@import "../components/chart";
|
||||||
@import "../components/video";
|
@import "../components/video";
|
||||||
|
@import "../components/audio";
|
||||||
@import "../components/panel";
|
@import "../components/panel";
|
||||||
@import "../components/service";
|
@import "../components/service";
|
||||||
@import "../components/spinner";
|
@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 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 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 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/Container';
|
||||||
import './renderers/Service';
|
import './renderers/Service';
|
||||||
import './renderers/Video';
|
import './renderers/Video';
|
||||||
|
import './renderers/Audio';
|
||||||
import './renderers/Nav';
|
import './renderers/Nav';
|
||||||
import './renderers/Tasks';
|
import './renderers/Tasks';
|
||||||
import './renderers/Drawer';
|
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