Long time I am tring to react-gif animate with play and pause.Like facebook gif image exactly.
I am using react-gif npm module.But does not working.Please anyone review my code.
coding.......
import Markdown from 'react-markdown';
import Gif from 'react-gif';
const linkRenderer = (linkNode) => {
return <a href={linkNode.href} title={linkNode.title} target="_blank" children={linkNode.children} />;
};
const imgixBase = 'https://chalees-min.imgix.net';
const imgixParameters = 'w=800&fit=max&auto=format,compress';
const imageRenderer = (imageNode) => {
const imageSource = imageNode.src.startsWith('/')
? `${imgixBase}${imageNode.src}?${imgixParameters}`
: imageNode.src;
return <Gif src={imageSource} alt={imageNode.alt} title={imageNode.title} />
};
const MarkdownCustomized = (props) => (
<Markdown
className={'markdown ' + (props.className || '')}
source={props.source || ''}
renderers={{
'Link': linkRenderer,
'Image': imageRenderer,
}} />
);
export default MarkdownCustomized;
You can't control (play-pause) an animated gif. Gifs in facebook are disguised videos.
There are some workarounds here, providing some kind of limited control.
I don't see any logic that makes the Gif component play or pause in your code.
Looking at the Gif react component code, I see two ways of making the Gif play and pause.
Set the prop playing to false to pause, and true to play. E.g Set gif to play
<Gif src={imageSource} alt={imageNode.alt} title={imageNode.title} playing={true} />
The Gif component has methods play and pause that can be accessed by using refs. So, on the component that uses Gif component, you can have something like the following:
class Parent extends Component {
handlePlayGif() {
this.currentGif.play();
}
handlePauseGif() {
this.currentGif.pause();
}
render() {
return ( //... rest of code
<Gif ref={(gif) => this.currentGif = gif} />
// ... rest of jsx
// onClick play gif
<div onClick={() => handlePlayGif()}>play gif</div>
);
}
}
Related
so i am creating a simple game and i render it if someone presses a start button on the first render. i want to preload all the images that the component is gonna use, so that when you hit the start button its already preloaded.
this is how the array of image paths look like:
const imagesArr = [
"/src/images/cheetah-min.webp",
"/src/images/dolphin-min.webp",
"/src/images/gorilla-min.webp",
"/src/images/lion-min.webp",
"/src/images/tiger-min.webp",
"/src/images/troll-min.webp",
"/src/images/werewolf.webp",
"/src/images/wolf-min.webp"
]
This is my App.jsx:
function App() {
const [isPlaying, setIsPlaying] = useState(false);
return (
<div className="game-container">
{!isPlaying ? (
<div className="welcome-screen">
<h1 className="welcome-heading">Welcome to the game</h1>
<button
className="start-btn"
onClick={() => {
setTimeout(() => {
GAMESTARTS_SOUND.play();
setIsPlaying(true);
}, 3000);
}}
>
Play the game
</button>
</div>
) : (
<Game />
)}
</div>
);
}
as you can see there is this Game container that gets rendered if you press the play button. Inside that component im using the images. at the moment im just rendering them out using <img tags in the returned JSX from Game.jsx.
what I want is to preload the images before that component gets rendered? any ideas?
I have the following logic that renders an image, the current flow is like the following: if the state is false a spinner shows then when the image loads the spinner disappears.
The core problem here is the state is re-rendering the component causing the image to load again I kind of ran out of options, on how to make an instant switch after the image loads.
It is not a loop but rather the image reloads again even if it is already loaded due to the render caused by setLoading(true).
How to prevent this reloading from happening. The useEffect logic is just a simulator for how it might take to load the image, but my real image coms from the icons variable.
export const iconsImg: React.FC<Props> = ({ img: string }) => {
useEffect(() => {
let newImg = new Image();
newImg.src = icons[img].props.src;
newImg.onload = () => {
setLoading(true);
};
}, []);
const icons: iconsInterface = {
a: <img className={classes.imgStyle} alt="a" src={link} />,
b: <img className={classes.imgStyle} alt="b" src={link} />,
}
const [loading, setLoading] = useState(false);
return (
<React.Fragment>
{!loading ? (
<Spinner>
) : (
icons[img]
)}
</React.Fragment>
)}
Issue
The problem with your last attempt is that you are starting with loading showing the Spinner, and you are trying to switch the state after the image is loaded, except that will never be the case, because the image is not mounted in the DOM.
Creating icons is not equivalent to creating img until it's added in the return and mounted into the DOM. And I think it's loading twice cause at some point you added the two of them in the DOM either directly or after loading changes.
Solution
You can simplify your component as below. Notice I removed icons and setting alt attribute (the one difference between the two) while creating the image. That setTimeout is so we see the loader. You can remove it later.
const IconsImg = ({ img }) => {
const [loading, setLoding] = React.useState(true);
return <div >{loading && "Loading..."} <img alt={img} style={{display: loading ? "none" : "block"}} onLoad= {()=> setTimeout(()=> setLoding(false), 1000)} src = "https://images.unsplash.com/photo-1657664042206-1a98fa4d153d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDF8MHxlZGl0b3JpYWwtZmVlZHwxfHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=500&q=60"/></div>;
};
/* The below code is to have a working example here at Stack Overflow */
ReactDOM.render(
<IconsImg img= "car" />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
newImg is a different object from icons[img]. When loading set
to true, browser loads same src this time for icons[img].
You can try this:
First set display:none for icons[img] if loading is false;
And:
<React.Fragment>
{loading || (
<Spinner>
)}
icons[img]
</React.Fragment>
Then:
useEffect(() => {
icons[img].onload = () => {
setLoading(true);
};
}, []);
or better:
<img className={classes.imgStyle} alt="a" src={link} onload={() => setLoading(true)}/>
There are a lot of topics about animating the loaded image, but I haven't seen a great example of this in React yet. I came with this component myself:
import { useState } from 'react';
export default function FadingImage({ src, ...props }) {
const [loaded, setLoaded] = useState(false);
return (
<img
src={src}
onLoad={() => setLoaded(true)}
className={!loaded ? 'loading' : ''}
{...props}
/>
);
};
img {
transition: opacity .25s;
}
.loading {
opacity: 0;
}
It works fine in the beginning, but then it's annoying that the same images get faded-in every single time. I'd like the cached images to appear instantly.
In vanilla JS it just works, because it's all done in the same render cycle. I'm not sure how it can be achieved in React.
Package suggestions are appreciated, but I'd also like to know how to do it for educational purpose.
I want to play 2 file in reactjs use ReactPlayer , file 1 is video music include audio human voice , file 2 is music only but the human voice has been deleted.
The problem when I run the code below is file 1 may start sooner than file 2 or vice versa , my question is can I play 2 file together , so when file 1 loading or render , file 2 will do same as file 1
This the code
import React, { useState } from "react";
import ReactPlayer from "react-player";
function App(){
const [playing, setPlaying] = useState(true);
const [muted, setMuted] = useState(true);
function handlePlayPause() {
setPlaying(!playing);
}
function handleMuted() {
setMuted(!muted);
}
return(
<div>
//play video music "I can fly include the music with human vocal"
<ReactPlayer
playing={playing}
url={"I can Fly.mp4"}
muted={muted}
/>
//play music only "I can fly (the file no human vocal)"
<ReactPlayer
playing={playing}
url={"I can fly(no vocal).mp3"}
muted={!muted}
hidden
/>
<button onClick={() => handlePlayPause()}>
{playing ? "pause" : "play"}
</button>
<button onClick={() => handleMuted()}>
{muted ? "vocal" : "no vocal"}
</button>
</div>
)}
export default App;
Hope you guys understand what I'm asking , sorry for my bad English :D
I guess the issue is from a video needs time to get ready before playing. Each video has a different its own time which means each video would have a different time to start playing.
As a result of that, we have to wait until all videos ready before playing them all at once. Luckily, react-player has offered a onReady callback telling that video is ready to play. Here is the general idea for you:
import React from "react";
import ReactPlayer from "react-player";
// Assuming to have 2 videos
const links = [
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4"
];
export default function App() {
// Count number of videos ready to play
const [readyCount, setReadyCount] = React.useState(0);
const [playing, setPlaying] = React.useState(false);
// Just keep counting as a video ready
const onReady = () => {
setReadyCount(readyCount + 1);
};
React.useEffect(() => {
// All videos ready to play, get them played
if (readyCount === links.length) {
setPlaying(true);
}
}, [readyCount]);
return (
<div className="App">
{links.map((url) => (
<ReactPlayer key={url} playing={playing} onReady={onReady} url={url} />
))}
</div>
);
}
I also have created a codesandbox for you: https://codesandbox.io/s/kind-bardeen-59t8f?file=/src/App.js
I have a blog implementation based on ReactJS that I would like to integrate with AddThis. I have my social icons and I want to use them. So I'm looking for a way to integrate just the AddThis backend service.
I tried looking around but I was not able to find how to integrate AddThis to a ReactJS component.
I saw this somewhere and it uses a special namespace which to the best of my knowledge is not react friendly.
<div addthis:url='blog_url' addthis:title='blog_title' class="addthis_toolbox">
<a class="addthis_button_facebook">
<svg ... />
</a>
<a class="addthis_button_twitter">
<svg ... />
</a>
<a class="addthis_button_linkedin">
<svg ... />
</a>
<a class="addthis_button_reddit">
<svg ... />
</a>
</div>
<script type="text/javascript" src="http://s7.addthis.com/js/250/addthis_widget.js#pubid=xa-4fc9383e1ee05f1b"></script>
Also, I saw this JSFiddle with some information on it, but it is not using ReactJS and does not use custom icons.
Question: Is there any good documentation around AddThis + React?
In addition to the data attribute changes you should use the addthis.layers.refresh() method to dynamically refresh/load your AddThis components:
render() {
return (
<div className="addthis_inline_share_toolbox"
data-url={this.props.myurl}
data-title="Check out this URL"
>
</div>
);
}
Then in componentDidMount():
componentDidMount() {
addthis.layers.refresh();
}
EDIT: The above method is the initial approach i took and does initialise the add this widget however, the widget seems to not update the data-url when the prop is changed. even if i call addthis.layers.refresh(); again after a props update
Dynamic update solution:
In my render method:
// Don't use data attributes
<div className="addthis_inline_share_toolbox"></div>
Use the lifecycle methods:
componentDidMount() {
addthis.layers.refresh(); // important! init the add this widget
addthis.update('share', 'url', 'my-initial-url'); // update with initial prop value
}
componentDidUpdate() {
addthis.update('share', 'url', this.props.myurl); // update with prop value
}
componentWillReceiveProps(nextProps) {
addthis.update('share', 'url', nextProps.myurl); // update with prop value
}
Replace addthis:url and addthis:title with data-addthis-url and data-addthis-title.
I put this div in to display the addthis buttons.
<div className="addthis_inline_share_toolbox" data-url={ `http://[Your URL]` } data-title={ `[Your Title]` }></div>
But I also needed to load the javascript after the component mounted or the buttons never display. I assume if you add the javascript to your template that it's loading before the share_toolbox is loaded.
componentDidMount() {
setTimeout( () => {
var addthisScript = document.createElement('script');
addthisScript.setAttribute('src', 'http://s7.addthis.com/js/300/addthis_widget.js#pubid=[your id here]')
if (document.body) document.body.appendChild(addthisScript)
});
},
Here is how I did it:
Please, note that I'm using the inline share toolbox.
Thanks #Mark for addthis.update and to #jvoros for react-load-script
import React, { useEffect } from 'react';
import Script from 'react-load-script';
const AddThis = (props) => {
useEffect(() => {
if (window.addthis) {
window.addthis.update('share', 'url', props.url);
}
}, [props.url]);
const handleAddthisLoaded = () => {
window.addthis.init();
window.addthis.update('share', 'url', props.url);
};
return (
<>
<div className="addthis_inline_share_toolbox"></div>
<Script
url="//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-xxxxxxxxxxxxxxxx"
onLoad={handleAddthisLoaded} />
</>
);
}
export default AddThis;
This was the only info I could find on implementing AddThis in a React app. Eventually helped me get a fix. I am posting my solution for anyone else who comes across this.
Using React Router and AddThis presented some challenges. The key was attaching the addThis javascript methods to window events and not React lifecycle events.
I used react-load-script to asynchronously load the script on the main page of my app, and implemented a callback to initialize the addThis widget and then set state to indicate if addThis was loaded. Then that state gets passed down to the component.
Partial code:
import * as LoadScript from 'react-load-script';
class App extends React.Component {
constructor(props) {
this.state = { addThisLoaded: false }
}
handleScriptLoad() {
this.setState({ addthisLoaded: true });
window.addthis.init();
}
render() {
return (
<LoadScript
url="http://s7.addthis.com/js/300/addthis_widget.js#pubid=ra-xxxxxxxxxxxxxxxx"
onLoad={this.handleScriptLoad.bind(this)}
/>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/page/:id"
render={routeProps => (<Page {...routeProps} addThisLoaded={this.state.addThisLoaded} />)}
/>
</Switch>
);
}
}
Then in the component that implements the addThis widget I attached the window event listeners to the React lifecycle hooks. The addThisLoaded prop can be used to conditionally render the widget.
export default class Page extends React.Component<Props> {
componentDidMount() {
window.addEventListener('load', window.addthis.layers.refresh());
}
componentWillUnmount() {
window.removeEventListener('load', window.addthis.layers.refresh);
}
render() {
const page_url = `YOUR URL GOES HERE`;
const page_title = `YOUR TITLE GOES HERE`;
return (
<div>
{this.props.addThisLoaded === true && (
<div className="addthis_inline_share_toolbox" data-url={page_url} data-title={page_title} />
)}
</div>
);
}
}
If there is a better way to handle it I'd love to hear. The AddThis API docs are sparse. And the fact that it manipulates the DOM to get the desired behavior makes it tricky to incorporate with React Router.
Replace addthis:url with data-url