React Cycling Image Gallery - javascript

i want to make a component that displays a number of images, it will change a single image after an interval of time.
So far i have this code:
import React from 'react';
const Gallery = (props) => {
const cellStyle = {
background: "red",
padding: "0"
}
const ImageCell = (props) => {
return (
<div className="col-lg-4" style={cellStyle}>
<div style={{
height: "100%",
width: "100%",
padding: "0",
background: `url(${props.theImage})`,
backgroundSize: "cover"
}}
>
</div>
</div>
);
}
return(
<div className="row full-width">
{
props.images.map(function(image) {
let theKey = image.slice(14, -4);
return <ImageCell key={theKey} theImage={image} />
})
}
</div>
);
}
export default Gallery;
I want it to switch out a random image from the 6x6 grid and switch it with a random image from the image set
So i don't really know how i would implement this, any ideas will be welcome.

Your Gallery should hold the randomization logic and pass the images down to its children.
So, the images-to-display can be set in an array within Gallery's state. Implement the componentDidMount method and instantiate a timer there; when the timer fires, you can replace one of the images in the state's array with a randomly selected image from the total images array. Updating state will then re-render Gallery, thus re-rendering its ImageCell children.

Related

React useState() array mapping inside of component doesn't update if I don't scroll or click on browser

I've made this effect with React and Tailwind:
GIF with the effect
As you can see, so far, so good. However, this effect just works when:
I scroll on the browser
I click on the browser
I open the Chrome tools inspector
This is what happens when I do not do something of the above:
GIF with the effect but not working as expected
As you can see, that's not the effect I'm looking for.
So, apparently, there is a React render problem ocurring right here, and I'm stucked.
Inside, this works as follows:
const HomeHeader = () => {
// This are imported images
const IMAGES = [insta, twitter, netflix, mix];
// This is an array that maps inside the component and renders the images as JSX
const [images, setImages] = useState([]);
useEffect(() => {
let counter = 0;
// An interval that creates a new element in the array "images" every 50 miliseconds
const interval = setInterval(() => {
setImages(prevState => [
...prevState,
{
id: counter++,
duration: getRandom(5, 9) * 1000,
image: IMAGES[getRandom(0, IMAGES.length)],
height: getRandom(1, 6),
top: getRandom(0, window.innerHeight - window.innerHeight / 4),
right: getRandom(0, window.innerWidth)
}
]);
}, 50);
return () => clearInterval(interval);
}, [])
// Every time the state variable "images" is changed, add the cool animation to the last image and, when it ends, delete the image from the HTML
useEffect(() => {
if(images.length > 0) {
let image = images[images.length - 1];
let element = document.getElementById(image.id);
element.classList.add('opacity-0');
element.classList.add('scale-125');
setTimeout(() => {
element.parentNode.removeChild(element);
}, image.duration);
}
}, [images])
return (
<div id="header" className="relative h-full bg-gray-900 flex justify-center items-center px-16 overflow-hidden">
<h1 className="bg-black text-gray-200 font-bold text-5xl sm:text-8xl md:text-9xl text-center z-10"></h1>
{
images.map((element, index) => {
return <img key={index} id={element.id} src={element.image} style={{top: element.top, right: element.right}} className={"imageRRSS absolute filter blur-sm w-auto h-" + element.height + "/6 w-auto transform transition-transform-opacity duration-" + element.duration}/>
})
}
</div>
)
}
What it does is to add every 50 miliseconds a new object to the state array images that contains the properties of the image to add.
After each addition, as it is a state variable, React re-renders the mapping of the component inside of the return:
{
images.map((element, index) => {
return <img key={index} id={element.id} src={element.image} style={{top: element.top, right: element.right}} className={"imageRRSS absolute filter blur-sm w-auto h-" + element.height + "/6 w-auto transform transition-transform-opacity duration-" + element.duration}/>
})
}
And also it goes to the useEffect and adds the cool animation and, when the animations ends, it deletes the tag from the HTML:
useEffect(() => {
if(images.length > 0) {
let image = images[images.length - 1];
let element = document.getElementById(image.id);
element.classList.add('opacity-0');
element.classList.add('scale-125');
setTimeout(() => {
element.parentNode.removeChild(element);
}, image.duration);
}
}, [images])
However, the render just occurs when I interact with the browser.
This is how I understand what it's happening. However, something is clearly wrong as it's not working as expected.
Anybody knows what it's happening?
Thanks!
Ok, solved. The problem was with React applying tailwind effects instantly in the element. I just added the onLoad event to the component instead of ussing useEffect and it works perfectly.

How to change background-image with React when a page is loading?

