scroll event and responsiveness in react - reactjs

i'm trying to somewhat replicate this parallax feature that i found on the website https://spacers.wannathis.one/ (scroll to "12 different poses" section) with React. Here's an example of my code so far:
const App = () => {
const [scrollX, setScrollX] = useState(0);
const [scrollY, setScrollY] = useState(0);
const [marginTop, setMarginTop] = useState();
const [position, setPosition] = useState("static");
const [top, setTop] = useState();
const [left, setLeft] = useState();
useEffect(() => {
const handleScroll = (event) => {
setScrollY(Math.round(window.scrollY));
if (window.scrollY > 2070 && window.scrollY < 6112) {
setMarginTop(0 + "px");
setLeft((-window.scrollY +2525) * 0.55 + "px");
setPosition("fixed");
setTop(window.scrollY * 0.005 + "px");
}
if (window.scrollY <= 2070) {
setPosition("initial");
}
if (window.scrollY >= 6112) {
setPosition("absolute");
}
};
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
return (
<div className="page">
<section className="title_wrapper">
<h3>My projects</h3>
</section>
<section
className="scroll_wrapper"
style={{
marginTop: marginTop,
position: position,
top: top,
left: left,
}}
>
<div className="images_wrapper">
<div className="images_container">
<img src={pic1} />
</div>
<div className="images_container">
<img src={pic2} />
</div>
</div>
</section>
</div>
CSS:
.page {
width: 100vw;
overflow: auto
height: 100vw
display: flex
align-items: center;
position: relative
.title_wrapper {
position: absolute;
display: flex;
.scroll_wrapper {
margin-left: 0px;
display: flex;
overflow: hidden;
width: auto;
justify-content: flex-start;
align-items: center;
z-index: 10;
vertical-align: middle;
margin-top: 0px;
}
.images_wrapper {
overflow-x: hidden;
display: flex;
width: 100%;
align-items: center;
gap: 30px;
max-height: 100vh;
position: absolute;
vertical-align: middle;
top: 10px;
white-space: nowrap;
flex-direction: row;
flex-wrap: nowrap;
left: 100px;
}
h3 {
z-index: 10;
position: absolute;
top: 0;
left: 10px;
}
.image_container {
max-width: 10vw;
width: 10vw;
}
img {
width: 100%;
height: auto;
}
The scroll effect seems to be working fine but whenever i resize the window everything breaks and i'm guessing it might be because the scrollY value should get smaller as the window resizes. I have no idea how to fix it, can somebody help?

Related

Shapes not rendering within Stage in react-konva

I have a few lines of code to create a Rectangle or any other shapes I have tried are not rendering within the Stage. I have tried adding html elements, however they do render within the Stage. Is this caused due to the use of refs? There are no errors that appear in the console as well.
App.js
import "./App.css";
import { Stage, Layer, Rect, Circle } from "react-konva";
import { Html } from "react-konva-utils";
import { useEffect, useRef, useState } from "react";
import Elements from "./components/elements/elements.component";
function App() {
const canRef = useRef(null);
const canvasContainerRef = useRef(null);
const [canvasHeight, setCanvasHeight] = useState(0);
const [canvasWidth, setCanvasWidth] = useState(0);
useEffect(() => {
setCanvasWidth(canvasContainerRef.current.offsetWidt);
setCanvasHeight(canvasContainerRef.current.offsetHeight);
}, []);
return (
<div className="app">
<div className="header"></div>
<div className="main">
<div className="sidebar"></div>
<div className="elements">
<Elements canvasRef={canRef} />
</div>
<div className="editor">
<div ref={canvasContainerRef} className="canvas">
<Stage ref={canRef} width={canvasWidth} height={canvasHeight}>
<Layer>
<Html>
<h1> testing</h1>
</Html>
<Rect x={20} y={50} width={100} height={100} fill="red" />
</Layer>
</Stage>
</div>
<div className="slides"></div>
</div>
</div>
</div>
);
}
export default App;
App.css
.app {
display: flex;
flex-direction: column;
height: 100vh;
width: 100%;
}
.header {
display: flex;
width: 100%;
height: 8%;
background-color: white;
border-style: solid;
}
.main {
display: flex;
flex-direction: row;
background-color: white;
width: 100%;
height: 92%;
}
.sidebar {
display: flex;
flex-direction: column;
height: 100%;
width: 6%;
border-style: solid;
background-color: white;
}
.elements {
display: flex;
background-color: white;
width: 40%;
max-width: 40%;
}
.editor {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
.canvas {
display: flex;
width: 100%;
height: 80%;
background-color: #ebf1ff;
border-style: solid;
}
.slides {
background-color: white;
width: 100%;
height: 20%;
border-style: solid;
}
I was able to fix this by changing height and width of the stage to window.innerHeight and window.innerWidth. I am not sure why it worked now as it used to have the same height and width before as well, but that fixed my issue

Problem with Load More functionality and responsiveness

I'm having trouble with the responsiveness of rendered items from an array of objects that are loaded by clicking a 'Load More' button. On every click 3 extra items are visible. So far so good. But when the screen-width gets smaller I want to render only two items per click and in the end only one item. The problem is that media queries, css-grid or flex-box and the load functionality presets, in the component, cancel each other out. If I want two items rendered I have to adjust the CSS rules and the actual JS code presets like the posts-per-page. In other words how to create a responsive 'Load More' functionality in react (hooks).
The component
const [ soldProperties, setSoldProperties ] = useState([])
const [ loadBtn, setLoadBtn ] = useState('Show More')
const [ currentSlice, setCurrentSlice ] = useState(3)
const [ perPage, setPerPage ] = useState(3)
useEffect(()=> {
setSoldProperties(soldProps.slice(0, currentSlice))
}, [currentSlice, soldProperties.length])
const loadMore = () => {
setCurrentSlice(currentSlice + perPage)
if (soldProperties.length === 6) {
setLoadBtn('Show Less')
}
if (soldProperties.length === soldProps.length){
setCurrentSlice(currentSlice - 6)
setLoadBtn('Show More')
}
}
return (
<div className="soldprops">
<div className="soldprops_header">Sold Properties:</div>
<div className="soldprops_grid">
{soldProperties && soldProperties.map(property => {
const { img, name, price, id } = property
return <div className="soldprops_grid_imageBox" key={id}>
<div className="image"><img src={process.env.PUBLIC_URL + `/soldProps/${img}`} alt="" style={{ width: '100%', height: 'auto' }} /></div>
<div className="footer"><span className="soldChateau">{name}</span><span className="soldPrice">${price},-</span></div>
</div>
})}
</div>
<button type="button" className="loadmoreBtn" onClick={loadMore} >{loadBtn}</button>
</div>
)
}
The scss
.soldprops {
position: relative;
max-width: 1920px;
margin: 0 auto;
height: auto;
#include center-content-column;
&_header {
width: 100%;
height: 50px;
color:rgb(163, 149, 184);
padding: 0 20px;
font-size: 1.2rem;
font-family: Arial, Helvetica, sans-serif;
#include center-content-row-start;
}
&_grid {
width: 100%;
padding: 0 20px;
margin-bottom: 50px;
height: auto;
#include center-content-row;
flex-wrap: wrap;
gap: 15px;
// display: grid;
// grid-template-columns: repeat(3, 1fr);
// grid-template-rows: repeat(1, 1fr);
// gap: 20px;
&_imageBox {
position: relative;
width:32%;
height: auto;
line-height: 0;
box-shadow: 0px 4px 4px rgb(11, 9, 14);
.image {
width: 100%;
height: auto;
opacity: 1;
}
}
}

Range Slider with React and Typescript

I have created range slider but when we change the min value then it will not work. I want dynamic range Slider with ticks and moving label with respect to thumb without using any package.
This is my code below. i am expecting to create a dynamic range slider without using any package and also slider have ticks and movable thumbs which shows value with respect to thumbs
import { useCallback } from 'react';
import { Styles } from './range-slider-style';
interface IVersionSliderProps {
latestVersion: number;
olderVersion: number;
onChange: (val: any) => void;
}
export function VersionSlider(props: IVersionSliderProps) {
const setValue = useCallback((e) => {
console.log(e.target.value);
const newVal = e.target.value;
const negNewVal = -10 * Number(newVal);
console.log(negNewVal);
(document.querySelector('.range-text') as HTMLDivElement).style.left = (newVal + '%'); //Set range left position
(document.querySelector('.range-text') as HTMLDivElement).style.transform = 'translate(' + negNewVal + '%, 20px)'; //Set range translate to correct
(document.querySelector('.range-text') as HTMLDivElement).innerHTML = newVal; //Set range text equal to input position
}, []);
return (
<Styles>
<div className="input-container">
<input
id='#input'
type="range"
min={olderVersion}
max={latestVersion}
//value="1"
onChange={setValue}
step='.01' />
<div className="range-text">0</div>
</div>
</Styles>
);
}
//Css part below:
import styled from 'styled-components';
export const Styles = styled.div`
align-items: center;
display: "flex";
height: 100px;
color: #888;
margin-top: 2rem;
flex-direction:column;
margin-bottom: 2rem;
width: 1200px;
.value {
font-size: 1rem;
}
.input-container {
position: relative;
height:10px;
display: flex;
background: border-box;
width: 100%;
padding: 0;
}
.marking {
height:20xp;
widht:100%;
background-color: "black";
margin-top: 20px;
}
input[type="range"] {
appearance: none;
background-color: #56B0C9;
width: 100%;
height: 8px;
padding: 0;
}
input[type="range"]::-webkit-slider-thumb {
-moz-appearance: none !important;
appearance: none !important;
position: relative;
height: 20px;
width: 100px;
background: transparent;
cursor: ew-resize;
z-index: 2;
}
input[type="range"]::-moz-range-thumb {
-moz-appearance: none !important;
appearance: none !important;
position: relative;
height: 20px;
width: 100px;
background: transparent;
cursor: pointer;
z-index: 2;
}
.range-text {
position: absolute;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
background: white;
text-align: center;
line-height: 1;
height: 18px;
width: 60px;
transform: translate(0, 2px);
}`;

gridArea React inline style with a variable

I'm trying to return a number of divs and dynamically place them in a grid. I've never tried this before, and I think my syntax may be off. None of the divs are appearing. The template is working, and the divs should currently fill grid container and turn it black with a button.
export const DayColumn = ({ day }) => {
const { timeColumn } = useTableContext();
return (
<Wrapper gridInterval={timeColumn.length}>
<h2>{day}</h2>
{timeColumn.forEach((_, index) => {
index++;
return (
<div
key={index}
className='task-slot'
style={{ gridArea: `${index + 1} / 1 / ${index + 2} / 2;` }}
>
<h1>text</h1>
<BsPlusSquareFill />
</div>
);
})}
</Wrapper>
);
};
The styled component
const Wrapper = styled.div`
height: 100%;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: ${(props) => `100px repeat(${props.gridInterval}, 1fr);`};
grid-gap: 3px;
background-color: var(--clr-background-dark3);
border-radius: 7px;
h2 {
justify-self: center;
font-family: 'Oswald', sans-serif;
letter-spacing: 4px;
font-size: 2rem;
font-weight: 200;
color: var(--clr-text-light);
text-transform: capitalize;
}
.task-slot {
height: 100%;
width: 100%;
background-color: black;
}
`;
I fixed it. Needed to use map instead of forEach

Reactjs - setState() make all texts of apps flickering

In component A, I got some setState functions to update states when clicking on some element. What strange happens is that all texts of the whole app flicked. Component A is completed isolated from others. This only happens on Chrome, firefox works just fine :( please help
My MenuButton component code :
import React, { Component } from 'react';
import styled, { css } from 'styled-components';
const InsideLineCommon = styled.span`
position: absolute;
width: 100%;
height: 100%;
left: 0%;
&:after {
content: '';
width: 100%;
height: 100%;
position: absolute;
background: ${props => props.theme.colors.primary1};
}
&:before {
content: '';
width: 100%;
height: 100%;
position: absolute;
background: ${props => props.theme.colors.primary1};
}
`;
const InsideLine = InsideLineCommon.extend`
&:after {
left: -200%;
}
transition: left ${props => props.theme.animationTime};
left: ${props => (props.isHalfWidth ? '-100%' : '-200%')};
${props => props.isInitialMoving && css`
left: -50%;
`}
${props => props.isInitialMoving && props.isHalfWidth && css`
left: 100%;
`}
${props => props.isHorizontalBarsMoving && css`
left: 200%;
`}
${props => props.isCrossoverBarMoving && css`
left: -200%;
`}
${props => props.isHalfWidth && css`
&:after {
width: 50%;
}
&:before {
width: 50%;
left: -100%;
}
`}
`;
const CommonLine = styled.div`
width: 100%;
height: 15%;
position: absolute;
`;
const MiddleLine = CommonLine.extend`
top: calc(100% / 2 - 7.5%);
`;
const TopLine = CommonLine.extend`
top: 0px;
`;
const BottomLine = CommonLine.extend`
bottom: 0px;
`;
const Wrapper = styled.button`
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
overflow: hidden;
cursor: pointer;
border-style: none;
background: transparent
padding: 0px;
&:focus {
outline: 0px;
}
`;
const CrossoverWrapper = styled.div`
width: 100%;
height: 100%;
position: absolute;
top: 0px;
display: flex;
flex-direction: column;
justify-content: center;
&:after {
content: '';
position: absolute;
width: 100%;
height: 15%;
background: ${props => props.theme.colors.primary1};
transform-origin: center;
transform: ${props => (props.isCrossoverBarMoving ? 'rotate(45deg) translate(0%)' : 'rotate(45deg) translate(-130%)')};
transition: ${props => (!props.isCrossoverBarMoving
? `transform ${props.theme.animationTime} 0s`
: `transform ${props.theme.animationTime} ${props.theme.animationTime}`)};
border-radius: 5px;
}
&:before {
content: '';
position: absolute;
width: 100%;
height: 15%;
background: ${props => props.theme.colors.primary1};
transform-origin: center;
transform: ${props => (props.isCrossoverBarMoving ? 'rotate(-45deg) translate(0%)' : 'rotate(-45deg) translate(130%)')};
border-radius: 5px;
transition : ${props => (!props.isCrossoverBarMoving
? `transform ${props.theme.animationTime} 0s`
: `transform ${props.theme.animationTime} ${props.theme.animationTime}`)};
}
`;
class MenuButton extends Component {
constructor(props) {
super(props);
this.state = {
isCrossoverBarMoving: false,
isHorizontalBarsMoving: false,
isInitialMoving: false,
};
this.handleMouseClick = this.handleMouseClick.bind(this);
this.handleMouseHover = this.handleMouseHover.bind(this);
}
componentDidMount() {
setTimeout(() => {
this.setState({
isInitialMoving: true,
});
}, 1000);
}
handleMouseClick() {
this.setState(prevState => ({
isCrossoverBarMoving: !prevState.isCrossoverBarMoving,
}));
}
handleMouseHover() {
this.setState(prevState => ({
isHorizontalBarsMoving: !prevState.isHorizontalBarsMoving,
}));
}
render() {
const { isHorizontalBarsMoving, isCrossoverBarMoving, isInitialMoving } = { ...this.state };
return (
<Wrapper
onClick={this.handleMouseClick}
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
<TopLine>
<InsideLine
isHorizontalBarsMoving={isHorizontalBarsMoving}
isCrossoverBarMoving={isCrossoverBarMoving}
isInitialMoving={isInitialMoving}
/>
</TopLine>
<MiddleLine>
<InsideLine
isHorizontalBarsMoving={isHorizontalBarsMoving}
isCrossoverBarMoving={isCrossoverBarMoving}
isInitialMoving={isInitialMoving}
/>
</MiddleLine>
<BottomLine>
<InsideLine
isHalfWidth
isHorizontalBarsMoving={isHorizontalBarsMoving}
isCrossoverBarMoving={isCrossoverBarMoving}
isInitialMoving={isInitialMoving}
/>
</BottomLine>
<CrossoverWrapper
isCrossoverBarMoving={isCrossoverBarMoving}
/>
</Wrapper>
);
}
}
export default MenuButton;
Whenever I click this component, the all texts of whole app did flickering :/

Resources