image coords changing on browser width change - javascript

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 });
}

Related

React request animation frame for event listener mousemove

I'm not sure how to implement requestAnimationFrame in this example as I always create an exponential infinite loop when trying it. I want the mousemove animation to be smooth that's why I want to use requestAnimationFrame but where should I set it. In my case I can't work with useEffect.
In the Codesandbox, if you move your Mouse over the red background it should activate it the mousemove event.
Codesandbox:
https://codesandbox.io/s/late-dream-97bfhp?file=/src/RequestAnimationFrameTest.tsx:0-1307
Code:
import React, { useState, useRef } from "react";
const RequestAnimationFrameTest = () => {
const [cursorPos, setCursorPos] = useState({ x: 0, y: 0 });
const [cursorVisible, setCursorVisible] = useState(false);
const requestRef = useRef();
const handleMouseMove = (e) => {
console.log("eventListener");
const x = e.pageX;
const y = e.pageY;
setCursorPos({ x: x, y: y });
// requestRef.current = requestAnimationFrame(handleMouseMove);
};
const handleMouseMoveRef = useRef(handleMouseMove);
const handleMouseEnter = () => {
document.body.style.cursor = "none";
addEventListener("mousemove", handleMouseMoveRef.current);
setCursorVisible(true);
};
const handleMouseLeave = () => {
document.body.style.cursor = "default";
removeEventListener("mousemove", handleMouseMoveRef.current);
// cancelAnimationFrame(requestRef.current);
setCursorVisible(false);
};
return (
<div className="container">
{cursorVisible && (
<div
style={{ top: cursorPos.y + "px", left: cursorPos.x + "px" }}
className="cursor"
>
Mouse
</div>
)}
<div
className="item"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
></div>
</div>
);
};

Parent container margin effecting the child margin

I am working on drawing a rectangle shape on a browser using divs on React project. Everything is working as expected but when I am adding the margin to the parent base and then drawing the box the boxes are drawing with some space between the mouse courser and the actual box.
The same thing happening if I am adding a top margin to a base
Here is my code:
const divRef = useRef<HTMLDivElement>(null);
const [mousedown, setMouseDown] = useState(false);
const [last_mousex, set_last_mousex] = useState(0);
const [last_mousey, set_last_mousey] = useState(0);
const [mousex, set_mousex] = useState(0);
const [mousey, set_mousey] = useState(0);
const [rectx, setrectx] = useState(0);
const [recty, setrecty] = useState(0);
const [rectwidth, setrectwidth] = useState(0);
const [rectheight, setrectheight] = useState(0);
const [visual, setVisualRect] = useState(false);
const mouseDown = (event: any) => {
set_last_mousex(event.clientX);
set_last_mousey(event.clientY);
setMouseDown(true);
};
const mouseMove = (event: any) => {
set_mousex(event.clientX);
set_mousey(event.clientY);
visualRect();
};
const visualRect = () => {
if (mousedown) {
const width = Math.abs(mousex - last_mousex);
const height = Math.abs(mousey - last_mousey);
const rx = mousex < last_mousex ? mousex : last_mousex;
const ry = mousey < last_mousey ? mousey : last_mousey;
rectx !== rx && setrectx(rx);
recty !== ry && setrecty(ry);
rectheight !== height && setrectheight(height);
rectwidth !== width && setrectwidth(width);
setVisualRect(true);
}
};
const mouseUp = () => {
setMouseDown(false);
setVisualRect(false);
};
return (
<div className={"base"}>
<div className={"container"} ref={divRef}>
<div
className={"drawArea"}
onMouseDown={mouseDown}
onMouseUp={mouseUp}
onMouseMove={mouseMove}
>
{visual &&
(<div className={"rectBox"} style={{left: rectx, top: recty, width:rectwidth, height:rectheight}}></div>)
}
</div>
</div>
</div>
);
Here is the CSS:
.base {
width: 600px;
height: 500px;
/* this margin is causing issue */
margin-left: 200px;
}
.drawArea {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
}
.rectBox {
position: absolute;
border: 3px solid #581d1d;
}
.container {
height: 500px;
width: 100%;
background-color: rgb(219, 219, 219);
position: relative;
}
I also created sandbox demo
#CBroe is correct, clientX and clientY is relative to viewport.
What you can do is subtract it from the element's viewport X & Y so you get the proper coordinates relative to the element's X & Y
For example, if the element's X is 200px and the click is 245px, both relative to viewport, if you compute 245px - 200px you get 45px which is relative to the element because you got rid of the element's X from the click's X.
const mouseDown = (event: any) => {
const x = event.clientX - event.currentTarget.getBoundingClientRect().left
const y = event.clientY - event.currentTarget.getBoundingClientRect().top
set_last_mousex(x);
set_last_mousey(y);
setMouseDown(true);
};
//...
const mouseMove = (event: any) => {
const x = event.clientX - event.currentTarget.getBoundingClientRect().left
const y = event.clientY - event.currentTarget.getBoundingClientRect().top
set_mousex(x);
set_mousey(y);
visualRect();
};
getBoundingClientRect: https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect

