开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天,点击查看活动详情
在这个组件中,我们将会使用 React 作为框架,Antd 组件库辅助设计,Swiper 库作为主要的功能实现。
我们可以先来看一下效果
图文
视频
评论
选择状态
页面结构
Nav
这个比较简单,就不详细说了,可以使用 dom 编写,也可以使用 img。
Swiper
导入
"swiper": "8.4.2",笔者实验,在这个版本中 swiper/css 导入会报错
// Import Swiper React components
import { Swiper, SwiperSlide } from 'swiper/react';
// Import Swiper styles
import 'swiper/css';
export default () => {
return (
<Swiper
spaceBetween={50}
slidesPerView={3}
onSlideChange={() => console.log('slide change')}
onSwiper={(swiper) => console.log(swiper)}
>
<SwiperSlide>Slide 1</SwiperSlide>
<SwiperSlide>Slide 2</SwiperSlide>
<SwiperSlide>Slide 3</SwiperSlide>
<SwiperSlide>Slide 4</SwiperSlide>
...
</Swiper>
);
};
视频
这里图文和视频可以通过 type 进行判断
{data.includes(3) && (
<>
<div
onClick={() => onSwitchPlayVideo(`video${data.id}`)}
>
<video
id={`video${data.id}`}
style={{
width: '100vw',
height: `calc(100vh - 80px)`
}}
src={data.attachmentVos[0].resourceUrl}
controls={false}
loop={true}
/>
</div>
{/* 暂停按钮 */}
{!videoList[`video${data.id}`]?.isPlay && (
<div
onClick={() => onSwitchPlayVideo(`video${data.id}`)}
>
<img src={PlayIcon} alt="" />
</div>
)}
</>
)}
图文
这里我们需要用到 Pagination,Autoplay 两个 Swiper 组件
import React, { useState } from 'react'
import { Pagination, Autoplay } from 'swiper'
import { Swiper, SwiperSlide } from 'swiper/react'
import './index.less'
const ImageTextComp = ({ images }) => {
return (
<Swiper
modules={[Pagination, Autoplay]}
centeredSlides
pagination={{ clickable: true }}
autoplay={true}
loop={isLoop}
>
{images?.map((img, index) => {
return (
<SwiperSlide key={index}>
<img width="100%" src={img.resourceUrl} alt="" />
</SwiperSlide>
)
})}
</Swiper>
)
}
这里的话,比较麻烦的点是 swiper pagination 的编写
.swiper-pagination 使用 flex 布局
.swiper-pagination {
width: 100%;
padding: 0 7px 0px 12px;
display: flex;
align-items: center;
position: absolute;
bottom: 11px;
z-index: 99;
box-sizing: border-box;
}
.swiper-pagination-bullet 单个,采用 flex 弹性适应
.swiper-pagination-bullet {
display: block;
flex: 1 1 auto;
height: 3px;
background: rgba(220, 220, 220, 0.4);
position: relative;
margin-right: 2px;
border-radius: 2px;
}
组件过渡的动画,通过改变 width
来实现。因此需要在原先的 dom
上面再次覆盖一层,通过 伪元素来实现。
最开始设置为 0
&::after {
content: '';
position: absolute;
width: 0%;
left: 0;
top: 0;
height: 3px;
}
当 active
的时候,使用 animation
来实现从 0 到 100% 的逐渐显示效果。
&.swiper-pagination-bullet-active {
&::after {
background-color: #fff;
animation-name: widthChange;
animation-duration: 2s;
animation-fill-mode: forwards;
border-radius: 2px;
}
}
滑动播放逻辑
拿到数据的最开始,通过 videoid 绑定当前 video 的播放状态
useEffect(() => {
let arr = originData
if (arr?.length) {
let _videoList = {}
arr?.forEach((v, i) => {
if (v?.simulationDisplayFormCodes?.includes(3)) {
_videoList[`video${v.id}`] = {
isPlay: false
}
}
})
setVideoList(_videoList)
}
setDataList(arr)
}, [originData])
触发 swiper 上的 onSlideChange 事件。
获取当前的 index,在 dataList 中找到当前的 video 的 id,使用 id 在 dom 中找到 video 标签,调用 video 的 play 或者 pause 方法
if (dataList[swiper.realIndex].type.includes(3)) {
let currentId = `video${dataList[swiper.realIndex].id}`
let _videoList = JSON.parse(JSON.stringify(videoList))
Object.keys(_videoList).forEach((videoId) => {
_videoList[videoId].isPlay = currentId === videoId
if (_videoList[videoId].isPlay) {
// @ts-ignore
document.getElementById(videoId)?.play()
} else {
// @ts-ignore
document.getElementById(videoId)?.pause()
}
})
setVideoList(_videoList)
}
向上滑动,判断当前为 video 调用播放,继续向上滑动,此时视频应该是处于暂停状态的才对。
判断当前如果是图文组件或者前一个是视频组件,那么就应该暂停播放。
if (
dataList[swiper.realIndex].type.includes(2) &&
dataList[prevIndex].type.includes(3)
) {
let currentId: string = `video${dataList[prevIndex].id}`
let _videoList = JSON.parse(JSON.stringify(videoList))
Object.keys(_videoList).forEach((videoId) => {
_videoList[videoId].isPlay = currentId === videoId
if (_videoList[videoId].isPlay) {
// @ts-ignore
document.getElementById(videoId)?.pause()
}
})
setVideoList(_videoList)
}
点击切换播放
传入当前视频的 id
const onSwitchPlayVideo = (videoId: number) => {
let _videoList = JSON.parse(JSON.stringify(videoList))
_videoList[videoId].isPlay = !_videoList[videoId].isPlay
if (_videoList[videoId].isPlay) {
// @ts-ignore
document.getElementById(videoId)?.play()
} else {
// @ts-ignore
document.getElementById(videoId)?.pause()
}
setVideoList(_videoList)
}
到此为止,主逻辑基本上编写完毕
剩下的,基本上是一些比较简单的逻辑。
完整代码
完整代码比较多,所以这里笔者这里就不一一粘贴了,感兴趣的各位可以访问