React Three fiber shows the display in blank from i-pad - reactjs

I am building my Portfolio using React three fiber, it shows the display on mobile and laptop or PC.
But when I'm trying to see on i-pad the display is blank.
I am trying to solve this problem, but I could not find any solution.
I will be happy if can give me some advice.
This is my code
import React, { useState, useRef, useEffect } from 'react';
import * as THREE from 'three';
import { extend, Canvas, useFrame } from 'react-three-fiber';
import * as meshline from 'threejs-meshline';
import Typical from 'react-typical';
// firebase
import firebase from '../firebase/firebase.utils';
// custom hook
import useAuthor from '../hooks/getAuthor';
// component
import Spinner from './Spinner';
import '../assets/styles/components/ThreeFiber.scss';
extend(meshline);
const numLines = 100;
const lines = new Array(numLines).fill();
const colors = ['#f6f6f6', '#fff8cd', '#6495ED'];
function Fatline() {
const material = useRef();
const [color] = useState(
() => colors[parseInt(colors.length * Math.random())]
);
const [ratio] = useState(() => 0.6 + 0.7 * Math.random());
const [width] = useState(() => Math.max(0.1, 0.1 * Math.random()));
// Calculate wiggly curve
const [curve] = useState(() => {
const pos = new THREE.Vector3(
30 - 60 * Math.random(),
-5,
10 - 20 * Math.random()
);
const points = new Array(30)
.fill()
.map(() =>
pos
.add(
new THREE.Vector3(
2 - Math.random() * 4,
4 - Math.random() * 2,
5 - Math.random() * 10
)
)
.clone()
);
return new THREE.CatmullRomCurve3(points).getPoints(500);
});
// Hook into the render loop and decrease the materials dash-offset
useFrame(() => {
material.current.uniforms.dashOffset.value -= 0.0005;
});
return (
<mesh>
<meshLine attach="geometry" vertices={curve} />
<meshLineMaterial
attach="material"
ref={material}
transparent
depthTest={false}
lineWidth={width}
color={color}
dashArray={0.2}
dashRatio={ratio}
/>
</mesh>
);
}
function Scene() {
let group = useRef();
let theta = 0;
// Hook into the render loop and rotate the scene a bit
useFrame(() => {
group.current.rotation.set(
0,
8 * Math.sin(THREE.Math.degToRad((theta += 0.02))),
0
);
});
return (
<group ref={group}>
{lines.map((_, index) => (
<Fatline key={index} />
))}
</group>
);
}
const ThreeFiber = React.memo(() => {
const collectionRef = firebase.firestore().collection('author');
const authorCollection = useAuthor(collectionRef);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
}, []);
return (
<div className="Hero_container">
<Canvas
camera={{ position: [0, 60, 10], fov: 150 }}
resize={{ scroll: false }}
>
<Scene />
</Canvas>
{loading ? (
authorCollection.map((author) => (
<h4 key={author.informations} className="Hero_content">
Hi! <br /> I'm {author.informations.name}, <br />
<Typical
loop={Infinity}
wrapper="b"
steps={[
'Front-end Developer with React',
1500,
'a self-taught Web Engineer',
1500,
]}
/>
</h4>
))
) : (
<Spinner />
)}
</div>
);
});
Thank you for your help.....

Related

React Link framer motion animation with AnimatePresence

