import { h, Component } from 'preact' import { clamp, norm } from '../util/math' const initialState = { dir: '/', start: null, end: null, cursor: null, selection: null, width: 0, ratio: 0, loading: true } export default class Timeline extends Component { state = { ...initialState } constructor() { super() this.handleMouseDown = this.handleMouseDown.bind(this) this.handleMouseMove = this.handleMouseMove.bind(this) this.handleMouseUp = this.handleMouseUp.bind(this) this.computeOffset = this.computeOffset.bind(this) } componentDidMount() { const { sequence } = this.props window.addEventListener('resize', this.computeOffset) window.addEventListener('mousemove', this.handleMouseMove) window.addEventListener('mouseup', this.handleMouseUp) this.computeOffset() if (sequence) { this.reset() } } componentDidUpdate(prevProps) { const { sequence } = this.props if (sequence !== prevProps.sequence) { this.reset() } } componentWillUnmount(){ window.removeEventListener('resize', this.computeOffset) window.removeEventListener('mousemove', this.handleMouseMove) window.removeEventListener('mouseup', this.handleMouseUp) } reset(){ const { sequence } = this.props if (!sequence || !sequence.length) return const len = sequence.length - 1 const start = { frame: sequence[0], i: 0 } const end = { frame: sequence[len], i: len } const width = Math.sqrt(clamp(sequence.length / 90000, 0.3, 1.0)) * Math.min(window.innerWidth - 40, 600) const ratio = width / sequence.length setTimeout(() => this.computeOffset()) this.setState({ ...initialState, width, ratio, start, end, }) } computeOffset(){ if (this.ref) { this.offset = this.ref.getBoundingClientRect() } } computeFrame(e){ const { sequence } = this.props if (!sequence || !sequence.length) return null let x = (e.pageX - this.offset.left) / this.offset.width let y = (e.pageY - this.offset.top) / this.offset.height if (this.state.dragging) { x = clamp(x, 0, 1) y = clamp(y, 0, 1) } if (0 <= x && x <= 1 && 0 <= y && y <= 1) { const index = Math.floor(x * (sequence.length-1)) return { frame: sequence[index], i: index } } return null } handleMouseDown(e) { this.setState({ dragging: true }) const frame = this.computeFrame(e) if (frame) { this.props.onPick && this.props.onPick(frame) this.props.onSelect && this.props.onSelect({ start: frame, end: frame }) this.setState({ start: frame, end: frame, }) } } handleMouseMove(e) { const frame = this.computeFrame(e) if (frame) { this.props.onCursor && this.props.onCursor(frame) if (this.state.dragging) { let start = this.state.start let end = frame this.setState({ cursor: frame, end: frame, }) if (this.props.onSelect) { if (end.i < start.i) { [start, end] = [end, start] } this.props.onSelect({ start, end }) } } else { this.setState({ cursor: frame }) } } } handleMouseUp(e) { if (!this.state.dragging) return let { start, end } = this.state if (end.i < start.i) { [start, end] = [end, start] } this.props.onSelect && this.props.onSelect({ start, end }) this.setState({ dragging: false }) } render() { const { sequence } = this.props const { loading, start, end, cursor, width, ratio } = this.state return (