summaryrefslogtreecommitdiff
path: root/animism-align/frontend/views/align/components/timeline
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2020-07-02 00:35:06 +0200
committerJules Laplace <julescarbon@gmail.com>2020-07-02 00:35:06 +0200
commit3e2c1d432d73823e66e19d0081b498ace467b231 (patch)
tree67a8b66eb8334b97e031f2c91da668591132ede3 /animism-align/frontend/views/align/components/timeline
parent250527589e003420a84f36c4191d2e574f1ad28c (diff)
display the form
Diffstat (limited to 'animism-align/frontend/views/align/components/timeline')
-rw-r--r--animism-align/frontend/views/align/components/timeline/cursor.component.js26
-rw-r--r--animism-align/frontend/views/align/components/timeline/playButton.component.js31
-rw-r--r--animism-align/frontend/views/align/components/timeline/playCursor.component.js43
-rw-r--r--animism-align/frontend/views/align/components/timeline/ticks.component.js89
-rw-r--r--animism-align/frontend/views/align/components/timeline/waveform.component.js100
5 files changed, 289 insertions, 0 deletions
diff --git a/animism-align/frontend/views/align/components/timeline/cursor.component.js b/animism-align/frontend/views/align/components/timeline/cursor.component.js
new file mode 100644
index 0000000..f621b37
--- /dev/null
+++ b/animism-align/frontend/views/align/components/timeline/cursor.component.js
@@ -0,0 +1,26 @@
+import React, { Component } from 'react'
+
+import { ZOOM_STEPS } from '../../constants'
+import { timestamp } from '../../../../util'
+
+const Cursor = ({ timeline, annotation }) => {
+ const { start_ts, zoom, cursor_ts, duration } = timeline
+ const ts = annotation.start_ts || cursor_ts
+ const secondsPerPixel = ZOOM_STEPS[zoom] * 0.1
+ const y = (ts - start_ts) / secondsPerPixel
+ return (
+ <div
+ className='cursor'
+ style={{
+ top: y,
+ }}
+ >
+ <div className='line' />
+ <div className='tickLabel'>
+ {timestamp(ts, 1)}
+ </div>
+ </div>
+ )
+}
+
+export default Cursor \ No newline at end of file
diff --git a/animism-align/frontend/views/align/components/timeline/playButton.component.js b/animism-align/frontend/views/align/components/timeline/playButton.component.js
new file mode 100644
index 0000000..486eaee
--- /dev/null
+++ b/animism-align/frontend/views/align/components/timeline/playButton.component.js
@@ -0,0 +1,31 @@
+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 alignActions from '../align.actions'
+
+import { ZOOM_STEPS } from '../../constants'
+import { clamp } from '../../../../util'
+
+const PlayButton = ({ audio }) => {
+ return (
+ <div
+ className={audio.playing ? 'playButton playing' : 'playButton paused'}
+ onClick={() => {
+ audio.playing ? actions.audio.pause() : actions.audio.play()
+ }}
+ />
+ )
+}
+
+const mapStateToProps = state => ({
+ audio: state.audio,
+})
+
+const mapDispatchToProps = dispatch => ({
+ // alignActions: bindActionCreators({ ...alignActions }, dispatch),
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(PlayButton)
diff --git a/animism-align/frontend/views/align/components/timeline/playCursor.component.js b/animism-align/frontend/views/align/components/timeline/playCursor.component.js
new file mode 100644
index 0000000..71e6a3a
--- /dev/null
+++ b/animism-align/frontend/views/align/components/timeline/playCursor.component.js
@@ -0,0 +1,43 @@
+import React, { Component } from 'react'
+// import { Link } from 'react-router-dom'
+import { bindActionCreators } from 'redux'
+import { connect } from 'react-redux'
+
+import { ZOOM_STEPS } from '../../constants'
+import { timestamp } from '../../../../util'
+
+const PlayCursor = ({ timeline, audio }) => {
+ const { start_ts, zoom, duration } = timeline
+ const { play_ts } = audio
+ const secondsPerPixel = ZOOM_STEPS[zoom] * 0.1
+ const y = (play_ts - start_ts) / secondsPerPixel
+ // console.log(play_ts, y)
+ return (
+ <div
+ className='cursor playCursor'
+ style={{
+ top: y,
+ }}
+ >
+ <div className='line' />
+ </div>
+ )
+}
+
+/*
+ <div className='tickLabel'>
+ {timestamp(cursor_ts, 1)}
+ </div>
+
+*/
+
+const mapStateToProps = state => ({
+ timeline: state.align.timeline,
+ audio: state.audio,
+})
+
+const mapDispatchToProps = dispatch => ({
+ // alignActions: bindActionCreators({ ...alignActions }, dispatch),
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(PlayCursor)
diff --git a/animism-align/frontend/views/align/components/timeline/ticks.component.js b/animism-align/frontend/views/align/components/timeline/ticks.component.js
new file mode 100644
index 0000000..72f9bd0
--- /dev/null
+++ b/animism-align/frontend/views/align/components/timeline/ticks.component.js
@@ -0,0 +1,89 @@
+import React, { Component } from 'react'
+
+import { ZOOM_STEPS, ZOOM_LABEL_STEPS, ZOOM_TICK_STEPS } from '../../constants'
+import { timestamp } from '../../../../util'
+
+export default class Ticks extends Component {
+ render() {
+ let { start_ts, zoom, duration } = this.props.timeline
+ const width = window.innerHeight
+
+ let secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 // 0.1 sec / step
+
+ let widthTimeDuration = width * secondsPerPixel // secs per pixel
+
+ let timeMin = start_ts
+ let timeMax = Math.min(start_ts + widthTimeDuration, duration)
+ let timeWidth = timeMax - timeMin
+
+ let pixelMin = timeMin / secondsPerPixel
+
+ let secondsPerLabel = ZOOM_LABEL_STEPS[zoom] // secs
+ let pixelsPerLabel = secondsPerLabel / secondsPerPixel
+ let secondsPerTick = ZOOM_TICK_STEPS[zoom]
+ let pixelsPerTick = secondsPerTick / secondsPerPixel
+
+ let startOffset = pixelsPerLabel - (pixelMin % pixelsPerLabel)
+ let startTiming = (pixelMin + startOffset) * secondsPerPixel
+
+ let labelCount = Math.ceil(width / pixelsPerLabel) + 1
+ let offset, timing, tickLabels = [], ticks = []
+ for (var i = -1; i < labelCount; i++) {
+ offset = i * pixelsPerLabel + startOffset
+ if (offset > width) continue
+ timing = i * secondsPerLabel + startTiming
+ if (timing > duration) {
+ break
+ }
+ tickLabels.push(
+ <div className='tickLabel' key={"tickLabel_" + i}
+ style={{
+ top: Math.floor(offset)
+ }}>
+ {timestamp(timing)}
+ </div>
+ )
+ }
+
+ let durationOffset = duration / secondsPerPixel - pixelMin
+ if (timing > duration) {
+ tickLabels.push(
+ <div className='tickLabel tickLabelTotal' key={"tickLabel_total"}
+ style={{
+ top: durationOffset
+ }}>
+ {timestamp(duration, 1)}
+ </div>
+ )
+ ticks.push(
+ <div className='tick' key={"tick_total"}
+ style={{
+ top: Math.floor(durationOffset),
+ }}
+ />
+ )
+ }
+ let tickCount = Math.ceil(width / pixelsPerTick) + 6
+ for (var i = 0; i < tickCount; i += 1) {
+ offset = i * pixelsPerTick + startOffset - pixelsPerLabel
+ if (offset > durationOffset) {
+ break
+ }
+ ticks.push(
+ <div className='tick' key={"tick_" + i}
+ style={{
+ top: Math.floor(offset),
+ }}
+ />
+ )
+ }
+ // console.log(ticks.length)
+
+ return (
+ <div className='ticks'>
+ {ticks}
+ {tickLabels}
+ </div>
+ )
+ }
+}
diff --git a/animism-align/frontend/views/align/components/timeline/waveform.component.js b/animism-align/frontend/views/align/components/timeline/waveform.component.js
new file mode 100644
index 0000000..128204a
--- /dev/null
+++ b/animism-align/frontend/views/align/components/timeline/waveform.component.js
@@ -0,0 +1,100 @@
+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 { WAVEFORM_SIZE, 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 = WAVEFORM_SIZE
+ canvas.height = window.innerHeight
+ }
+ draw() {
+ const canvas = this.canvasRef.current
+ const ctx = canvas.getContext('2d')
+ const h = window.innerHeight
+ this.clearCanvas(ctx, h)
+ this.drawCurve(ctx, h)
+ }
+ clearCanvas(ctx, h) {
+ const w = WAVEFORM_SIZE
+ 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, h) {
+ let { peaks, timeline } = this.props
+ let { start_ts, zoom, duration } = timeline
+
+ let secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 // 0.1 sec / step
+ let stepsPerPixel = ZOOM_STEPS[zoom] // 0.1 sec / step
+ let indexesPerPixel = stepsPerPixel * 2
+
+ let widthTimeDuration = h * secondsPerPixel // secs per pixel
+
+ let timeMin = start_ts
+ let timeMax = Math.min(start_ts + widthTimeDuration, duration)
+ let timeWidth = timeMax - timeMin
+
+ let stepMin = Math.floor(timeMin * 10 * 2)
+ let pixelWidth = Math.ceil(timeWidth / secondsPerPixel)
+
+ let i = 0
+ let step = stepMin
+ let waveformPeak = WAVEFORM_SIZE / 2
+ let origin = (1 - peaks[step]) * waveformPeak
+ let y
+ let peak
+ // console.log(0 * indexesPerPixel + stepMin, pixelWidth * indexesPerPixel + stepMin)
+ ctx.beginPath()
+ ctx.moveTo(origin, 0)
+ for (i = 0; i < pixelWidth; i++) {
+ step = i * indexesPerPixel + stepMin
+ peak = peaks[step]
+ y = (1 - peak) * waveformPeak
+ ctx.lineTo(y, i)
+ }
+ for (i = pixelWidth - 1; i > 0; i--) {
+ step = i * indexesPerPixel + stepMin
+ peak = peaks[step]
+ y = (1 + peak) * waveformPeak
+ ctx.lineTo(y, i)
+ }
+ ctx.lineTo(origin, 0)
+ ctx.fillStyle = 'rgba(255,255,255,0.8)'
+ ctx.fill()
+ }
+ render() {
+ return (
+ <canvas ref={this.canvasRef} onClick={this.props.onClick} />
+ )
+ }
+}
+
+const mapStateToProps = state => ({
+ timeline: state.align.timeline,
+ peaks: state.site.peaks,
+})
+
+const mapDispatchToProps = dispatch => ({
+ // uploadActions: bindActionCreators({ ...uploadActions }, dispatch),
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(Waveform)