How to use react to draw rectangles over objects in an image? - javascript

I'm using react and HTML5 canvas to draw rectangles over faces on an image, but react life-cycle is limiting me from doing this.
As I should use refs to reference to the canvas and this have to be done in componentDidMount() and I could not get props or states inside this function.
Here is the code:
import React, { Component } from 'react'
import { render } from 'react-dom'
import {Row} from 'react-bootstrap';
import '../App.css';
class TagImg extends Component {
constructor(props){
super(props);
this.state={
rects:[[110, 30, 70, 70], [40, 30, 70, 70]],
ctx : ''
};
this.tagPerson = this.tagPerson.bind(this);
}
tagPerson(e){
var x = e.offsetX,
y = e.offsetY;
for(var i=0;i<this.props.rects.length;i++) { // check whether:
if(x > this.props.rects[i][0] // mouse x between x and x + width
&& x < this.props.rects[i][0] + this.props.rects[i][2]
&& y > this.props.rects[i][1] // mouse y between y and y + height
&& y < this.props.rects[i][1] + this.props.rects[i][3]) {
alert('Rectangle ' + i + ' clicked');
}
}
}
componentDidMount() {
console.log("componentDidMount");
const ctx = this.refs.canvas.getContext('2d');
var myImage = new Image();
myImage.onload = function() {
var width = myImage.naturalWidth; // this will be 300
var height = myImage.naturalHeight; // this will be 400
ctx.drawImage(myImage, 0, 0, 300, 200);
ctx.beginPath();
ctx.strokeStyle="white";
// for(var i=0;i<this.state.rects.length;i++) {
// // ctx.rect(this.state.rects[i][0], // fill at (x, y) with (width, height)
// // this.state.rects[i][1],
// // this.state.rects[i][2],
// // this.state.rects[i][3]);
// console.log("helloo");
// }
console.log(this.state.rects);
ctx.stroke();
}
myImage.src = this.props.path;
}
render() {
return (
<div>
<form id="afterUpload" action="" method="post" encType="multipart/form-data">
<Row id="image_preview" className="row">
<canvas ref="canvas" onClick={this.tagPerson}/>
</Row>
</form>
</div>
);
}
}
export default TagImg;
After searching I figured out that I could use componentWillRecieveProps() but did not understand how to use it in my case.

componentDidMount will be called only once from React, after the component is mounted.
Since you want to draw something every time a user clicks on the canvas, you have to do that in a place that it's called at every rendering, like inside the render function itself.
Something like this:
import React, { Component } from 'react'
import { render } from 'react-dom'
import {Row} from 'react-bootstrap';
class TagImg extends Component {
constructor(props){
super(props);
this.state={
rects:[[110, 30, 70, 70], [40, 30, 70, 70]]
};
this.tagPerson = this.tagPerson.bind(this);
}
tagPerson(e){
var x = e.offsetX,
y = e.offsetY;
for(var i=0;i<this.props.rects.length;i++) { // check whether:
if(x > this.props.rects[i][0] // mouse x between x and x + width
&& x < this.props.rects[i][0] + this.props.rects[i][2]
&& y > this.props.rects[i][1] // mouse y between y and y + height
&& y < this.props.rects[i][1] + this.props.rects[i][3]) {
alert('Rectangle ' + i + ' clicked');
}
}
}
drawRects() {
const ctx = this.refs.canvas.getContext('2d');
ctx.drawImage(myImage, 0, 0, 300, 200);
ctx.beginPath();
ctx.strokeStyle="white";
// for(var i=0;i<this.state.rects.length;i++) {
// // ctx.rect(this.state.rects[i][0], // fill at (x, y) with (width, height)
// // this.state.rects[i][1],
// // this.state.rects[i][2],
// // this.state.rects[i][3]);
// console.log("helloo");
// }
console.log(this.state.rects);
ctx.stroke();
}
componentDidMount() {
console.log("componentDidMount");
var myImage = new Image();
myImage.onload = this.drawRects.bind(this);
myImage.src = this.props.path;
}
render() {
drawRects();
return (
<div>
<form id="afterUpload" action="" method="post" encType="multipart/form-data">
<Row id="image_preview" className="row">
<canvas ref="canvas" onClick={this.tagPerson}/>
</Row>
</form>
</div>
);
}
}
export default TagImg;
In the code I'm downloading the image once, in the componentDidMount method. And I'm running the drawRects() method at every rendering, so every time you call a setState you'll have your new rects

