add support for vertical slider

This commit is contained in:
NikolaBorislavovHristov 2018-11-15 13:04:38 +02:00
parent 3838ad022c
commit 2335e6d7a0
4 changed files with 142 additions and 33 deletions

View file

@ -4,6 +4,12 @@ import classnames from 'classnames';
import styles from './styles';
class Slider extends Component {
componentWillReceiveProps(nextProps) {
if (this.props.orientation !== nextProps.orientation) {
console.warn('changing orientation property at runtime is not supported');
}
}
shouldComponentUpdate(nextProps, nextState) {
return nextProps.value !== this.props.value ||
nextProps.minValue !== this.props.minValue ||
@ -30,14 +36,17 @@ class Slider extends Component {
}
}
calculateSlidingValue = (mouseX, sliderElement) => {
const { x: sliderX, width: sliderWidth } = sliderElement.getBoundingClientRect();
const thumbX = Math.min(Math.max(mouseX - sliderX, 0), sliderWidth);
const slidingValue = (thumbX / sliderWidth) * (this.props.maxValue - this.props.minValue) + this.props.minValue;
calculateSlidingValue = ({ mouseX, mouseY, sliderElement }) => {
const { x: sliderX, y: sliderY, width: sliderWidth, height: sliderHeight } = sliderElement.getBoundingClientRect();
const sliderStart = this.props.orientation === 'horizontal' ? sliderX : sliderY;
const sliderLength = this.props.orientation === 'horizontal' ? sliderWidth : sliderHeight;
const mouseStart = this.props.orientation === 'horizontal' ? mouseX : mouseY;
const thumbStart = Math.min(Math.max(mouseStart - sliderStart, 0), sliderLength);
const slidingValue = (thumbStart / sliderLength) * (this.props.maxValue - this.props.minValue) + this.props.minValue;
return Math.floor(slidingValue);
}
onStartSliding = ({ currentTarget: sliderElement, clientX: mouseX, button }) => {
onStartSliding = ({ currentTarget: sliderElement, clientX: mouseX, clientY: mouseY, button }) => {
if (button !== 0) {
return;
}
@ -54,12 +63,14 @@ class Slider extends Component {
releaseThumb();
this.onSlidingAborted();
};
const onMouseUp = ({ clientX: mouseX }) => {
const onMouseUp = ({ clientX: mouseX, clientY: mouseY }) => {
releaseThumb();
this.onSlidingCompleted(this.calculateSlidingValue(mouseX, sliderElement));
const slidingValue = this.calculateSlidingValue({ mouseX, mouseY, sliderElement });
this.onSlidingCompleted(slidingValue);
};
const onMouseMove = ({ clientX: mouseX }) => {
this.onSliding(this.calculateSlidingValue(mouseX, sliderElement));
const onMouseMove = ({ clientX: mouseX, clientY: mouseY }) => {
const slidingValue = this.calculateSlidingValue({ mouseX, mouseY, sliderElement });
this.onSliding(slidingValue);
};
window.addEventListener('blur', onBlur);
@ -68,17 +79,18 @@ class Slider extends Component {
document.body.style['pointer-events'] = 'none';
document.documentElement.style.cursor = 'pointer';
sliderElement.classList.add(styles['active']);
onMouseMove({ clientX: mouseX });
onMouseMove({ clientX: mouseX, clientY: mouseY });
}
render() {
const thumbLeft = (this.props.value - this.props.minValue) / (this.props.maxValue - this.props.minValue);
const thumbStartProp = this.props.orientation === 'horizontal' ? 'left' : 'top';
const thumbStart = (this.props.value - this.props.minValue) / (this.props.maxValue - this.props.minValue);
return (
<div className={classnames(styles['slider-container'], this.props.containerClassName)} onMouseDown={this.onStartSliding}>
<div className={classnames(styles['slider-container'], styles[this.props.orientation], this.props.containerClassName)} onMouseDown={this.onStartSliding}>
<div className={styles['line']} />
<div
className={classnames(styles['thumb'], this.props.thumbClassName)}
style={{ left: `calc(100% * ${thumbLeft})` }}
style={{ [thumbStartProp]: `calc(100% * ${thumbStart})` }}
/>
</div>
);
@ -91,6 +103,7 @@ Slider.propTypes = {
value: PropTypes.number.isRequired,
minValue: PropTypes.number.isRequired,
maxValue: PropTypes.number.isRequired,
orientation: PropTypes.oneOf(['horizontal', 'vertical']).isRequired,
onSliding: PropTypes.func,
onSlidingCompleted: PropTypes.func,
onSlidingAborted: PropTypes.func
@ -98,7 +111,8 @@ Slider.propTypes = {
Slider.defaultProps = {
value: 0,
minValue: 0,
maxValue: 100
maxValue: 100,
orientation: 'horizontal'
};
export default Slider;

View file

@ -6,10 +6,6 @@
.line {
position: absolute;
left: 0;
right: 0;
top: 40%;
bottom: 40%;
z-index: 1;
background-color: @colorprim;
}
@ -19,8 +15,6 @@
z-index: 2;
border-radius: 50%;
background-color: @colormedium;
transform: translateX(-50%);
transition-property: left;
transition-duration: 0.06s;
&:hover {
@ -28,6 +22,34 @@
}
}
&.horizontal {
.line {
left: 0;
right: 0;
top: 40%;
bottom: 40%;
}
.thumb {
transform: translateX(-50%);
transition-property: left;
}
}
&.vertical {
.line {
left: 40%;
right: 40%;
top: 0;
bottom: 0;
}
.thumb {
transform: translateY(-50%);
transition-property: top;
}
}
&.active {
.thumb {
background-color: @colorprimlight;

View file

@ -59,18 +59,19 @@ class ControlBar extends Component {
return `${('0' + hours).slice(-2)}:${('0' + minutes).slice(-2)}:${('0' + seconds).slice(-2)}`;
}
renderSeekBar() {
renderSeekSlider() {
if (this.props.time === null || this.props.duration === null) {
return null;
}
return (
<Slider
containerClassName={styles['seek-bar']}
thumbClassName={styles['thumb']}
containerClassName={styles['seek-slider']}
thumbClassName={styles['seek-thumb']}
value={this.state.seekTime !== -1 ? this.state.seekTime : this.props.time}
maxValue={this.props.duration}
minValue={0}
orientation={'horizontal'}
onSliding={this.onSliding}
onSlidingAborted={this.onSlidingAborted}
onSlidingCompleted={this.onSlidingCompleted}
@ -108,14 +109,49 @@ class ControlBar extends Component {
);
}
renderVolumeButton() {
if (this.props.volume === null) {
return null;
}
const volumeIcon = this.props.volume === 0 ? 'ic_volume0' :
this.props.volume < 50 ? 'ic_volume1' :
this.props.volume < 100 ? 'ic_volume2' :
'ic_volume3';
return (
<div className={classnames(styles['button'], styles['volume-button'])}>
<Icon
className={styles['icon']}
icon={volumeIcon}
/>
<div className={styles['volume-slider-container']}>
<Slider
containerClassName={styles['volume-slider']}
thumbClassName={styles['volume-thumb']}
value={50}
maxValue={110}
minValue={0}
orientation={'vertical'}
// onSliding={this.onSliding}
// onSlidingAborted={this.onSlidingAborted}
// onSlidingCompleted={this.onSlidingCompleted}
/>
</div>
</div>
);
}
render() {
return (
<div className={classnames(styles['control-bar-container'], this.props.className)}>
{this.renderSeekBar()}
{this.renderSeekSlider()}
<div className={styles['buttons-bar-container']}>
{this.renderPlayPauseButton()}
<div className={styles['separator']} />
{this.renderTimeLabel()}
<div className={styles['spacing']} />
{this.renderVolumeButton()}
</div>
</div>
);

View file

@ -1,8 +1,8 @@
@import 'stremio-colors';
@control-bar-height: 84px;
@seek-bar-height: 26px;
@buttons-bar-height: (@control-bar-height - @seek-bar-height);
@seek-slider-height: 26px;
@buttons-bar-height: (@control-bar-height - @seek-slider-height);
.control-bar-container {
height: @control-bar-height;
@ -13,13 +13,14 @@
padding-left: 2%;
padding-right: 2%;
.seek-bar {
.seek-slider {
width: 100%;
height: @seek-bar-height;
height: @seek-slider-height;
z-index: 1;
.thumb {
width: @seek-bar-height;
height: @seek-bar-height;
.seek-thumb {
width: @seek-slider-height;
height: @seek-slider-height;
}
}
@ -29,6 +30,7 @@
display: flex;
flex-direction: row;
align-items: center;
z-index: 2;
.button {
height: @buttons-bar-height;
@ -39,8 +41,8 @@
cursor: pointer;
.icon {
width: 64%;
height: 64%;
width: 60%;
height: 60%;
fill: @colorwhite80;
}
@ -49,6 +51,37 @@
fill: @colorwhite;
}
}
&.volume-button {
position: relative;
.volume-slider-container {
display: none;
justify-content: center;
width: 100%;
height: 300%;
position: absolute;
bottom: 100%;
.volume-slider {
width: round((@buttons-bar-height * 0.4));
height: 100%;
.volume-thumb {
width: round((@buttons-bar-height * 0.4));
height: round((@buttons-bar-height * 0.4));
}
}
}
&:hover {
.volume-slider-container {
display: flex;
}
}
}
}
.separator {
@ -63,5 +96,9 @@
color: @colorwhite;
font-size: 16px;
}
.spacing {
flex: 1
}
}
}