What I am currently developing
I am using React hook to develop the function to reposition the DOM by drag and drop.
The code up to the halfway point can be found at this link.
https://codesandbox.io/s/exciting-williams-z4ngdz?file=/src/App.js
The code in question
I want to update the value of useRef() in React onMouseDown event, but I cannot update it and it remains at the initial value.
import axios from 'axios';
import React, { useEffect, useState, useRef } from 'react';
import './App.css';
const baseURL = new URL('https://api.ks-portfolio.site/skills');
/**
* #return {Promise<{Object}>}
*/
const apiGet = async () => await axios.get(baseURL);
/**
* #param {MouseEvent} event
* #param {HTMLElement} element
* #return {boolean}
*/
const isHover = (event, element) => {
const clientX = event.clientX;
const clientY = event.clientY;
const rect = element.getBoundingClientRect();
return clientY < rect.bottom && clientY > rect.top && clientX < rect.right && clientX > rect.left;
};
function App() {
const [blocks, setBlocks] = useState([]);
// Manage status with ref.
const state = useRef({
dndItems: [],
keys: new Map(),
dragElement: null,
canCheckHovered: true,
pointerPosition: { x: 0, y: 0 },
sortIds: [],
}).current;
const sortBlocks = (sortItems) => {
const canCheckTime = 300;
/**
* #param {MouseEvent} event
* #returns
*/
const onMouseMove = (event) => {
const { clientX, clientY } = event;
const { dndItems, dragElement, pointerPosition } = state;
if (!dragElement) return;
const x = clientX - pointerPosition.x;
const y = clientY - pointerPosition.y;
const dragStyle = dragElement.element.style;
dragStyle.zIndex = '100';
dragStyle.cursor = 'grabbing';
dragStyle.transform = `translate(${x}px,${y}px)`;
if (!state.canCheckHovered) return;
state.canCheckHovered = false;
setTimeout(() => state.canCheckHovered = true, canCheckTime);
const dragIndex = dndItems.findIndex(({ key }) => key === dragElement.key);
const hoveredIndex = dndItems.findIndex(({ element }, index) => index !== dragIndex && isHover(event, element));
if (hoveredIndex !== -1) {
state.pointerPosition.x = clientX;
state.pointerPosition.y = clientY;
dndItems.splice(dragIndex, 1);
dndItems.splice(hoveredIndex, 0, dragElement);
const { left: x, top: y } = dragElement.element.getBoundingClientRect();
dragElement.position = { x, y };
setBlocks(dndItems.map((item) => item.value));
}
};
/**
* #return {void}
*/
const onMouseUp = () => {
const { dragElement } = state;
if (!dragElement) return;
const dragStyle = dragElement.element.style;
dragStyle.zIndex = '';
dragStyle.cursor = '';
dragStyle.transform = '';
state.dragElement = null;
window.removeEventListener('mouseup', onMouseUp);
window.removeEventListener('mousemove', onMouseMove);
};
return sortItems.map((value) => {
const key = state.keys.get(`item_${value.language_id}`) || Math.random().toString(16);
state.keys.set(`item_${value.language_id}`, key);
if (!state.sortIds.includes(value.language_id)) {
state.sortIds.push(value.language_id);
}
return {
value,
key,
events: {
ref: (element) => {
const { dndItems, dragElement, pointerPosition, sortIds } = state;
if (!element || !dragElement) return;
const block = element.closest('.block');
block.style.transform = '';
const { left: x, top: y } = block.getBoundingClientRect();
const position = { x, y };
const itemIndex = dndItems.findIndex((item) => item.key === key);
if (itemIndex === -1 && sortIds.length > dndItems.length) {
return dndItems.push({ key, value, element: block, position });
}
if (dragElement.key === key) {
const dragX = dragElement.position.x - position.x;
const dragY = dragElement.position.y - position.y;
block.style.transform = `translate(${dragX}px,${dragY}px)`;
pointerPosition.x -= dragX;
pointerPosition.y -= dragY;
}
if (dragElement.key !== key) {
const item = dndItems[itemIndex];
const x = item.position.x - position.x;
const y = item.position.y - position.y;
block.style.transition = '';
block.style.transform = `translate(${x}px,${y}px)`;
requestAnimationFrame(() => {
block.style.transform = '';
block.style.transition = `all ${canCheckTime}ms`;
});
}
state.dndItems[itemIndex] = { key, value, element: block, position };
},
/**
* #param {React.MouseEvent<HTMLElement>} event
* #return {void}
*/
onMouseDown: (event) => {
const element = event.currentTarget.closest('.block');
state.pointerPosition.x = event.clientX;
state.pointerPosition.y = event.clientY;
element.style.transition = '';
element.style.cursor = 'grabbing';
const { left: x, top: y } = element.getBoundingClientRect();
const position = { x, y };
state.dragElement = { key, value, element, position };
console.log('No value is stored in dragElement.')
console.log('onMouseDown', {key, value, element, position});
console.log('state', state);
window.addEventListener('mouseup', onMouseUp);
window.addEventListener('mousemove', onMouseMove);
},
},
};
});
};
useEffect(() => {
async function fetchData() {
const response = await apiGet();
setBlocks(response.data[0].programming);
}
fetchData();
}, []);
return (
<ul className="App">
{sortBlocks(blocks).map((block,i) => {
const { language } = block.value;
return (
<li key={i} className="block">
<div className="panel-body" {...block.events}>
{language}
</div>
</li>
)
})}
</ul>
);
}
export default App;
Corresponding code
The dragElement on line 158 is null.
return sortItems.map((value) => {
events: {
ref: (element) => {
const { dndItems, dragElement, pointerPosition, sortIds } = state;
console.log("events", dragElement); // dragElement = null
}
}
Please let me know what the problem is and how to deal with it.
Related
Here's Mozilla's code for handling touch gestures, such as two finger pinch/zoom, etc.
https://github.com/mdn/dom-examples/blob/main/touchevents/Multi-touch_interaction.html
And here is the live demo:
https://mdn.github.io/dom-examples/touchevents/Multi-touch_interaction.html
(This will only work on mobile—or at least it doesn't work on my Macbook Chrome browser.
I'm trying to replicate the same output using React. Here's what I have:
import React, { useState, useEffect } from 'react';
const TouchEventsContainer = () => {
const [logEvents, setLogEvents] = useState(false);
const [tpCache, setTpCache] = useState([]);
const [logText, setLogText] = useState("");
const [testText, setTestText] = useState("Tap Swipe");
const enableLog = () => {
setLogEvents(!logEvents);
setTestText(logText);
};
const clearLog = () => setLogText('');
const log = (name, ev, printTargetIds) => {
let s = `${name}: touches = ${ev.touches.length} ; targetTouches = ${ev.targetTouches.length} ; changedTouches = ${ev.changedTouches.length}`;
if (printTargetIds) {
s += "";
for (var i = 0; i < ev.targetTouches.length; i++) {
s += `... id = ${ev.targetTouches[i].identifier} <br>`;
}
}
setLogText((prevLogText) => prevLogText + s + "<br>");
};
const updateBackground = (ev) => {
switch (ev.targetTouches.length) {
case 1:
ev.target.style.background = "yellow";
break;
case 2:
ev.target.style.background = "pink";
break;
default:
ev.target.style.background = "lightblue";
}
}
const handlePinchZoom = (ev) => {
if (ev.targetTouches.length === 2 && ev.changedTouches.length === 2) {
let point1 = -1, point2 = -1;
for (let i = 0; i < tpCache.length; i++) {
if (tpCache[i].identifier === ev.targetTouches[0].identifier) point1 = i;
if (tpCache[i].identifier === ev.targetTouches[1].identifier) point2 = i;
}
if (point1 >= 0 && point2 >= 0) {
let diff1 = Math.abs(tpCache[point1].clientX - ev.targetTouches[0].clientX);
let diff2 = Math.abs(tpCache[point2].clientX - ev.targetTouches[1].clientX);
const PINCH_THRESHHOLD = ev.target.clientWidth / 10;
if (diff1 >= PINCH_THRESHHOLD && diff2 >= PINCH_THRESHHOLD)
ev.target.style.background = "green";
} else {
setTpCache([]);
}
}
}
const startHandler = (ev) => {
ev.preventDefault();
if (ev.targetTouches.length === 2) {
const newTpCache = [...tpCache];
for (let i = 0; i < ev.targetTouches.length; i++) {
newTpCache.push(ev.targetTouches[i]);
}
setTpCache(newTpCache);
}
if (logEvents) {
log("touchStart", ev, true);
}
updateBackground(ev);
};
const moveHandler = (ev) => {
ev.preventDefault();
if (logEvents) {
log("touchMove", ev, false);
}
if (!(ev.touches.length === 2 && ev.targetTouches.length === 2)) {
updateBackground(ev);
}
ev.target.style.outline = "dashed";
handlePinchZoom(ev);
};
const endHandler = (ev) => {
ev.preventDefault();
if (logEvents) {
log(ev.type, ev, false);
}
if (ev.targetTouches.length === 0) {
ev.target.style.background = "white";
ev.target.style.outline = "1px solid black";
}
};
const setHandlers = (name) => {
const el = document.getElementById(name);
el.ontouchstart = startHandler;
el.ontouchmove = moveHandler;
el.ontouchcancel = endHandler;
el.ontouchend = endHandler;
};
useEffect(() => {
setHandlers("target1");
setHandlers("target2");
setHandlers("target3");
setHandlers("target4");
}, []);
return (
<>
<h1>Multi-touch interaction</h1>
<div id="target1">{testText}</div>
<div id="target2"> Tap, Hold or Swipe me 2</div>
<div id="target3"> Tap, Hold or Swipe me 3</div>
<div id="target4"> Tap, Hold or Swipe me 4</div>
<button id="log" onClick={() => enableLog()}>Start/Stop event logging</button>
<button id="clearlog" onClick={() => clearLog()}>Clear the log</button>
<p></p>
<div>{logText}</div>
</>
);
};
export default TouchEventsContainer;
I have 2 problems:
My log output (currently in a ) doesn't produce anything. I think it's because my string contains HTML. I tried using "dangerousInnerHTML" and some other things, but to no avail.
The two finger pinch-zoom (when background color turns green) doesn't seem to be detected.
What is wrong with my React conversion?
Also I'm currently rendering my TouchEventsContainer within the context of a different JS view (on my app) for testing, but I don't think that's the issue.
Edit: I am having trouble with my rendering, every time I moved my sprite, the Frame animation repeats. I am not sure what is causing this.
For the camera, I want the camera to follow the mouse and point in the direction of which the sprite is facing. Basically a third person shooter perspective.
The tutorials I watched were using Typescript and I am making this is NextJs.
Here is my code.
Shooter.js
import React, { useRef, useEffect } from "react";
import { useGLTF, useAnimations, OrbitControls } from "#react-three/drei";
import { userInput } from "../hooks/userInput";
import { useFrame, useThree } from "#react-three/fiber";
import * as THREE from 'three';
let walkDirection = new THREE.Vector3();
let rotateAngle = new THREE.Vector3(0, 1, 0);
let rotateQuaternion = new THREE.Quaternion();
let cameraTarget = new THREE.Vector3();
const directionOffset = ({ forward, backward, left, right}) => {
var directionOffset = 0; // w
if (forward) {
if (left) {
directionOffset = Math.PI / 4; // w+a
} else if (right) {
directionOffset = -Math.PI / 4; // w+d
}
} else if (backward) {
if (left) {
directionOffset = Math.PI / 4 + Math.PI / 2; // s+a
} else if (right) {
directionOffset = -Math.PI / 4 - Math.PI / 2; // s+d
} else {
directionOffset = Math.PI // s
}
} else if (left) {
directionOffset = Math.PI / 2 // a
} else if (right) {
directionOffset = -Math.PI / 2 // d
}
return directionOffset;
};
export function Model() {
const { forward, backward, left, right, jump, shift, crouch } = userInput();
const model = useGLTF("/shooter.glb");
const { actions } = useAnimations(model.animations, model.scene);
model.scene.scale.set(1, 1, 1);
model.scene.traverse((object) => {
if (object.isMesh) {
object.castShadow = true;
}
})
const currentAction = useRef("");
const controlRef = useRef();
const camera = useThree((state) => state.camera);
const updateCamereTarget = (moveX, moveZ) => {
// move camera
camera.position.x += moveX;
camera.position.z += moveZ;
// update camera target
cameraTarget.x = model.scene.position.x;
cameraTarget.y = model.scene.position.y + 5 ;
cameraTarget.z = model.scene.position.z;
if (controlRef.current) controlRef.current.target = cameraTarget;
}
useEffect(() => {
console.log(actions)
let action = ""
if (forward || backward || left || right) {
action = "walking";
if (shift) {
action = "running";
}
} else if (jump) {
action = "jumping";
} else if (crouch) {
action = "crouchIdle";
} else {
action = "idle";
}
if (currentAction.current != action) {
const nextActionToPlay = actions[action];
const current = actions[currentAction.current];
if (current) current.fadeOut(0.2);
if(nextActionToPlay) nextActionToPlay.reset().fadeIn(0.2).play();
currentAction.current = action;
}
}, [forward, backward, left, right, jump, shift]);
useFrame((state, delta) => {
if (currentAction.current == "running" || currentAction.current == "walking") {
// calculate towards camera direction
let angleYCameraDirection = Math.atan2(
camera.position.x - model.scene.position.x,
camera.position.z - model.scene.position.z
);
// diagonal movement angle offset
let newDirectionOffset = directionOffset({
forward,
backward,
left,
right
});
// rotate model
rotateQuaternion.setFromAxisAngle(
rotateAngle,
angleYCameraDirection + newDirectionOffset
);
model.scene.quaternion.rotateTowards(rotateQuaternion, 0.2);
// calculate direction
camera.getWorldDirection(walkDirection);
walkDirection.y = 0;
walkDirection.normalize();
walkDirection.applyAxisAngle(rotateAngle, newDirectionOffset);
// walk/iddle velocity
const velocity = currentAction.current == "running" ? 10 : 5;
// move model and camera
const moveX = walkDirection.x + velocity * delta;
const moveZ = walkDirection.z + velocity * delta;
model.scene.position.x += moveX;
model.scene.position.z += moveZ;
updateCamereTarget(moveX, moveZ);
}
});
return (
<>
{/* <OrbitControls ref={controlRef} /> */}
<primitive object={model.scene} />
</>
);
}
index.js
import { Physics } from '#react-three/cannon'
import { Canvas , useFrame} from "#react-three/fiber";
import { Model } from '../components/Shooter';
import Ground from '../components/Ground';
import PointLight from '../components/Light';
export default function Home() {
return (
<div>
<Canvas>
<ambientLight color={"white"} intensity={0.3} />
<PointLight position={[0, 50, 0]} />
<Model/>
{/* <Ground /> */}
</Canvas>
</div>
);
}
userInput.js
import { useEffect, useState } from 'react';
export const userInput = () => {
const [input, setInput] = useState({
forward: false,
backward: false,
left: false,
right: false,
crouch: false,
shift: false,
jump: false,
});
const findkey = (key) => keys[key];
const keys = {
KeyW: "forward",
KeyS: "backward",
KeyA: "left",
KeyD: "right",
KeyC: "crouch",
ShiftLeft: "shift",
Space: "jump",
};
useEffect(() => {
const handleKeyDown = (e) => {
setInput((m) => ({...m, [findkey(e.code)]: true}));
};
const handleKeyUp = (e) => {
setInput((m) => ({...m, [findkey(e.code)]: false}));
};
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('keyup', handleKeyUp);
return () => {
document.addEventListener("keydown", handleKeyDown);
document.addEventListener("keyup", handleKeyUp);
};
}, []);
return input;
};
I have two events drag start and drag end. At the time of drag start I am deciding whether to move it or copy depending on some logic and setSelectedElement is running and setting the new element to it but as you can see console of new element in drag end and console for previously selected in drag start are both empty.
after some debugging, I found out it is turning to an empty string supplied in the very beginning of use state.
App.js
export default function App() {
const [selectedElement, setSelectedElement] = useState("");
const [diffX, setDiffX] = useState(0);
const [diffY, setDiffY] = useState(0);
const [group, setGroup] = useState([]);
useEffect(() => {
console.log("selected element changed");
}, [selectedElement]);
const handleDragStart = (event) => {
console.log("drag start ");
// console.log("class list", event.currentTarget.classList);
console.log("previous selected element is ", selectedElement);
let functionalityType = "";
let elementSelected = "";
let classList = event.currentTarget.classList;
for (let i = 0; i < classList.length; i++) {
//move element
if (classList[i].includes("group")) {
functionalityType = "move";
break;
}
}
if (functionalityType !== "move") {
console.log("inside copy");
elementSelected = event.currentTarget.cloneNode(true);
elementSelected.style.position = "absolute";
elementSelected.addEventListener("dragstart", handleDragStart);
elementSelected.addEventListener("dragend", handleDragEnd);
} else {
console.log("inside move");
elementSelected = event.currentTarget;
// console.log("event current target", event.currentTarget);
}
setDiffX(event.clientX - event.currentTarget.getBoundingClientRect().left);
setDiffY(event.clientY - event.currentTarget.getBoundingClientRect().top);
setSelectedElement(elementSelected);
};
const handleDragEnd = (event) => {
console.log("drag end");
let newElement = selectedElement;
console.log("new element is", newElement);
newElement.style.top = event.clientY - diffY + "px";
newElement.style.left = event.clientX - diffX + "px";
document.getElementById("MidArea").appendChild(newElement);
}
return (
<div
draggable={true}
onDragStart={props.handleDragStart}
onDragEnd={props.handleDragEnd}
className="draggable"
>);
}
Is there any reason you're doing setSelectedElement(elementSelected); outside else statement
Your code lacks some details to debug and find the missing piece however, drag and drop, I had this implemented sometime back, try the below
const stopDrag = () => {
document.onmouseup = null;
document.onmousemove = null;
};
export const dragComponent = (dragRef) => {
let coordinateOne = 0;
let coordinateTwo = 0;
let coordinateThree = 0;
let coordinateFour = 0;
const dragHandle = dragRef.current;
const elementDrag = (event) => {
event.preventDefault();
coordinateOne = coordinateThree - event.clientX;
coordinateTwo = coordinateFour - event.clientY;
coordinateThree = event.clientX;
coordinateFour = event.clientY;
dragHandle.style.top = `${dragHandle.offsetTop - coordinateTwo}px`;
dragHandle.style.left = `${dragHandle.offsetLeft - coordinateOne}px`;
};
const dragDown = (event) => {
event.preventDefault();
coordinateThree = event.clientX;
coordinateFour = event.clientY;
document.onmouseup = stopDrag;
document.onmousemove = elementDrag;
};
dragHandle.onmousedown = dragDown;
};
function MyComponent(){
const dragRef = useRef();
useEffect(() => {
dragComponent(dragRef);
}, []);
return (
<div draggable ref={dragRef}>
<h1> I am draggable..drag me around</h1>
</div>
)
}
Did you try this:
setDiffX(event.clientX - event.currentTarget.getBoundingClientRect().left);
setDiffY(event.clientY - event.currentTarget.getBoundingClientRect().top);
dragElementRef.current = elementSelected;
setSelectedElement(elementSelected);
};
let dragElementRef = useRef(selectedElement);
....
....
const handleDragEnd = (event) => {
//setTimeout(() => {
console.log("drag end");
let newElement = dragElementRef.current;
console.log("new element is", newElement);
newElement.style.top = event.clientY - diffY + "px";
newElement.style.left = event.clientX - diffX + "px";
document.getElementById("MidArea").appendChild(newElement);
//},500);
}
What I think is happening is your handleDragEnd() is being called before the next render cycle with updated value of state exists.
Try adding a setTimeout to add some delay.
I have a custom cursor component based off Codrops https://tympanus.net/codrops/2019/01/31/custom-cursor-effects/.
I have it working nicely on every page except one, which is a portfolio page that dynamically shows/hides content based on the current state.
If I change states (show component not initially loaded + hide initial component OR hide component not initially loaded + show initially loaded content), the custom cursor no longer reacts to the class name.
I am showing/hiding the content based on the current state of the component.
I've tried to re "init" the cursor, but that causes problems with the size/position of the custom cursor.
I understand the Cursor.js doesn't know when to "look" for the new classes after the state change, but I can't figure out how to make it "watch" for the changes, and since re initializing the cursor isn't working, I'm not sure what I should try. Should I try an eventListener that forces the cursor to look at the classes again?
Ideally the custom cursor will interact with both dynamic and non-dynamic elements, but I'm at a point that I question if this is even possible.
Any advice would be greatly appreciated. Or if anyone has an example of a React Custom Cursor that works in this scenario, I'd love to check it out.
Portfolio.js
render() {
return (
<div>
<div className="cursor">
<div className="circle-cursor circle-cursor--inner"></div>
</div>
<canvas
className="circle-cursor circle-cursor--outer"
resize="true"
></canvas>
//CURSOR
<CustomCursor />
//SHOW ALL PROJECTS ON STATE CHANGE
{ this.state.showAllItems ?
<div className={styles.grid}>
{list.map(item => (
<div key={item.index} className={styles.grid__item}>
<a href="#/a" key={item.index} value={item.index} onClick={e => this.onClick(e, item)} >
<div key={item.index} className={styles.glitch + " portfolioLinkHover " + styles.glitchStyle1}>
<div className={styles.glitch__img + " img"} style={{background: 'url('+ item.imgSrc + ') no-repeat 50% 0'}}></div>
<div className={styles.glitch__img + " img"} style={{background: 'url('+ item.imgSrc + ') no-repeat 50% 0'}}></div>
</div>
</a>
<h2>{item.itemTitle} <span>{item.itemSubtitle}</span></h2>
</a>
</p>
</div>
))}
</div>
: null }
//SHOW PROJECT ON STATE CHANGE
{ this.state.currentProject ?
<div className={styles.projectContainer + " portfolioLinkHover"}>
<Project />
</div> : null }
</div>
);
}
Cursor.js
import React, {Component} from 'react';
import Util from "./utils/util";
import './styles/cursor.scss';
import TweenMax from 'gsap';
class CustomCursor extends Component{
constructor(props) {
super(props);
this.state = { isOn: this.props.isOn };
}
componentDidMount(){
if(this.state.isOn == "true"){
this.initHovers();
}
else{
this.initCursor();
this.initCanvas();
this.initHovers();
}
}
initCursor() {
this.clientX = -100;
this.clientY = -100;
this.innerCursor = document.querySelector(".circle-cursor--inner");
this.outerCursor = document.querySelector(".custom-cursor--outer");
this.outerCursorSpeed = 1;
this.lastX = 0;
this.lastY = 0;
this.isStuck = false;
this.showCursor = false;
const { paper } = window;
const unveilCursor = e => {
this.group.position = new paper.Point(e.clientX, e.clientY);
setTimeout(() => {
this.outerCursorSpeed = 0.2;
}, 100);
this.showCursor = true;
};
document.addEventListener("mousemove", unveilCursor);
document.addEventListener("mousemove", e => {
this.clientX = e.clientX;
this.clientY = e.clientY;
});
const render = () => {
TweenMax.set(this.innerCursor, {
x: this.clientX,
y: this.clientY
});
if (this.showCursor) {
document.removeEventListener("mousemove", unveilCursor);
}
requestAnimationFrame(render);
};
requestAnimationFrame(render);
}
initCanvas() {
const { paper, SimplexNoise } = window;
const canvas = document.querySelector(".circle-cursor--outer");
const shapeBounds = {
width: 75,
height: 75
};
paper.setup(canvas);
const strokeColor = "rgba(255, 0, 0, 0.5)";
const strokeWidth = 1;
const segments = 8;
const radius = 25;
const noiseScale = 150; // speed
const noiseRange = 4; // range of distortion
let isNoisy = false;
const polygon = new paper.Path.RegularPolygon(
new paper.Point(0, 0),
segments,
radius
);
polygon.strokeColor = strokeColor;
polygon.strokeWidth = strokeWidth;
polygon.smooth();
this.group = new paper.Group([polygon]);
this.group.applyMatrix = false;
const noiseObjects = polygon.segments.map(() => new SimplexNoise());
let bigCoordinates = [];
paper.view.onFrame = event => {
if (!this.isStuck) {
// move circle around normally
this.lastX = Util.lerp(this.lastX, this.clientX, this.outerCursorSpeed);
this.lastY = Util.lerp(this.lastY, this.clientY, this.outerCursorSpeed);
this.group.position = new paper.Point(this.lastX, this.lastY);
} else if (this.isStuck) {
// fixed position on a nav item
this.lastX = Util.lerp(this.lastX, this.stuckX, this.outerCursorSpeed);
this.lastY = Util.lerp(this.lastY, this.stuckY, this.outerCursorSpeed);
this.group.position = new paper.Point(this.lastX, this.lastY);
}
if (this.isStuck && polygon.bounds.width < shapeBounds.width) {
// scale up the shape
polygon.scale(1.08);
} else if (!this.isStuck && polygon.bounds.width > 30) {
// remove noise
if (isNoisy) {
polygon.segments.forEach((segment, i) => {
segment.point.set(bigCoordinates[i][0], bigCoordinates[i][1]);
});
isNoisy = false;
bigCoordinates = [];
}
// scale down the shape
const scaleDown = 0.92;
polygon.scale(scaleDown);
}
// while stuck and when big, do perlin noise
if (this.isStuck && polygon.bounds.width >= shapeBounds.width) {
isNoisy = true;
// first get coordinates of large circle
if (bigCoordinates.length === 0) {
polygon.segments.forEach((segment, i) => {
bigCoordinates[i] = [segment.point.x, segment.point.y];
});
}
// calculate noise value for each point at that frame
polygon.segments.forEach((segment, i) => {
const noiseX = noiseObjects[i].noise2D(event.count / noiseScale, 0);
const noiseY = noiseObjects[i].noise2D(event.count / noiseScale, 1);
const distortionX = Util.map(noiseX, -1, 1, -noiseRange, noiseRange);
const distortionY = Util.map(noiseY, -1, 1, -noiseRange, noiseRange);
const newX = bigCoordinates[i][0] + distortionX;
const newY = bigCoordinates[i][1] + distortionY;
segment.point.set(newX, newY);
});
}
// hover state for main nav items
if (this.fillOuterCursor && polygon.fillColor !== strokeColor) {
polygon.fillColor = strokeColor;
polygon.strokeColor = "transparent";
} else if (!this.fillOuterCursor && polygon.fillColor !== "transparent") {
polygon.strokeColor = "rgba(255, 0, 0, 0.5)";
polygon.fillColor = "transparent";
}
// hover state for portfolio nav items
if (this.isOnPortfolioItem) {
polygon.fillColor = strokeColor;
polygon.strokeColor = "rgba(255, 226, 0, 0.5)";
// scale up the shape
polygon.bounds.width = 45;
polygon.bounds.height = 45;
} else if (!this.fillOuterCursor) {
polygon.strokeColor = "rgba(255, 0, 0, 0.5)";
polygon.fillColor = "transparent";
}
polygon.smooth();
};
}
initHovers() {
const handleMouseEnter = e => {
const navItem = e.currentTarget;
const navItemBox = navItem.getBoundingClientRect();
this.stuckX = Math.round(navItemBox.left + navItemBox.width / 2);
this.stuckY = Math.round(navItemBox.top + navItemBox.height / 2);
this.isStuck = true;
};
const handleMouseLeave = () => {
this.isStuck = false;
};
const linkItems = document.querySelectorAll(".browser-window__link");
linkItems.forEach(item => {
item.addEventListener("mouseenter", handleMouseEnter);
item.addEventListener("mouseleave", handleMouseLeave);
});
const mainNavItemMouseEnter = () => {
this.outerCursorSpeed = 0.8;
this.fillOuterCursor = true;
};
const mainNavItemMouseLeave = () => {
this.outerCursorSpeed = 0.2;
this.fillOuterCursor = false;
};
const portfolioItems = document.querySelectorAll(".portfolioLinkHover");
portfolioItems.forEach(item => {
item.addEventListener("mouseenter", handleLinkEnter);
item.addEventListener("mouseleave", handleLinkLeave);
});
}
render() {
return (
<div></div>
);
}
}
export default CustomCursor;
i'm setting my state after calculating it in mouseup and mouse leave event but it's not updating it how to solve in mouseup and mouse leave
Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
onMouseMove = (e) => {
if (!this.isDown) {
return;
}
e.preventDefault();
var x = e.pageX - this.slider.current.offsetLeft;
var walk = x - this.startX;
this.startX = x;
var z = walk;
var finalValue = this.state.left + (z / 3);
finalValue = Math.floor(finalValue * 100) / 100;
this.setState({ left: finalValue }, () => { });
this.setState({ percent: false })
}
onMouseLeave = () => {
this.isDown = false;
var left = this.state.left;
for (let i = 0; i < 6; i++) {
this.el = 306*[i]
console.log(this.el);
if (left<=this.el) {
this.setState({left:this.el},()=>{})
// return
}
console.log(this.state.left);
}
}
onMouseUp = () => {
this.isDown = false;
this.slider.current.style.cursor = 'pointer';
var left = this.state.left;
for (let i = 0; i < 6; i++) {
this.el = 306*[i]
console.log(this.el);
if (left<=this.el) {
this.setState({left:this.el},()=>{})
// return
}
console.log(this.state.left);
}
}
render() {
return (
<div className="slider-wrapper" >
<div onMouseDown={this.onMouseDown}
style={this.state.percent ? this.goLeftPercent() : this.mouseMove()}
onMouseUp={this.onMouseUp} onMouseLeave={this.onMouseLeave}
onMouseMove={this.onMouseMove} ref={this.slider} className="slider-container">
)
}
If I understand correctly you can try the below changes.
Constructor
constructor() {
super();
this.onMouseLeave = this.onMouseLeave.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
}
Render()
<li onMouseUp={this.onMouseUp} onMouseLeave={this.onMouseLeave} >