diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2020-06-30 13:54:38 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2020-06-30 13:54:52 +0200 |
| commit | 7d166ddbbbb8a7db6da3052ab01bd9e44c6f94e5 (patch) | |
| tree | f1099035fa23a8c359e996cab6b11f6bf1e22fab /animism-align/frontend/views/align | |
| parent | 3132458de93217dbd2ebaee3faae046f30f818e1 (diff) | |
zoom and scroll the waveform
Diffstat (limited to 'animism-align/frontend/views/align')
5 files changed, 158 insertions, 73 deletions
diff --git a/animism-align/frontend/views/align/align.actions.js b/animism-align/frontend/views/align/align.actions.js index 10dec44..36e17b0 100644 --- a/animism-align/frontend/views/align/align.actions.js +++ b/animism-align/frontend/views/align/align.actions.js @@ -3,3 +3,15 @@ import { store, history } from '../../store' import { api, post, pad, preloadImage } from '../../util' import actions from '../../actions' import { session } from '../../session' + +import { ZOOM_STEPS } from './constants' + +export const setScrollPosition = start_ts => dispatch => ( + dispatch({ type: types.align.set_display_setting, key: 'start_ts', value: start_ts }) +) + +export const setZoom = zoom => dispatch => { + if (0 <= zoom && zoom < ZOOM_STEPS.length) { + dispatch({ type: types.align.set_display_setting, key: 'zoom', value: zoom }) + } +} diff --git a/animism-align/frontend/views/align/align.reducer.js b/animism-align/frontend/views/align/align.reducer.js index e9e98f3..c0dcfac 100644 --- a/animism-align/frontend/views/align/align.reducer.js +++ b/animism-align/frontend/views/align/align.reducer.js @@ -24,6 +24,16 @@ export default function alignReducer(state = initialState, action) { duration: action.data.length / 2, } } + + case types.align.set_display_setting: + return { + ...state, + timeline: { + ...state.timeline, + [action.key]: action.value, + } + } + default: return state } diff --git a/animism-align/frontend/views/align/components/ticks.component.js b/animism-align/frontend/views/align/components/ticks.component.js index 832144b..69866f0 100644 --- a/animism-align/frontend/views/align/components/ticks.component.js +++ b/animism-align/frontend/views/align/components/ticks.component.js @@ -12,13 +12,13 @@ export default class Ticks extends Component { let secondsPerPixel = ZOOM_STEPS[zoom] / 10 // 0.1 sec / step let pixelTimeDuration = 1 / secondsPerPixel // secs per pixel let widthTimeDuration = width / pixelTimeDuration // secs per pixel - console.log(secondsPerPixel, pixelTimeDuration) - console.log('width in seconds', widthTimeDuration) + // console.log(secondsPerPixel, pixelTimeDuration) + // console.log('width in seconds', widthTimeDuration) let secondsPerTick = ZOOM_LABEL_STEPS[zoom] // secs let pixelsPerLabel = secondsPerTick * pixelTimeDuration let pixelsPerTick = ZOOM_TICK_STEPS[zoom] - console.log('pixels per label', pixelsPerLabel) + // console.log('pixels per label', pixelsPerLabel) let subdivision = secondsPerTick while (pixelsPerLabel < 200) { @@ -27,13 +27,13 @@ export default class Ticks extends Component { subdivision *= 2 } - console.log('start ts', start_ts) + // console.log('start ts', start_ts) let pixelOffset = (start_ts / secondsPerPixel) let pixelRemainder = pixelOffset % pixelsPerLabel let startOffset = pixelsPerLabel - pixelRemainder let startTiming = (pixelOffset + startOffset) * secondsPerPixel - let labelCount = Math.ceil(width / pixelsPerLabel) + let labelCount = Math.ceil(width / pixelsPerLabel) + 2 let offset, timing, tickLabels = [], ticks = [] for (var i = -1; i < labelCount; i++) { offset = i * pixelsPerLabel + startOffset - 20 @@ -70,7 +70,7 @@ export default class Ticks extends Component { /> ) } - let tickCount = Math.ceil(width / pixelsPerTick) + 1 + let tickCount = Math.ceil(width / pixelsPerTick) + 6 for (var i = 0; i < tickCount; i += 1) { offset = i * pixelsPerTick + startOffset - pixelsPerLabel if (offset > durationOffset) { @@ -84,7 +84,7 @@ export default class Ticks extends Component { /> ) } - console.log(ticks.length) + // console.log(ticks.length) return ( <div className='ticks'> diff --git a/animism-align/frontend/views/align/components/timeline.component.js b/animism-align/frontend/views/align/components/timeline.component.js index 0a97c3a..5e07c0b 100644 --- a/animism-align/frontend/views/align/components/timeline.component.js +++ b/animism-align/frontend/views/align/components/timeline.component.js @@ -1,28 +1,25 @@ import React, { Component } from 'react' // import { Link } from 'react-router-dom' -// import { bindActionCreators } from 'redux' +import { bindActionCreators } from 'redux' import { connect } from 'react-redux' import actions from '../../../actions' -// import * as uploadActions from './upload.actions' +// import * as alignActions from '../align.actions' +import Waveform from './waveform.component' import Ticks from './ticks.component' -import { DEFAULT_HEIGHT, WAVEFORM_HEIGHT, ZOOM_STEPS, ZOOM_LABEL_STEPS, ZOOM_TICK_STEPS } from '../constants' +import { ZOOM_STEPS } from '../constants' +import { clamp } from '../../../util' class Timeline extends Component { constructor(props){ super(props) - this.canvasRef = React.createRef() this.handleKeydown = this.handleKeydown.bind(this) + this.handleWheel = this.handleWheel.bind(this) } componentDidMount() { this.bind() - this.resize() - this.draw() - } - componentDidUpdate() { - this.draw() } componentWillUnmount() { this.unbind() @@ -34,65 +31,24 @@ class Timeline extends Component { document.removeEventListener('keydown', this.handleKeydown) } handleKeydown(e) { - // console.log(e.shiftKey, e.keyCode) - } - resize() { - const canvas = this.canvasRef.current - canvas.width = window.innerWidth - canvas.height = DEFAULT_HEIGHT - } - draw() { - const canvas = this.canvasRef.current - const ctx = canvas.getContext('2d') - const w = window.innerWidth - this.clearCanvas(ctx, w) - this.drawCurve(ctx, w) - } - clearCanvas(ctx, w) { - const h = WAVEFORM_HEIGHT - ctx.clearRect(0, 0, w, h) - ctx.fillStyle = 'rgba(0,0,0,0.5)' - ctx.fillRect(0, 0, w, h) - ctx.fillStyle = 'rgba(64,128,192,0.5)' - } - drawCurve(ctx, w) { - let { peaks, timeline } = this.props - let { start_ts, zoom, duration } = timeline - let stepsPerPixel = ZOOM_STEPS[zoom] // 0.1 sec / step - let indexesPerPixel = stepsPerPixel * 2 - let stepMin = start_ts * 10 - let stepDuration = duration * 10 / stepsPerPixel - let stepMax = stepDuration - stepMin - let stepWidth = Math.min(stepMax, w) - stepWidth += 2 + (stepWidth % 2) - let i = 0 - let step = stepMin - let waveformPeak = WAVEFORM_HEIGHT / 2 - let origin = (1 - peaks[step]) * waveformPeak - let y = origin - let peak - ctx.beginPath() - ctx.moveTo(0, y) - for (i = 0; i < stepWidth; i++) { - step = i * indexesPerPixel + stepMin - peak = peaks[step] - y = (1 - peak) * waveformPeak - ctx.lineTo(i, y) - } - for (i = stepWidth - 1; i > 0; i--) { - step = i * indexesPerPixel + stepMin - peak = peaks[step] - y = (1 + peaks[step]) * waveformPeak - ctx.lineTo(i, y) + if (e.shiftKey && e.keyCode === 189) { + actions.align.setZoom(this.props.timeline.zoom - 1) + } else if (e.shiftKey && e.keyCode === 187) { + actions.align.setZoom(this.props.timeline.zoom + 1) } - ctx.lineTo(0, origin) - ctx.fillStyle = 'rgba(255,255,255,0.8)' - ctx.fill() + } + handleWheel(e) { + let { start_ts, zoom, duration } = this.props.timeline + let secondsPerPixel = ZOOM_STEPS[zoom] / 10 // 0.1 sec / step + let widthTimeDuration = window.innerWidth * secondsPerPixel // secs per pixel + + start_ts = clamp(start_ts + e.deltaY * ZOOM_STEPS[zoom], 0, duration - widthTimeDuration / 2) + actions.align.setScrollPosition(start_ts) } render() { return ( - <div className='timeline'> - <canvas ref={this.canvasRef} /> + <div className='timeline' onWheel={this.handleWheel}> + <Waveform /> <Ticks timeline={this.props.timeline} /> </div> ) @@ -101,11 +57,10 @@ class Timeline extends Component { const mapStateToProps = state => ({ timeline: state.align.timeline, - peaks: state.site.peaks, }) const mapDispatchToProps = dispatch => ({ - // uploadActions: bindActionCreators({ ...uploadActions }, dispatch), + // alignActions: bindActionCreators({ ...alignActions }, dispatch), }) export default connect(mapStateToProps, mapDispatchToProps)(Timeline) diff --git a/animism-align/frontend/views/align/components/waveform.component.js b/animism-align/frontend/views/align/components/waveform.component.js new file mode 100644 index 0000000..d4ff913 --- /dev/null +++ b/animism-align/frontend/views/align/components/waveform.component.js @@ -0,0 +1,108 @@ +import React, { Component } from 'react' +// import { Link } from 'react-router-dom' +// import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' + +import actions from '../../../actions' +// import * as uploadActions from './upload.actions' + +import { DEFAULT_HEIGHT, WAVEFORM_HEIGHT, ZOOM_STEPS, ZOOM_LABEL_STEPS, ZOOM_TICK_STEPS } from '../constants' + +class Waveform extends Component { + constructor(props){ + super(props) + this.canvasRef = React.createRef() + } + componentDidMount() { + this.resize() + this.draw() + } + componentDidUpdate() { + this.draw() + } + resize() { + const canvas = this.canvasRef.current + canvas.width = window.innerWidth + canvas.height = DEFAULT_HEIGHT + } + draw() { + const canvas = this.canvasRef.current + const ctx = canvas.getContext('2d') + const w = window.innerWidth + this.clearCanvas(ctx, w) + this.drawCurve(ctx, w) + } + clearCanvas(ctx, w) { + const h = WAVEFORM_HEIGHT + ctx.clearRect(0, 0, w, h) + ctx.fillStyle = 'rgba(0,0,0,0.5)' + ctx.fillRect(0, 0, w, h) + ctx.fillStyle = 'rgba(64,128,192,0.5)' + } + drawCurve(ctx, w) { + let { peaks, timeline } = this.props + let { start_ts, zoom, duration } = timeline + + // console.log(start_ts) + // start_ts *= 10 + + let secondsPerPixel = ZOOM_STEPS[zoom] / 10 // 0.1 sec / step + let stepsPerPixel = ZOOM_STEPS[zoom] // 0.1 sec / step + let indexesPerPixel = stepsPerPixel * 2 + + let widthTimeDuration = window.innerWidth * secondsPerPixel // secs per pixel + + let timeMin = start_ts + let timeMax = Math.min(start_ts + widthTimeDuration, duration) + + let pixelMin = timeMin / secondsPerPixel + let pixelMax = timeMax / secondsPerPixel + + // console.log('wf start_ts', pixelMin) + + let stepMin = Math.floor(pixelMin * 2) + let stepMax = Math.floor(pixelMax * 2) + let stepWidth = stepMax - stepMin + + let i = 0 + let step = stepMin + let waveformPeak = WAVEFORM_HEIGHT / 2 + let origin = (1 - peaks[step]) * waveformPeak + let y = origin + let peak + + ctx.beginPath() + ctx.moveTo(0, y) + for (i = 0; i < stepWidth; i++) { + step = i * indexesPerPixel + stepMin + peak = peaks[step] + y = (1 - peak) * waveformPeak + ctx.lineTo(i, y) + } + for (i = stepWidth - 1; i > 0; i--) { + step = i * indexesPerPixel + stepMin + peak = peaks[step] + y = (1 + peak) * waveformPeak + ctx.lineTo(i, y) + } + ctx.lineTo(0, origin) + ctx.fillStyle = 'rgba(255,255,255,0.8)' + ctx.fill() + } + render() { + return ( + <canvas ref={this.canvasRef} /> + ) + } +} + +const mapStateToProps = state => ({ + timeline: state.align.timeline, + peaks: state.site.peaks, +}) + +const mapDispatchToProps = dispatch => ({ + // uploadActions: bindActionCreators({ ...uploadActions }, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(Waveform) |
