Good day!
I am working on a project in React where I have several images that look almost the same, but differ in some way. Here are the four images
What I want to do is to have a slider that changes the image on slide. So the image essentially gets replaced with the next one "in place" - if that makes sense. Thus, it's not a carousel, because the image is not moving, it's being replaced by another image. Here is a mock-up of what I want to do
The images are maps with locations that change each year. So I want the user to see how the locations change without the image moving.
I hope this makes sense. Are there any libraries or principles I should look at?
Thanks!
You question is very vague. There are many ways to achieve what you want. From the tag you used i assume that you are using React? I've created a basic example on what it could look like.
One way is to position the images on top of each other and toggle the visibility/opacity, like so:
class App extends React.Component {
state = {
imageIndex: 0,
}
setIndex = (e) => {
this.setState({imageIndex: parseInt(e.target.value)});
}
render() {
const {imageIndex} = this.state;
return (
<div>
<div className="image-wrapper">
<img style={{opacity: imageIndex === 0 ? 1 : 0}} src="https://via.placeholder.com/150/ff0"/>
<img style={{opacity: imageIndex === 1 ? 1 : 0}} src="https://via.placeholder.com/150/f00"/>
<img style={{opacity: imageIndex === 2 ? 1 : 0}} src="https://via.placeholder.com/150/f0f"/>
<img style={{opacity: imageIndex === 3 ? 1 : 0}} src="https://via.placeholder.com/150/0f0"/>
</div>
<input
type="range"
value={imageIndex}
min="0"
max="3"
onChange={this.setIndex}
/>
</div>
);
}
};
ReactDOM.render(<App/>, document.body);
img {
position: absolute;
width: 100%;
height: 100%;
transition: opacity .3s;
}
.image-wrapper {
width: 150px;
height: 150px;
position: relative;
}
input[type="range"] {
width: 150px;
}
html,
body {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Another way would be to render the image that is currently shown to the user, which can make animation handling a bit iffy, as unmounting needs to be delayed (in that case you should take a look at a React Animation Framework, e.g. React Transition Group) The image should be positioned with css in a way that it does not move.
I hope you can salvage something from this answer.
Related
I'm new to react. I am creating a cardlist with react bootstrap. Problem is no matter what I do When my list become larger it overflow and show like this.
Note that my other half is hidden on top of the browser.
my fragment of code is below.
class Admin extends Component {
render() {
const examlist = exams ? (
exams.map((exam) => {
let tempTime = new Date(exam.startTime.seconds * 1000);
let time = tempTime.toLocaleDateString("en-GB", {
hour: "numeric",
hour12: true,
});
return (
<AddExamTab
key={exam.id}
title={exam.title}
description={exam.description}
id={exam.id}
className={exam.class}
time={time}
/>
);
})
) : (
<div className="container">
<h4>
<Spinner />
</h4>
</div>
);
return (
<div className="container">
<AddExam />
{examlist}
</div>
);
}
}
Maybe I have done somthing silly. what am I doing wrong here? Can anyone tell me? I'd be very grateful if anyone help me in this problem.
It's not a React related problem, but a CSS related.
You should experiment with the parent div (with "container" class). Try to create your own class, add height and overflow properties.
Here is a small example of how you can implement that:
.container {
height: 200px;
width: 50%;
overflow-y: scroll;
background-color: #828282;
}
.container > div {
height: 100px;
margin-bottom: 2px;
background-color: #929292;
}
<div class="container">
<div>x1</div>
<div>x2</div>
<div>x3</div>
<div>x4</div>
<div>x5</div>
</div>
For those stumbling on this question wondering how to improve their lighthouse score in general, I posted an answer on this topic on another question with lots of general tips.
I'm running PageSpeed insights and my biggest problem is the largest contentful paint, at about 8-10 seconds. Below they list my largest contentful paint element
Largest Contentful Paint element 1 element found
This is the largest contentful element painted within the viewport. Learn More
Element
This is the a paragraph that appears above here
<section class="mainBgImage gbi--716926567-eDXcajFRMpQ2F1s7wNgLk1" style="background-position:center top;background-repeat:no-repeat;background-size:cover;position:relative;opacity:0.99" role="img">
This element is an image that spans my whole website in the background. It was originally a 1.2 MB png that i load using ...GatsbyImageSharpFluid_withWebp_noBase64 with a maxWidth of 1950.
This is the code for how I render it
import BackgroundImage from 'gatsby-background-image';
...
<BackgroundImage
Tag="section"
role="img"
className='mainBgImage'
fadeIn={false}
// style={{objectFit: 'contain', width: '100%' }}
style={{
opacity: 0.03,
backgroundPosition: "center top",
}}
fluid={wheatImgProps}
>
{children}
</BackgroundImage>
And this is the static graphql query
const data = useStaticQuery(graphql
`query LayoutQuery {
wheatImg: file(
extension: {regex: "/(jpg)|(jpeg)|(png)/"},
name: {eq: "wheat-background"}
) {
childImageSharp {
fluid(maxWidth: 1950) {
...GatsbyImageSharpFluid_withWebp_noBase64
}
}
}
}
`
)
Turns out the solution was to split my background image into 2. One for "above the fold"(Whats visible without scrolling) and one for "below the fold". I also found this image compressor website to be one of the most helpful and straight forward when it came to reducing my file size.
I then created an absolutely positioned div which looked like
const BGImg = ({img, className}:{img:FluidObject, className?:string}) => (
<Img
Tag="section"
className={className}
durationFadeIn={250}
style={{
opacity: 0.03,
minWidth:"100%",
objectFit: 'cover',
objectPosition: "center top",
}}
fluid={img}
/>
)
...
<div id='layout'>
<div id='bgImageContainer'>
<BGImg img={above_the_fold_bg} />
<BGImg img={below_the_fold_bg} />
</div>
...
with styling that looked like
#bgImageContainer{
position: absolute;
z-index: -999;
min-width:100%;
min-height:100%;
display:flex;
flex-direction: column;
justify-content: flex-end;
align-self: stretch;
}
#layout{
overflow: hidden;
position: relative;
}
How would one go for setting up the z-index, so that the 4 layers are as follow:
background color
logoImg (png with opacity 00.7) (!)
circleImg (png)
coundownClock (text)
I tried changing z-index in every possible way, and I can not get that the 3. circleImg & 4. coundownClock overlap.
#1. background color & #2. logoImg are set correctly as background
TimerScreen.js:
import React from 'react';
import UIfx from 'uifx';
import Logo from "../images/logo.png";
import Circle from "../images/circle.png";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
class TimerScreen extends React.Component {
state = {
minutes: 3,
seconds: 0,
reps: 3
}
render() {
const { minutes, seconds, reps } = this.state
return (
<div className="mainDiv" >
<img
className="logoImg"
style={{
width: "100%",
opacity: "0.07",
position: "absolute",
zIndex: "-1"
}}
src={Logo}
alt="berolina-stralau logo"
/>
<div className="coundownClock"> { minutes < 10 ? `0${ minutes }` : minutes }:{ seconds < 10 ? `0${ seconds }` : seconds } </div>
<img id="circleImg" src={Circle} />
<div className="repetitionsCount"> <i>reps left: {reps}</i> </div>
<div id="buttonDiv">
<Link to="/TimerScreen"
className="goButton">PAUSE</Link>
</div>
<br />
</div>
);
}
}
export default TimerScreen;
App.css part:
circleImg {
width: 100%;
margin-bottom: 40px;
position: "absolute";
z-index: "0";
}
.coundownClock {
display: flex;
justify-content: center;
font-size: 111px;
font-weight: bold;
position: "relative";
z-index: "1";
}
Full code on github.com FilipZafran/Interval-Timer
Definition from https://www.w3schools.com/cssref/pr_pos_z-index.asp:
The z-index property specifies the stack order of an element.
An element with greater stack order is always in front of an element
with a lower stack order.
Note: z-index only works on positioned elements (position: absolute,
position: relative, position: fixed, or position: sticky).
So make sure your elements have a position and z-index to create the wanted order. If I understood you correctly that would be:
background color -> z-index: 0
logoImg (png with opacity 00.7) (!) -> z-index: 1
circleImg (png) -> z-index: 2
coundownClock (text) -> z-index: 3
background color is then in the back and coundownClock in the front
As an advice: It would be easier to wrap the elements you want to stack above each other in a which has position: relative. Inside you put all the elements you want to stack with position: absolute and the z-index for creating your order
The page has a prev and next buttons that lets us to iterate through the pages, displaying the data in rows of cards containing a image. On click of next of prev button for first time the text fields display instantly, but the img tag lags a bit, showing the old page image for a second or two, and then showing the new image in react.
Any idea how I can prevent this? Or at least get the first image to disappear immediately?
Judging from the information that you presented, it is hard to share a solution, without us even seeing it. What I can suggest you is adding a loader to the image, so it brings you much better user experience. Try the following:
class LoadableImage extends Component {
state = {
isLoaded: false,
};
render() {
let { path, alt, onLoad, ...rest } = this.props;
return (
<div className='position-relative h-100'>
<img
className='img-fluid p-3'
src={ path }
alt={ alt }
{ ...rest }
onLoad={ this.handleImageLoad }
/>
{ !this.state.isLoaded && (
<div className="loader-container">
<span className="loader text-center">
<div> Custom loader text or animation </div>
</span>
</div>
)
}
</div>
);
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (prevProps.path !== this.props.path) {
this.setState({ isLoaded: false });
}
}
/**
* Handles the load of the image and executes any overriden onLoad functions if available.
* #param {Event} e
*/
handleImageLoad = (e) => {
if (this.props.onLoad) {
this.props.onLoad(e);
}
this.setState({
isLoaded: true,
});
};
}
CSS:
.loader-container {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
background: rgba(0, 0, 0, 0.4);
animation: fadeIn 0.3s ease-out;
z-index: 110;
overflow: hidden;
}
.loader {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #0083db;
}
I am using Bootstrap 4 dependencies, but if you don't, here are the classes bodies:
.position-relative {
position: relative!important;
}
.h-100 {
height: 100%!important;
}
.img-fluid {
max-width: 100%;
height: auto;
}
Usage:
<LoadableImage path={'/image/path'} alt='Alternative text' />
You could also add custom parameters to the <img> tag. Feel free to make your custom loader design.
I've used time-picker from PrimeNG, I need the time only on left and the time to be displayed on the right side of text. I'm unable to add css and change its view can any one please look into this ?
<div class="ui-g-12 ui-md-4">
<h3>Select a Time</h3>
<p-calendar [(ngModel)]="date8" [timeOnly]="true" class="timer"></p-calendar>
</div>
Try this snippet.
.custom-time-picker .ui-inputtext {
width: 80px;
}
.custom-time-picker .ui-datepicker {
top: 0;
left: 80px;
}
Don't forget to set ViewEncapsulation.None in the container component.