I have a Navigation component in which the Menu Items float in separately on load and float out on click.
When I added Router and changed the items to Links, the exit animation didn't work because it loaded the new Route component right away.
I want to keep the items individual animation with Link functionality.
Here is the link:
https://codesandbox.io/s/elastic-leaf-fxsswo?file=/src/components/Navigation.js
Code:
export const Navigation = () => {
const navRef = useRef(null);
const onResize = () => {
setIsColumn(window.innerWidth <= 715);
};
const [clickOnMenu, setClick] = useState(false);
const [itemtransition, setTransition] = useState(
Array(menuItems.length).fill(0)
);
const [isColumn, setIsColumn] = useState(window.innerWidth <= 715);
const click = (e) => {
const copy = [...itemtransition];
const index = e.target.id;
setTransition(copy.map((e, i) => (Math.abs(index - i) + 1) / 10));
setTimeout(() => setClick(true), 50);
};
useEffect(() => {
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);
return (
<AnimatePresence exitBeforeEnter>
{!clickOnMenu && (
<Nav ref={navRef}>
{menuItems.map((e, i) => {
const text = Object.keys(e)[0];
const value = Object.values(e)[0];
return (
<Item
id={i}
key={value}
animate={{
x: 0,
y: 0,
opacity: 1,
transition: { delay: (i + 1) / 10 }
}}
initial={{
x: isColumn ? 1000 : 0,
y: isColumn ? 0 : 1000,
opacity: 0
}}
exit={{
x: isColumn ? -1000 : 0,
y: isColumn ? 0 : -1000,
opacity: 0,
transition: { delay: itemtransition[i] }
}}
onClick={click}
>
{/*<Link to={`/${value}`}>{text}</Link>*/}
{text}
</Item>
);
})}
</Nav>
)}
</AnimatePresence>
);
};
In the sandbox in Navigation.js 69-70. row:
This is the desired animation.
69. {/*<Link to={`/${value}`}>{text}</Link>*/}
70. {text}
But when I use Link there is no exit animation
69. <Link to={`/${value}`}>{text}</Link>
70. {/*text*/}
Is there a workaround or I should forget router-dom.
Thank you in forward!
This may be a bit hackish, but with routing and transitions sometimes that is the nature. I suggest rendering the Link so the semantic HTML is correct and add an onClick handler to prevent the default navigation action from occurring. This allows any transitions/animations to go through. Then update the click handler of the Item component to consume the link target and issue an imperative navigation action on a timeout to allow transitions/animations to complete.
I used a 750ms timeout but you may need to tune this value to better suit your needs.
Example:
...
import { Link, useNavigate } from "react-router-dom";
...
export const Navigation = () => {
const navRef = useRef(null);
const navigate = useNavigate(); // <-- access navigate function
...
const click = target => (e) => { // <-- consume target
const copy = [...itemtransition];
const index = e.target.id;
setTransition(copy.map((e, i) => (Math.abs(index - i) + 1) / 10));
setTimeout(() => {
setClick(true);
}, 50);
setTimeout(() => {
navigate(target); // <-- navigate after some delta
}, 750);
};
...
return (
<AnimatePresence exitBeforeEnter>
{!clickOnMenu && (
<Nav ref={navRef}>
{menuItems.map((e, i) => {
const text = Object.keys(e)[0];
const value = Object.values(e)[0];
return (
<Item
...
onClick={click(`/${value}`)} // <-- pass target to handler
>
<Link
to={`/${value}`}
onClick={e => e.preventDefault()} // <-- prevent link click
>
{text}
</Link>
</Item>
);
})}
</Nav>
)}
</AnimatePresence>
);
};
...

On react simple maps, is there a way to get coordinates of location in map by clicking on one?

I'm trying to make an interactive page in which there's a world map, and when you click on a location it creates a marker and saving the coordinates.
Any idea on how to achieve that?
After spending a weekend looking for a solution, this should solve the problem. Works with most events too, not just click.
Same code in Sandbox
import React, { useState } from "react";
import {
ComposableMap,
Geographies,
Geography,
Marker,
ZoomableGroup
} from "react-simple-maps";
import { geoPath } from "d3-geo";
const geoUrl =
"https://raw.githubusercontent.com/zcreativelabs/react-simple-maps/master/topojson-maps/world-110m.json";
const markersBegin = [
{
markerOffset: -30,
name: "Buenos Aires",
coordinates: [-58.3816, -34.6037]
}
];
const MapChart = () => {
const [scale, setSclae] = useState(1);
const [markers, setMarkers] = useState(markersBegin);
return (
<ComposableMap projection="geoMercator">
<ZoomableGroup zoom={1} onMoveEnd={({ zoom }) => setSclae(zoom)}>
<Geographies geography={geoUrl}>
{({ geographies, projection }) =>
geographies
.filter((d) => d.properties.REGION_UN === "Americas")
.map((geo) => {
const onGeoEventFactory = (handleCoordinates) => {
const gPath = geoPath().projection(projection);
return (evt) => {
const dim = evt.target.getBoundingClientRect();
const cx = evt.clientX - dim.left;
const cy = evt.clientY - dim.top;
const [geoX, geoY] = gPath.bounds(geo)[0];
//we need root SVG element of our map
const svg = evt.nativeEvent.path[4];
//adjust for SVG width on the page / internal rendering width
const adjustScale =
scale * (svg.clientWidth / svg.viewBox.baseVal.width);
// these are the coords in SVG coordinate system
const clickCoordsInsideSvg = [
geoX + (cx / adjustScale),
geoY + (cy / adjustScale)
];
// 'unproject' the SVG coords to get lat and long
handleCoordinates(projection.invert(clickCoordsInsideSvg));
};
};
return (
<Geography
key={geo.rsmKey}
geography={geo}
fill="#EAEAEC"
stroke="#D6D6DA"
onClick={onGeoEventFactory((coordinates) => {
const newMarkers = [
...markers,
{
markerOffset: -30,
name: "Marker",
coordinates
}
];
setMarkers(newMarkers);
})}
/>
);
})
}
</Geographies>
{markers.map(({ name, coordinates, markerOffset }) => (
<Marker key={name} coordinates={coordinates}>
<g
fill="none"
stroke="#FF5533"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
transform="translate(-12, -24)"
>
<circle cx="12" cy="10" r="3" />
<path d="M12 21.7C17.3 17 20 13 20 10a8 8 0 1 0-16 0c0 3 2.7 6.9 8 11.7z" />
</g>
<text
textAnchor="middle"
y={markerOffset}
style={{ fontFamily: "system-ui", fill: "#5D5A6D" }}
>
{name}
</text>
</Marker>
))}
</ZoomableGroup>
</ComposableMap>
);
};
export default MapChart;

Derive a local state in React

I would like to derive a local state in my React app and use it in the same component.
import * as React from 'react'
import * as ReactDOM from 'reactDOM'
function App(){
const [origins, setorigins] = React.useState([1, 45, 7, 11]) // source of truth
const [muted, setmuted] = React.useState([]) // derive from source
React.useEffect(() => {
evolve()
}, [origins])
// Transforms [23] => [{origin: 23, muted: 24}]
const evolve = () => {
setmuted(origins.map(o => ({origin: o, muted: o += 1})))
}
const addEntity = () => {
// add random integer between 1 and 45
const newEntity = Math.floor(Math.random() * 45 ) + 1
setorigins([...origins, newEntity])
}
return (
<>
<button onClick={addEntity}>Add entity random</button>
{muted.map(m => (
<div className="entity">
<p>Origin : {m.origin}</p>
<p>Muted: {m.muted}</p>
</div>
)) }
</>
)
}
ReactDOM.render(
<App />,
document.getElementById('reactz')
)
Can you improve this code above ?
Any performance issues I should be warned of ?
Definitely I was overthinking. The simpliest way IMO is to copy and transform the source of truth on the fly as described by Kent on his blog.
import * as React from 'react'
import * as ReactDOM from 'reactDOM'
function App(){
const [origins, setorigins] = React.useState([1, 45, 7, 11]) // source of truth
const muted = evolve(origins) // derived state (useMemo ?)
// Transforms [23] => [{origin: 23, muted: 24}]
function evolve(data) {
return data.map(o => ({origin: o, muted: o += 1}))
}
const addEntity = () => {
// add random integer between 1 and 45
const newEntity = Math.floor(Math.random() * 45 ) + 1
setorigins([...origins, newEntity])
}
return (
<>
<button onClick={addEntity}>Add entity random</button>
{muted.map(m => (
<div className="entity">
<p>Origin : {m.origin}</p>
<p>Muted: {m.muted}</p>
</div>
)) }
</>
)
}
ReactDOM.render(
<App />,
document.getElementById('reactz')
)
And for computational optimization, make use of useMemo hook.

react konva keep one layer fixed while zoom on stage

I'm developing a map (green rectangle ) and a path drawn over it ( black lines ) in Konva:
The map has a fixed dimensions while the path is get from an API call.
I want to be able to zoom the map either with the mousescroll or by clicking the buttons + o - that I created.
I follow this example how to react-konva zooming on scroll in order to implement the zoom, but when I scroll, also the buttons change their positions, see image below.
I would like to keep the buttons fixed at the bottom side of the map.
This is a piece of my code, I defined some custom components as Map, Ground, WholePath, GroundButtons and Button:
export const Map = ( props ) => {
const [mouseScale, setMouseScale] = useState(1);
const [x0, setX0] = useState(0);
const [y0, setY0] = useState(0);
const scaleBy = 1.02;
const handleWheel = (e) => {
const group = e.target.getStage();
e.evt.preventDefault();
const mousePointTo = {
x: group.getPointerPosition().x / mouseScale - group.x() / mouseScale,
y: group.getPointerPosition().y / mouseScale - group.y() / mouseScale
};
const newScale = e.evt.deltaY < 0 ? mouseScale * scaleBy : mouseScale / scaleBy;
setMouseScale(newScale);
setX0(-(mousePointTo.x - group.getPointerPosition().x / newScale) * newScale );
setY0(-(mousePointTo.y - group.getPointerPosition().y / newScale) * newScale );
};
const SimulateMouseWheel = (e, BtnType, mScale = scaleBy) => {
const newScale = BtnType > 0 ? mouseScale * mScale : mouseScale / mScale;
setMouseScale(newScale);
};
const onClickPlus = (e) => {
SimulateMouseWheel(e, +1, 1.08);
};
const onClickMinus = (e) => {
SimulateMouseWheel(e, -1, 1.08);
};
return (
<Stage
width={800}
height={400}
draggable={true}
onWheel={handleWheel}
scaleX={mouseScale}
scaleY={mouseScale}
x={x0}
y={y0}
>
<Layer>
<Ground pitch={props.pitch}/>
<WholePath path={props.path}/>
</Layer>
<Layer>
<GroundButtons reference={props.reference} onClickMinus={onClickMinus} onClickPlus={onClickPlus} />
</Layer>
</Stage>
)
}
const GroundButtons = ( props ) => {
return (
<Group>
<Button xText={10} yText={5} text={"+"} x={790} y={360} side={30} onClick={props.onClickPlus}/>
<Button xText={12} yText={5} text={"-"} x={790} y={360} side={30} onClick={props.onClickMinus}/>
</Group>
)
}
const Button = props => {
return (
<Group x={props.x} y={props.y} onClick={props.onClick}>
<Rect
width={props.side}
height={props.side}
fill='#f2f1f0'
stroke='#777'
strokeWidth={1}
/>
<Text
x={props.xText}
y={props.yText}
fontSize={20}
text={props.text}
stroke='#777'
align="center"
strokeWidth={1}
/>
</Group>
)
}
This is the solution I implemented:
Thanks to Ondolin's answer.
export const Map = ( props ) => {
const [mouseScale, setMouseScale] = useState(1);
const [x0, setX0] = useState(0);
const [y0, setY0] = useState(0);
const scaleBy = 1.02;
const handleWheel = (e) => {
const group = e.target.getStage();
e.evt.preventDefault();
const mousePointTo = {
x: group.getPointerPosition().x / mouseScale - group.x() / mouseScale,
y: group.getPointerPosition().y / mouseScale - group.y() / mouseScale
};
const newScale = e.evt.deltaY < 0 ? mouseScale * scaleBy : mouseScale / scaleBy;
setMouseScale(newScale);
setX0(-(mousePointTo.x - group.getPointerPosition().x / newScale) * newScale );
setY0(-(mousePointTo.y - group.getPointerPosition().y / newScale) * newScale );
};
const SimulateMouseWheel = (e, BtnType, mScale = scaleBy) => {
const newScale = BtnType > 0 ? mouseScale * mScale : mouseScale / mScale;
setMouseScale(newScale);
};
const onClickPlus = (e) => {
SimulateMouseWheel(e, +1, 1.08);
};
const onClickMinus = (e) => {
SimulateMouseWheel(e, -1, 1.08);
};
return (
<div>
<Stage
width={800}
height={400}
draggable={true}
onWheel={handleWheel}
scaleX={mouseScale}
scaleY={mouseScale}
x={x0}
y={y0}
>
<Layer>
<Ground pitch={props.pitch}/>
<WholePath path={props.path}/>
</Layer>
<Layer>
<GroundButtons reference={props.reference} onClickMinus={onClickMinus} onClickPlus={onClickPlus} />
</Layer>
</Stage>
<button className="ground_controls" onClick={onClickPlus}><i className="fa fa-plus" aria-hidden="true"></i></button>
<button className="ground_controls" onClick={onClickMinus}><i className="fa fa-minus" aria-hidden="true"></i></button>
</div>
)
}
I would strongly recommend you not to use Buttons drawn on the canvas (stage). You should use standard HTML buttons. You can position them on top of the stage with css (Positon absolute).
If you do it this way, there are many benefits to you. E.g. you can design it easier and better. In addition, it is more efficient and also solves the problem from this question.

Conditional rendering and inline IF doesn't work

I have two problems:
the conditional rendering is not working well on Statistics component
inline IF on the line 46 didn't work so I commented out. I checked the values with typeof jc func and all values are numbers
Please let me know if you spot what I've done wrong.
Thanks a lot!
import React, {useState} from 'react'
import ReactDOM from 'react-dom'
const Feedback = (props) => (
<button onClick={props.handleFeedback}>{props.text}</button>
)
const Statistics = (props) => {
console.log(props);
if (props.total.length === 0) {
return (
<div>empty</div>
)
}
return (
<div>
<p>all {props.total}</p>
<p>average {props.average}</p>
<p>positive {props.positive}</p>
</div>
)
}
const App = () => {
const [good, setGood] = useState(0)
const [neutral, setNeutral] = useState(0)
const [bad, setBad] = useState(0)
const setToGood = (newValue) => () => {
setGood(newValue)
}
const setToNeutral = (newValue) => () => {
setNeutral(newValue)
}
const setToBad = (newValue) => () => {
setBad(newValue)
}
const total = good + neutral + bad;
const average = (good - bad) / total
const positive = (good / total * 100 === 0 ) ? '' : (good / total * 100)
console.log(positive);
return (
<div>
<h1>give feedback</h1>
<Feedback handleFeedback={setToGood(good +1)} text="good" />
<Feedback handleFeedback={setToNeutral(neutral +1)} text="neutral" />
<Feedback handleFeedback={setToBad(bad +1)} text="bad" />
<h1>statisctics</h1>
<p>good {good}</p>
<p>neutral {neutral}</p>
<p>bad {bad}</p>
<Statistics total={total} average={average} positive={positive}/>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
Inside IF don't use props.total.length as total is of number type not string.

Resources