Match image container width with image width after it is rendered - React

I am using react-image-marker which overlays marker on image inside a div.
<ImageMarker
src={props.asset.url}
markers={markers}
onAddMarker={(marker) => setMarkers([...markers, marker])}
className="object-fit-contain image-marker__image"
/>
The DOM elements are as follows:
<div class=“image-marker”>
<img src=“src” class=“image-marker__image” />
</div>
To make vertically long images fit the screen i have added css to contain the image within div
.image-marker {
width: inherit;
height: inherit;
}
.image-marker__image {
object-fit:contain !important;
width: inherit;
height: inherit;
}
But now the image is only a subpart of the entire marker area. Due to which marker can be added beyond image bounds, which i do not want.
How do you think i can tackle this. After the image has been loaded, how can i change the width of parent div to make sure they have same size and markers remain in the image bounds. Please specify with code if possible
Solved it by adding event listeners in useLayoutEffect(). Calculating image size info after it is rendered and adjusting the parent div dimensions accordingly. Initially and also when resize occurs.
If you think you have a better solution. Do specify.
useLayoutEffect(() => {
const getRenderedSize = (contains, cWidth, cHeight, width, height, pos) => {
var oRatio = width / height,
cRatio = cWidth / cHeight;
return function () {
if (contains ? (oRatio > cRatio) : (oRatio < cRatio)) {
this.width = cWidth;
this.height = cWidth / oRatio;
} else {
this.width = cHeight * oRatio;
this.height = cHeight;
}
this.left = (cWidth - this.width) * (pos / 100);
this.right = this.width + this.left;
return this;
}.call({});
}
const getImgSizeInfo = (img) => {
var pos = window.getComputedStyle(img).getPropertyValue('object-position').split(' ');
return getRenderedSize(true,
img.width,
img.height,
img.naturalWidth,
img.naturalHeight,
parseInt(pos[0]));
}
const imgDivs = reactDom.findDOMNode(imageCompRef.current).getElementsByClassName("image-marker__image")
if (imgDivs) {
if (imgDivs.length) {
const thisImg = imgDivs[0]
thisImg.addEventListener("load", (evt) => {
if (evt) {
if (evt.target) {
if (evt.target.naturalWidth && evt.target.naturalHeight) {
let renderedImgSizeInfo = getImgSizeInfo(evt.target)
if (renderedImgSizeInfo) {
setOverrideWidth(Math.round(renderedImgSizeInfo.width))
}
}
}
}
})
}
}
function updateSize() {
const thisImg = imgDivs[0]
if (thisImg){
let renderedImgSizeInfo = getImgSizeInfo(thisImg)
if (renderedImgSizeInfo) {
setOverrideWidth((prev) => {
return null
})
setTimeout(() => {
setOverrideWidth((prev) => {
return setOverrideWidth(Math.round(renderedImgSizeInfo.width))
})
}, 390);
}
}
}
window.addEventListener('resize', updateSize);
return () => window.removeEventListener('resize', updateSize);
}, [])

React Konva Transformer Error "Cannot read property 'getStage' of undefined"

