Not getting accurate mouse mouseup and mousedown coordinate - reactjs

I am using react with typescript. I have a canvas and I am trying to draw a rectangle inside the canvas based on mouseup and mousedown event. I don't know what I am doing wrong here my rectangles are drawn but in different positions.
This is how my code looks like:
const Home = () => {
const divRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
let paint = false;
let StartClientXCord: any;
let StartClientYCord: any;
let FinishedClientXCord: any;
let FinishedClientYCord: any;
function startPosition(e:any){
paint = true;
StartClientXCord = e.clientX;
StartClientYCord = e.clientY;
}
function finishPosition(e:any){
FinishedClientXCord = e.clientX;
FinishedClientYCord = e.clientY;
paint = false;
}
function draw(e: any){
if(!paint){
return
}
if(divRef.current){
const canvas = divRef.current.getContext('2d');
if(canvas){
canvas?.rect(StartClientXCord, StartClientYCord, FinishedClientXCord - StartClientXCord, FinishedClientYCord - StartClientYCord);
canvas.fillStyle = 'rgba(100,100,100,0.5)';
canvas?.fill();
canvas.strokeStyle = "#ddeadd";
canvas.lineWidth = 1;
canvas?.stroke();
}
}
}
if(divRef.current){
divRef.current.addEventListener("mousedown", startPosition);
divRef.current.addEventListener("mouseup", finishPosition);
divRef.current.addEventListener("mousemove", draw);
}
})
return (
<canvas className='canvas' style={{height: height, width: width}} ref={divRef}>
</canvas>
)
}
I am drawing on a different position box is drawing on a different position. Here I attached the screenshot as well

I believe it's because you are using clientX and clientY, which MDN defines as:
The clientX read-only property of the MouseEvent interface provides the horizontal coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page).
This is the absolute position on the entire page. What you want is the is the position reative to the top left corner of the canvas. You want offsetX and offsetY which MDN defines as:
The offsetX read-only property of the MouseEvent interface provides the offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node.
So give this a try:
StartClientXCord = e.offsetX;
StartClientYCord = e.offsetY;

Related

Canvas onclick event react

Hello and thank you for reading this question.
I'm struggling to deal with the onclick event on canvas with React.
I am currently building a component that has to draw bounding boxes on an image and make those boxes fire a onclick event with its coordinates and/or metadata.
I am using the following react-bounding-box component (https://www.npmjs.com/package/react-bounding-box) with a bit of customization.
The idea is that my component receive the following data :
an image
a JSON with a list of items that contains coordinates of bounding boxes and other data related to those boxes.
Once the JSON is loaded, my code iterates on the list of items and draws the bounding boxes on the image using canvas.
My component definition looks like that (I omitted useless lines of code) :
[...]
import BoundingBox from 'react-bounding-box'
[...]
export const ComicImageDrawer = (props) => {
const [boundingBoxesItems, setBoundingBoxesItems] = useState(Array<any>());
const [selectedBoxItem, setSelectedBoxItem] = useState({})
const [selectedBoxIndex, setSelectedBoxIndex] = useState<Number>(-1);
const [currentImageBoxes, setCurrentImageBoxes] = useState(Array<any>())
useEffect(() => {
[...] // loading data
}, [])
// That function is fired when a box is hovered
// param value is the index of the box
// I would like to do the same but with the `onclick` event
function onOver(param) {
[...] // don't care
}
const params = {
[...] // don't care
}
};
return (
<BoundingBox
image={currentImage}
boxes={currentImageBoxes}
options={params.options}
onSelected={onOver}
drawBox={drawBoxCustom}
drawLabel={() => {}}
/>
)
}
The redefined the component drawBox() function to add some customization. So that function definition looks like this :
function drawBoxCustom(canvas, box, color, lineWidth) {
if(!box || typeof box === 'undefined')
return null;
const ctx = canvas.getContext('2d');
const coord = box.coord ? box.coord : box;
let [x, y, width, height] = [0, 0, 0, 0]
[...] // removed useless lines of codes
ctx.lineWidth = lineWidth;
ctx.beginPath();
[...] // drawing element definition
ctx.stroke();
};
I haved tried the following stuff to make the canvas fire an onclick event but it never fires (i also tried other event like mouseover) :
// Listen for mouse moves
canvas.addEventListener('onmouseover', function (event) {
console.log('click event', event);
});
What I would like to obtain is to fire a function in my React component that looks like that. The idea is to determine which box has been clicked :
const handleCanvasClick = (event, box) => {
console.log('event', event);
console.log('box', box);
}
Any help or suggestion would be appreciated.
Thanks.

FabricJS object is not selectable/clickable/resizable in top left corner (React + FabricJs)