How to change the background of the page when it loads without having to click the button?
import img1 from '../images/img1.jpg';
import img2 from '../images/img2.jpg';
import img3 from '../images/img3.jpg';
const [background, setBackground] = useState(img1);
const list = [img1, img2, img3];
const css = {
backgroundImage: `url(${background})`,
};
return (
<div style={css}>
<button onLoad={() => setBackground(list)}>Change background</button>
</div>
);
but setBackground(list)} does not read the array and the onLoad event does not work when the page is reloaded.
thanks for any help!
As mentioned above the solution requires localStorage, useEffect, useRef. All logic is inside useEffect, because when the component is mounting all the magic happens here. working example
Initialize an absolute number with useRef
Trying to get value from localStorage, if is empty then we set
default value. And the same place, we checking the array index.
Second time avoid the check and we increace gotted value andabsolute
number.
Then sending result to state and localStorage
App.js
import { useState, useEffect, useRef } from "react";
export default function App() {
const img1 = "https://dummyimage.com/400x200/a648a6/e3e4e8.png";
const img2 = "https://dummyimage.com/400x200/4e49a6/e3e4e8.png";
const img3 = "https://dummyimage.com/400x200/077d32/e3e4e8.png";
let [lsNum, setLsNum] = useState(0);
// Absolute number '1'
let count = useRef(1);
const list = [img1, img2, img3];
useEffect(() => {
// Get value from localStorage, transform to number
const lS = Number(localStorage.getItem("image"));
// Check! If localStorage have number 2 / more
// Set number 0
if (lS >= 2) {
localStorage.setItem("image", 0);
return;
}
// Get absolute number and increase with number from localStorage
count.current = count.current + lS;
// Set result to state
setLsNum(count.current);
// Set the resulting number to localStorage
localStorage.setItem("image", count.current);
}, []);
const css = {
height: "200px",
width: "400px",
display: "block",
backgroundImage: `url(${list[lsNum]})`, // index change
backgroundPosition: "center",
backgroundSize: "cover",
border: "1px solid red"
};
return (
<div className="App">
<div style={css}></div>
</div>
);
}

How to access canvas for a screenshot when using react-stl-obj-viewer component

I'm using a component react-stl-obj-viewer to render a 3d stl image. I can render the 3d stl image correctly. Once the image is rendered, I'm trying to move it around and have a button to take a screenshot of it.
<div>
<STLViewer
onSceneRendered={(element) => {
console.log(element);
}}
sceneClassName="test-scene"
file={this.state.selectedFile}
modelColor="#073FE9"
/>
</div>
<div style={{ display: "inline-block" }}>
<Button
id="thumbnail"
style={{ zIndex: "-1", margin: "20px 0px 20px 0px" }}
onClick={(e) => {
this.save3dRender(e, webGlContextExists);
}}
>
Save Frame as Thumbnail
</Button>
{!this.state.showThumbnail ? (
<Container>
<h2>Did you get this image</h2>
<Image src={this.state.thumbnailFile} />
</Container>
) : null}
</div>
In order to take a screenshot of the image, I was trying to use .toDataURL("image/png"). My button does this when pressed.
save3dRender = (e, geeL) => {
e.preventDefault();
var testThumbnail = geeL.canvas.toDataURL("image/png");
this.state.thumbnailFile = testThumbnail;
this.state.thumbnailFileRender = true;
this.state.showThumbnail = false;
};
I am somewhat new at this but thought I was on the right track. Since the STLViewer component is creating the canvas, I was trying to access it this way with .canvas. Not sure if that is correct but I do get a data:image/png;base64 address. However it is a blank image.
Am I accessing the correct canvas? Is there something I need to do to the image after getting it as data:/image/png;base64.
Fixed the issue. It was an array that I needed to reference.
var testThumbnail = geeL[0].toDataURL("image/png");
instead of
var testThumbnail = geeL.canvas.toDataURL("image/png");

Building a simple component for a 360 degree Image in React

I am attempting to build a React component that displays a 360 degree view of a product. I am attempting to convert this script which uses jQuery to display a draggable 360 degree product image into a React component.
The way this script works is by first loading ~20 images of the product at each angle up to 360 degrees. Then, using jQuery, automatically switching the image based on mouse click and move event.
As a part of my React component I have managed to load the images from a folder and am trying to switch the image based on a mouse click and move event. Additionally, I have found this article which creates a 3D perspective view of an image and has some useful functions for React synthetic events.
How do I use a React synthetic event to capture mouse click and move event so I can switch to the next image in the array?
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
// var foo = new Array(36);
var N = 37;
var imageCount = Array.apply(null, {length: N}).map(Number.call, Number)
imageCount.shift()
console.log(imageCount)
let images = imageCount.map( (name, index) => {
return <img key={index} className="img-responsive" alt="" src={require(`./360-demo/${name}.jpg`)} />
} );
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
{images}
</div>
);
}
}
export default App;
Once I get a working component I plan to open source this code so others can also benefit.
You can track the rotation start with onMouseDown, which means the dragging has to originate within the component. For movement and rotation stop, you can use onMouseMove and onMouseUp, but it might be better to attach to the document for that. Generally I wouldn't recommend ever touching the document in React, but with event handlers on the component, it won't detect movement outside of the component (like your jQuery one does).
The gist of it would be something like:
handleMouseDown = event => {
this.setState({
dragging: true,
dragStart: event.screenX,
});
};
handleMouseUp = () => {
this.setState({ dragging: false });
};
handleMouseMove = event => {
if (this.state.dragging) {
const position = event.screenX;
// <-- update your image index
}
};
render = () => {
return (
<div onMouseDown={this.handleMouseDown}>
{this.renderImage()}
</div>
);
};
componentDidMount = () => {
document.addEventListener('mousemove', this.handleMouseMove, false);
document.addEventListener('mouseup', this.handleMouseUp, false);
};
componentWillUnmount = () => {
document.removeEventListener('mousemove', this.handleMouseMove, false);
document.removeEventListener('mouseup', this.handleMouseUp, false);
};
Here is a sandbox with a working example: https://codesandbox.io/s/6v3491kxw3

