What is correct way to make 'appear' effect for img with react?
What we have:
const ProductCard = ({product}) => {
const [imgLoading, setImgLoading] = useState(true);
const onLoad = () => {
setImgLoading(false);
};
return (
<div className={classNames({
'product-card': true,
'img-loading': imgLoading,
})}
>
<img
src={product.photo}
alt={product.name}
onLoad={onLoad}
/>
<div className='img-cover'></div>
{/* ... */}
</div>
);
};
I use class img-loading to cover the image while it's loading.
.product-card {
position: relative;
.img-cover {
position: absolute;
inset: 0;
background-color: #000;
opacity: 0;
transition: opacity 1.75s;
}
&.img-loading .img-cover {
opacity: 1;
}
}
Prop product changes when user goes to another page (route).
And first time it works well: when new product comes to props, it renders as expected.
But when user comes back to a page that he has already visited and img loads from cache, it renders without effect I set, it's just shown immediately. I think it's about cache because when I turned on Disable cache in chrome dev tools it works perfect for me.
try this:
https://michalsnik.github.io/aos/
scroll down to the end of the page and see how it works
"i use this in web design (html,css) not sure about react"
you just add CDN sources and call the class you want
Related
I am trying to create a app Download Strip in react .When the page renders for the first time it should show app download strip but after the user click cross icon it should not occur again if someone click on any other link that routes to different product. Also if the user closes the tab and again visits the page it should work fine.
i tried to write the code taking local storage in the mind here is the code for every neccessary file and i expected that for the first time app-download strip will be visible and when someone cancels and move to another link in the website. app-strip-download should not be visible again.
Also if the user closes the tab and revists the page app-download strip should be visible again.
Just keep in miund the app-download-strip container is being called from the different locations as well so the code i wrote is focused around showing a app-strip-download only for the first time .
i am stuck at the part where i need to at the last need to clear local storage so that again when the user visits the page my logic could work fine .
Here are the neccessary files i wrote to make it happen .
for styling
const fadeInTop = keyframes`
from {
height: 0;
}
to {
height: auto;
}
`;
export const AppDownloadContainer = styled.div`
animation: ${fadeInTop};
animation-duration: 1s;
animation-fill-mode: both;
background-color: #ffa600;
position: relative;
position: fixed;
top: 0;
width: 100%;
padding: 0 20px;
z-index: 10009;
#media (min-width: 769px) {
justify-content: center;
}
#media (min-width: 379px) {
display: flex;
align-items: center;
min-height: 62px;
}
#media (max-width: 378px) {
text-align: center;
min-height: 87px;
}
`;
Component code
constructor(props){
super(props);
this.state = {
showAppDownload: false
};
}
componentDidMount() {
console.log("inside component did mount line 42");
if (window && Object.keys(window).length > 0) {
let isShown = "isShown";
console.log(window.localStorage.getItem(isShown),"line52");
if (window.localStorage.getItem(isShown) === null) {
console.log("Am i coming inside this ","Line 54");
window.localStorage.setItem(isShown, true);
setTimeout(() => {
window.addEventListener("beforeunload", function (e) {
window.localStorage.removeItem(isShown);
})
this.setState({ showAppDownload: true });
}, 4000);
}
}
}
hideAppDownload = () => {
this.setState({ showAppDownload: false });
};
{showAppDownload ? (
<AppDownloadContainer>
<AppDownloadContent>
Download the GoPaisa App Now and Get Exclusive Bonus Cashback Offers on
Flipkart and Amazon
</AppDownloadContent>
<a
target="_blank"
href="">
<DownloadAppImg
src=""
alt="Download GoPaisa App"
/>
</a>
<CloseDownloadContent onClick={this.hideAppDownload}>
✕
</CloseDownloadContent>
</AppDownloadContainer>
) : (
""
)}
I really appreciate any help regarding this issue .
thanks .
I have a vue Nuxt project where I explore lazyloading with lazysizes package.
I created a spinner component (html css only) who should be visible only while the image is loading.
I also created an ImageItem component who includes the spinner component and it looks like this:
< script >
import spinner from '~/components/spinner.vue'
export default {
components: {
spinner,
},
props: {
source: {
type: String,
required: true,
}
},
} <
/script>
<style lang="scss" scoped>.imageItem {
position: relative;
.image {
z-index: 2;
&.lazyload,
&.lazyloading {
opacity: 0;
}
&.lazyloaded {
opacity: 1;
transition: all 1s linear 0.35s;
}
}
}
</style>
<template>
<div class="imageItem">
<spinner />
<img class="image lazyload" :data-srcset="source" />
</div>
</template>
To explain my code, I have props: source where In parent component i pass the image i want to lazyload. Also in the CSS while the image is loading, the image has .lazyloading class and when is loaded .lazyloaded class. Right now when Image is loaded i put it on top of the spinner.
My problem is, when I load the image I want to hide or destroy the spinner element since I think just putting the image on top is not the best way to do it. Can someone give me direction how should I properly hide the spinner when the image is loaded ?
Lazysizes fires an event when loading the image is finished : lazyloaded event, So you can do this :
<template>
<div class="imageItem">
<spinner v-if="lazyloading"/>
<img class="image lazyload" :data-srcset="source" />
</div>
</template>
<script>
import spinner from '~/components/spinner.vue'
export default {
data(){
return {
lazyloading
}
},
mounted(){
document.addEventListener('lazyloaded', (e) => {
this.lazyloading = false;
}
});
}
}
</script>
I have a header, whose className changes depending on State. Each class has a different background image, specified in the CSS. Everything works fine, but the transitions are quite abrupt without a fade-in effect.
I wrote:
.jumbotron-img-1{
background-image: url("/images/myImg1.jpg");
transition: all 1s ease-in-out;
It works, but it's ugly. There is a zoom, and a distortion of the image before it shows up in its final form. I've watched some tutorials on Google, but nothing was simple and to the point for background-image transition in pure CSS or React.
Any help would be greatly appreciated, thanks!
background-image is not an animatable property. I feel what best serves your purpose is to render multiple headers with all the classnames available stacked over each other with position: absolute; relative to common parent and make only one of them visible using opacity property based on which classname is active in your state and use transition on opacity
Sample working code:
render() {
const {imgClassList} = this.props;
const {activeimgClass} = this.state;
return (
<div className="header-container">
{imgClassList.map(imgClass => {
return (
<div
className={`header ${imgClass} ${(imgClass === activeimgClass)? 'active' : ''}`}
/>)
})}
</div>
)
}
And css be something like:
.header-container {
position: relative;
}
.header{
position: absolute;
top: 0;
left: 0;
opacity: 0
transition: opacity 1s ease-in-out;
}
.header.active {
opacity: 1
}
.img-1 {
background:url('images/img-1')
}
.img-2 {
background: url('images/img-2')
} ... and so on
There's no good way to transition a background image using CSS because it's not an animatable property, per the CSS spec. One way to do this is to just have multiple images on top of one another, each containing a different one of the images you'd like to display, and then cycle through them by transitioning them to opacity: 0 and changing their z-index order.
I made a quick demo showing how you can achieve smooth changes by manipulating opacity and z-index. In pure Javascript, this is done by simply adjusting the styles with DOM manipulation and using setTimeout().
Of course in React you don't want to be doing DOM manipulation, so you can experiment with multiple classes with different opacity levels and transitions to accomplish this. There also seems to be a React component that enables all types of transitions: https://reactcommunity.org/react-transition-group/css-transition
Check out the Javascript solution demo to see how changing the opacity can get a crossfade effect on images:
function backgroundScheduler_1() {
setTimeout(() => {
document.querySelector(".img1").style.opacity = 0;
document.querySelector(".img2").style.opacity = 1;
document.querySelector(".img3").style.opacity = 1;
order(["-3", "-1", "-2"], () => { backgroundScheduler_2() }, 1000);
}, 3000);
}
function backgroundScheduler_2() {
setTimeout(() => {
document.querySelector(".img1").style.opacity = 1;
document.querySelector(".img2").style.opacity = 0;
document.querySelector(".img3").style.opacity = 1;
order(["-2", "-3", "-1"], () => { backgroundScheduler_3() }, 1000);
}, 3000);
}
function backgroundScheduler_3() {
setTimeout(() => {
document.querySelector(".img1").style.opacity = 1;
document.querySelector(".img2").style.opacity = 1;
document.querySelector(".img3").style.opacity = 0;
order(["-1", "-2", "-3"], () => { backgroundScheduler_1() }, 1000);
}, 3000);
}
function order(array, callback, time) {
setTimeout(() => {
document.querySelector(".img1").style.zIndex = array[0];
document.querySelector(".img2").style.zIndex = array[1];
document.querySelector(".img3").style.zIndex = array[2];
callback();
}, time);
}
backgroundScheduler_1();
.background-image {
position: absolute;
top: 0;
left: 0;
opacity: 1;
transition: 1s;
}
.img1 {
z-index: -1;
}
.img2 {
z-index: -2;
}
.img3 {
z-index: -3;
}
<div class="background-container">
<img class="background-image img1" src="https://placeimg.com/640/640/nature"></img>
<img class="background-image img2" src="https://placeimg.com/640/640/animals"></img>
<img class="background-image img3" src="https://placeimg.com/640/640/tech"></img>
<h2 style="color: white;">WOW!</h2>
</div>
I checked NPM momentarily and didn't see anything that promises this exact functionality. Hope this helps!
I've written a React app, using CSS transitions. But those transitions does not work correctly in some of the components. In my app, only the components who are moving upwards works well, those who are moving downwards moves instantly without animation. (I want them both moves with animation.)
Here is the CSS I used there:
div.canvas {
position: absolute;
top: 90px;
left: 60px;
width: 640px;
height: 480px;
border: 1px solid #999;
background: white;
}
div.canvas-rect {
position: relative;
margin-left: 20px;
margin-top: 10px;
height: 20px;
background: green;
transition: all 1s linear;
-moz-transition: all 1s linear; /* Firefox 4 */
-webkit-transition: all 1s linear; /* Safari 和 Chrome */
-o-transition: all 1s linear; /* Opera */
}
UPDATED:
I also built a codepen.io project to show the problem. It has the complete code of this demo project.
I've tried to add a log entry to componentDidUpdate, componentDidMount and componentWillUnmount methods to show whether these component are re-created or updated, it shows that they are all updated (not re-created, or removed) every second.
Well, after I started a bounty because I also have this problem I finally found what seems to be the problem.
When you are using absolute position (or relative, as in your case), if you re-render the whole list every time, React will re-order the elements in the DOM (as you said, the elements are not being recreated, just updated). But this creates the problem with the transitions... apparently, if you move an element while the transition is running then you end up cutting the animation.
So, for cases in which you want to use position absolute, the key concept is to render the containers of your elements once (in this case, just divs) and only change the inner contents based on the new order. If you need to add more elements, just add them at the end.
I modified your codepen so that it reflects what I am saying. My example is very dumb because I just created 4 ad-hoc divs, but it illustrates the idea: create as many containers as you need, but DO NOT use a map that recreates them every time, or your transitions will be cut.
https://codepen.io/damianmr/pen/boEmmy?editors=0110
const ArrList = ({
arr
}) => {
return (
<div style={{position: 'relative'}}>
<div className={`element element-${arr[0]} index-${arr[0]}`}>{arr[0]}</div>
<div className={`element element-${arr[1]} index-${arr[1]}`}>{arr[1]}</div>
<div className={`element element-${arr[2]} index-${arr[2]}`}>{arr[2]}</div>
<div className={`element element-${arr[3]} index-${arr[3]}`}>{arr[3]}</div>
</div>
);
}
So, the problem is basically how you create a static list of containers and how you iterate through that list so that the first container renders the first element of your data, the second container the second element, etc.
Hope that it helps, this problem was driving me crazy too! :)
I know this wasn't the case, but since I got here also looking for React css transition does not work correctly, I just wanted to share:
If you create an element using arrow functions inside render, it won't get properly animated, since a new componente is always being created.
You should create a function outside and invoke it in 'render'.
You can trick React by using index as key. If you think about el, and index as starting position (index) and end position (el), the element has moved to the old end position by the end of the transition, and by when it's there, it's taken over by the new start position and (index) is switched to match the new setup. This is because when you set key in an element in react, the virtual DOM will always interpret it as it is the same element. And for the sake of it, you're right in setting index as the "id" in general.
I made a working example only by switching index/el (and setting element position to absolute).
const {combineReducers, createStore} = Redux;
const { Provider, connect } = ReactRedux;
const ArrList = ({
arr
}) => (
<div>{
arr.map((el, index)=>
<div
key={""+index}
className={`element element-${el}` + ` index-${el}`}
>
{el}
</div>) }
</div>
)
const mapStateToArrList = (state) => {
return {
arr: state.appReducer.arr
}
};
const App = connect(mapStateToArrList, null)(ArrList);
const initialState = {
arr: [1, 2, 3, 4]
}
const appReducer = (state = initialState, action) => {
switch(action.type) {
case "tick":
return {
...state,
arr: _.shuffle(state.arr)
}
default:
return state
}
}
const reducer = combineReducers({
appReducer
})
const store = createStore(reducer)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
const dispatcher = () => {
store.dispatch({
type: "tick"
})
setTimeout(dispatcher, 1000)
}
dispatcher()
.element {
position: absolute;
height: 20px;
background: green;
margin-left: 20px;
margin-top: 20px;
text-align: right;
color: white;
line-height: 20px;
transition: all 1s ease-in;
-moz-transition: all 1s ease-in; /* Firefox 4 */
-webkit-transition: all 1s ease-in; /* Safari 和 Chrome */
-o-transition: all 1s ease-in; /* Opera */
}
.element-1 {
width: 20px;
}
.element-2 {
width: 40px;
}
.element-3 {
width: 60px;
}
.element-4 {
width: 80px;
}
.index-1 {
top: 20px;
}
.index-2 {
top: 40px;
}
.index-3 {
top: 60px;
}
.index-4 {
top: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.6/react-redux.js"></script>
<div id="root"></div>
If you are removing the element from the virtual DOM, then the react will update its contents, so you won't see the animations. What you can do is either use react-transition-group OR tell your app to wait x ms before updating the dom once the event is called OR use visibility to toggle between hidden and showing instead of removing it completely from the DOM.
You did recreate DOM elements each time.
You should define collect key value.
I changed your key value '' + el to '' + index.
<div key={'' + index} className={'element element-' + el + ' index-' + index} >
Just change css properties only :)
I''ll just leave that here in case helps somebody but to me it was solved by moving from
export default function Contact(props) {...}
To:
const Contact = (props) => {...}
export default Contact
I would like to highlight a div when it's clicked.
Heres the example: www.spidex.org
On this website if you hover any of the navigation buttons a div on the top of the page is highlighted.
You may use jQuery for achieving this.
get jQuery here.
now consider that you have a div that you want to highlight on mouseover called item.
do this by adding an overlay div.
div.overlay{
opacity:0;
background:#000;
width:100%;
height:100%;
position:absolute;
top:50px;left:0;
}
then use jquery
jQuery(document).ready(function($){
$('.item').mouseover(function(){
$('.overlay').css({opacity:0.3});
});
});
You can change the appearance of elements when hovered using the :hover pseudo-class.
For example
div:hover {
color: red;
}
Secondly, you can change the text color via using the color property and the background color using the background-color property.
Both are shown below:
div:hover {
color: black;
background-color: white;
}
In your given example, when you hover over the primary navigation items in the super-header, then the body dims. I agree with your analysis that this is managed with some cover div of the body.
One cross-browser approach (using jQuery in this example) you might consider would be the following:
EXAMPLE HTML:
<div class="header">
Some Link
</div>
<div class="body">
<div class="body-content">
[ CONTENT HTML ]
</div>
<div class="body-cover"></div>
</div>
EXAMPLE CSS:
.body {
position: relative; /* container needs position */
}
.body-cover {
position: absolute;
top: 0px;
left: 0px;
background-color: blue;
/*
you could use a sligtly transparent background here,
or tween your opacity in your javascript
*/
}
EXAMPLE JavaScript:
// on dom ready
jQuery(function ($) {
// closures
var $links = $('.header a');
var $body = $('.body');
var $content = $body.find('.body-content');
var $cover = $body.find('.body-cover');
var sCoverHiddenCssClassName = 'body-cover-hidden';
var sCoverTweeningCssClassName = 'body-cover-tweening';
var sCoverShowingCssClassName = 'body-cover-showing';
// closure methods
var fMouseOver = function () {
// check to see if hidden (not already tweening or showing)
if ($cover.hasClass(sCoverHiddenCssClassName)) {
// check content, may have changed.
$cover.css({
height: $content.outerHeight(),
width: $content.outerWidth()
});
// animate or tween cover (do this however you want)
$cover
.removeClass(sCoverHiddenCssClassName)
.addClass(sCoverTweeningCssClassName)
.fadeIn(function () {
// when completed, mark as showing/visible
$cover
.removeClass(sCoverTweeningCssClassName)
.addClass(sCoverShowingCssClassName);
});
}
};
var fMouseOut = function () {
// check to see if visible (not already tweening or hidden)
if ($cover.hasClass(sCoverShowingCssClassName)) {
// animate or tween cover (do this however you want)
$cover
.removeClass(sCoverShowingCssClassName)
.addClass(sCoverTweeningCssClassName)
.fadeOut(function () {
// when completed, mark as showing/visible
$cover
.removeClass(sCoverTweeningCssClassName)
.addClass(sCoverHiddenCssClassName);
});
}
};
var fClick = function (e) {
// prevent default if needed for anchors or submit buttons
// e.preventDefault();
if ($cover.hasClass(sCoverHiddenCssClassName)) {
fMouseOver();
}
else if ($cover.hasClass(sCoverShowingCssClassName)) {
fMouseOut();
}
};
// init interaction
$cover.hide().addClass(sCoverHiddenCssClassName);
$links.each(function () {
// wire links
jQuery(this)
.mouseover(fMouseOver)
.mouseout(fMouseOut);//
//.click(fClick); // use click event if desired
});
});
JQuery UI is also gives an good option to quickly highlight div .
https://jqueryui.com/effect/
$( "#divId" ).effect( "highlight", 500 );