I'm using Konva 4.0.18 within a React 16.8. Application, which works well so far in the Standalone (CRA). When integrating this working solution into an existing Application Frame I'm facing an error which appears on transforming the rectangle (and constantly appears on moving the mouse even if the rectangle transformation stopped when clicked again):
Transformer.js:316 Uncaught TypeError: Cannot read property 'getStage' of undefined
at e._handleMouseMove (Transformer.js:316)
at default-passive-events.js:6
Screenshot of the debugger at the function 'findOne' which returns an 'undefined'
There is an Maskingcomponent which imports Konva's Stage, Layout and Image and as well as the MaskingRectangle which can be transformed.
import React, { useState, useEffect, useRef } from "react";
import MaskingRectangle from "./maskingRectangle";
const Masking = ({ canvas }) => {
const stageWrapperRef = useRef(null);
const [stageWidth, setStageWidth] = useState(600);
const [stageHeight, setStageHeight] = useState(400);
const [maskingRectangles, setMaskingRectangles] = useState([]);
const [shapes, setShapes] = useState([]);
const [selectedId, selectShape] = useState(null);
const [Stage, setStage] = useState(null);
const [Layer, setLayer] = useState(null);
const [Image, setImage] = useState(null);
const addRectangle = e => {
const getRandomInt = max => {
return Math.floor(Math.random() * Math.floor(max));
};
let rectX = getRandomInt(100);
let rectY = getRandomInt(100);
if (e) {
const { x, y } = e.currentTarget.getPointerPosition();
rectX = x;
rectY = y;
}
const rect = {
x: rectX,
y: rectY,
width: 100,
height: 100,
fill: "rgba(255,0,0,0.4)",
id: `rect${maskingRectangles.length + 1}`
};
const rects = maskingRectangles.concat([rect]);
setMaskingRectangles(rects);
const shs = shapes.concat([`rect${maskingRectangles.length + 1}`]);
setShapes(shs);
};
const deleteActiveRectangle = () => {
setMaskingRectangles(
maskingRectangles.filter(rect => rect.id !== selectedId)
);
setShapes(shapes.filter(shape => shape !== selectedId));
selectShape(null);
};
const onStageClick = e => {
if (e.target.attrs.className !== "mask-rect") {
selectShape(null);
}
};
const onStageDblClick = e => {
const stage = e.target.parent.parent;
const elem = e.currentTarget;
if (elem.clickStartShape.attrs.className !== "mask-rect") {
addRectangle(e);
}
if (elem.clickStartShape.attrs.id === selectedId) {
deleteActiveRectangle();
stage.container().style.cursor = "crosshair";
}
};
useEffect(() => {
async function loadKonvaModules() {
await import("react-konva").then(module => {
setStage(() => module.Stage);
setLayer(() => module.Layer);
setImage(() => module.Image);
});
}
loadKonvaModules();
const scaleX = stageWrapperRef.current.clientWidth / canvas.width;
const scaleY = stageWrapperRef.current.clientHeight / canvas.height;
const scalingFactor = Math.min(scaleX, scaleY);
setStageWidth(canvas.width * scalingFactor);
setStageHeight(canvas.height * scalingFactor);
}, []);
return (
<div>
<div className="stage-wrapper" ref={stageWrapperRef}>
<Stage
className="stage"
width={stageWidth}
height={stageHeight}
onClick={onStageClick}
onDblClick={onStageDblClick}
>
<Layer className="layer1">
<Image
image={canvas}
width={stageWidth}
height={stageHeight}
/>
{maskingRectangles.map((rect, i) => {
return (
<MaskingRectangle
key={i}
shapeProps={rect}
isSelected={rect.id === selectedId}
onSelect={() => {
selectShape(rect.id);
}}
onChange={newAttrs => {
const rects = maskingRectangles.slice();
rects[i] = newAttrs;
setMaskingRectangles(rects);
}}
onMouseEnter={e => {
const stage = e.target.parent.parent;
stage.container().style.cursor = "move";
}}
onMouseOut={e => {
const stage = e.target.parent.parent;
stage.container().style.cursor =
"crosshair";
}}
stageProps={{
width: stageWidth,
height: stageHeight
}}
/>
);
})}
</Layer>
</Stage>
</div>
</div>
);
};
export default Masking;
My MaskinRectangle componeht looks like this:
import React from "react";
import { Rect, Transformer } from "react-konva";
const MaskingRectangle = ({
shapeProps,
isSelected,
onSelect,
onChange,
stageProps,
onMouseEnter,
onMouseOver,
onMouseOut,
onMouseMove
}) => {
const shapeRef = React.useRef();
const trRef = React.useRef();
React.useEffect(() => {
if (isSelected) {
trRef.current.setNode(shapeRef.current);
trRef.current.getLayer().batchDraw();
}
}, [isSelected]);
const dragBoundFunc = pos => {
//Limit Min/Max x,y position
const stageWidth = (stageProps && stageProps.width) || 800;
const stageHeight = (stageProps && stageProps.height) || 600;
const minX = 0;
const minY = 0;
const maxX = stageWidth - shapeRef.current.attrs.width;
const maxY = stageHeight - shapeRef.current.attrs.height;
const newX = pos.x < minX ? minX : pos.x > maxX ? maxX : pos.x;
const newY = pos.y < minY ? minY : pos.y > maxY ? maxY : pos.y;
return {
x: newX,
y: newY
};
};
return (
<>
<Rect
className="mask-rect"
onClick={onSelect}
onTap={onSelect}
ref={shapeRef}
{...shapeProps}
draggable
onTouchMove={null}
onDragEnd={e => {
onChange({
...shapeProps,
x: e.target.x(),
y: e.target.y()
});
}}
onTransformEnd={e => {
const node = shapeRef.current;
const scaleX = node.scaleX();
const scaleY = node.scaleY();
node.scaleX(1);
node.scaleY(1);
onChange({
...shapeProps,
x: node.x(),
y: node.y(),
width: node.width() * scaleX,
height: node.height() * scaleY
});
e.evt.preventDefault();
}}
dragBoundFunc={dragBoundFunc}
onMouseOver={onMouseOver}
onFocus={onMouseOver}
onMouseEnter={onMouseEnter}
onMouseOut={onMouseOut}
onBlur={onMouseOut}
onMouseMove={onMouseMove}
/>
{isSelected && <Transformer ref={trRef} rotateEnabled={false} />}
</>
);
};
export default MaskingRectangle;
Some thoughts/guesses:
I wonder why this work perfectly well as a standalone App, but fails when included into an existing HTML Page (which, by the way, includes also libraries like jquery and Angular 1.x).
Maybe you can give me some hints how to search or answer one of these questions:
There are a couple of event handlers (click, mousemove) registered
on the body and other elements outside the React App itself, but
they shouldn't affect the react app, or am I wrong here?
I see the mention of default-passive-events in the error. How this
might have to do with the issue (and if so, how could that be
avoided)?
Is ist more likely that the Konva.js library has an
issue (see screenshot attached)
Thanks in advance for any help, comments, suggestions where/how to look

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