I am trying to move a div with the help of mousemove event.
Here is the code for the same.
https://codepen.io/anurag92/pen/VEoQOZ
class ImageMarker extends React.Component {
constructor(props) {
super(props);
this.mouseDown = this.mouseDown.bind(this);
this.mouseUp = this.mouseUp.bind(this);
this.mouseMove = this.mouseMove.bind(this);
this.paint = this.paint.bind(this);
}
mouseDown(e) {
const position = {
left: this.marker.offsetLeft,
top: this.marker.offsetTop
};
this.hitOffset = {
x: e.pageX - position.left,
y: e.pageY - position.top,
diameter: this.diameter(),
markerRadius: 10
};
this.marker.addEventListener('mousemove', this.mouseMove);
this.marker.addEventListener('mouseup', this.mouseUp);
this.marker.addEventListener('mouseleave', this.mouseUp);
e.preventDefault();
}
mouseMove(e) {
this.position = {
x: e.pageX - this.hitOffset.x,
y: e.pageY - this.hitOffset.y
};
this.position.x = Math.round(this.position.x);
this.position.y = Math.round(this.position.y);
this.position.x = Math.min(700 - 1, Math.max(0, this.position.x));
this.position.y = Math.min(700 - 1, Math.max(0, this.position.y));
this.paint();
}
mouseUp(e) {
this.marker.removeEventListener('mousemove', this.mouseMove);
this.marker.removeEventListener('mouseup', this.mouseUp);
this.marker.removeEventListener('mouseleave', this.mouseUp);
}
diameter() {
return 1;
}
paint() {
if (JSON.stringify(this.paintedPosition) !== JSON.stringify(this.position)) {
this.paintedPosition = Object.assign({}, this.position);
}
if (this.position) {
this.marker.style.left = `${100 * this.position.x / 700}%`;
this.marker.style.top = `${100 * this.position.y / 700}%`;
}
return this;
}
render() {
this.position = this.position || {
x: 5,
y: 5
};
this.offset = 0;
return <div className='outer'
ref = {ref => {
this.canvasRef = ref;
}}
>
<div className = 'marker'
onMouseDown = {event => this.mouseDown(event)}
ref = {ref => {
this.marker = ref;
}} >
</div>
</div>;
}
}
// export default ImageMarker;
ReactDOM.render(<ImageMarker />,
document.getElementById('root')
);
When i move my cursor slowly its working fine, but on fast movement mouseleave gets triggered and as a result div is not able to keep up with the cursor.
Can someone please tell me a potential fix for this.
You could resolve that by attaching the mouseMove (and mouseUp)to the whole document This way they will be fired no matter if the mouse gets out of the element you want to drag.
Just remember to detach the event during componentWillUnmount to avoid leaks.
Further more if you want you site to work on mobile you need to attach touch, pointer or drag events. See the code of the kendo-draggable abstraction for a reference. We are using it in our react components. Bind to the element on ComponentDidMount and detach on ComponentWillUnmount
Related
I am using react with typescript. I use SVG to draw rectangles inside it. I am working on two features, the first one is I have to draw any number of shapes inside the SVG, and the other one is to allow mouse drag option. Now, the problem is when even I am drawing a shape and then drawing another shape in it the first drawn shape is moving and the new shape is drawing.
I want to do if I click and move the shape my drawing rectangle functionality should not work and if I am drawing the rectangle the already drawn shape would not move. this happening because I am using mouseup and mousemove events for both logic and that is why they collapsing. I don't know to separate them.
here is my code:
import "./styles.css";
import React, { useEffect, useRef } from 'react';
interface Iboxes{
id: string,
coordinates:{
x: number
y: number
width: number
height: number
}
}
export default function App() {
const divRef = useRef<HTMLDivElement>(null);
const svgRef = useRef<SVGSVGElement>(null);
const boxes: Array<Iboxes> = [];
useEffect(() => {
const containerSvg = svgRef.current;
let p:DOMPoint;
let w:number;
let h:number;
if(containerSvg){
const svgPoint = (elem:any, x:number, y:number) => {
const point = containerSvg.createSVGPoint();
point.x = x;
point.y = y;
return point.matrixTransform(elem.getScreenCTM().inverse());
};
containerSvg.addEventListener('mousedown', (event) => {
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
const start = svgPoint(containerSvg, event.clientX, event.clientY);
const drawRect = (e:any) => {
p = svgPoint(containerSvg, e.clientX, e.clientY);
w = Math.abs(p.x - start.x);
h = Math.abs(p.y - start.y);
if (p.x > start.x) {
p.x = start.x;
}
if (p.y > start.y) {
p.y = start.y;
}
rect.setAttributeNS(null, 'x', p.x as unknown as string);
rect.setAttributeNS(null, 'y', p.y as unknown as string);
rect.setAttributeNS(null, 'width', w as unknown as string);
rect.setAttributeNS(null, 'height', h as unknown as string);
rect.setAttributeNS(null, 'class', 'svg_rec');
rect.setAttributeNS(null, 'id', 'rec_' + boxes.length as unknown as string)
containerSvg.appendChild(rect);
};
const endDraw = (e:any) => {
containerSvg.removeEventListener('mousemove', drawRect);
containerSvg.removeEventListener('mouseup', endDraw);
boxes.push({id:'rec_' + boxes.length as unknown as string, coordinates:{x: p.x, y: p.y, width: w, height: h}})
let offset:any;
let selectedRect: SVGRectElement | null;
rect.addEventListener('mousedown', startDrag);
rect.addEventListener('mousemove', drag);
rect.addEventListener('mouseup', endDrag);
rect.addEventListener('mouseleave', endDrag);
function startDrag(evt:any) {
selectedRect = rect;
if(selectedRect){
offset = getMousePosition(evt);
if(offset){
let rectX = selectedRect.getAttributeNS(null, 'x');
let rectY = selectedRect.getAttributeNS(null, 'y');
if(rectX && rectY){
offset.x -= parseFloat(rectX);
offset.y -= parseFloat(rectY);
}
}
}
}
function drag(evt:any) {
if(selectedRect){
var coord = getMousePosition(evt);
if(coord && offset){
let x = coord.x - offset.x;
let y = coord.y - offset.y;
if(x && y){
selectedRect.setAttributeNS(null, "x", x as unknown as string);
selectedRect.setAttributeNS(null, "y", y as unknown as string);
}
}
}
}
function endDrag() {
selectedRect = null;
}
function getMousePosition(evt:any) {
var CTM = rect.getScreenCTM();
if(CTM){
return {
x: (evt.clientX - CTM.e) / CTM.a,
y: (evt.clientY - CTM.f) / CTM.d
};
}
}
};
containerSvg.addEventListener('mousemove', drawRect);
containerSvg.addEventListener('mouseup', endDraw);
});
}
},[]);
return (
<div className="App">
<div className='container' ref={divRef}>
<svg id="svg" ref={svgRef}>
</svg>
</div>
</div>
);
}
I also created a sandbox environment to demonstrate my issue:
here is a sandbox link
You just need to add
evt.stopPropagation();
to the function startDrag() on line 79.
function startDrag(evt: any) {
evt.stopPropagation();
selectedRect = rect;
// ...
}
Here's the fixed sandbox
Edit
Because other rects on top may obscure the current dragging rect. One solution is to disable the pointer-events for other rects while dragging one rect:
Add following code to the startDrag function
containerSvg.classList.add("dragging");
selectedRect?.classList.add("target");
and then add following code to the endDrag function
containerSvg.classList.remove("dragging");
selectedRect?.classList.remove("target");
then in your css code, add following rule:
.dragging rect:not(.target) {
pointer-events: none;
}
Checked updated sandbox
i'm creating function which send information to webserver about mouse position, when mouse button is down (click).
Generaly it works fine, but it fires too many times.
After page refresh:
first click -> method fires 1 time
second click -> methos fires 2 times
third click -> method fires 3 times
and so on...
The HTML code is:
<div id="draw-image-test">
<canvas
id="canvasId"
ref="canRef"
#mousedown="clickMe"
/>
</div>
and method :
clickMe() {
const canvas = document.getElementById('canvasId')
canvas.addEventListener('click', event => {
const rect = canvas.getBoundingClientRect()
const x = event.clientX - rect.left - canvas.clientLeft
const y = event.clientY - rect.top - canvas.clientTop
this.xpos = Math.round(x)
this.ypos = Math.round(y)
this.click = {
cmd: 'rightClick',
x: Math.round(x),
y: Math.round(y),
}
this.sendMessage(this.click)
})
},
Could you please help me with it?
everytime you click on your canvas, you add an event listener to it.
canvas.addEventListener('click', event => { [...]
this is your issue. You just need to do the logic you need in the clickMe, not add an other event listener to your canvas. The #mousedown event will send the event parameter with the wanted coordinates :
clickMe(event) {
const canvas = document.getElementById('canvasId')
const rect = canvas.getBoundingClientRect()
const x = event.clientX - rect.left - canvas.clientLeft
const y = event.clientY - rect.top - canvas.clientTop
this.xpos = Math.round(x)
this.ypos = Math.round(y)
this.click = {
cmd: 'rightClick',
x: Math.round(x),
y: Math.round(y),
}
this.sendMessage(this.click)
},
I am working on an html canvas for the frist time I am using gatsby to develop my project
my problem is that my html canvas is cut when there is a rotation I seem to have a problem with the width and the height but I can't seem to understand what the problem is
here are some pictures of the problem :
without rotation
after the first rotation
after the second rotation
and here is the part of my code where I handle my canvas
Thank you for your help I am just a beginner so I am sorry if my code is not really clean
function MainScratch () {
let canvas = useRef(null)
function getSize() {
const isBrowser = typeof window !== 'undefined'
if (isBrowser) return {
width: window.innerWidth,
height: window.innerHeight,
}
else return {
width: 5,
height: 5,
}
}
const [windowSize, setWindowSize] = useState(getSize)
useEffect (() => {
function handleResize() {
setWindowSize(getSize())
}
window.addEventListener("resize", handleResize)
return () => window.removeEventListener("resize", handleResize)
},[]);
useEffect(() => {
let renderingElement = canvas.current
let drawingElement = renderingElement.cloneNode()
let drawingCtx = drawingElement.getContext('2d')
let renderingCtx = renderingElement.getContext('2d')
let lastX
let lastY
let moving = false
renderingCtx.globalCompositeOperation = "source-over"
renderingCtx.fillStyle = '#000000'
renderingCtx.fillRect(0, 0, windowSize.width, windowSize.height)
function reload()
{
renderingCtx.clearRect(0, 0, drawingElement.width, drawingElement.height);
drawingCtx.clearRect(0, 0, renderingElement.width, renderingElement.height);
renderingCtx.fillRect(0, 0, windowSize.width, windowSize.height)
}
window.addEventListener("resize", reload)
window.addEventListener("orientationchange", reload)
renderingElement.addEventListener('touchstart', e => {
moving = true
lastX = e.touches[0].pageX - renderingElement.offsetLeft
lastY = e.touches[0].pageY - renderingElement.offsetTop
e.preventDefault();
})
renderingElement.addEventListener('touchend', e => {
moving = false
lastX = e.lastX - renderingElement.offsetLeft
lastY = e.lastY - renderingElement.offsetTop
})
renderingElement.addEventListener('touchmove', e => {
moving = true
if (moving) {
drawingCtx.globalCompositeOperation = "source-over"
renderingCtx.globalCompositeOperation = "destination-out"
let currentX = e.touches[0].pageX - renderingElement.offsetLeft
let currentY = e.touches[0].pageY - renderingElement.offsetTop
drawingCtx.beginPath();
drawingCtx.lineJoin = "round"
drawingCtx.moveTo(lastX, lastY)
drawingCtx.lineTo(currentX, currentY)
drawingCtx.closePath()
drawingCtx.lineWidth = 80;
drawingCtx.stroke()
lastX = currentX
lastY = currentY
renderingCtx.drawImage(drawingElement, 0, 0)
}
e.preventDefault();
})
}, []);
return (
<Banner>
<Canvas
id="canvas"
height={windowSize.height}
width={windowSize.width}
ref={canvas}
/>
</Banner>
)
This is my code
componentDidMount() {
document
.getElementsByClassName('landingScreen')[0]
.addEventListener('mousemove', this.updateDisplay, false);
document.addEventListener('scroll', this.isScrolling);
setTimeout(() => {
if (window.scrollY < 200) {
this.setState({ opacity: 1 });
}
}, 1000);
}
componentWillUnmount() {
document
.getElementsByClassName('landingScreen')[0]
.removeEventListener('mousemove', this.updateDisplay, false);
}
updateDisplay = event => {
const procentWidth = Math.round((event.pageX / this.state.width) * 100) - 50;
const procentHeight = Math.round((event.pageY / this.state.height) * 100 - 50);
const transform = `skew(${procentWidth / 5}deg,${procentHeight / 5}deg)`;
const textShadow = `${procentWidth / 1}px ${procentHeight / 1}px #5BDC94`;
const textShadow1 = `${procentWidth / 1}px ${procentHeight / 1}px #EDF5E0`;
const transform1 = `skew(${-procentWidth / 5}deg,${-procentHeight / 5}deg)`;
this.setState({ transform, textShadow, textShadow1, transform1 });
};
isScrolling = event => {
console.log(event);
if (window.scrollY >= 200) {
this.setState({ opacity: 0 });
}
if (window.scrollY <= 200) {
this.setState({ opacity: 1 });
}
};
I trigger a transform skew based on where my mouse is and it works fine when I move around my cursor but when I scroll and therefore my cursor changes position the 'mousemove' is not triggered. I tried to add a eventlistener for scroll but there I can't access these values event.pageX and event.pageY. Do you guys have any trick to trigger a 'mousemove' event after scrolling?
I'm fresh to fabric.js. And I want to draw an Ellipse to the canvas. when I draw it, but I can not select it. I added an event listener to the Object, the event not fired as well.
The code as follow.
import { fabric } from 'fabric'
export class EllipseManager {
isDrawing = false
object: fabric.Ellipse
initPos: fabric.Point
constructor(
private canvas: fabric.Canvas
) {
this.canvas.setCursor('crosshair')
this.canvas.on('mouse:down', this.onMouseDown)
this.canvas.on('mouse:move', this.onMouseMove)
this.canvas.on('mouse:up', this.onMouseUp)
}
destroy() {
// unbind event
}
private onMouseDown = (option) => {
this.isDrawing = true
this.initPos = option.pointer
this.object = new fabric.Ellipse({
rx: 0,
ry: 0,
fill: 'red',
top: option.pointer.y,
left: option.pointer.x,
})
this.canvas.add(this.object)
// for test
this.object.on('mousedown', (e) => console.log(e))
console.log(this.object)
}
private onMouseMove = (event) => {
if (!this.isDrawing) return
const currentPos = event.pointer
const x = currentPos.x - this.initPos.x
const y = currentPos.y - this.initPos.y
this.object.setOptions({
rx: Math.abs(x/2),
ry: Math.abs(y/2)
})
// as x, y is smaller than 0 , so use '+'
if (x < 0) {
this.object.setOptions({
left: this.initPos.x + x,
})
}
if ( y < 0) {
this.object.setOptions({
top: this.initPos.y + y,
})
}
}
private onMouseUp = () => {
this.isDrawing = false
}
}
I searched for lots of demos, I can not find where is different.
My canvas like that picture. those two ellipses cannot select and the event bound cannot be fired.