Skip to content

Commit 22ae9d9

Browse files
committed
Get Bars visualizer working with Base component and visualizer-micro
0 parents  commit 22ae9d9

29 files changed

+11115
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules/**

.storybook/config.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { configure } from '@storybook/react';
2+
3+
function loadStories() {
4+
require('../src/components/Audio/Audio.story');
5+
require('../src/components/Visualizer/Visualizer.story');
6+
}
7+
8+
configure(loadStories, module);

.storybook/constants.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const IS_STORYBOOK = process.env.NODE_ENV === 'storybook'

.storybook/decorators.js

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react';
2+
import store from "../src/store"
3+
import getProvider from '../src/components/Provider'
4+
5+
const Provider = getProvider(store)
6+
7+
export const dispatch = store.dispatch
8+
9+
export const withProvider = (stories) => {
10+
return <Provider>
11+
{stories()}
12+
</Provider>
13+
}

.storybook/webpack.config.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const webpack = require('webpack')
2+
3+
module.exports = (baseConfig, env, defaultConfig) => {
4+
defaultConfig.plugins.push(
5+
new webpack.DefinePlugin({
6+
__DEV__: true,
7+
})
8+
)
9+
10+
defaultConfig.devServer = {
11+
...defaultConfig.devServer,
12+
inline: true,
13+
hot: true,
14+
}
15+
16+
return defaultConfig
17+
}

package-lock.json

+10,262
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "css-visualizer",
3+
"version": "0.1.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1",
8+
"storybook": "NODE_ENV=storybook start-storybook -p 9001 -c .storybook"
9+
},
10+
"author": "",
11+
"license": "ISC",
12+
"devDependencies": {
13+
"@storybook/react": "^4.1.6",
14+
"react-hot-loader": "^4.6.3",
15+
"redux-devtools-extension": "^2.13.7",
16+
"reselect-tools": "0.0.7",
17+
"storybook-addon-redux-listener": "^0.1.7"
18+
},
19+
"dependencies": {
20+
"@babel/core": "^7.2.2",
21+
"babel-loader": "^8.0.5",
22+
"chroma-js": "^2.0.2",
23+
"gradient-color": "^2.0.1",
24+
"lodash": "^4.17.11",
25+
"react": "^16.7.0",
26+
"react-dom": "^16.7.0",
27+
"react-redux": "^6.0.0",
28+
"redux": "^4.0.1",
29+
"redux-saga": "^0.16.2",
30+
"visualizer-micro": "^0.1.0"
31+
}
32+
}

sample.mp3

6.85 MB
Binary file not shown.

src/components/Audio/Audio.actions.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const AUDIO__VISUALIZER_LOADED = 'AUDIO__VISUALIZER_LOADED'
2+
const AUDIO__AUDIO_MOUNTED = 'AUDIO__AUDIO_MOUNTED'
3+
4+
export const types = {
5+
AUDIO__VISUALIZER_LOADED,
6+
AUDIO__AUDIO_MOUNTED,
7+
}
8+
9+
const onAudioMounted = () => {
10+
return {
11+
type: types.AUDIO__AUDIO_MOUNTED,
12+
}
13+
}
14+
15+
const onVisualizerLoaded = () => {
16+
return {
17+
type: types.AUDIO__VISUALIZER_LOADED,
18+
}
19+
}
20+
21+
export const actions = {
22+
onAudioMounted,
23+
onVisualizerLoaded,
24+
}
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import React, { Component } from 'react'
2+
3+
class _Audio extends Component {
4+
5+
constructor(props) {
6+
super(props)
7+
this.audioEl = React.createRef();
8+
}
9+
10+
onAudioLoaded = () => {
11+
this.props.vm.load(this.audioEl.current, this.props.onVisualizerLoaded)
12+
}
13+
14+
onAudioMounted = () => {
15+
16+
const { current } = this.audioEl
17+
18+
if (current.readyState === 3 || current.readyState === 4) {
19+
this.onAudioLoaded()
20+
} else {
21+
current.addEventListener('canplay', this.onAudioLoaded)
22+
}
23+
24+
}
25+
26+
componentDidUpdate(prevProps, prevState) {
27+
if (this.props.audioMounted && !prevProps.audioMounted) {
28+
this.onAudioMounted()
29+
}
30+
31+
}
32+
33+
componentDidMount() {
34+
this.audioEl.current.crossOrigin = "anonymous"
35+
}
36+
37+
render() {
38+
39+
return <div>
40+
<button onClick={this.props.onAudioMounted}>Start</button>
41+
<audio ref={this.audioEl} controls src={'https://likethemammal.github.io/visualizer-micro/sample.mp3'}></audio>
42+
{this.props.children}
43+
</div>
44+
}
45+
}
46+
47+
export default _Audio

src/components/Audio/Audio.connect.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {
2+
connect
3+
} from 'react-redux'
4+
5+
import { actions } from './Audio.actions'
6+
7+
export default (Component) => connect((state) => {
8+
return {
9+
audioMounted: state.Audio.audioMounted,
10+
visualizerLoaded: state.Audio.visualizerLoaded,
11+
vm: state.Audio.vm
12+
}
13+
}, {
14+
...actions,
15+
})(Component)

src/components/Audio/Audio.reducer.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { types } from './Audio.actions'
2+
import VisualizerMicro from "visualizer-micro"
3+
4+
export const initialState = {
5+
visualizerLoaded: false,
6+
audioMounted: false,
7+
vm: new VisualizerMicro(),
8+
}
9+
10+
export const reducer = {
11+
[types.AUDIO__VISUALIZER_LOADED]: (state, action) => {
12+
return {
13+
...state,
14+
visualizerLoaded: true,
15+
}
16+
},
17+
[types.AUDIO__AUDIO_MOUNTED]: (state, action) => {
18+
return {
19+
...state,
20+
audioMounted: true,
21+
}
22+
},
23+
}

src/components/Audio/Audio.story.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react'
2+
import { storiesOf, addDecorator } from '@storybook/react'
3+
import { withProvider } from '../../../.storybook/decorators'
4+
5+
import _Audio from './'
6+
7+
storiesOf('Audio', module)
8+
.addDecorator(withProvider)
9+
.add('default', () => {
10+
return <_Audio />
11+
})
12+

src/components/Audio/index.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import React from 'react'
2+
import connect from './Audio.connect'
3+
import Component from './Audio.component'
4+
5+
export default connect(Component)
6+

src/components/Provider.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react'
2+
import { Provider } from 'react-redux'
3+
4+
import { hot } from 'react-hot-loader'
5+
6+
const ProviderWrapper = (store) => ({ children }) => (
7+
<Provider store={store}>
8+
{ children }
9+
</Provider>
10+
)
11+
12+
export default (store) => hot(module)(ProviderWrapper(store))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import React, { Component } from 'react'
2+
3+
import { lighterColor, changeColor, darkerColor, fadeToColor, } from '../../../units/utils/color'
4+
import { sampleArray, } from '../../../units/utils/visualizer'
5+
6+
import { prefix } from '../../../constants/app'
7+
8+
import Base from '../Base.component'
9+
10+
const _numOfBars = (viewportWidth, barWidth) => viewportWidth / barWidth
11+
12+
const BAR_WIDTH = 24
13+
14+
class Bars extends Base {
15+
16+
name = 'Bars'
17+
currentAmp = 0
18+
fps = 40
19+
colorsNeeded = 2
20+
21+
bars = []
22+
23+
setupElements = () => {
24+
25+
const numOfBars = _numOfBars(window.innerWidth, BAR_WIDTH)
26+
27+
var reflectionOverlay = document.createElement('div');
28+
29+
reflectionOverlay.id = "reflection-overlay";
30+
31+
this.visualizer.current.innerHTML = ''
32+
this.visualizer.current.appendChild(reflectionOverlay)
33+
34+
for (var i = 0; i < numOfBars; i++) {
35+
var bar = document.createElement('div'),
36+
barWrapper = document.createElement('div');
37+
38+
bar.className = 'bar';
39+
barWrapper.className = 'bar-wrapper';
40+
barWrapper.style.left = i*BAR_WIDTH + "px";
41+
42+
barWrapper.appendChild(bar);
43+
this.visualizer.current.appendChild(barWrapper);
44+
}
45+
46+
this.bars = document.getElementsByClassName('bar');
47+
}
48+
49+
setupColors = () => {
50+
const styleSheet = document.getElementById('visualizer-css');
51+
var stylesStr = '';
52+
var color = this.props.color1;
53+
54+
const numOfBars = _numOfBars(window.innerWidth, BAR_WIDTH)
55+
56+
for (var i = 0; i < numOfBars; i++) {
57+
58+
var startOfSelectorStr = '.bar-wrapper:nth-of-type(' + (i + 2) + ') .bar', // Its '+ 2' because reflectionOverlay is first-child
59+
beforeStr = startOfSelectorStr + ':before { background-color: ' + lighterColor(color, 0.1) + '; }',
60+
afterStr = startOfSelectorStr + ':after { background-color: ' + darkerColor(color, 0.1) + '; }',
61+
barStr = startOfSelectorStr + ' { background-color: ' + darkerColor(color, 0.2) + '; }';
62+
63+
stylesStr += beforeStr + afterStr + barStr;
64+
65+
color = fadeToColor(color, this.props.color2, i/numOfBars);
66+
}
67+
68+
styleSheet.innerHTML = stylesStr;
69+
}
70+
71+
onWaveform = (waveform) => {
72+
const numOfBars = _numOfBars(window.innerWidth, BAR_WIDTH)
73+
74+
var sampleAvgs = sampleArray(waveform, numOfBars, 1);
75+
var bars = this.bars;
76+
77+
78+
79+
for (var j = 0; j < numOfBars; j++) {
80+
var magnitude = (Math.floor(sampleAvgs[j]*1000)/1000);
81+
bars[j].parentNode.style[prefix.css + 'transform'] = ["scaleY(", magnitude, ") translate3d(0,0,0)"].join("");
82+
}
83+
}
84+
85+
onMount = () => {
86+
this.setupElements()
87+
this.setupColors()
88+
}
89+
90+
onResize = () => {
91+
this.setupElements()
92+
this.setupColors()
93+
}
94+
95+
96+
}
97+
98+
export default Bars
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#reflection-overlay {
2+
position: absolute;
3+
top: 0;
4+
left: 0;
5+
right: 0;
6+
bottom: 0;
7+
z-index: 10;
8+
opacity: 0.5;
9+
10+
background: transparent; /* Old browsers */
11+
background: -moz-linear-gradient(top, transparent 50%, #000000 50%, #000000 50%, transparent 100%); /* FF3.6+ */
12+
background: -webkit-gradient(linear, left top, left bottom, color-stop(50%,transparent), color-stop(50%,#000000), color-stop(50%,#000000), color-stop(100%,transparent)); /* Chrome,Safari4+ */
13+
background: -webkit-linear-gradient(top, transparent 50%,#000000 50%,#000000 50%,transparent 100%); /* Chrome10+,Safari5.1+ */
14+
background: -o-linear-gradient(top, transparent 50%,#000000 50%,#000000 50%,transparent 100%); /* Opera 11.10+ */
15+
background: -ms-linear-gradient(top, transparent 50%,#000000 50%,#000000 50%,transparent 100%); /* IE10+ */
16+
background: linear-gradient(to bottom, transparent 50%,#000000 50%,#000000 50%,transparent 100%); /* W3C */
17+
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='transparent', endColorstr='transparent',GradientType=0 ); /* IE6-9 */
18+
}
19+
20+
.bar-wrapper {
21+
height: 100%;
22+
position: absolute;
23+
24+
-webkit-transform: scaleY(0);
25+
-moz-transform: scaleY(0);
26+
transform: scaleY(0);
27+
28+
-webkit-transition: -webkit-transform 50ms linear;
29+
-moz-transition: -moz-transform 50ms linear;
30+
-o-transition: -o-transform 50ms linear;
31+
transition: transform 50ms linear;
32+
}
33+
34+
.bar {
35+
width: 12px;
36+
height: 100%;
37+
}
38+
39+
.bar::before,
40+
.bar::after {
41+
position: absolute;
42+
content: ' ';
43+
}
44+
45+
.bar::before {
46+
top: -3px;
47+
left: -6px;
48+
width: 6px;
49+
height: 100%;
50+
51+
-webkit-transform: skew(0,45deg);
52+
-moz-transform: skew(0,45deg);
53+
-o-transform: skew(0,45deg);
54+
transform: skew(0,45deg);
55+
}
56+
57+
.bar::after {
58+
top: -6px;
59+
left: -4px;
60+
width: 100%;
61+
height: 6px;
62+
63+
-webkit-transform: skew(45deg);
64+
-moz-transform: skew(45deg);
65+
-o-transform: skew(45deg);
66+
transform: skew(45deg);
67+
}
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import React from 'react'
2+
import connect from '../Visualizer.connect'
3+
import Component from './Bars.component'
4+
5+
export default connect(Component)
6+

0 commit comments

Comments
 (0)