Related

How should you pass canvas context to methods in React?

First time working with canvas in react. I am wondering what the best way to provide methods with the canvas context is (like paint below). Should I use Refs? State? Or is there another way. Thanks
Here is what I have for using refs so far:
class Canvas extends React.Component{
canvasRef = React.createRef();
contextRef = React.createRef();
componentDidMount() {
const canvas = this.canvasRef.current;
const context = canvas.getContext("2d");
this.contextRef.current = context;
}
paint = (e) => {
this.contextRef.current.lineTo(e.clientX, e.clientY);
this.contextRef.current.stroke();
};
render() {
return ( <
canvas id = "canvas"
ref = {
this.canvasRef
}
onMouseMove = {
this.paint
}
/>
);
}
}
Refs are correct for getting the canvas, but I'd suggest getting the 2D context each time you need it (getContext just returns the same context as the previous time).
You have a few other minor issues with that code, here's an example putting the context in an instance property, and fixing various issues called out inline.
class Canvas extends React.Component { // *** Note: You need to extend `React.Component`
canvasRef = React.createRef(); // *** Note: You shouldn't have `this.` here
paint = (e) => {
// ** Get the canvas
const canvas = this.canvasRef.current;
if (!canvas) {
return; // This probably won't happen
}
// Get the context
const context = canvas.getContext("2d");
// Get the point to draw at, allowing for the possibility
// that the canvas isn't at the top of the doc
const { left, top } = canvas.getBoundingClientRect();
const x = e.clientX - left;
const y = e.clientY - top;
context.lineTo(x, y);
context.stroke();
}; // *** Added missing ; here
render() {
return <canvas ref={this.canvasRef} onMouseMove={this.paint} />;
}
}
(I removed the id on the canvas, since your component might get used in more than one place.)
In a comment you mentioned issues with state changes causing trouble (I saw that as well when trying to get and keep the context rather than getting it each time), so I've added state both to a parent container element and also to the Canvas element in the following example to check that it continues to work, which it does:
const { useState } = React;
class Canvas extends React.Component { // *** Note: You need to extend `React.Component`
canvasRef = React.createRef(); // *** Note: You shouldn't have `this.` here
state = {
ticker: 0,
};
paint = (e) => {
// ** Get the canvas
const canvas = this.canvasRef.current;
if (!canvas) {
return; // This probably won't happen
}
// Get the context
const context = canvas.getContext("2d");
// Get the point to draw at, allowing for the possibility
// that the canvas isn't at the top of the doc
const { left, top } = canvas.getBoundingClientRect();
const x = e.clientX - left;
const y = e.clientY - top;
context.lineTo(x, y);
context.stroke();
}; // *** Added missing ; here
increment = () => {
this.setState(( { ticker }) => ({ ticker: ticker + 1 }));
};
render() {
const { ticker } = this.state;
return <div>
<canvas ref={this.canvasRef} onMouseMove={this.paint} />
<div onClick={this.increment}>Canvas ticker: {ticker}</div>
</div>;
}
}
class Example extends React.Component {
state = {
ticker: 0,
};
increment = () => {
this.setState(({ ticker }) => ({ ticker: ticker + 1 }));
};
render() {
const { ticker } = this.state;
return <div>
<div onClick={this.increment}>Parent ticker: {ticker}</div>
<Canvas />
</div>;
}
}
ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

Uncaught TypeError: Cannot read property 'getContext' of null in react render

