commit
ea64280666
|
@ -3477,7 +3477,8 @@ CRUD 支持三种模式:`table`、`cards`、`list`,默认为 `table`。
|
|||
| src | `string` | | 音频地址 |
|
||||
| loop | `boolean` | false | 是否循环播放 |
|
||||
| autoPlay | `boolean` | false | 是否自动播放 |
|
||||
| rates | `array` | `[1.0, 2.0, 4.0]` | 加速播放 |
|
||||
| rates | `array` | `[]` | 加速播放 |
|
||||
| controls | `array` | `['rates', 'play', 'time', 'process', 'volume']` | 内部模块定制化 |
|
||||
|
||||
```schema:height="200" scope="body"
|
||||
{
|
||||
|
|
|
@ -1192,41 +1192,26 @@ $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-width: px2rem(20px) !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-times-width: px2rem(75px) !default;
|
||||
$Audio-times-margin: 0 px2rem(5px) !default;
|
||||
$Audio-process-width: px2rem(80px) !default;
|
||||
$Audio-volume-width: px2rem(22px) !default;
|
||||
$Audio-volume-width: px2rem(20px) !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-volumeControl-width: px2rem(110px) !default;
|
||||
$Audio-volumeControl-height: px2rem(50px) !default;
|
||||
$Audio-volumeControl-lineHeight: px2rem(50px) !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;
|
||||
|
@ -1237,4 +1222,5 @@ $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;
|
||||
$Audio-svg-top: px2rem(6px) !default;
|
||||
$Audio-item-margin: px2rem(10px) !default;
|
|
@ -29,6 +29,8 @@
|
|||
@mixin svg {
|
||||
width: $Audio-svg-width;
|
||||
height: $Audio-svg-height;
|
||||
position: relative;
|
||||
top: $Audio-svg-top;
|
||||
}
|
||||
|
||||
.#{$ns}Audio-original {
|
||||
|
@ -42,52 +44,47 @@
|
|||
}
|
||||
|
||||
.#{$ns}Audio {
|
||||
width: $Audio-width;
|
||||
box-sizing: border-box;
|
||||
height: $Audio-height;
|
||||
line-height: $Audio-lineHeight;
|
||||
border: $Audio-border;
|
||||
display: inline-block;
|
||||
&-rates-holder {
|
||||
display: inline-block;
|
||||
width: $Audio-rate-width / 2;
|
||||
}
|
||||
padding-left: $Audio-item-margin;
|
||||
&-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;
|
||||
width: $Audio-rate-width;
|
||||
height: $Audio-rate-height;
|
||||
text-align: center;
|
||||
background: $Audio-rate-bg;
|
||||
cursor: pointer;
|
||||
margin-right: $Audio-item-margin;
|
||||
}
|
||||
&-rateControl {
|
||||
display: inline-block;
|
||||
margin-right: $Audio-item-margin;
|
||||
&::after {
|
||||
display: inline-block;
|
||||
clear: both;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.#{$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;
|
||||
}
|
||||
.#{$ns}Audio-rateControlItem {
|
||||
display: inline-block;
|
||||
width: $Audio-rate-width;
|
||||
height: $Audio-rate-height;
|
||||
background: $Audio-rate-bg;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
float: left;
|
||||
box-sizing: border-box;
|
||||
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;
|
||||
margin-right: $Audio-item-margin;
|
||||
|
||||
svg {
|
||||
@include svg();
|
||||
|
@ -96,13 +93,14 @@
|
|||
&-times {
|
||||
display: inline-block;
|
||||
width: $Audio-times-width;
|
||||
margin: $Audio-times-margin;
|
||||
margin-right: $Audio-item-margin;
|
||||
cursor: default;
|
||||
}
|
||||
&-process {
|
||||
display: inline-block;
|
||||
width: $Audio-process-width;
|
||||
cursor: pointer;
|
||||
margin-right: $Audio-item-margin;
|
||||
|
||||
input[type=range] {
|
||||
@include input-range();
|
||||
|
@ -112,43 +110,31 @@
|
|||
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;
|
||||
margin-right: $Audio-item-margin;
|
||||
|
||||
svg {
|
||||
@include svg();
|
||||
}
|
||||
}
|
||||
&-volumeControl {
|
||||
display: inline-block;
|
||||
width: $Audio-volumeControl-width;
|
||||
height: $Audio-volumeControl-height;
|
||||
line-height: $Audio-volumeControl-lineHeight;
|
||||
margin-right: $Audio-item-margin;
|
||||
input[type=range] {
|
||||
@include input-range();
|
||||
}
|
||||
|
||||
.#{$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 {
|
||||
margin-right: $Audio-item-margin;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.#{$ns}Audio-volumeControlIcon {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
svg {
|
||||
position: relative;
|
||||
top: $Audio-svg-top;
|
||||
@include svg();
|
||||
}
|
||||
svg {
|
||||
@include svg();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -69,6 +69,11 @@
|
|||
white-space: nowrap;
|
||||
align-items: flex-start;
|
||||
padding-top: $Form-label-paddingTop;
|
||||
|
||||
> .is-disabled {
|
||||
pointer-events: none;
|
||||
opacity: $Button-onDisabled-opacity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import upperFirst = require('lodash/upperFirst');
|
||||
import {Renderer, RendererProps} from '../factory';
|
||||
import {autobind} from '../utils/helper';
|
||||
import {volumeIcon, muteIcon, playIcon, pauseIcon} from '../components/icons';
|
||||
|
@ -10,6 +11,7 @@ export interface AudioProps extends RendererProps {
|
|||
autoPlay?: boolean;
|
||||
loop?: boolean;
|
||||
rates?: number[];
|
||||
controls?: string[];
|
||||
}
|
||||
|
||||
export interface AudioState {
|
||||
|
@ -33,14 +35,15 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
|||
|
||||
static defaultProps: Pick<
|
||||
AudioProps,
|
||||
'inline' | 'autoPlay' | 'playbackRate' | 'loop' | 'rates' | 'progressInterval'
|
||||
'inline' | 'autoPlay' | 'playbackRate' | 'loop' | 'rates' | 'progressInterval' | 'controls'
|
||||
> = {
|
||||
inline: true,
|
||||
autoPlay: false,
|
||||
playbackRate: 1,
|
||||
loop: false,
|
||||
rates: [1.0, 2.0, 4.0],
|
||||
rates: [],
|
||||
progressInterval: 1000,
|
||||
controls: ['rates', 'play', 'time', 'process', 'volume']
|
||||
};
|
||||
|
||||
state: AudioState = {
|
||||
|
@ -59,6 +62,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
|||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.progressTimeout);
|
||||
clearTimeout(this.durationTimeout);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -256,9 +260,123 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
|||
});
|
||||
}
|
||||
|
||||
renderRates() {
|
||||
const {
|
||||
rates,
|
||||
classnames: cx
|
||||
} = this.props;
|
||||
const {
|
||||
showHandlePlaybackRate,
|
||||
playbackRate
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
rates && rates.length ?
|
||||
showHandlePlaybackRate ? (
|
||||
<div className={cx('Audio-rateControl')}>
|
||||
{rates.map((rate, index) =>
|
||||
<div
|
||||
key={index}
|
||||
className={cx('Audio-rateControlItem')}
|
||||
onClick={() => this.handlePlaybackRate(rate)}>
|
||||
x{rate.toFixed(1)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={cx('Audio-rates')}
|
||||
onClick={this.toggleHandlePlaybackRate}>
|
||||
x{playbackRate.toFixed(1)}
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
)
|
||||
}
|
||||
|
||||
renderPlay() {
|
||||
const {classnames: cx} = this.props;
|
||||
const {playing} = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx('Audio-play')}
|
||||
onClick={this.handlePlaying}>
|
||||
{playing ? pauseIcon : playIcon}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderTime() {
|
||||
const {classnames: cx} = this.props;
|
||||
|
||||
return (
|
||||
<div className={cx('Audio-times')}>
|
||||
{this.getCurrentTime()} / {this.getDuration()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderProcess() {
|
||||
const {classnames: cx} = this.props;
|
||||
const {played} = this.state;
|
||||
|
||||
return (
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
||||
renderVolume() {
|
||||
const {classnames: cx} = this.props;
|
||||
const {
|
||||
volume,
|
||||
showHandleVolume
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
showHandleVolume ? (
|
||||
<div
|
||||
className={cx('Audio-volumeControl')}
|
||||
onMouseLeave={() => this.toggleHandleVolume(false)}>
|
||||
<div
|
||||
className={cx('Audio-volumeControlIcon')}
|
||||
onClick={this.handleMute}>
|
||||
{volume > 0 ? volumeIcon : muteIcon}
|
||||
</div>
|
||||
<input
|
||||
type='range' min={0} max={1} step='any'
|
||||
value={volume}
|
||||
onChange={this.setVolume} />
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={cx('Audio-volume')}
|
||||
onMouseEnter={() => this.toggleHandleVolume(true)}>
|
||||
{volume > 0 ? volumeIcon : muteIcon}
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {className, inline, src, autoPlay, loop, rates, classnames: cx} = this.props;
|
||||
const {playing, played, volume, muted, playbackRate, showHandlePlaybackRate, showHandleVolume} = this.state;
|
||||
const {
|
||||
className,
|
||||
inline,
|
||||
src,
|
||||
autoPlay,
|
||||
loop,
|
||||
controls,
|
||||
classnames: cx
|
||||
} = this.props;
|
||||
const {muted} = this.state;
|
||||
|
||||
return (
|
||||
<div className={cx(inline ? 'Audio--inline' : '')}>
|
||||
|
@ -269,76 +387,19 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
|||
autoPlay={autoPlay}
|
||||
controls
|
||||
muted={muted}
|
||||
loop={loop}
|
||||
>
|
||||
loop={loop}>
|
||||
<source src={src} />
|
||||
</audio>
|
||||
<div className={cx('Audio', className)}>
|
||||
{rates && rates.length ? (
|
||||
<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>
|
||||
) : (
|
||||
<div className={cx('Audio-rates-holder')} />
|
||||
)}
|
||||
<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>
|
||||
{controls && controls.map((control:string, index:number) => {
|
||||
control = 'render' + upperFirst(control);
|
||||
const method:'renderRates'|'renderPlay'|'renderTime'|'renderProcess'|'renderVolume'|'render' = control as any;
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
{this[method]()}
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue