I'm trying to develop a swipe component using React w/ Redux and hammerjs. The problem is that each time I move my component the position.x and position.y start from [0,0] and not from the current state they are at.
Here's the code:
import React, { useState, useEffect } from 'react';
import Hammer from 'hammerjs';
import WeatherForecast from './WeatherForecast';
const SwipeableCard = ({ children }) => {
const [gesture, setGesture] = useState(null);
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const element = document.getElementById('swipeable-card');
const hammer = new Hammer(element);
hammer.on('pan', (event) => {
setPosition({
x: event.deltaX + position.x,
y: event.deltaY + position.y,
});
});
setGesture(hammer);
return () => {
hammer.off('pan');
setGesture(null);
};
}, []);
return (
<div
id="swipeable-card"
className="App-swipeable"
style={{
transform: `translate(${position.x}px, ${position.y}px)`,
}}
>
<WeatherForecast />
</div>
);
};
export default SwipeableCard;
Related
Trying to make a different animation when on mobile or on desktop, to do so I'm using a useMediaQueryHoook and changing the variant in function of it. But the init animation seems to always assume that I'm on desktop. I guess because the useMediaQueryHook doesn't have time to actualise before the anim is launch. How can I deal with that issue ?
Btw I'm on nextjs :)
Here is my code :
const onMobile = useMediaQuery("(min-width : 428px)");
const wishCardVariant = {
hidden: (onMobile) => ({
opacity: 0,
y: onMobile ? "100%" : 0,
x: onMobile ? 0 : "100%",
transition,
}),
visible: (onMobile) => ({
opacity: 1,
x: 0,
y: 0,
}),
};
here is the hook :
import react, { useState, useEffect } from "react";
export default function useMediaQuery(query) {
const [matches, setMatches] = useState(false);
useEffect(() => {
const media = window.matchMedia(query);
if (media.matches !== matches) {
setMatches(media.matches);
}
const listener = () => {
setMatches(media.matches);
};
media.addListener(listener);
return () => media.removeListener(listener);
}, [matches, query]);
return matches;
}
There is a Main component, which has 4 separate components. It is necessary that these components are not visible before the user does not use the search.
The first component is responsible for displaying the weather graph, and the second for displaying the map. I do not know how to hide these two components specifically.
first component 1
import React, { useContext, useState, useEffect } from 'react';
import Chart from 'react-apexcharts';
import { Context } from '../../contex';
import './weather-graph.scss';
import { useTranslation } from 'react-i18next';
const WeatherGrapth = () => {
const { t } = useTranslation()
const {dailyForecast} = useContext(Context);
const [category, setCategory] = useState([])
const [data, setData] = useState([])
useEffect(() => {
const day = [];
const temp =[];
dailyForecast.forEach((d) => {
const unixTimestamp = d.dt;
const getTemp = Math.round(d.temp.day)
let getDay = new Date(unixTimestamp * 1000).getDate();
day.push(getDay)
temp.push(getTemp)
})
setCategory(day)
setData(temp)
}, [dailyForecast]);
return(
<>
{dailyForecast.temp &&
<div className="graph__container">
<h3 className="graph__title">{t("weekly_foreacst")}</h3>
<Chart options={{
chart: {
id: 'weather-graph'
},
xaxis: {
categories: category,
title: {
text: [t("date")],
},
},
yaxis: {
title: {
text: [t("temperature")],
},
},
}}
series={[{
name: 'temp',
data: data
}]} type="line" height={'349px'} />
</div>
}
</>
)
}
export default WeatherGrapth;
second component 2
import React, { useEffect } from 'react';
import './weather-map.scss';
import {API_KEY} from './../../apis/config';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import 'leaflet-openweathermap/leaflet-openweathermap.css';
import 'leaflet-openweathermap';
import { useTranslation } from 'react-i18next';
const WeatherMap = () => {
const { t } = useTranslation();
useEffect(() => {
const osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18, attribution: 'copyright OpenStreetMap contributors' });
const clouds = L.OWM.clouds({showLegend: false, opacity: 0.5, appId: `${API_KEY}`});
const cloudscls = L.OWM.cloudsClassic({showLegend: false,appId: `${API_KEY}`});
const precipitation = L.OWM.precipitation({showLegend: false, appId: `${API_KEY}`});
const precipitationcls = L.OWM.precipitationClassic({showLegend: false,appId: `${API_KEY}`});
const rain = L.OWM.rain({showLegend: false,appId: `${API_KEY}`});
const raincls = L.OWM.rainClassic({showLegend: false,appId: `${API_KEY}`});
const snow = L.OWM.snow({showLegend: false,appId: `${API_KEY}`});
const pressure = L.OWM.pressure({showLegend: false,appId: `${API_KEY}`});
const pressurecntr = L.OWM.pressureContour({showLegend: false,appId: `${API_KEY}`});
const temp = L.OWM.temperature({showLegend: false,appId: `${API_KEY}`});
const wind = L.OWM.wind({showLegend: false,appId: `${API_KEY}`});
const map = L.map('map', { center: new L.LatLng(53.9, 27.5667), zoom: 5, layers: [osm] });
const baseMaps = { "OSM Standard": osm };
const overlayMaps = {
[t("clouds")]: clouds,
[t('cloudscls')]: cloudscls,
[t('precipitation')]: precipitation,
[t('precipitationcls')]: precipitationcls,
[t('rain')]: rain,
[t('raincls')]: raincls,
[t('snow')]: snow,
[t('pressure')]: pressure,
[t('pressurecntr')]: pressurecntr,
[t('temp')]: temp,
[t('wind')]: wind,
};
const layerControl = L.control.layers(baseMaps, overlayMaps,{collapsed:window.innerWidth < 768}).addTo(map);
}, []);
return(
<div className="weathermap-container">
<div id="map" style={{height: '260pt', borderRadius:'20px'}} className="map-weather"></div>
</div>
)
}
export default WeatherMap;
You can achieve that by passing down a prop
For instance
return (
<>
<div className="main-container">
{prop.visible ?
<CardWeather />
<Forecast/>
<WeatherGrapth/>
<WeatherMap/>
: ""
}
</div>
<div className="pr">weather app</div>
</>
)
}
export default Main;```
So to make it visible just pass in
```visible={true} ```
when calling the function
import React, { useEffect, useRef, useState } from "react";
import WaveSurfer from "wavesurfer.js";
const formWaveSurferOptions = ref => ({
container: ref,
barWidth:1,
waveColor: "#eee",
progressColor: "OrangeRed",
cursorColor: "OrangeRed",
barRadius: 10,
responsive: true,
height: 200,
barGap:0,
pixelRatio: 5,
barMinHeight:100,
normalize: true,
partialRender: true
});
export default function Waveform({ url }) {
const waveformRef = useRef(null);
const wavesurfer = useRef(null);
const [playing, setPlay] = useState(false);
const [volume, setVolume] = useState(0.5);
useEffect(() => {
setPlay(false);
const options = formWaveSurferOptions(waveformRef.current);
wavesurfer.current = WaveSurfer.create(options);
wavesurfer.current.load(url);
wavesurfer.current.on("ready", function() {
if (wavesurfer.current) {
wavesurfer.current.setVolume(volume);
setVolume(volume);
}
});
return () => wavesurfer.current.destroy();
}, [url]);
const handlePlayPause = () => {
...
};
const onVolumeChange = e => {
...
};
return (
<div>
<div id="waveform" ref={waveformRef} />
<div className="controls">
<button onClick={handlePlayPause}>{!playing ? "Play" : "Pause"}</button>
<input
...
/>
<label htmlFor="volume">Volume</label>
</div>
</div>
);
}
The problem in this is that, i want to set the height of the minpeak of the wave and also give the height an average that at this level it does not go up. i tried the minHeightBar, but unfortunatelydoes't work at all, I have connected the two images for better understanding, so if any one knows this so please help, thankyou :)
Basically, I want to add cropping functionality. If user select a file then, user have choice to crop the image if he/she want. When I preview cropped image.
Error: Invalid hook call. Hooks can only be called inside of the body
of a function component. This could happen for one of the following
reasons: 1. You might have mismatching versions of React and the
renderer (such as React DOM) 2. You might be breaking the Rules of
Hooks 3. You might have more than one copy of React in the same app
import React, { useEffect, useState, useRef } from 'react';
import ReactCrop from 'react-image-crop';
import 'react-image-crop/dist/ReactCrop.css';
// import Styles from './Image.module.css';
const Image = (props) => {
const [crop, setCrop] = useState({
aspect: 3/4,
unit: 'px',
x: 0,
y: 0,
width: 500,
height: 500
});
const [file, setFile] = useState(null);
const [imgPreview, setImgPreview] = useState(null);
const canvasRef = useRef(null);
const filePicker = (e) => {
setFile(e.target.files[0]);
};
function image64toCanvasRef (cnvRef, image64, pixelCrop) {
const canvas = cnvRef;
canvas.width = pixelCrop.width;
canvas.height = pixelCrop.height;
const ctx = canvas.getContext('2d');
const image = new Image(); // On this line throwing error
image.src = image64
image.onload = () => {
ctx.drawImage(
image,
pixelCrop.x,
pixelCrop.y,
pixelCrop.width,
pixelCrop.height,
0,
0,
pixelCrop.width,
pixelCrop.height
)
}
}
useEffect(() => {
if (file) {
const fileReader = new FileReader();
fileReader.onload = () => {
setImgPreview(fileReader.result);
}
fileReader.readAsDataURL(file);
}
}, [file]);
const handleOnCropChanged = (crop) => {
// console.log('handleOnCropChanged: ', crop);
const state = {
...crop,
x: crop.x,
y: crop.y,
width: crop.width,
height: crop.height
}
setCrop(state);
};
const handleOnCropComplete = (crop, pixelCrop) => {
image64toCanvasRef(canvasRef.current, imgPreview, pixelCrop);
}
return (
<div
style={{
margin: '10px 28px',
}}
>
{
imgPreview ? (
<div>
<ReactCrop
src={imgPreview}
crop={crop}
keepSelection
locked
onChange={(crop) => handleOnCropChanged(crop)}
onComplete={handleOnCropComplete}
onImageLoaded={handleOnImageLoaded}
/>
</div>
) : (
<input type='file' onChange={filePicker} />
)
}
<div>
<canvas
ref={canvasRef}
></canvas>
</div>
</div>
)
};
export default Image;
I have a component where I use the onWheel event to detect scrolling in all directions (this works). My problem is preventing this component to rerender so I can utilize throttle from underscore.js:
Example
import React, {useState} from 'react';
import { throttle } from 'underscore';
const App = () => {
const [position, setPosition] = useState({x: 0, y: 0});
const updatePosition = throttle((e) => {
e.preventDefault(); // Required for left/right swiping.
setPosition({
x: position.x + e.deltaX,
y: position.y + e.deltaY,
});
}, 1000);
return (
<div className="viewport" onWheel={updatePosition}>
<Box x={position.x} y={position.y} />
</div>
);
};
export default App;
The throttle function does not work here, since every time the state updates the component rerenderes.
Can you please try the below one. I just wrapped the throttle with the new function.
import { throttle } from "underscore";
import Box from "./Box";
const App = () => {
const [position, setPosition] = useState({ x: 0, y: 0 });
function updatePosition(e) {
e.preventDefault(); // Required for left/right swiping.
throttle(e => {
setPosition({
x: position.x + e.deltaX,
y: position.y + e.deltaY
});
}, 1000)(e);
}
return (
<div className="viewport" onWheel={updatePosition}>
<Box posX={position.x} posY={position.y} />
</div>
);
};
export default App;
You can throttle rendering of a component using the throttle function from underscore by creating a new component called ThrottledBox.
export default function App() {
const [position, setPosition] = React.useState({ x: 0, y: 0 });
function handleOnWheen(e) {
e.preventDefault(); // Required for left/right swiping.
setPosition({
x: position.x + e.deltaX,
y: position.y + e.deltaY
});
}
return (
<div className="viewport" onWheel={handleOnWheen}>
<ThrottledBox x={position.x} y={position.y} />
</div>
);
}
const ThrottledBox = throttle((props) => <Box {...props}/>, 1000);
https://codesandbox.io/s/zealous-booth-x8lfd