import React from 'react';
import * as ReactDOM from 'react-dom';
import { withNamespaces } from 'react-i18next';
/**
* Show zones on canvas
* @extends Component
* @hideconstructor
*/
class ZoneCanvas extends React.Component {
/**
* @constructs
* @param props
*/
constructor(props) {
super(props);
this.state = {
activeUrl: this.props.loadUrl,
loading: true,
spinnerInterval: null,
loaded: false,
actualZone: []
};
this.actualizeScreenshot = this.actualizeScreenshot.bind(this);
this.onCanvasClick = this.onCanvasClick.bind(this);
this.clearActualZone = this.clearActualZone.bind(this);
}
/**
*
*/
actualizeScreenshot() {
if (this.state.loading === true) return;
let context = this.state.canvas.getContext("2d");
context.clearRect(0, 0, this.state.canvas.width, this.state.canvas.height);
this.setState({
loading: true,
loaded: false,
actualZone: [],
activeUrl: this.props.refreshUrl + "&date=" + new Date()
});
this.showSpinner();
}
/**
*
*/
componentDidMount() {
let canvas = ReactDOM.findDOMNode(this.refs.drawCanvas);
let image = ReactDOM.findDOMNode(this.refs.image);
let context = canvas.getContext("2d");
this.setState({
image: image,
canvas: canvas
});
if (this.state.loading === false) context.drawImage(image, 0, 0, canvas.width, canvas.height);
}
/**
*
* @param prevProps
* @param prevState
* @param snapshot
*/
componentDidUpdate(prevProps, prevState, snapshot) {
let canvas = this.state.canvas;
let image = this.state.image;
let context = canvas.getContext("2d");
if (this.state.loading === true || this.state.loaded === false) {
return;
}
context.drawImage(image, 0, 0, canvas.width, canvas.height);
context.font = "24px Arial";
context.textAlign = "center";
if (this.props.zones.length > 0) {
this.props.zones.map(zone => {
if (zone.cameraVertices === undefined || zone.cameraVertices.length === 0) return;
context.strokeStyle = 'rgb(255, 134, 20)';
context.fillStyle = 'rgba(255, 152, 56, 0.5)';
context.lineWidth = 3;
let start = this.displayToCanvas(this.imageToDisplay(zone.cameraVertices[0]));
let sumX = start.x;
let sumY = start.y;
context.beginPath();
context.moveTo(start.x, start.y);
for (let i = 1; i < zone.cameraVertices.length; ++i) {
let coord = this.displayToCanvas(this.imageToDisplay(zone.cameraVertices[i]));
sumX += coord.x;
sumY += coord.y;
context.lineTo(coord.x, coord.y);
}
context.closePath();
context.fill();
context.stroke();
context.fillStyle = 'rgba(255, 255, 255, 1)';
if (this.props.showZoneNames === undefined || this.props.showZoneNames === true) {
context.fillText(zone.zoneNumber.toString(),
sumX / zone.cameraVertices.length,
sumY / zone.cameraVertices.length);
}
});
}
let actualZoneVertices = this.state.actualZone;
context.strokeStyle = 'rgb(0, 255, 0)';
context.fillStyle = 'rgba(0, 255, 0, 0.5)';
if (actualZoneVertices.length === 1) {
let start = this.displayToCanvas(this.imageToDisplay(actualZoneVertices[0]));
context.beginPath();
context.arc(start.x, start.y, 2, 0, 2 * Math.PI);
context.fill();
} else if (actualZoneVertices.length > 1) {
let start = this.displayToCanvas(this.imageToDisplay(actualZoneVertices[0]));
context.beginPath();
context.moveTo(start.x, start.y);
for (let i = 1; i < actualZoneVertices.length; ++i) {
let coords = this.displayToCanvas(this.imageToDisplay(actualZoneVertices[i]));
context.lineTo(coords.x, coords.y);
}
context.closePath();
context.fill();
context.stroke();
}
}
onCanvasClick(event) {
if (this.props.interactive !== undefined && this.props.interactive === false) return;
let actualZoneVertices = this.state.actualZone.slice();
actualZoneVertices.push(
this.displayToImage({ x: event.nativeEvent.offsetX, y: event.nativeEvent.offsetY })
);
this.setState({
actualZone: actualZoneVertices
});
this.props.onActualZoneChange(actualZoneVertices);
}
/**
*
*/
onImageLoaded() {
window.clearInterval(this.state.spinnerInterval);
let canvas = ReactDOM.findDOMNode(this.refs.drawCanvas);
let image = ReactDOM.findDOMNode(this.refs.image);
this.setState({
loading: false,
loaded: true,
image: image,
canvas: canvas
});
}
/**
*
*/
onImageError() {
this.setState({
loading: false,
loaded: false
})
}
/**
*
*/
clearActualZone() {
this.setState({
actualZone: []
});
this.props.onActualZoneChange([]);
}
/**
*
* @returns {*}
*/
render() {
const { t } = this.props;
return (
<div>
<div className="box-header no-border">
<div className="pull-right spacing">
<button
type="submit"
className="btn btn-primary"
onClick={this.actualizeScreenshot}
disabled={this.state.loading === true}
>{t('forms.newScreenshot')}</button>
</div>
</div>
<div className="box-body">
<canvas
ref="drawCanvas"
id="drawCanvas"
onClick={event => this.onCanvasClick(event)}
height={600}
width={700}
style={{
position: 'relative',
}}
/>
<img
id="image"
ref="image"
src={this.state.activeUrl}
alt="monitored area view"
onLoad={() => this.onImageLoaded()}
onError={() => this.onImageError()}
className="hide"
style={{
position: 'relative',
}}
/>
</div>
<label className="control-label fa fa-info-circle info-icon pull-right"
data-toggle="tooltip"
title={t('forms.tooltip.canvas')}/>
{
(this.props.interactive === undefined || this.props.interactive === true) ?
<div className="box-footer no-border">
<button
type="submit"
className="btn btn-primary pull-left"
onClick={this.clearActualZone}
>{t('forms.erase')}</button>
</div>
: null
}
</div>
)
}
/**
* converts from displayed canvas coordinates to image coordinates
* @param coords
* @returns {{x: number, y: number}}
*/
displayToImage(coords) {
const widthRatio = this.state.image.width / this.state.canvas.clientWidth;
const heightRatio = this.state.image.height / this.state.canvas.clientHeight;
return { x: Math.round(coords.x * widthRatio), y: Math.round(coords.y * heightRatio) };
}
/**
* converts from image coordinates to displayed canvas coordinates
* @param coords
* @returns {{x: number, y: number}}
*/
imageToDisplay(coords) {
const widthRatio = this.state.image.width / this.state.canvas.clientWidth;
const heightRatio = this.state.image.height / this.state.canvas.clientHeight;
return { x: Math.round(coords.x / widthRatio), y: Math.round(coords.y / heightRatio) };
}
/**
* converts from displayed canvas coordinates to logical canvas coordinates
* @param coords
* @returns {{x: number, y: number}}
*/
displayToCanvas(coords) {
const widthRatio = this.state.canvas.width / this.state.canvas.clientWidth;
const heightRatio = this.state.canvas.height / this.state.canvas.clientHeight;
return { x: coords.x * widthRatio, y: coords.y * heightRatio };
}
/** code source: https://codepen.io/reneras/pen/HFrmC
* edited size of spinner */
showSpinner() {
const canvas = ReactDOM.findDOMNode(this.refs.drawCanvas);
const context = canvas.getContext('2d');
const start = new Date();
const lines = 16,
cW = context.canvas.width,
cH = context.canvas.height;
let draw = function () {
let rotation = parseInt(((new Date() - start) / 1000) * lines) / lines;
context.save();
context.clearRect(0, 0, cW, cH);
context.translate(cW / 2, cH / 2);
// context.translate(cW / 2, cH / 2);
context.rotate(Math.PI * 2 * rotation);
for (let i = 0; i < lines; i++) {
context.beginPath();
context.rotate(Math.PI * 2 / lines);
// context.moveTo(cW / 10, 0);
// context.lineTo(cW / 4, 0);
// context.lineWidth = cW / 30;
context.moveTo(cW / 30, 0);
context.lineTo(cW / 12, 0);
context.lineWidth = cW / 90;
context.strokeStyle = "rgba(0, 0, 0," + i / lines + ")";
context.stroke();
}
context.restore();
};
this.state.spinnerInterval = window.setInterval(draw, 1000 / 30);
}
}
export default withNamespaces()(ZoneCanvas);