cabana/src/components/RouteSeeker/RouteSeeker.js

261 lines
7.2 KiB
JavaScript
Raw Normal View History

2017-12-12 19:27:20 -07:00
import React, { Component } from "react";
import { connect } from "react-redux";
import Obstruction from "obstruction";
2017-12-12 19:27:20 -07:00
import PropTypes from "prop-types";
import PlayButton from "../PlayButton";
import debounce from "../../utils/debounce";
2017-06-13 18:40:05 -06:00
import { autoSeek, seek } from "../../actions";
class RouteSeeker extends Component {
2017-12-12 19:27:20 -07:00
static propTypes = {
secondsLoaded: PropTypes.number.isRequired,
segmentIndices: PropTypes.arrayOf(PropTypes.number),
video: PropTypes.node,
onPause: PropTypes.func,
onPlay: PropTypes.func,
playing: PropTypes.bool,
segmentProgress: PropTypes.func,
ratioTime: PropTypes.func,
nearestFrameTime: PropTypes.number
};
static hiddenMarkerStyle = { display: "none", left: 0 };
static zeroSeekedBarStyle = { width: 0 };
static hiddenTooltipStyle = { display: "none", left: 0 };
static markerWidth = 20;
static tooltipWidth = 50;
constructor(props) {
super(props);
this.state = {
seekedBarStyle: RouteSeeker.zeroSeekedBarStyle,
markerStyle: RouteSeeker.hiddenMarkerStyle,
tooltipStyle: RouteSeeker.hiddenTooltipStyle,
ratio: 0,
tooltipTime: "0:00",
isPlaying: false,
isDragging: false
2017-06-13 18:40:05 -06:00
};
2017-12-12 19:27:20 -07:00
this.onMouseMove = this.onMouseMove.bind(this);
this.onMouseLeave = this.onMouseLeave.bind(this);
this.onMouseDown = this.onMouseDown.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.onClick = this.onClick.bind(this);
this.onPlay = this.onPlay.bind(this);
this.onPause = this.onPause.bind(this);
this.executePlayTimer = this.executePlayTimer.bind(this);
}
componentWillReceiveProps(nextProps) {
const { ratio } = this.state;
if (
JSON.stringify(this.props.segmentIndices) !==
JSON.stringify(nextProps.segmentIndices)
) {
this.setState({
seekedBarStyle: RouteSeeker.zeroSeekedBarStyle,
markerStyle: RouteSeeker.hiddenMarkerStyle,
ratio: 0
});
} else if (nextProps.secondsLoaded !== this.props.secondsLoaded) {
// adjust ratio in line with new secondsLoaded
const secondsSeeked = ratio * this.props.secondsLoaded;
const newRatio = secondsSeeked / nextProps.secondsLoaded;
this.updateSeekedBar(newRatio);
2017-06-13 18:40:05 -06:00
}
2017-12-12 19:27:20 -07:00
if (this.props.nearestFrameTime !== nextProps.nearestFrameTime) {
const newRatio = this.props.segmentProgress(nextProps.nearestFrameTime);
this.updateSeekedBar(newRatio);
2017-06-13 18:40:05 -06:00
}
2017-12-12 19:27:20 -07:00
if (nextProps.playing && !this.state.isPlaying) {
this.onPlay();
} else if (!nextProps.playing && this.state.isPlaying) {
this.onPause();
2017-06-13 18:40:05 -06:00
}
2017-12-12 19:27:20 -07:00
}
2017-06-13 18:40:05 -06:00
2017-12-12 19:27:20 -07:00
componentWillUnmount() {
window.cancelAnimationFrame(this.playTimer);
}
2017-06-13 18:40:05 -06:00
2017-12-12 19:27:20 -07:00
mouseEventXOffsetPercent(e) {
const rect = this.progressBar.getBoundingClientRect();
const x = e.clientX - rect.left;
2017-06-13 18:40:05 -06:00
2017-12-12 19:27:20 -07:00
return 100 * (x / this.progressBar.offsetWidth);
}
2017-06-13 18:40:05 -06:00
updateDraggingSeek = debounce(ratio => this.props.dispatch(seek(ratio)), 250);
2017-06-13 18:40:05 -06:00
2017-12-12 19:27:20 -07:00
onMouseMove(e) {
const markerOffsetPct = this.mouseEventXOffsetPercent(e);
if (markerOffsetPct < 0) {
this.onMouseLeave();
return;
2017-06-13 18:40:05 -06:00
}
2017-12-12 19:27:20 -07:00
const markerWidth = RouteSeeker.markerWidth;
2017-06-13 18:40:05 -06:00
2017-12-12 19:27:20 -07:00
const markerLeft = `calc(${markerOffsetPct + "%"} - ${markerWidth / 2}px)`;
const markerStyle = {
display: "",
left: markerLeft
};
const tooltipWidth = RouteSeeker.tooltipWidth;
const tooltipLeft = `calc(${markerOffsetPct + "%"} - ${tooltipWidth /
2}px)`;
const tooltipStyle = { display: "flex", left: tooltipLeft };
const ratio = Math.max(0, markerOffsetPct / 100);
if (this.state.isDragging) {
this.updateSeekedBar(ratio);
// this.updateDraggingSeek(ratio);
2017-06-13 18:40:05 -06:00
}
2017-12-12 19:27:20 -07:00
this.setState({
markerStyle,
tooltipStyle,
tooltipTime: this.props.ratioTime(ratio).toFixed(3)
});
}
onMouseLeave(e) {
this.setState({
markerStyle: RouteSeeker.hiddenMarkerStyle,
tooltipStyle: RouteSeeker.hiddenTooltipStyle,
isDragging: false
});
}
updateSeekedBar(ratio) {
const seekedBarStyle = { width: 100 * ratio + "%" };
this.setState({ seekedBarStyle, ratio });
}
onClick(e) {
let ratio = this.mouseEventXOffsetPercent(e) / 100;
ratio = Math.min(1, Math.max(0, ratio));
this.updateSeekedBar(ratio);
this.seek(this.props.ratioTime(ratio));
}
seek(time) {
this.isSeeking = true;
this.props.dispatch(seek(time));
const { videoElement } = this.props;
if (videoElement) {
videoElement.currentTime = time;
}
2017-12-12 19:27:20 -07:00
}
onPlay() {
this.playTimer = window.requestAnimationFrame(this.executePlayTimer);
let { ratio } = this.state;
if (ratio >= 1) {
ratio = 0;
}
this.setState({ isPlaying: true, ratio });
this.props.onPlay();
}
executePlayTimer() {
const { videoElement } = this.props;
if (this.isSeeking || !videoElement) {
this.isSeeking = false;
2017-12-12 19:27:20 -07:00
this.playTimer = window.requestAnimationFrame(this.executePlayTimer);
return;
2017-06-13 18:40:05 -06:00
}
2017-12-12 19:27:20 -07:00
const { currentTime } = videoElement;
if (!currentTime) {
this.playTimer = window.requestAnimationFrame(this.executePlayTimer);
return;
}
2017-12-12 19:27:20 -07:00
let newRatio = this.props.segmentProgress(currentTime);
2017-08-03 15:41:52 -06:00
2017-12-12 19:27:20 -07:00
if (newRatio === this.state.ratio) {
this.playTimer = window.requestAnimationFrame(this.executePlayTimer);
return;
2017-08-03 15:41:52 -06:00
}
2017-12-12 19:27:20 -07:00
if (newRatio >= 1) {
newRatio = 0;
// whats this?
// this.props.dispatch(seek(newRatio));
2017-06-13 18:40:05 -06:00
}
2017-12-12 19:27:20 -07:00
if (newRatio >= 0) {
this.updateSeekedBar(newRatio);
this.props.dispatch(autoSeek(currentTime));
}
2017-12-12 19:27:20 -07:00
this.playTimer = window.requestAnimationFrame(this.executePlayTimer);
}
onPause() {
window.cancelAnimationFrame(this.playTimer);
this.setState({ isPlaying: false });
this.props.onPause();
}
onMouseDown() {
if (!this.state.isDragging) {
this.setState({ isDragging: true });
}
2017-12-12 19:27:20 -07:00
}
2017-12-12 19:27:20 -07:00
onMouseUp() {
if (this.state.isDragging) {
this.setState({ isDragging: false });
2017-06-13 18:40:05 -06:00
}
2017-12-12 19:27:20 -07:00
}
render() {
const { seekedBarStyle, markerStyle, tooltipStyle } = this.state;
return (
<div className="cabana-explorer-visuals-camera-seeker">
<PlayButton
className={"cabana-explorer-visuals-camera-seeker-playbutton"}
onPlay={this.onPlay}
onPause={this.onPause}
isPlaying={this.state.isPlaying}
/>
<div
className={"cabana-explorer-visuals-camera-seeker-progress"}
onMouseMove={this.onMouseMove}
onMouseLeave={this.onMouseLeave}
onMouseDown={this.onMouseDown}
onMouseUp={this.onMouseUp}
onClick={this.onClick}
ref={ref => (this.progressBar = ref)}
>
<div
className={"cabana-explorer-visuals-camera-seeker-progress-tooltip"}
style={tooltipStyle}
>
{this.state.tooltipTime}
</div>
<div
className={"cabana-explorer-visuals-camera-seeker-progress-marker"}
style={markerStyle}
/>
<div
className={"cabana-explorer-visuals-camera-seeker-progress-inner"}
style={seekedBarStyle}
/>
</div>
</div>
);
}
2017-06-13 18:40:05 -06:00
}
const stateToProps = Obstruction({
segmentIndices: "segment.segmentIndices"
});
export default connect(stateToProps)(RouteSeeker);