./carousel.module.css:
.container {
overflow: hidden;
position: relative;
}
.inner {
white-space: nowrap;
transition: transform 0.3s;
width: 100%;
height: 100%;
}
.carousel_item {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.carousel_info{
width: 100%;
height: 100%;
}
.carousel_info_image {
width: 100%;
height: 100%;
border-radius: 20px
}
.carousel_info_text {
animation: info_text 2s ease;
animation-fill-mode: forwards;
color: white;
position: absolute;
text-align: center;
width: 250px;
top: 100px;
}
.carousel_info_title {
font-size: 18px;
width: 100%;
}
.carousel_info_content {
font-size: 15px;
width: 100%;
white-space:normal;
word-break:break-all;
line-height: 40px;
}
.loading {
position: absolute;
bottom: 50px;
display: flex;
align-items: center;
margin-bottom: 10px;
width: 100%;
left: 0;
flex-direction: row;
}
.indicator_outer {
width: 90px;
height: 7px;
background-color: #d4d4d4;
margin-left: 20px;
border-radius: 5px;
}
.indicator_inside {
height: 100%;
border-radius: 5px;
animation-fill-mode: forwards;
animation-name: progressBar;
animation-iteration-count: infinite;
}
@keyframes info_text {
0% {
transform: translateX(0px);
-webkit-transform: translateX(0px);
}
100% {
transform: translateX(10%);
-webkit-transform: translateX(10%);
}
}
@keyframes progressBar {
0% {
width: 0%;
}
100% {
width: 100%;
}
}
carousel.js:
import React, { useState, useEffect } from "react";
import style from "./carousel.module.css";
/**
* @param {children} children ReactNode
* @param {styles} styles 样式
* @returns 轮播图 单项
*/
export const CarouselItem = ({
children = React.createElement("div"),
styles = {},
}) => {
return (
<div
className={style.carousel_item}
style={
{ ...styles }}>
{children}
</div>
);
};
/**
* @param {image} image 图片
* @returns 轮播图 主体
*/
export const CarouselInfo = ({ image = "", title="", content="" }) => {
return (
<div className={style.carousel_info}>
<img src={image} alt="Jay" className={style.carousel_info_image} />
<div className={style.carousel_info_text}>
<div className={style.carousel_info_title}>{title}</div>
<div className={style.carousel_info_content}>{content}</div>
</div>
</div>
);
};
/**
* @param {children} children ReactNode
* @param {switchingTime} switchingTime 间隔时间 默认3秒 以毫秒为单位 3000ms = 3s
* @returns 轮播图 容器
*/
const Carousel = ({
children = React.createElement("div"),
switchingTime = 3000,
height="500px",
width="800px",
dot_max_width="100px",
dot_min_width="50px",
}) => {
const time = ((switchingTime % 60000) / 1000).toFixed(0); // 将毫秒转换为秒
const [activeIndex, setActiveIndex] = useState(0); // 对应索引
/**
* 更新索引
* @param {newIndex} newIndex 更新索引
*/
const onUpdateIndex = (newIndex) => {
if (newIndex < 0) {
newIndex = React.Children.count(children) - 1;
} else if (newIndex >= React.Children.count(children)) {
newIndex = 0;
}
setActiveIndex(newIndex);
replayAnimations();
};
/**
* 重置动画
*/
const replayAnimations = () => {
document.getAnimations().forEach((anim) => {
anim.cancel();
anim.play();
});
};
/**
* 底部加载条点击事件
* @param {index} index 跳转索引
*/
const onClickCarouselIndex = (index) => {
onUpdateIndex(index);
replayAnimations();
};
useEffect(() => {
const interval = setInterval(() => {
onUpdateIndex(activeIndex + 1);
}, switchingTime);
return () => {
if (interval) {
clearInterval(interval);
}
};
});
return (
<div className={style.container} style={
{height:height,width:width}}>
<div
className={style.inner}
style={
{ transform: `translateX(-${activeIndex * 100}%)` }}>
{React.Children.map(children, (child) => {
return React.cloneElement(child);
})}
</div>
<div className={style.loading}>
{React.Children.map(children, (child, index) => {
return (
<div
className={style.indicator_outer}
onClick={() => onClickCarouselIndex(index)}
style={
{
width: index === activeIndex ? dot_max_width : dot_min_width,
}}
>
<div
className={style.indicator_inside}
style={
{
animationDuration: index === activeIndex ? `${time}s` : "0s",
backgroundColor: index === activeIndex ? "#FFFFFF" : null,
}}
/>
</div>
);
})}
</div>
</div>
);
};
export default Carousel;
use:
import React from 'react'
import Carousel, { CarouselItem, CarouselInfo } from "./carousel";
// 轮播图数据
const info = [
{
id: 1,
image: "https://img2.baidu.com/it/u=138111414,3014715895&fm=253&fmt=auto&app=138&f=JPEG?w=1000&h=300",
title:"岐王李茂贞",
content:"轮子哥测试文本内容轮子哥测试文本内容轮子哥测试文本内容"
},
{
id: 2,
image: "https://img1.baidu.com/it/u=652481803,1746310969&fm=253&fmt=auto&app=138&f=JPEG?w=1000&h=300",
title:"我们的船长可是要立志成为【海王】的男人",
content:"轮子哥测试文本内容轮子哥测试文本内容轮子哥测试文本内容"
},
{
id: 3,
image: "https://img0.baidu.com/it/u=84299672,3744735177&fm=253&fmt=auto&app=138&f=JPEG?w=1000&h=300",
title:"魁拔!魁拔!",
content:"轮子哥测试文本内容轮子哥测试文本内容轮子哥测试文本内容"
},
{
id: 4,
image: "https://img2.baidu.com/it/u=1544206047,4026750196&fm=253&fmt=auto&app=138&f=JPEG?w=1000&h=300",
title:"我白小纯,果真是天道之人",
content:"轮子哥测试文本内容轮子哥测试文本内容轮子哥测试文本内容"
},
];
export default function Test() {
return (
<Carousel switchingTime={2000} width="800px" height="450px">
{info?.map((item) => {
return (
<CarouselItem key={item.id}>
<CarouselInfo image={item.image} title={item.title} content={item.content}/>
</CarouselItem>
);
})}
</Carousel>
)
}
The above is written in h5
Slightly different from the above, the parameters need to be changed. Because the applet does not have a document, the animation of the specified node can only be canceled and re-executed by changing the animation style binding. If you have a better solution, please let me know
Use ts, and the writing method of applets can be transplanted:
Carousel.js:
import React, { useState, useEffect, ReactElement } from "react";
import style from "./Carousel.module.scss";
/**
* 轮子哥
*/
interface CarouselItem {
children: ReactElement;
styles?: object;
}
export const CarouselItem: any = React.memo((props: CarouselItem) => {
return (
<div
className={style.carousel_item}
style={
{ ...props.styles }}>
{props.children}
</div>
);
});
interface CarouselInfo {
image: string,
title?: string,
content?: string
}
export const CarouselInfo = React.memo((props: CarouselInfo) => {
const [isChange, setChange] = useState(false)
useEffect(() => {
const interval = setInterval(() => {
setChange(!isChange);
}, 5000);
return () => {
if (interval) {
clearInterval(interval);
}
};
});
return (
<div className={style.carousel_info}>
<img src={props.image} alt="Jay" className={style.carousel_info_image} />
<div className={isChange ? style.carousel_info_text : style.carousel_info_text_2}>
<div className={style.carousel_info_title}>{props.title}</div>
<div className={style.carousel_info_content}>{props.content}</div>
</div>
</div>
);
});
interface Carousel {
children :ReactElement,
switchingTime : number,
height ?: string,
width ?: string,
dot_max_width ?: string,
dot_min_width ?: string,
}
const Carousel:any =React.memo( (props:Carousel) => {
const time = (props.switchingTime % (1000 * 60)) / 1000; // 将毫秒转换为秒
const [activeIndex, setActiveIndex] = useState(0); // 对应索引
const [isNewPlay, setIsNewPlay] = useState(true); // 对应索引
const onUpdateIndex = (newIndex) => {
if (newIndex < 0) {
newIndex = React.Children.count(props.children) - 1;
} else if (newIndex >= React.Children.count(props.children)) {
newIndex = 0;
}
setIsNewPlay(!isNewPlay);
setActiveIndex(newIndex);
//replayAnimations();
};
// const replayAnimations = () => {
// document.getAnimations().forEach((anim) => {
// anim.cancel();
// anim.play();
// });
// };
const onClickCarouselIndex = (index) => {
onUpdateIndex(index);
setIsNewPlay(!isNewPlay)
//replayAnimations();
};
useEffect(() => {
const interval = setInterval(() => {
onUpdateIndex(activeIndex + 1);
}, props.switchingTime);
return () => {
if (interval) {
clearInterval(interval);
}
};
});
return (
<div className={style.container} style={
{ height: props.height, width: props.width }}>
<div
className={style.inner}
style={
{ transform: `translateX(-${activeIndex * 100}%)` }}>
{React.Children.map(props.children, (child) => {
return React.cloneElement(child);
})}
</div>
<div className={style.loading}>
{React.Children.map(props.children, (index: any, child) => {
return (
<div
className={style.indicator_outer}
style={
{
width: child === activeIndex ? props.dot_max_width : props.dot_min_width,
}}>
<div
className={isNewPlay ? style.indicator_inside : style.indicator_inside_2}
onClick={() => onClickCarouselIndex(child)}
style={
{
animationDuration: child === activeIndex ? `${time}s` : "0s",
backgroundColor: child === activeIndex ? "#FFFFFF" : "",
}}
/>
</div>
);
})}
</div>
</div>
);
});
Carousel.defaultProps ={
switchingTime : 3000,
height : "500px",
width : "800px",
dot_max_width : "40px",
dot_min_width : "20px",
}
export default Carousel;
Carousel.module.less:
.container {
overflow: hidden;
position: relative;
}
.inner {
white-space: nowrap;
transition: transform 0.3s;
width: 100%;
height: 100%;
}
.carousel_item {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.carousel_info{
width: 100%;
height: 100%;
}
.carousel_info_image {
width: 100%;
height: 100%;
border-radius: 20px
}
.carousel_info_text {
animation: info_text 2s ease;
animation-fill-mode: forwards;
color: white;
position: absolute;
text-align: center;
width: 250px;
top: 100px;
}
.carousel_info_title {
font-size: 18px;
width: 100%;
}
.carousel_info_content {
font-size: 15px;
width: 100%;
white-space:normal;
word-break:break-all;
line-height: 40px;
}
.loading {
position: absolute;
bottom: 50px;
display: flex;
align-items: center;
margin-bottom: 10px;
width: 100%;
left: 0;
flex-direction: row;
}
.indicator_outer {
width: 90px;
height: 7px;
background-color: #d4d4d4;
margin-left: 20px;
border-radius: 5px;
}
.indicator_inside {
height: 100%;
border-radius: 5px;
animation-fill-mode: forwards;
animation-name: progressBar;
animation-iteration-count: infinite;
}
.indicator_inside_2 {
height: 100%;
border-radius: 5px;
animation-fill-mode: forwards;
animation-name: progressBar_2;
animation-iteration-count: infinite;
}
@keyframes info_text {
0% {
transform: translateX(0px);
-webkit-transform: translateX(0px);
}
100% {
transform: translateX(10%);
-webkit-transform: translateX(10%);
}
}
@keyframes progressBar {
0% {
width: 0%;
}
100% {
width: 100%;
}
}
@keyframes progressBar_2 {
0% {
width: 0%;
}
100% {
width: 100%;
}
}
use:
// import { useState } from 'react'
import style from '../style/Carousel.module.less'
// import { Swiper, SwiperItem } from '@tarojs/components'
// import images from '../resources'
import Carousels, { CarouselItem, CarouselInfo } from "./Carousel";
// 轮播图数据
const info = [
{
id: 1,
image: "https://img2.baidu.com/it/u=138111414,3014715895&fm=253&fmt=auto&app=138&f=JPEG?w=1000&h=300",
title:"岐王李茂贞",
content:"轮子哥测试文本内容轮子哥测试文本内容轮子哥测试文本内容"
},
{
id: 2,
image: "https://img1.baidu.com/it/u=652481803,1746310969&fm=253&fmt=auto&app=138&f=JPEG?w=1000&h=300",
title:"我们的船长可是要立志成为【海王】的男人",
content:"轮子哥测试文本内容轮子哥测试文本内容轮子哥测试文本内容"
},
{
id: 3,
image: "https://img0.baidu.com/it/u=84299672,3744735177&fm=253&fmt=auto&app=138&f=JPEG?w=1000&h=300",
title:"魁拔!魁拔!",
content:"轮子哥测试文本内容轮子哥测试文本内容轮子哥测试文本内容"
},
{
id: 4,
image: "https://img2.baidu.com/it/u=1544206047,4026750196&fm=253&fmt=auto&app=138&f=JPEG?w=1000&h=300",
title:"我白小纯,果真是天道之人",
content:"轮子哥测试文本内容轮子哥测试文本内容轮子哥测试文本内容"
},
];
export default function Carousel() {
return (
<div className={style.Carousel}>
<Carousels switchingTime={5000} width="100%" height="7rem">
{info?.map((item) => {
return (
<CarouselItem key={item.id}>
<CarouselInfo image={item.image} title={item.title} content={item.content} />
</CarouselItem>
);
})}
</Carousels>
</div>
)
}