import { FormEvent } from "react";
import styles from "./ToggleButton.module.css";
type ToggleProps = {
onToggle: (isChecked: boolean) => void;
};
export default function ToggleButton({ onToggle }: ToggleProps) {
const checkToggle = function (e: FormEvent<HTMLInputElement>) {
const isChecked = e.currentTarget.checked;
onToggle(isChecked);
};
return (
<div className={styles.toggle}>
<input
onClick={checkToggle}
className={`${styles.tgl} ${styles.tglSkewed}`}
id="switch"
type="checkbox"
/>
<label
className={styles.tglBtn}
data-tg-off="OFF"
data-tg-on="ON"
htmlFor="switch"
></label>
</div>
);
}
.toggle .tgl {
display: none;
}
.toggle .tgl,
.toggle .tgl:after,
.toggle .tgl:before,
.toggle .tgl *,
.toggle .tgl *:after,
.toggle .tgl *:before,
.toggle .tgl + .tglBtn {
box-sizing: border-box;
}
.toggle .tgl::-moz-selection,
.toggle .tgl:after::-moz-selection,
.toggle .tgl:before::-moz-selection,
.toggle .tgl *::-moz-selection,
.toggle .tgl *:after::-moz-selection,
.toggle .tgl *:before::-moz-selection,
.toggle .tgl + .tglBtn::-moz-selection,
.toggle .tgl::selection,
.toggle .tgl:after::selection,
.toggle .tgl:before::selection,
.toggle .tgl *::selection,
.toggle .tgl *:after::selection,
.toggle .tgl *:before::selection,
.toggle .tgl + .tglBtn::selection {
background: none;
}
.toggle .tgl + .tglBtn {
outline: 0;
display: block;
width: 4em;
height: 2em;
position: relative;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.toggle .tgl + .tglBtn:after,
.toggle .tgl + .tglBtn:before {
position: relative;
display: block;
content: "";
width: 50%;
height: 100%;
}
.toggle .tgl + .tglBtn:after {
left: 0;
}
.toggle .tgl + .tglBtn:before {
display: none;
}
.toggle .tgl:checked + .tglBtn:after {
left: 50%;
}
.toggle .tglSkewed + .tglBtn {
overflow: hidden;
transform: skew(-10deg);
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
transition: all 0.2s ease;
font-family: sans-serif;
background: #222;
}
.toggle .tglSkewed + .tglBtn:after,
.toggle .tglSkewed + .tglBtn:before {
transform: skew(10deg);
display: inline-block;
transition: all 0.2s ease;
width: 100%;
text-align: center;
position: absolute;
line-height: 2em;
font-weight: bold;
color: #fff;
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
}
.toggle .tglSkewed + .tglBtn:after {
left: 100%;
content: attr(data-tg-on);
}
.toggle .tglSkewed + .tglBtn:before {
left: 0;
content: attr(data-tg-off);
}
.toggle .tglSkewed + .tglBtn:active {
background: #222;
}
.toggle .tglSkewed + .tglBtn:active:before {
left: -10%;
}
.toggle .tglSkewed:checked + .tglBtn {
background: #ff5e24;
}
.toggle .tglSkewed:checked + .tglBtn:before {
left: -100%;
}
.toggle .tglSkewed:checked + .tglBtn:after {
left: 0;
}
.toggle .tglSkewed:checked + .tglBtn:active:after {
left: 10%;
}
import React, { FormEvent, useRef } from "react";
import { BiChevronLeft, BiChevronRight } from "react-icons/bi";
import styles from "./Slider.module.css";
import Image from "next/image";
import ToggleButton from "./ToggleButton";
type props = {
beforeImage: string;
afterImage: string;
beforeTitle: string;
afterTitle: string;
plcOne: string;
plcTwo: string;
};
export default function SliderComponent({
beforeImage,
afterImage,
beforeTitle,
afterTitle,
plcOne,
plcTwo,
}: props) {
const container = useRef<HTMLDivElement | null>(null);
const toggleSlider = function (e: FormEvent<HTMLInputElement>) {
container.current?.style.setProperty(
"--position",
`${e.currentTarget.value}%`
);
};
function togglePercentage(value: string) {
container.current?.style.setProperty("--position", value);
}
// Toggles before or after function based on checkbox state
const handleToggle = (isChecked: boolean) =>
isChecked ? togglePercentage("0%") : togglePercentage("100%");
return (
<div ref={container} className={styles.container}>
<div className={styles.imageContainer}>
<Image
className={`${styles.sliderImage} ${styles.imageBefore}`}
src={beforeImage}
width={1000}
height={320}
alt={beforeTitle}
placeholder="blur"
blurDataURL={plcOne}
quality={100}
/>
<Image
className={`${styles.sliderImage} ${styles.imageAfter}`}
width={1000}
height={320}
src={afterImage}
alt={afterTitle}
placeholder="blur"
blurDataURL={plcTwo}
quality={100}
/>
</div>
<input
onInput={toggleSlider}
type="range"
min="0"
max="100"
defaultValue="50"
className={styles.slider}
aria-label="Percentage of image showing"
/>
<div className={styles.sliderLine} aria-hidden="true"></div>
<button
className={styles.sliderButton}
aria-label="Toggle image difference"
>
<BiChevronLeft />
<BiChevronRight />
</button>
<div className="absolute bottom-10 left-1/2 -translate-x-1/2 lg:hidden block">
<ToggleButton onToggle={handleToggle} />
</div>
</div>
);
}
.container {
display: grid;
place-content: center;
position: relative;
max-width: 1200px;
margin: 0 auto;
width: 1000px;
--position: 50%;
}
.imageContainer {
overflow: hidden;
}
.sliderImage {
height: 100%;
object-fit: cover;
object-position: left;
}
.imageBefore {
position: absolute;
inset: 0;
width: var(--position);
}
.imageAfter {
object-fit: cover;
object-position: right;
inset: 0;
}
.slider {
position: absolute;
inset: 0;
cursor: pointer;
opacity: 0;
width: 100%;
height: 100%;
}
.sliderLine {
position: absolute;
inset: 0;
width: 0.2rem;
height: 100%;
background-color: #fff;
left: var(--position);
transform: translateX(-50%);
pointer-events: none;
}
.sliderButton {
display: flex;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
position: absolute;
background-color: #fff;
border: none;
color: black;
padding: 0.5rem;
border-radius: 50%;
font-size: 1.3rem;
top: 50%;
left: var(--position);
transform: translate(-50%, -50%);
pointer-events: none;
box-shadow: 1px 1px 1px hsl(0, 50%, 2%, 0.5);
}
@media (max-width: 1024px) {
.container {
max-width: 100%;
}
.sliderLine,
.sliderButton {
display: none;
}
}
<SliderComponent
key={slides._id}
beforeImage={slides.beforeImage}
afterImage={slides.afterImage}
beforeTitle={slides.beforeAlt}
afterTitle={slides.afterAlt}
plcOne={slides.beforeLqip}
plcTwo={slides.afterLqip}
/>