功能实现trick1
开发中的功能实现trick ·
滚动吸顶的实现·
1.1 功能及分析:·
滚动过程中,滚至对应区域,对应nav高亮显示;
所以,要记录页面滚动的高度,这样切换到对应高度区间时,通知该区间对应的子项,激活显示,so,这里还涉及消息传递;
点击nav中的子项,跳转至该区域
相反的,知道了子项,可以得到对应高度区间,再scroll到该区间的左值即可
1.2 自定义Hook:useWindowScroll·
- 拿到每一帧中滚动的位置;
import { isClient } from '../utils/common'; |
1.3 Context:·
- 功能:?
- 为啥要这样做?
// 这个文件layout.js中导出了两个函数:LayoutStateProvider、useLayout |
1.4 Nav导航组件:·
import { useLayout } from '../../context/layout'; |
1.5 使用导航组件的页面:·
import { Events, scrollSpy } from 'react-scroll'; |
伸缩折叠实现·
import { Element } from 'react-scroll'; |
切换效果实现总结·
预期效果:切换按钮,展示对应的内容。记录开发过程中:
- 遇见的不同同学写的几种实现方式;
- 自己写的方式,优化问题;
- 在此过程中,学习到的新的React概念,比如组件复用问题等;

待展示的数据设计:
export const DEMO_LIST = [{ |
1、方式1:通过antd库·
1.1 封装思路·
-
思路:tab切换通过antd的Tab组件实现,内容展示再单独封装成组件:https://ant.design/components/tabs-cn/
-
通过状态控制:同步两个组件的信息
const [previous, setPrevious] = useState(0); |
1.1 左侧手机展示:·
-
专门封装了组件:
- 实现功能:拿到想要播放的视频的索引,播放它就ok
- 不仅需要知道当前切换的索引是什么,还需要知道切换前的索引,这样才能拿到视频暂停上一个视频的播放
<PhonePlayer
background={'/img/play/cat/cat-background.png'}
demoList={demoList}
current={current}
previous={previous}
size="medium"
/> -
具体实现:
细节问题:切换时,初始时其实视频标签都渲染了,但opacity控制它只显示一个:而且通过定位,他们都在同一个位置
.video-phone-case-inner {
position: relative;
.phone-video {
position: absolute;
top: 10px;
left: 10px;
width: calc(100% - 20px);
height: calc(100% - 25px);
}export const PhonePlayer = ({
background,
demoList,
current,
previous,
size = 'small',
}) => {
const mode = {
small: { width: 250, height: 520, case: '/img/phone1.png' },
medium: { width: 450, height: 540, case: '/img/phone2.png' },
};
const videoRefs = useRef([]);
useEffect(() => {
videoRefs.current = videoRefs.current.slice(0, demoList.length);
}, [demoList]);
useEffect(() => {
videoRefs.current[0] && videoRefs.current[0].play();
}, []);
useEffect(() => {
if (previous === current) return;
videoRefs.current[previous].pause(); //暂停上一个播放的视频 并把时间调回起始点
videoRefs.current[previous].currentTime = 0;
videoRefs.current[current].play(); // 播放当前video
}, [previous, current]);
return (
<section>
<div><img ....放背景图 /></div>
<div >
{demoList.map((item, index) => {
return (
<video
// ...只显示部分属性
// 控制元素显隐:如果这里用display性能不好 opacity会有丝滑效果
style={{opacity: index === current ? 1 : 0,}}
ref={(el) => (videoRefs.current[index] = el)} // 控制视频播放过程
preload="auto" // 预加载 提前加载,避免播放等待
/>
);
})}
<Image
src={mode[size].case}
height={mode[size].height} width={mode[size].width}
/>
</div>
</section>
);
};
1.2 右侧切换展示:·
-
引入antd中的tab,需要注意文档中说明:activeKey是string,注意转number
<Tabs
defaultActiveKey="0"
onChange={(activeKey) => {
setPrevious(current);
setCurrent(Number(activeKey)); }}
>
{demoList.map((item, index) => (
<TabPane tab={item.title} key={index}> //是string
<div>
<div>{item.subTitle}</div>
<div>
{item.desc.map((text, i) => (
<div key={i}>
....icon显示
....文字显示
</div>
))}
</div>
</div>
</TabPane>
))}
</Tabs>
2、方式2:通过display·
这是我原本写的方式,但并不推荐:
- 因为会带来dom的重建与销毁,不利于性能;
- 此外,对于视频类的,会感觉播放卡顿
2.1 封装的实现思路:·
- 父组件包括切换组件、显示组件,并有curplay状态控制,将状态+改变状态的方法传递给子组件,
- 子:切换组件,通过匹配该信息,(1)显示切换的激活样式;(2)用户切换时,更新状态curplay
- 子:显示组件,通过匹配curplay,显示对应的展示内容,不匹配的隐藏;
2.2 切换组件·
-
注意,这个key那边接受不到,所以想传索引,用单独的props
-
然后CurShowTab中,激活样式下划线是伪元素实现
{Data.map((item, index) => (
<CurShowTab
key={index}
item={item}
activeIndex={curPlay}
itemIndex={index}
onTabChange={() => {setCurPlay(index);}}
/>
))} -
伪元素:
&::after {
content: '';
display: block;
width: 100%;
height: 2px;
background: pink;
}
2.3 显示组件·
-
只是展示对应的内容
{Data.map((item, index) => {
const isCurPlay = index === curPlay;
return (
<CurShowCard
item={item}
isCurPlay={isCurPlay}
itemIndex={index}
key={index}
/>
);
})}
3、方式3:通过props.children·
- 实现目的:将显示部分的内容以插槽的形式传入组件;
- 子组件通过props.children拿到传入的组件,渲染出来;
4、方式4:高阶组件的方式·
评论