import React from 'react'; import PropTypes from 'prop-types';
import equals from 'mout/src/array/equals';
import style from 'PVWStyle/ReactWidgets/EqualizerWidget.mcss';
import MouseHandler from '../../../Interaction/Core/MouseHandler'; import SizeHelper from '../../../Common/Misc/SizeHelper';
export default class EqualizerWidget extends React.Component { constructor(props) { super(props); this.state = { layers: props.layers, width: props.width, height: props.height, };
this.updateDimensions = this.updateDimensions.bind(this); this.draw = this.draw.bind(this); this.clicked = this.clicked.bind(this); }
componentDidMount() { this.sizeSubscription = SizeHelper.onSizeChangeForElement( this.rootContainer.parentNode, this.updateDimensions );
SizeHelper.startListening(); this.updateDimensions(); this.draw(); this.mouseHandler = new MouseHandler(this.canvas); this.mouseHandler.attach({ click: this.clicked, drag: this.clicked, }); }
componentWillReceiveProps(nextProps) { const layers = nextProps.layers;
if (!equals(this.state.layers, layers)) { this.setState({ layers }); } }
componentDidUpdate(prevProps, prevState) { this.draw(); }
componentWillUnmount() { this.mouseHandler.destroy(); if (this.sizeSubscription) { this.sizeSubscription.unsubscribe(); this.sizeSubscription = null; } }
updateDimensions() { const el = this.rootContainer.parentNode; const innerWidth = SizeHelper.getSize(el, true).clientWidth;
if (el && innerWidth && this.state.width !== innerWidth) { this.setState({ width: innerWidth }); return true; } return false; }
draw() { const ctx = this.canvas.getContext('2d'); ctx.strokeStyle = this.props.stroke; ctx.lineWidth = '1';
const array = this.state.layers; const width = this.state.width; const height = this.state.height; const size = array.length; const spacing = this.props.spacing; const layerWidth = Math.floor((width - 5 * spacing) / size - spacing); const maxLayerHeight = height - 4 * spacing; const layerStep = layerWidth + (width - layerWidth * array.length - 2 * spacing) / (array.length + 1);
ctx.clearRect(0, 0, this.state.width, this.state.height);
ctx.beginPath(); ctx.rect(spacing, spacing, width - 2 * spacing, height - 2 * spacing); ctx.stroke();
for (let i = 0; i < size; i++) { const layerHeight = array[i] * maxLayerHeight;
ctx.fillStyle = this.props.colors[i % this.props.colors.length]; ctx.fillRect( layerStep * i + 2 * spacing, height - layerHeight - 2 * spacing, layerWidth, layerHeight );
ctx.beginPath(); ctx.rect( layerStep * i + 2 * spacing, height - layerHeight - 2 * spacing, layerWidth, layerHeight ); ctx.stroke(); }
}
clicked(e) { const rect = this.canvas.getClientRects()[0]; const x = e.pointers[0].clientX - rect.left - 2 * this.props.spacing; const y = e.pointers[0].clientY - rect.top - 2 * this.props.spacing; const effectiveHeight = rect.height - 4 * this.props.spacing; const idx = Math.min( this.state.layers.length - 1, Math.floor( (x / (rect.width - 4 * this.props.spacing)) * this.state.layers.length ) ); let opacity = 1.0 - y / effectiveHeight; const layers = [].concat(this.state.layers);
opacity = opacity > 1.0 ? 1.0 : opacity; opacity = opacity < 0.0 ? 0.0 : opacity; layers[idx] = opacity;
this.setState({ layers }); if (this.props.onChange) { this.props.onChange(layers); } this.draw(); }
render() { return ( <div className={style.container} ref={(c) => { this.rootContainer = c; }} > <canvas className={style.canvas} ref={(c) => { this.canvas = c; }} width={this.state.width} height={this.state.height} /> </div> ); } }
EqualizerWidget.propTypes = { colors: PropTypes.array, height: PropTypes.number, layers: PropTypes.array, onChange: PropTypes.func, spacing: PropTypes.number, stroke: PropTypes.string, width: PropTypes.number, };
EqualizerWidget.defaultProps = { layers: [1, 1, 1, 1, 1, 1, 1], colors: ['#0000ff', '#ffffff', '#ff0000'], stroke: '#000000', height: 120, width: 300, spacing: 2, onChange: undefined, };
|