I'm trying to implement web sockets in my react frontend to stream some frames to it with canvas.
First the code was working fine and I could receive the data very well ... but when I needed to add a function call from the parent component, I got an error:
videoSocket.js:51 Uncaught TypeError: Cannot read property 'getContext' of null
This is the code :
import React, { Component } from "react";
import "./canvas.css";
function b64toBlob(dataURI) {
var byteString = atob(dataURI.split(",")[0]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: "image/jpeg" });
} /*
ws.onmessage = function(event) {
var img=document.getElementById("video_frames");
var urlObject = URL.createObjectURL(event.data);
img.src = urlObject;
document.body.appendChild(img);
}*/
class VideoSocket extends Component {
state = {
websocket: null,
};
constructor(props) {
super(props);
var ws = new WebSocket("ws://" + props.link);
this.state = {
websocket: ws,
};
}
render() {
const self = this;
this.state.websocket.onmessage = function (event) {
var js = JSON.parse(atob(event.data.split(",")[0]));
var data = b64toBlob(js["data"]);
var personinfo = { infos: js["infos"] };
console.log(self.props);
self.props.setdata(personinfo);
var urlObject = URL.createObjectURL(data);
var canvas = document.getElementById("tools_sketch");
/*
canvas.width = window.innerWidth; // equals window dimension
canvas.height = window.innerHeight;*/
var ctx = canvas.getContext("2d");
var image = new Image();
image.onload = function () {
ctx.drawImage(
image,
0,
0,
image.width,
image.height, // source rectangle
0,
0,
canvas.width,
canvas.height
);
};
image.src = urlObject;
};
return (
<div>
<canvas
id="tools_sketch"
width={this.props.width}
height={this.props.height}
>
Sorry, your browser doesn't support the <canvas> element.
</canvas>
</div>
);
}
}
export default VideoSocket;
When I remove
self.props.setdata(personinfo);
it works fine but when I add it no matter what I tried it's not working .
I tried to add the function onmessage to componentdidmount like below but I get the same error.
import React, { Component } from "react";
import "./canvas.css";
function b64toBlob(dataURI) {
var byteString = atob(dataURI.split(",")[0]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: "image/jpeg" });
} /*
ws.onmessage = function(event) {
var img=document.getElementById("video_frames");
var urlObject = URL.createObjectURL(event.data);
img.src = urlObject;
document.body.appendChild(img);
}*/
class VideoSocket extends Component {
state = {
websocket: null,
};
constructor(props) {
super(props);
var ws = new WebSocket("ws://" + props.link);
this.state = {
websocket: ws,
};
}
componentDidMount() {
const self = this;
this.state.websocket.onmessage = function (event) {
var js = JSON.parse(atob(event.data.split(",")[0]));
var data = b64toBlob(js["data"]);
var personinfo = { infos: js["infos"] };
console.log(self.props);
self.props.setdata(personinfo);
var urlObject = URL.createObjectURL(data);
var canvas = document.getElementById("tools_sketch");
/*
canvas.width = window.innerWidth; // equals window dimension
canvas.height = window.innerHeight;*/
var ctx = canvas.getContext("2d");
var image = new Image();
image.onload = function () {
ctx.drawImage(
image,
0,
0,
image.width,
image.height, // source rectangle
0,
0,
canvas.width,
canvas.height
);
};
image.src = urlObject;
};
}
render() {
return (
<div>
<canvas
id="tools_sketch"
width={this.props.width}
height={this.props.height}
>
Sorry, your browser doesn't support the <canvas> element.
</canvas>
</div>
);
}
}
export default VideoSocket;
This is the server side code in python :
#!/usr/bin/env python
import random
import websockets
import asyncio
import os
import numpy as np
import cv2
import json
import base64
#!/usr/bin/env python
# WS server that sends messages at random intervals
class VideoCamera(object):
def __init__(self):
self.video = cv2.VideoCapture('video2.mp4')
def __del__(self):
self.video.release()
def get_frame(self):
while True:
ret, image = self.video.read()
ret, jpeg = cv2.imencode('.jpg', image)
base6 = base64.b64encode(jpeg.tobytes())
yield base6.decode('utf-8')
def gen(camera):
while True:
image = next(camera.get_frame())
yield(image)
async def time(websocket, path):
camera = VideoCamera()
i = 0
while True:
i = i+1
data = next(gen(camera))
js = {'data': data, "infos": [
{'id': 1, 'name': 'name1'}, {'id': 2, 'name': 'name2'}]}
res_bytes = json.dumps(js).encode('utf-8')
base6 = base64.b64encode(res_bytes)
message = base6.decode('utf-8')
await websocket.send(message)
start_server = websockets.serve(time, "127.0.0.1", 5678)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
Edit : the parent component code :
import React, { Component, useState } from "react";
import "antd/dist/antd.css";
import VideoSocket from "../components/videoSocket";
import MyTable from "../components/table";
class Home extends Component {
state = { personinfo: {} };
constructor(props) {
super(props);
let links = {
1: "127.0.0.1:5678",
2: "127.0.0.1:5679",
3: "127.0.0.1:5680",
4: "127.0.0.1:5681",
};
const CameraId = props.match.params.cameraId;
console.log(CameraId);
let link = links[CameraId];
if (link == null) {
link = "127.0.0.1:5678";
}
let curent = link;
this.state = {
links: links,
curent: curent,
};
console.log(link);
}
setData = (personinfo) => {
this.setState({
personinfo: personinfo,
});
};
render() {
return (
<div className=".container-fluid overflow-auto" width="100%">
<div></div>
<VideoSocket
className=""
width={(window.innerWidth * 75) / 100}
height="400px"
link={this.state.curent}
setdata={this.setData}
key="1"
></VideoSocket>
<MyTable personinfo={this.state.personinfo}></MyTable>
</div>
);
}
}
export default Home;
Edit: I tried to add
shouldComponentUpdate() {
return false;
}
to the component videosocket but it still do the same problem

