Merge pull request #21 from catchonme/master

audio 组件增加支持自定义配置控件
This commit is contained in:
liaoxuezhi 2019-05-08 17:38:25 +08:00 committed by GitHub
commit ea64280666
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 197 additions and 158 deletions

View File

@ -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"
{

View File

@ -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;

View File

@ -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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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>
);