Notice.tsx文件:
import { BellOutlined, NotificationOutlined } from '@ant-design/icons';
import React, { useEffect, useState } from 'react'
import styles from './xxx.module.css';// css文件自行命名
interface IProps {
data: IObj;
onClickNoticeDetail?:(item, index) => any;
}
interface IObj {
noticeList: INoticeList[];
animate: boolean;
}
interface INoticeList {
key: number|string;
text: string;
detailText?: string|null;
}
export const Notice = (props: IProps) => {
const {onClickNoticeDetail, data} = props;
let [obj, setObj] = useState<any>(data);
//页面加载的时候,设置一个永恒的定时器,1.5s后就会执行定时器中的逻辑
useEffect(()=>{
setTimeout(() => {
obj['animate'] = true
changeAnim()
}, 2000);
}, [obj])
//在setInterval执行中,会调用该函数,在内部会设置一个一次性的定时器,每次都会将数组的第一个元素添加到数组的最后,并且将数组的第一个元素删除,
const changeAnim = () => {
const { noticeList } = obj
setTimeout(() => {
const noticeList1 = JSON.parse(JSON.stringify(noticeList))
noticeList1.push(noticeList1[0]);
noticeList1.shift();
setObj({
...obj,
noticeList: noticeList1,
animate: false
})
}, 1500)
}
const renderContent = () => {
const { noticeList, animate } = obj;
return (
<div className={styles["scrollPage"]}>
<div className={styles["scrollWrapper"]}>
<ul className={animate ? styles['anim'] : ''}>
{
noticeList.map((item, index) => {
return <li key={index}>
<span onClick={()=>{
onClickNoticeDetail&&onClickNoticeDetail(item, index)
}}><NotificationOutlined translate={undefined}/></span>
<span className="text">{item.text}</span>
{/* <span><BellOutlined translate={undefined} /><i>222222</i></span> */}
</li>
})
}
</ul>
</div>
</div>
)
}
return renderContent()
}
css文件:
.scrollPage {
margin: 0px;
z-index: 999999999999999999999;
height: 100px;
position: fixed;
top: 0;
.scrollWrapper {
position: relative;
height: 24px;
line-height: 24px;
overflow: hidden;
}
& ul {
position: absolute;
top: 20px;
left: 0;
height: 24px;
overflow: hidden;
left: 400px;
box-sizing: border-box;
}
& li {
list-style: none;
height: 24px;
line-height: 24px;
display: flex;
width: 800px;
}
& li>span:first-child,
& li>span:nth-child(3) {
display: inline-block;
font-size: 24px;
}
& li>span:nth-child(3) {
position: relative;
flex: 0 0 100px;
}
& li>span:nth-child(3)>i {
display: inline-block;
min-width: 20px;
height: 14px;
line-height: 12px;
border-radius: 5px;
position: absolute;
left: 10px;
font-size: 12px;
color: #fff;
}
& li>span:nth-child(2) {
display: inline-block;
flex:1;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}
}
.anim {
transition: all .5s;
margin-top: 24px;
}
模拟数据及引用:
let obj1 = {
noticeList: [
{
key: 1,
text: '11iPhone11挥泪降价1600元 iPhone12出道即巅峰?5G手机11',
detailText: '11'
},
{
key: 2,
text: '22R式体验奔驰博物馆重新开张 广东最惨的"88888"车牌VR式体验奔驰博物馆重新开张 VR式体验奔驰博物馆重新开张 广东最惨的"88888"车牌VR式体验奔驰博物馆重新开张 广东最惨的"88888"车牌22222222222222222222222222222222222222244444444444444444',
detailText: '22'
},
{
key: 3,
text: '334年5队的落选秀太香了 巅峰2.6帽!力压魔兽夺最佳新秀33',
detailText: '33'
},
{
key: 4,
text: '44你好世界:寻找心中的风景 [征集]寻找中式风景禅意美44',
detailText: '44'
},
],
animate: false,
}
<Notice data={obj1} onClickNoticeDetail={(item,index)=>{
showMsgInfo(JSON.stringify(item));
}}/>
如果需要点击通知图标时停止定时器:
引入文件--略
interface IProps {
data: IObj;
onClickNoticeDetail?: (item, index, fn) => any;
}
interface IObj {
noticeList: INoticeList[];
animate: boolean;
}
interface INoticeList {
key: number | string;
text: string;
detailText?: string | null;
}
export const Notice = (props: IProps) => {
const { onClickNoticeDetail, data } = props;
let [obj, setObj] = useState<any>(data);
let [timerList, setTimerList] = useState<any>([]);
let [timer] = useState<any>(null);
let [noticeFlagStorage] = useState<any>({
cancelToken2: 'Y'
});
//页面加载的时候,设置一个永恒的定时器
useEffect(() => {
intervalStart();
}, [obj])
const intervalStart = () => {
if (timer) {
clearTimeout(timer);
timer = null;
}
noticeFlagStorage.cancelToken2 = 'Y';
timer = int1();
timerList.push(timer);
}
const intervalEnd = () => {
if (timer || timerList.length > 0) {
console.log('=============结束了222222222==========');
each(timerList, (item, index) => {
clearTimeout(item);
});
setTimerList([]);
clearTimeout(timer);
timer = null;
}
}
const int1 = () => {
if ('cancelToken2' in noticeFlagStorage) {
return setTimeout(() => {
console.log('开始了2222-----', timerList.length)
obj['animate'] = true;
if (noticeFlagStorage.cancelToken2 === 'N') {
intervalEnd();
}
else {
changeAnim()
}
}, 2000);
}
else {
intervalEnd();
return null;
}
}
//在定时器执行中,会调用该函数,在内部会设置一个一次性的定时器,每次都会将数组的第一个元素添加到数组的最后,并且将数组的第一个元素删除,
const changeAnim = () => {
setTimeout(() => {
const { noticeList } = obj;
noticeList.push(noticeList[0]);
noticeList.shift();
setObj({
...obj,
noticeList,
animate: false
})
}, 0);
}
const renderContent = () => {
const { noticeList, animate } = obj;
return (
<div className={styles["scrollPage"]}>
<div className={styles["scrollWrapper"]}>
<ul className={animate ? styles['anim'] : ''}>
{
noticeList.map((item, index) => {
return <li key={index}>
<span onClick={()=>{
noticeFlagStorage.cancelToken2 = 'N'
onClickNoticeDetail&&onClickNoticeDetail(item, index, intervalStart)
}}><NotificationOutlined translate={undefined}/></span>
<span className="text">{item.text}</span>
</li>
})
}
</ul>
</div>
</div>
)
}
return renderContent()
}
使用: 点击弹窗确认按钮继续定时器
<Notice data={obj1} onClickNoticeDetail={(item,index, intervalStart)=>{
Modal.success({
mask: false,
okText: '确认',
content: '公告内容',
centered: true,
onOk: () => {
showMsgInfo(JSON.stringify(item));
intervalStart()
},
});
}}/>
但有的公告栏是只展示第一个, 循环跑马灯式播放:
css:
@keyframes wordsloop {
0% {
transform: translateX(0px);
}
100% {
transform: translateX(-100%);
}
}
& li>p>span {
height: 24px;
line-height: 24px;
background: rgba(0,0,0,0);
border: 0;
display: inline-block;
flex:1;
white-space: nowrap;
width: auto;
margin-left: 10px;
font-size: 14px;
color: #333;
}
tsx文件:
// 根据公告内容的所在的标签的宽度与其父标签比较, 设置宽度; 传入动画时间比写在css中可控
useEffect(() => {
if (get(ref1, 'children[0].scrollWidth') + 10 < ref1.clientWidth/2) {
setStyle1({
width: `${get(ref1, 'clientWidth')}px`,
minWidth: `${get(ref1, 'clientWidth')}px`,
animation: `${styles['wordsloop']} ${animationDuration}s linear infinite normal`,
})
}
else {
setStyle1({
width: 'auto',
minWidth: 'auto',
animation: `${styles['wordsloop']} ${animationDuration}s linear infinite normal`,
})
}
}, [ref1, animationDuration])
// 公告栏标签部分代码,其他略...
<div className={styles["scrollPage"]}>
<div className={classNames(styles["scrollWrapper"], appear === 'solidBlue' ? styles.scrollWrapperBlue : '')}>
<ul>
{
noticeList.map((item, index) => {
return <li key={index} onClick={() => {
clickNotice(item, index, undefined);
}}>
<span>
<i style={appear === 'solidBlue' ? {
filter: 'drop-shadow(40px 0 0 #007aff)',
transform: 'translateX(-40px)'
} : {}}></i>
</span>
<p ref={ref => setRef1(ref)}>
<span style={style1}>{item.CONTENT}</span>
<span style={style1}>{item.CONTENT}</span>
</p >
</li>
})
}
</ul>
</div>
</div>
还有一个需要优化的地方: 当页面纵向滚动时因为position是fixed的缘故,一直停留在固定位置, 如果需要随着滚动平移到正确位置, 可以监听滚动事件解决:
const handleScroll = (event: any = {}) => {
// 滚动的高度
const scrollTop: number =
(event?.srcElement ? event?.srcElement?.scrollTop : 0) ||
window.pageYOffset ||
(event?.srcElement ? event?.srcElement?.body?.scrollTop : 0) ||
0
if (scrollTop >= 0) {
setStyle0({
transform: `translateY(-${scrollTop}px)`
})
}
}
useEffect(() => {
window.addEventListener('scroll', handleScroll, true); // 滚动事件
}, [window]);
// 公告栏标签
<div className={classNames(styles["scrollPage"], appear === 'solidRed' ? styles['scrollPageRed'] : '')} style={style0}>
<div className={classNames(styles["scrollWrapper"], appear === 'solidBlue' ? styles.scrollWrapperBlue : '')}>
<ul>
{
noticeList.map((item, index) => {
return <li key={index} onClick={() => {
clickNotice(item, index, undefined);
}}>
<span>
<i style={appear === 'solidBlue' ? {
filter: 'drop-shadow(40px 0 0 #007aff)',
transform: 'translateX(-40px)'
} : {}}></i>
</span>
<p ref={ref => setRef1(ref)}>
<span style={style1}>{item.CONTENT}</span>
<span style={style1}>{item.CONTENT}</span>
</p >
</li>
})
}
</ul>
</div>
</div>