Canvas flickering when reactjs re-renders

I've got a problem using React JS and canvas together.
everything seems to work smooth, but when render method is called due to changes in other part of my app canvas flicker. It seems that react is re-appending it into the DOM (maybe because I'm playing an animation on it and its drawn frame is different each time).
This is my render method:
render: function() {
var canvasStyle = {
width: this.props.width
};
return (
<canvas id="interactiveCanvas" style={canvasStyle}/>
);
}
As a temporal solution I append my canvas tag manually, without reactjs:
render: function() {
var canvasStyle = {
width: this.props.width
};
return (
<div id="interactivediv3D">
</div>
);
}
And then:
componentDidMount: function() {
var canvasStyle = {
width: this.props.width
};
var el = document.getElementById('interactivediv3D');
el.innerHTML = el.innerHTML +
'<canvas id="interactive3D" style={'+canvasStyle+'}/>';
},
And it works right. But I think this is not a clean way and it's better to render this canvas tag using reactjs.
Any idea of how can I solve the problem? or at least avoiding reactjs re-rendering this canvas when only the drawn frame changed.
I spent time observing with your advice in mind and I find out this flickering was caused by the fact that I was accessing the canvas DOM element and changing canvas.width and canvas.height every time onComponentDidUpdate was called.
I needed to change this property manually because while rendering I don't know canvas.width yet. I fill style-width with "100%" and after rendering I look at "canvas.clientWidth" to extract the value in pixels.
As explained here: https://facebook.github.io/react/tips/dom-event-listeners.html I attached resize event and now I'm changing canvas properties only from my resizeHandler.
Re-rendering the entire tree shouldn't cause your canvas to flicker. But if you think that re-rendering is causing this issue, you could use shouldComponentUpdate lifecycle function and return false whenever you don't want to to update.
https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate
So for example:
shouldComponentUpdate: function(nextProps, nextState) {
return false;
}
Any component with the above function defined will not ever run render more than once (at component mounting).
However I've not been able to replicate the flickering issue you complain of. Is your entire tree rendering too often perhaps? In which case the above will fix your canvas, but you should take a look at how many times and how often you are re-rendering your entire tree and fix any issues there.
I had the same problem. I used react-konva library and my component structure looked something like this: canvas had a background image <SelectedImage /> which flicked every time any state property changed.
Initial code
import React, { useState } from "react";
import { Stage, Layer, Image } from "react-konva";
import useImage from "use-image";
function SomeComponent() {
const [imgWidth, setImgWidth] = useState(0);
const [imgHeight, setImgHeight] = useState(0);
//...and other state properties
const SelectedImage = () => {
const [image] = useImage(imgSRC);
if (imgWidth && imgHeight) {
return <Image image={image} width={imgWidth} height={imgHeight} />;
}
return null;
};
return (
<div>
<Stage width={imgWidth} height={imgHeight}>
<Layer>
<SelectedImage />
{/* some other canvas components*/}
</Layer>
</Stage>
</div>
);
}
export default SomeComponent;
The solution
The problem was in a wrong structure of SomeComponent where one component was initialized inside another. In my case it was <SelectedImage /> inside of SomeComponent. I took it out to a separate file and flickering disappeared!
Here is a correct structure
SelectedImage.js
import React from "react";
import { Image } from "react-konva";
import useImage from "use-image";
function SelectedImage({ width, height, imgSRC }) {
const [image] = useImage(imgSRC);
if (width && height) {
return <Image image={image} width={width} height={height} />;
}
return null;
}
export default SelectedImage;
SomeComponent.js
import React, { useState } from "react";
import { Stage, Layer, Image } from "react-konva";
import SelectedImage from "./SelectedImage";
function SomeComponent() {
const [imgWidth, setImgWidth] = useState(0);
const [imgHeight, setImgHeight] = useState(0);
//...and other state properties
return (
<div>
<Stage width={imgWidth} height={imgHeight}>
<Layer>
<SelectedImage width={imgWidth} height={imgHeight} imgSRC={imgSRC} />
{/* some other canvas components*/}
</Layer>
</Stage>
</div>
);
}
export default SomeComponent;

Categories

Resources