image coords changing on browser width change

Hi I am clicking on particular point on image and displaying a icon on selected area. When i change resolution that point is moving to other part of image. below is my code.How to fix the coords on resolution or browser width change. Added eventlistner and need to know on window resize any calculation have to be done?
My component is:
import React, { Component } from "react";
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";
import { showCardDetails } from "../../store/Action";
let temp = [];
class cardDetails extends Component {
constructor(props) {
super(props);
}
state = {
left: "",
right: "",
coords: []
};
componentDidMount() {
const { dispatch } = this.props;
console.log(this.props.backOffice + "props");
console.log(this.props.match.params.userId);
let coords = JSON.parse(localStorage.getItem("coords"));
this.setState({ coords: coords });
window.addEventListener("resize", this.handlePress);
}
handlePress = () => {
const { coords } = this.state;
console.log(this.state);
//anycalculation here?
};
handleclick = e => {
var canvas = document.getElementById("imgCoord");
var w = document.documentElement.clientWidth;
var h = document.documentElement.clientHeight;
console.log(e.offsetLeft);
var x =
e.clientX +
document.body.scrollLeft +
document.documentElement.scrollLeft;
var y =
e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
let left = x + "px";
let top = y + "px";
let obj = {
left: left,
top: top,
width: w,
height: h
};
temp.push(obj);
this.setState({ left: left, top: top, coords: temp });
localStorage.setItem("coords", JSON.stringify(temp));
};
render() {
const { backOffice, match } = this.props;
const { left, top, coords } = this.state;
console.log(coords);
return (
<React.Fragment>
<div>
<img
id="imgCoord"
src={require("../../assets/images/imageTagging.PNG")}
onClick={$event => this.handleclick($event)}
/>
{coords &&
coords.map(item => (
<img
style={{
width: "20px",
position: "absolute",
left: item.left,
top: item.top,
backgroundColor: "white"
}}
src={require("../../assets/images/tag.png")}
id="marker"
/>
))}
</div>
</React.Fragment>
);
}
}
/**
* Redux map state
#param {} state
#param {} ownParams
*/
function mapStateToProps(state, ownParams) {
console.log(state);
return {
backOffice: state.selectedCardData
};
}
export default withRouter(connect(mapStateToProps)(cardDetails));
[EDITED]
you need to add a event listener (here is docs https://www.w3schools.com/jsref/event_onresize.asp ) in componentDidMount method, and remove this listener in componentWillUnmount method.
inside listener put a function to set new coordinates.
and also need to calculate a percentage of icon's coordinates relative to width and height of the window
handleclick = e => {
...your previous code
const yPercent = h / top;
const xPercent = w / left;
this.setState({ yPercent, xPercent });
};
handleResize = () => {
var w = document.documentElement.clientWidth;
var h = document.documentElement.clientHeight;
const newTop = (this.state.yPercent * h) + "px";
const newLeft = (this.state.xPercent * w) + "px";
this.setState({ left: newLeft; top: newTop });
}

manage the result of two eventlistener

I trying to get back the selected value in the dropdown and attribute it to atom.name in order to change the atom name. By default there is ch2 molecule and when click on Na. Ch2 should be replace by Na but the problem is the scope of the event listener and the capacity to manage these two eventlistener. The one who manage the dropdown result
var a = document.getElementById('atomDropdown');
a.addEventListener('change', function() {
console.log(this.value);
}, false);
the console.log give here the right result and
the eventlistener which manage the position of the dropdown menu with
document.body.addEventListener('mouseup', e => {
let atom = atoms.find(a => distance(a.position, { x: e.pageX, y: e.pageY}) <= a.r)
atomDropdown.classList.remove('hidden')
if(atom){
atomDropdown.style.left = atom.position.x + 'px'
atomDropdown.style.top = (atom.position.y + atom.r) + 'px'
}
console.log(atom.name);
})
What I'm trying to do without success is to attribute atom.name to this value.
const canvas = document.createElement('canvas'),
context = canvas.getContext('2d'),
width = canvas.width = window.innerWidth,
height = canvas.height = window.innerHeight,
atoms = [],
bonds = [],
atomDropdown = document.getElementById('atomDropdown')
document.body.appendChild(canvas)
class Atom {
constructor({ x, y, name }){
this.position = {}
this.position.x = x
this.position.y = y
this.name = name
this.r = name.length * 10
atoms.push(this)
}
draw(){
let { position, name, r } = this,
{ x, y } = position
context.fillStyle = '#EEEEEE'
context.beginPath()
context.arc(x, y, r, 0, 2 * Math.PI)
context.fill()
context.fillStyle = '#000000'
context.font = '20px sans-serif'
context.textAlign = 'center'
context.fillText(name, x, y + 5)
}
}
class Bond {
constructor({ atom1, atom2, type }){
this.atom1 = atom1
this.atom2 = atom2
bonds.push(this)
}
draw(){
let { atom1, atom2 } = this
context.beginPath()
context.strokeStyle = '#000000'
context.moveTo(atom1.position.x, atom1.position.y)
context.lineTo(atom2.position.x, atom2.position.y)
context.stroke()
}
}
let hexagon = {
size: 100,
x: width/2,
y: height/2
}
let lastAtom
for (var side = 0; side < 7; side++) {
let newAtom = new Atom({
x: hexagon.x + hexagon.size * Math.cos(side * 2 * Math.PI / 6),
y: hexagon.y + hexagon.size * Math.sin(side * 2 * Math.PI / 6),
name: 'CH2'
})
if(lastAtom) new Bond({ atom1: lastAtom, atom2: newAtom })
if(side == 6) new Bond({ atom1: newAtom, atom2: atoms[0] })
lastAtom = newAtom
}
function render(){
context.fillStyle = '#FFFFFF'
context.fillRect(0,0,width,height)
bonds.map(b => b.draw())
atoms.map(a => a.draw())
}
render()
function distance(p1, p2){
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2))
}
var a = document.getElementById('atomDropdown');
a.addEventListener('change', function() {
console.log(this.value);
}, false);
document.body.addEventListener('mouseup', e => {
let atom = atoms.find(a => distance(a.position, { x: e.pageX, y: e.pageY}) <= a.r)
atomDropdown.classList.remove('hidden')
if(atom){
atomDropdown.style.left = atom.position.x + 'px'
atomDropdown.style.top = (atom.position.y + atom.r) + 'px'
}
console.log(atom.name);
})
#atomDropdown {
position: absolute;
&.hidden {
display: none;
}
}
<select id="atomDropdown" class="hidden">
<option>Test1</option>
<option>Test2</option>
<option>Test3</option>
</select>
It seems like the desired behavior is to change the value of atom.name for the atom that was clicked, replacing it with a name from the dropdown menu.
E.g., click on a CH2 atom -> select "Test1" from the dropdown menu -> the value of this.name for the atom you click changes from "CH2" to "Test1".
If that's the case, the issue is how to target the same atom from your last "mouseup" event in the "change" handler for atomDropdown. In which case, you can add a new variable in your definitions:
const canvas = document.createElement('canvas'),
context = canvas.getContext('2d'),
width = canvas.width = window.innerWidth,
height = canvas.height = window.innerHeight,
atoms = [],
bonds = [],
atomDropdown = document.getElementById('atomDropdown')
var selectedAtom = null
set selectedAtom to the atom instance in the "mouseup" handler:
document.body.addEventListener('mouseup', e => {
let atom = atoms.find(a => distance(a.position, { x: e.pageX, y: e.pageY}) <= a.r)
atomDropdown.classList.remove('hidden')
if(atom){
selectedAtom = atom
atomDropdown.style.left = atom.position.x + 'px'
atomDropdown.style.top = (atom.position.y + atom.r) + 'px'
}
console.log(atom.name);
})
and update selectedAtom.name in the "change event":
var a = document.getElementById('atomDropdown');
a.addEventListener('change', function() {
if (selectedAtom) {
selectedAtom.name = this.value;
render(); //added so that the GUI updates when the name value changes
}
console.log(this.value);
}, false);
EDIT: to immediately update the name of the atom as it appears in the GUI/display, you also have to call render() after selectedAtom.name is changed in atomDropDown's "change" event.
If I'm understanding this question correctly, you're looking to set the dropdown menu's value to atom.name?
If so, you need to add an attribute "value" to the options, then you can say something like:
document.getElementById("atomDropdown").value = atom.name
The HTML would look something like this:
<select id="atomDropdown" class="hidden">
<option value="Na">Test1</option>
<option value="Ch2">Test2</option>
<option value="O2">Test3</option>
</select>