I have FabricJs canvas. And I have multiple objects outside canvas. When I click on object, it appears in Canvas. I am not doing anything else with this object programmatically.
Here is part of code that add image to Canvas (it is React code):
const { state, setState } = React.useContext(Context);
const { canvas } = state;
...
const handleElementAdd = (event, image) => {
const imageSize = event.target.childNodes[0].getBoundingClientRect();
const imageUrl = `/storage/${image.src}`;
new fabric.Image.fromURL(imageUrl, img => {
var scale = 1;
if (img.width > 100) {
scale = 100/img.width;
}
var oImg = img.set({
left: state.width / 2 - img.width * scale,
top: state.height / 2 - img.height * scale,
angle: 0})
.scale(scale);
canvas.add(oImg).renderAll.bind(canvas);
canvas.setActiveObject(oImg);
});
};
...
<div
key={img.name}
onClick={(e) => handleElementAdd(e, img)}
className={buttonClass}
>
It appears, I can drag and drop, resize, select it. However, when my object is placed in top left corner of canvas, it is not clickable, resizable etc. I can select larger area and then object is selected, but it doesn't respond to any events like resize, select etc.
Not sure where to look at.
I read that if coordinates are changed programmatically object.setCoord() to object can help, but in this case I do not understand where to put it.
You have to put (call) object.setCoords() each time you have modified the object, or moved the object. So, you can call it on mouseUp event of the canvas.
You can write a function naming "moveObject" in which you get the object you are trying to move/moved/updated. And then just selectedObject.setCoords().
Then call that function inside canvas.on('selection:updated', e => {----})

How to get event pixel when I have coordinates in database

I have clicked on a coordinates and remove with the help of MouseEvent clientY and ClientX value. I have some coordinates in database and I want to hit them same as it clicked as event. I have used bellow code.
const pixel = map.getPixelFromCoordinate(coordinates);
event = {}
event.clientX = pixel[0];
event.clientY = pixel[1];
simulateSingleClick(map, event);
But not able to click because event's clientY and above clientY value has lot of different.
The map viewport is a viewport within the client viewport https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect so unless you have a full page map you will need to add the left and top offsets of that
const vpClient = map.getViewport().getBoundingClientRect();
const pixel = map.getPixelFromCoordinate(coordinates);
event = {}
event.clientX = vpClient.left + pixel[0];
event.clientY = vpClient.top + pixel[1];
simulateSingleClick(map, event);

How to get coordinates (x,y) from a svg and put a text tag with a value

I'm trying to do a web app on React to write guitar tabs, i'm doing the tabs with SVG. I think i know how to get the coords but the problem is i don't know how to put that text tag with a value, because it doesn't have a value attribute (i think so)
my function:
// Doesn't work
// Suppose that i want to add a 2
const clicked = (evt) =>{
const { currentTarget: svg, pageX, pageY } = evt
const coords = svg.getBoundingClientRect() //<----- get the coords
const text = document.createElementNS('http://www.w3.org/2000/svg','text') //<--- create the svg
text.setAttribute('x', `$pageX - coords.x`) //<----- set the attributes X and Y with the mouse coords
text.setAttribute('y', `$pagey - coords.y`)
text.setAttribute('value', "2") //<----- It doesn't work
svg.appendChild(text)
}
heres other function, but to put circles:
//IT WORKS
clicked(evt) {
const {currentTarget: svg, pageX, pageY} = evt;
const coords = svg.getBoundingClientRect();
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
circle.setAttribute('cx', `${pageX - coords.x}`);
circle.setAttribute('cy', `${pageY - coords.y}`);
circle.setAttribute('r', '5');
svg.appendChild(circle);
}
Text is not an attribute, it's content so you want to replace
text.setAttribute('value', "2")
with
text.appendChild(document.createTextNode("2"))

Animating a height value for text input

So i am using react-native-autogrow-textinput in order to have an editable document viewable in my application. I am currently trying to work around the keyboard in order to adjust the height of textinput box, so that all text is visible. I have found the following code to do so
componentWillMount () {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this.keyboardDidShow.bind(this));
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this.keyboardDidHide.bind(this));
}
componentWillUnmount () {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
keyboardDidShow(e){
let newSize = Dimensions.get('window').height- e.endCoordinates.height - 150;
console.log(e.endCoordinates);
this.setState({docViewHeight: newSize});
}
keyboardDidHide(e){
let newSize = Dimensions.get('window').height - 170;
this.setState({docViewHeight: newSize})
}
However, the result i am getting is: When the keyboard is animating off screen, the height of the textinput remains the same, let newSize = Dimensions.get('window').height- e.endCoordinates.height - 150, untill the keyboard has finished sliding off screen.
The height then adjusts to fill the whole screen again, except it sort of 'pops' into the new height. How do i get the value of this height to gradually grow, so it looks like it is simply extending to fit the whole screen? Ill post my Autogrow TextInput code below also. Any help would be much appreciated.
<AutoGrowingTextInput
ref="editText"
editable = {this.state.editting}
style = {{fontSize: fontProperties.fontSize+3, marginLeft: 18, marginRight: 18, marginTop: 15}}
/*animate this*/ minHeight = {this.state.docViewHeight}
animation = {{animated: true, duration: 300}}
//has some other confidential props here for onChange etc
</AutoGrowingTextInput>
Found the answer myself, after digging through some library files.
The solution is to use a keyboardWillHide event listener instead of keyboardDidHide.
This will fire before the keyboard begins its outro animation. Ive put the code below.
componentWillMount () {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this.keyboardDidShow.bind(this));
this.keyboardWillHideListener = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide.bind(this));
}
componentWillUnmount () {
this.keyboardDidShowListener.remove();
this.keyboardWillHideListener.remove();
}
keyboardWillHide(e){
let newSize = Dimensions.get('window').height - 170;
this.setState({docViewHeight: newSize})
}

Resources