dragging image around canvas

I am working on a react application at the minute that has some functionality that allows the user to drag and image around the canvas and reposition it, I have move functionality working but it's not particularly nice, it just seems to redraw a new image over the top of the old giving an image within an image within an image feedback. Secondly the it seems to drag to the top left corner of the of image not the click point I am not sure what I am doing wrong.
Here is an example, https://j3mzp8yxwy.codesandbox.io/, and my react code below,
import React, { Component } from 'react';
import { connect } from 'react-redux';
import styled from "styled-components";
import { toggleCommentModal, setCommentPos } from '../../actions/index';
import CommentHandle from "../../CommentHandle";
class ConnectedCanvasStage extends Component {
constructor(props) {
super(props);
this.handleMouseDown = this.handleMouseDown.bind(this);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.handleMouseUp = this.handleMouseUp.bind(this);
this.state = {
dragging : false,
dragoffx : 0,
dragoffy : 0
}
}
render() {
const CanvasStage = styled.div`
width: 100%;
height: ${document.body.clientHeight}px;
position:relative;
`;
return(
<CanvasStage>
<canvas id="canvas"
ref="canvas"
width={document.body.clientWidth - (document.body.clientWidth * 0.10)}
height={document.body.clientHeight}
onMouseDown={this.handleMouseDown}
onMouseMove={this.handleMouseMove}
onMouseUp={this.handleMouseUp}
/>
</CanvasStage>
);
}
componentDidMount() {
this.updateCanvas();
}
updateCanvas(x=0, y=0) {
this.ctx = this.refs.canvas.getContext("2d");
let base_image = new Image();
base_image.src = this.props.image;
base_image.onload = () => {
this.ctx.drawImage(base_image, x, y, (document.body.clientWidth - (document.body.clientWidth * 0.10)) * 2, document.body.clientHeight * 2);
}
}
handleMouseDown(event) {
switch(this.props.activeTool) {
case "move":
let mx = event.clientX;
let my = event.clientY;
let pos = this.getRelativeClickPosition(document.getElementById('canvas'), event);
this.setState({
dragging: !this.state.dragging,
dragoffx : parseInt(mx - pos.x),
dragoffy : parseInt(my - pos.y)
});
break;
case "comment":
let comment_position = this.getRelativeClickPosition(document.getElementById('canvas'), event);
this.addComment(comment_position.x, comment_position.y);
break;
}
}
handleMouseMove(event) {
if(this.props.activeTool == 'move' && this.state.dragging) {
var dx = event.clientX - this.state.dragoffx;
var dy = event.clientY - this.state.dragoffy;
this.updateCanvas(dx, dy);
}
}
handleMouseUp() {
this.setState({
dragging :false
});
}
shouldComponentUpdate() {
return false;
}
addComment(x, y) {
x = x - 24;
y = y - 20;
this.props.toggleCommentModal(true);
this.props.setCommentPos({x, y});
}
getRelativeClickPosition(canvas, event) {
var rect = canvas.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
}
}
const mapDispatchToProps = dispatch => {
return {
toggleCommentModal : visible => dispatch(toggleCommentModal(visible)),
setCommentPos : position => dispatch(setCommentPos(position))
};
};
const mapStateToProps = state => {
return {
activeTool : state.activeTool,
comments : state.comments
}
};
const CanvasStage = connect(mapStateToProps, mapDispatchToProps)(ConnectedCanvasStage);
export default CanvasStage;
How would I go about smoothing this drag out so it doesn't just draw a new image on top and it also drags from the click point and not the top left of the image?

Categories