未验证 提交 2cfe034b 编写于 作者: V vickyYe 提交者: GitHub

fix: noticebar组件修复垂直滚动自定义内容点击问题,增加onClickItem事件 (#596)

上级 69cac5bf
......@@ -13,11 +13,11 @@ const NoticeBarDemo = () => {
mode: '通告栏--关闭模式',
multiline: '多行展示',
vertical: '垂直滚动',
complexAm: '纵向--复杂滚动动画',
complexAm: '纵向--自定义左侧图标',
customAm: '纵向--自定义滚动内容',
customRightIcon: '纵向--自定义右侧图标',
text: 'NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验',
textShort: 'NutUI 是移动端组件库',
text: 'NutUI-React 是京东风格的 React 移动端组件库,开发和服务于移动 Web 界面的企业级产品',
textShort: 'NutUI-React 是移动端组件库',
horseLamp: [
'NoticeBar 公告栏',
'Cascader 级联选择',
......@@ -72,7 +72,7 @@ const NoticeBarDemo = () => {
{translated.text}
</NoticeBar>
<br />
<NoticeBar closeMode rightIcon="circle-close" click={hello}>
<NoticeBar closeMode rightIcon="circle-close" onClick={hello}>
{translated.text}
</NoticeBar>
<br />
......@@ -106,7 +106,14 @@ const NoticeBarDemo = () => {
list={horseLamp2}
speed={10}
standTime={2000}
complexAm
leftIcon="https://img13.360buyimg.com/imagetools/jfs/t1/72082/2/3006/1197/5d130c8dE1c71bcd6/e48a3b60804c9775.png"
onClick={(e) => {
console.log('listClick', e.target)
}}
onClickItem={(e, val) => {
console.log('dom', e.target)
console.log('value', val)
}}
/>
</div>
......@@ -121,9 +128,6 @@ const NoticeBarDemo = () => {
onClose={() => {
console.log('close')
}}
onClick={(e) => {
console.log(e)
}}
>
{horseLamp3.map((item, index) => {
return (
......@@ -131,6 +135,9 @@ const NoticeBarDemo = () => {
className="custom-item"
style={{ height: '50px', lineHeight: '50px' }}
key={index}
onClick={() => {
console.log('inner', item)
}}
>
{item}
</div>
......@@ -147,6 +154,9 @@ const NoticeBarDemo = () => {
list={horseLamp1}
speed={10}
standTime={1000}
onClickItem={(e, v) => {
console.log('onclick-custom', v)
}}
rightIcon={<Icon name="fabulous" size="16" color="#f0250f" />}
/>
</div>
......
import React from 'react'
import { NoticeBar } from './noticebar'
import { useTranslate } from '../../sites/assets/locale'
import Icon from '../icon'
import './demo.scss'
import Icon from '@/packages/icon'
const NoticeBarDemo = () => {
const [translated] = useTranslate({
......@@ -12,11 +12,11 @@ const NoticeBarDemo = () => {
mode: '通告栏--关闭模式',
multiline: '多行展示',
vertical: '垂直滚动',
complexAm: '纵向--复杂滚动动画',
complexAm: '纵向--自定义左侧图标',
customAm: '纵向--自定义滚动内容',
customRightIcon: '纵向--自定义右侧图标',
text: 'NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验',
textShort: 'NutUI 是移动端组件库',
text: 'NutUI-React 是京东风格的 React 移动端组件库,开发和服务于移动 Web 界面的企业级产品',
textShort: 'NutUI-React 是移动端组件库',
horseLamp: [
'NoticeBar 公告栏',
'Cascader 级联选择',
......@@ -102,7 +102,14 @@ const NoticeBarDemo = () => {
list={horseLamp2}
speed={10}
standTime={2000}
complexAm
leftIcon="https://img13.360buyimg.com/imagetools/jfs/t1/72082/2/3006/1197/5d130c8dE1c71bcd6/e48a3b60804c9775.png"
onClick={(e) => {
console.log('listClick', e.target)
}}
onClickItem={(e, val) => {
console.log('dom', e.target)
console.log('value', val)
}}
/>
</div>
......@@ -117,9 +124,6 @@ const NoticeBarDemo = () => {
onClose={() => {
console.log('close')
}}
onClick={(e) => {
console.log(e)
}}
>
{horseLamp3.map((item, index) => {
return (
......@@ -127,6 +131,9 @@ const NoticeBarDemo = () => {
className="custom-item"
style={{ height: '50px', lineHeight: '50px' }}
key={index}
onClick={() => {
console.log('custom-inner', item)
}}
>
{item}
</div>
......@@ -143,6 +150,9 @@ const NoticeBarDemo = () => {
list={horseLamp1}
speed={10}
standTime={1000}
onClickItem={(e, v) => {
console.log('onclick-custom', v)
}}
rightIcon={<Icon name="fabulous" size="16" color="#f0250f" />}
/>
</div>
......
......@@ -150,7 +150,7 @@ export default App
### Vertical Scroll Complex Animation
### Vertical Scroll Custom Left Icon
:::demo
......@@ -167,7 +167,14 @@ const App = () => {
list={horseLamp2}
speed={10}
standTime={2000}
complexAm
leftIcon="https://img13.360buyimg.com/imagetools/jfs/t1/72082/2/3006/1197/5d130c8dE1c71bcd6/e48a3b60804c9775.png"
onClick={(e) => {
console.log('listClick', e.target)
}}
onClickItem={(e, val) => {
console.log('dom', e.target)
console.log('value', val)
}}
/>
</>
)
......@@ -192,14 +199,16 @@ const App = () => {
<>
<NoticeBar direction="vertical" height={50} speed={10} standTime={1000}
closeMode
onClose={() => {console.log('close')}}
onClick={(e) => {console.log(e)}}>
onClose={() => {console.log('close')}}>
{horseLamp3.map((item, index) => {
return (
<div
className="custom-item"
style={{ height: '50px', lineHeight: '50px' }}
key={index}
onClick={() => {
console.log('custom-inner', item)
}}
>
{item}
</div>
......@@ -233,6 +242,9 @@ const App = () => {
list={horseLamp1}
speed={10}
standTime={1000}
onClickItem={(e, v) => {
console.log('onclick-custom', v)
}}
rightIcon={<Icon name="fabulous" size="16" color="#f0250f" />}
/>
</>
......@@ -285,4 +297,5 @@ export default App
| ---------- | --------------------------------------- | ------------ |
| onClick `v1.3.8` | Emitted when NoticeBar is clicked | event: Event |
| onClose `v1.3.8` | Emitted when NoticeBar is closed | event: Event |
| onClickItm `v1.4.5` | Emitted when the currently displayed information is clicked when scrolling multiple pieces of data vertically | (event: Event,listItem) |
......@@ -23,7 +23,7 @@ import React, {useState} from "react";
import { NoticeBar } from '@nutui/nutui-react';
const App = () => {
const text = 'NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验'
const text = 'NutUI-React 是京东风格的 React 移动端组件库,开发和服务于移动 Web 界面的企业级产品'
return (
<>
<NoticeBar text={text} />
......@@ -52,7 +52,7 @@ const App = () => {
/>
<NoticeBar
text="NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验。" scrollable={false}
text="NutUI-React 是京东风格的 React 移动端组件库,开发和服务于移动 Web 界面的企业级产品。" scrollable={false}
/>
</>
)
......@@ -77,11 +77,11 @@ const App = () => {
return (
<>
<NoticeBar closeMode onClick={hello}>
NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验
NutUI-React 是京东风格的 React 移动端组件库,开发和服务于移动 Web 界面的企业级产品
</NoticeBar>
<br />
<NoticeBar closeMode rightIcon="circle-close" onClick={hello}>
NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验
NutUI-React 是京东风格的 React 移动端组件库,开发和服务于移动 Web 界面的企业级产品
</NoticeBar>
<br />
<NoticeBar leftIcon="https://img13.360buyimg.com/imagetools/jfs/t1/72082/2/3006/1197/5d130c8dE1c71bcd6/e48a3b60804c9775.png">
......@@ -107,7 +107,7 @@ import React, {useState} from "react";
import { NoticeBar } from '@nutui/nutui-react';
const App = () => {
const text = 'NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验'
const text = 'NutUI-React 是京东风格的 React 移动端组件库,开发和服务于移动 Web 界面的企业级产品'
return (
<NoticeBar text={text} wrapable />
......@@ -156,7 +156,7 @@ export default App
### 复杂滚动动画
### 自定义左侧图标
:::demo
......@@ -173,7 +173,14 @@ const App = () => {
list={horseLamp2}
speed={10}
standTime={2000}
complexAm
leftIcon="https://img13.360buyimg.com/imagetools/jfs/t1/72082/2/3006/1197/5d130c8dE1c71bcd6/e48a3b60804c9775.png"
onClick={(e) => {
console.log('listClick', e.target)
}}
onClickItem={(e, val) => {
console.log('dom', e.target)
console.log('value', val)
}}
/>
</>
)
......@@ -198,14 +205,16 @@ const App = () => {
<>
<NoticeBar direction="vertical" height={50} speed={10} standTime={1000}
closeMode
onClose={() => {console.log('close')}}
onClick={(e) => {console.log(e)}}>
onClose={() => {console.log('close')}}>
{horseLamp3.map((item, index) => {
return (
<div
className="custom-item"
style={{ height: '50px', lineHeight: '50px' }}
key={index}
onClick={() => {
console.log('custom-inner', item)
}}
>
{item}
</div>
......@@ -244,6 +253,9 @@ const App = () => {
list={horseLamp1}
speed={10}
standTime={1000}
onClickItem={(e, v) => {
console.log('onclick-custom', v)
}}
rightIcon={<Icon name="fabulous" size="16" color="#f0250f" />}
/>
</>
......@@ -279,7 +291,7 @@ export default App
| list | 纵向滚动数据列表 | Array | [] |
| speed | 滚动的速度 | Number | 50 |
| standTime | 停留时间(毫秒) | Number | 1000 |
| complexAm | 稍复杂的动画,耗能会高 | Boolean | false |
| complexAm `即将废弃`| 稍复杂的动画,耗能会高 | Boolean | false |
| height | 每一个滚动列的高度(px),注意:在使用 slot 插槽定义滚动单元时,按照实际高度修改此值 | Number | 40 |
| closeMode | 是否启用右侧关闭图标,可以通过slot[name=rightIcon]自定义图标 | Boolean | false |
......@@ -289,10 +301,11 @@ export default App
|--------------|----------------------------------|
| default | 通知文本的内容 |
| rightIcon | 自定义右侧图标 |
| leftIcon | 自定义左侧图标 |
| leftIcon | 自定义左侧图标,垂直滚动模式下默认无左侧图标,配置后展示,配置为"close" |
### Event
| 字段 | 说明 | 回调参数 |
| ----- | ---------------- | ------------ |
| onClick `v1.3.8` | 外层点击事件回调 | event: Event |
| onClose `v1.3.8` | 关闭通知栏时触发 | event: Event |
| onClickItm `v1.4.5` | 垂直滚动多条数据时,点击当前展示的信息时触发 | (event: Event,listItem) |
# NoticeBar 公告
# NoticeBar 公告
### 介
### 介
于循环播放展示一组消息通知。
於循環播放展示一組消息通知。
### 安
### 安
```javascript
// react
......@@ -12,7 +12,7 @@ import { NoticeBar } from '@nutui/nutui-react';
```
## 代演示
## 代演示
### 基本用法
......@@ -23,7 +23,7 @@ import React, {useState} from "react";
import { NoticeBar } from '@nutui/nutui-react';
const App = () => {
const text = 'NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验'
const text = 'NutUI-React 是京東風格的 React 移動端組件庫,開發和服務於移動 Web 界面的企業級產品'
return (
<>
<NoticeBar text={text} />
......@@ -34,8 +34,8 @@ export default App
```
:::
### 滚动播放
通知栏的内容长度溢出时会自动开启滚动播放,可通过 scrollable 属性可以控制该行为
### 滾動播放
通知欄的內容長度溢出時會自動開啟滾動播放,可通過 scrollable 屬性可以控制該行為
:::demo
......@@ -47,12 +47,12 @@ const App = () => {
return (
<>
<NoticeBar
text="NutUI 是京东风格的移动端组件库"
text="NutUI 是京東風格的移動端組件庫"
scrollable
/>
<NoticeBar
text="NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验。" scrollable={false}
text="NutUI-React 是京東風格的 React 移動端組件庫,開發和服務於移動 Web 界面的企業級產品。" scrollable={false}
/>
</>
)
......@@ -62,7 +62,7 @@ export default App
:::
### 通告栏模式--关闭模式
### 通告欄模式--關閉模式
:::demo
......@@ -77,11 +77,11 @@ const App = () => {
return (
<>
<NoticeBar closeMode onClick={hello}>
NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验
NutUI-React 是京東風格的 React 移動端組件庫,開發和服務於移動 Web 界面的企業級產品
</NoticeBar>
<br />
<NoticeBar closeMode rightIcon="circle-close" onClick={hello}>
NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验
NutUI-React 是京東風格的 React 移動端組件庫,開發和服務於移動 Web 界面的企業級產品
</NoticeBar>
<br />
<NoticeBar leftIcon="https://img13.360buyimg.com/imagetools/jfs/t1/72082/2/3006/1197/5d130c8dE1c71bcd6/e48a3b60804c9775.png">
......@@ -98,7 +98,7 @@ export default App
### 多行展示
文字较长时,可以通过设置 wrapable 属性来开启多行展示。默认为不滚动,可以通过设置 scrollable 控制为滚动
文字較長時,可以通過設置 wrapable 屬性來開啟多行展示。默認為不滾動,可以通過設置 scrollable 控制為滾動
:::demo
......@@ -107,7 +107,7 @@ import React, {useState} from "react";
import { NoticeBar } from '@nutui/nutui-react';
const App = () => {
const text = 'NutUI 是京东风格的移动端组件库,使用 Vue 语言来编写可以在 H5,小程序平台上的应用,帮助研发人员提升开发效率,改善开发体验'
const text = 'NutUI-React 是京東風格的 React 移動端組件庫,開發和服務於移動 Web 界面的企業級產品'
return (
<NoticeBar text={text} wrapable />
......@@ -117,7 +117,7 @@ export default App
```
:::
### 纵向滚动
### 縱向滾動
:::demo
......@@ -127,10 +127,10 @@ import { NoticeBar } from '@nutui/nutui-react';
const App = () => {
const horseLamp1 = [
'NoticeBar 公告',
'Cascader 级联选择',
'DatePicker 日期选择',
'CheckBox 复选按钮',
'NoticeBar 公告',
'Cascader 級聯選擇',
'DatePicker 日期選擇',
'CheckBox 複選按鈕',
]
const go = (item: any) => {
console.log(item)
......@@ -156,7 +156,7 @@ export default App
### 复杂滚动动画
### 自定義左側圖標
:::demo
......@@ -165,7 +165,7 @@ import React, {useState} from "react";
import { NoticeBar } from '@nutui/nutui-react';
const App = () => {
const horseLamp2 = ref(['NoticeBar 公告', 'Cascader 级联选择', 'DatePicker 日期选择器', 'CheckBox 复选按钮']);
const horseLamp2 = ref(['NoticeBar 公告', 'Cascader 級聯選擇', 'DatePicker 日期選擇器', 'CheckBox 複選按鈕']);
return (
<>
<NoticeBar
......@@ -173,7 +173,14 @@ const App = () => {
list={horseLamp2}
speed={10}
standTime={2000}
complexAm
leftIcon="https://img13.360buyimg.com/imagetools/jfs/t1/72082/2/3006/1197/5d130c8dE1c71bcd6/e48a3b60804c9775.png"
onClick={(e) => {
console.log('listClick', e.target)
}}
onClickItem={(e, val) => {
console.log('dom', e.target)
console.log('value', val)
}}
/>
</>
)
......@@ -183,7 +190,7 @@ export default App
:::
### 自定义滚动内
### 自定義滾動內
:::demo
......@@ -192,20 +199,22 @@ import React, {useState} from "react";
import { NoticeBar } from '@nutui/nutui-react';
const App = () => {
const horseLamp3 = ['NoticeBar 公告', 'Cascader 级联选择', 'DatePicker 日期选择器', 'CheckBox 复选按钮']
const horseLamp3 = ['NoticeBar 公告', 'Cascader 級聯選擇', 'DatePicker 日期選擇器', 'CheckBox 複選按鈕']
return (
<>
<NoticeBar direction="vertical" height={50} speed={10} standTime={1000}
closeMode
onClose={() => {console.log('close')}}
onClick={(e) => {console.log(e)}}>
onClose={() => {console.log('close')}}>
{horseLamp3.map((item, index) => {
return (
<div
className="custom-item"
style={{ height: '50px', lineHeight: '50px' }}
key={index}
onClick={() => {
console.log('custom-inner', item)
}}
>
{item}
</div>
......@@ -231,10 +240,10 @@ import { NoticeBar,Icon } from '@nutui/nutui-react';
const App = () => {
const horseLamp1 = [
'NoticeBar 公告',
'Cascader 级联选择',
'DatePicker 日期选择',
'CheckBox 复选按钮',
'NoticeBar 公告',
'Cascader 級聯選擇',
'DatePicker 日期選擇',
'CheckBox 複選按鈕',
]
return (
<>
......@@ -244,6 +253,9 @@ const App = () => {
list={horseLamp1}
speed={10}
standTime={1000}
onClickItem={(e, v) => {
console.log('onclick-custom', v)
}}
rightIcon={<Icon name="fabulous" size="16" color="#f0250f" />}
/>
</>
......@@ -279,7 +291,7 @@ export default App
| list | 纵向滚动数据列表 | Array | [] |
| speed | 滚动的速度 | Number | 50 |
| standTime | 停留时间(毫秒) | Number | 1000 |
| complexAm | 稍复杂的动画,耗能会高 | Boolean | false |
| complexAm `即将废弃`| 稍复杂的动画,耗能会高 | Boolean | false |
| height | 每一个滚动列的高度(px),注意:在使用 slot 插槽定义滚动单元时,按照实际高度修改此值 | Number | 40 |
| closeMode | 是否启用右侧关闭图标,可以通过slot[name=rightIcon]自定义图标 | Boolean | false |
......@@ -289,10 +301,11 @@ export default App
|--------------|----------------------------------|
| default | 通知文本的内容 |
| rightIcon | 自定义右侧图标 |
| leftIcon | 自定义左侧图标 |
| leftIcon | 自定义左侧图标,垂直滚动模式下默认无左侧图标,配置后展示,配置为"close" |
### Event
| 字段 | 说明 | 回调参数 |
| ----- | ---------------- | ------------ |
| onClick `v1.3.8` | 外层点击事件回调 | event: Event |
| onClose `v1.3.8` | 关闭通知栏时触发 | event: Event |
| onClickItm `v1.4.5` | 垂直滚动多条数据时,点击当前展示的信息时触发 | (event: Event,listItem) |
......@@ -117,6 +117,14 @@
padding: $noticebar-box-padding;
background: $noticebar-background;
color: $noticebar-color;
.left-icon {
height: $noticebar-left-icon-width;
min-width: $noticebar-left-icon-width;
margin: $noticebar-lefticon-margin;
background-size: 100% 100%;
display: flex;
align-self: center;
}
.horseLamp_list {
margin: 0;
padding: 0;
......@@ -126,8 +134,17 @@
display: flex;
align-items: center;
height: $noticebar-height;
width: 100%;
}
}
.nut-noticebar__inner {
display: flex;
height: 100%;
width: 100%;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
flex-direction: column;
}
.go {
align-self: center;
......
......@@ -5,6 +5,7 @@ import React, {
FunctionComponent,
MouseEvent,
CSSProperties,
useMemo,
} from 'react'
import classNames from 'classnames'
......@@ -33,10 +34,11 @@ export interface NoticeBarProps extends BasicComponent {
scrollable: boolean | null
speed: number
rightIcon?: HTMLElement | any
close?: (list?: any) => void
click?: (item?: any) => void
onClose?: (list?: any) => void
onClick?: (item?: any) => void
close?: (event: any) => void
click?: (event: any) => void
onClose?: (event: any) => void
onClick?: (event: any) => void
onClickItem?: (event: any, value: any) => void
}
const defaultProps = {
......@@ -84,6 +86,7 @@ export const NoticeBar: FunctionComponent<
click,
onClose,
onClick,
onClickItem,
iconClassPrefix,
iconFontClassName,
} = {
......@@ -105,19 +108,67 @@ export const NoticeBar: FunctionComponent<
const [timer, SetTimer] = useState(0)
const [isCanScroll, SetIsCanScroll] = useState<null | boolean>(null)
const isVertical = direction === 'vertical'
const [rect, setRect] = useState(null as any | null)
let active = 0
const [vLeftIcon, setLeftIcon] = useState('')
const [ready, setReady] = useState(false)
const container = useRef<any>(null)
const innerRef = useRef<any>(null)
const _swiper = useRef<any>({
moving: false,
autoplayTimer: null,
width: 0,
height: 0,
offset: 0,
size: 0,
})
const [childOffset, setChildOffset] = useState<any[]>([])
const [offset, setOffset] = useState(0)
const { childs, childCount } = useMemo(() => {
let childCount = 0
const childs: any = React.Children.map(children, (child) => {
if (!React.isValidElement(child)) return null
childCount++
return child
})
return {
childs,
childCount,
}
}, [children])
let trackSize = childCount * Number(height)
const minOffset = (() => {
if (rect) {
const base = isVertical ? rect.height : rect.width
return base - Number(height) * childCount
}
return 0
})()
useEffect(() => {
if (direction === 'vertical') {
if (isVertical) {
// 兼容老版本无左侧Icon问题
if (leftIcon !== '') {
setLeftIcon(leftIcon)
} else {
setLeftIcon('close')
}
if (children) {
const arr: string[] | any = []
React.Children.map(children, (child) => {
arr.push((child as any).props.children)
})
scrollList.current = [].concat(arr)
scrollList.current = [].concat(childs)
} else {
scrollList.current = [].concat(list)
// startRollEasy()
setTimeout(() => {
startRollEasy()
}, Number(standTime))
}
setTimeout(() => {
complexAm ? startRoll() : startRollEasy()
complexAm && startRoll()
}, Number(standTime))
} else {
initScrollWrap(text)
......@@ -130,18 +181,6 @@ export const NoticeBar: FunctionComponent<
}
}, [])
const cloneChild = (listItem: string, listIndex: number) => {
return React.Children.map(children, function (child: any, index: number) {
if (child && index === listIndex) {
return React.cloneElement(child, {
key: listIndex,
children: listItem,
})
}
return null
})
}
useEffect(() => {
initScrollWrap(text)
}, [text])
......@@ -259,7 +298,7 @@ export const NoticeBar: FunctionComponent<
}
const iconShow = () => {
if (leftIcon === 'close') {
if (leftIcon === 'close' || vLeftIcon === 'close') {
return false
}
return true
......@@ -288,7 +327,7 @@ export const NoticeBar: FunctionComponent<
const barStyle = {
color,
background,
height: direction === 'vertical' ? `${height}px` : '',
height: isVertical ? `${height}px` : '',
}
const duringTime =
......@@ -313,6 +352,173 @@ export const NoticeBar: FunctionComponent<
wrapable,
})
/**
* 垂直自定义滚动方式
*/
const init = (active = +0) => {
if (!container?.current) return
setTimeout(async () => {
const rects = await getRectByTaro(container?.current)
const _active = Math.max(Math.min(childCount - 1, active), 0)
const _height = rects.height
trackSize = childCount * Number(_height)
const targetOffset = getOffset(_active)
_swiper.current.moving = true
if (ready) {
_swiper.current.moving = false
}
active = _active
setRect(rects)
setOffset(targetOffset)
setReady(true)
}, 0)
}
useEffect(() => {
if (ready) {
stopAutoPlay()
autoplay()
}
return () => {
setReady(false)
}
}, [ready])
useEffect(() => {
if (isVertical && children) {
setTimeout(() => {
init()
stopAutoPlay()
autoplay()
}, 300)
}
}, [children, container?.current])
// 清除定时器
const stopAutoPlay = () => {
clearTimeout(_swiper.current.autoplayTimer)
_swiper.current.autoplayTimer = null
}
// 定时轮播
const autoplay = () => {
if (childCount <= 1) return
stopAutoPlay()
_swiper.current.autoplayTimer = setTimeout(() => {
next()
autoplay()
}, Number(standTime) + 100 * speed)
}
// 切换方法
const move = ({ pace = 0, offset = 0 }) => {
if (childCount <= 1) return
const targetActive = getActive(pace)
// 父级容器偏移量
const targetOffset = getOffset(targetActive, offset)
// 如果循环,调整开头结尾图片位置
if (Array.isArray(children) && children[0] && targetOffset !== minOffset) {
const rightBound = targetOffset < minOffset
childOffset[0] = rightBound ? trackSize : 0
}
if (
Array.isArray(children) &&
children[childCount - 1] &&
targetOffset !== 0
) {
const leftBound = targetOffset > 0
childOffset[childCount - 1] = leftBound ? -trackSize : 0
}
setChildOffset(childOffset)
active = targetActive
setOffset(targetOffset)
getStyle(targetOffset)
}
// 下一页
const next = () => {
resettPosition()
requestFrame(() => {
requestFrame(() => {
_swiper.current.moving = false
move({
pace: 1,
})
})
})
}
const handleItemClick = (event: MouseEvent, value: any) => {
onClickItem && onClickItem(event, value)
}
const getStyle = (moveOffset = offset) => {
const target = innerRef.current
let _offset = 0
// 容器高度-元素高度
const val = rect.height - height
_offset = moveOffset + Number(active === childCount - 1 && val / 2)
target.style.transitionDuration = `${
_swiper.current.moving ? 0 : standTime
}ms`
target.style.height = `${Number(height) * childCount}px`
target.style.transform = `translate3D(0,${_offset}px,0)`
}
// 无缝滚动第一个元素位移控制
const itemStyle = (index: any) => {
const style: any = {}
if (height) {
style.height = `${height}px`
style.lineHeight = `${height}px`
}
const offset = childOffset[index]
if (offset) {
style.transform = `translate3D(0,${offset}px,0)`
}
return style
}
// 确定当前active 元素
const getActive = (pace: number) => {
if (pace) {
const _active = active + pace
return range(_active, -1, childCount)
}
return active
}
// 计算位移
const getOffset = (active: number, offset = 0) => {
const currentPosition: any = active * Number(height)
const targetOffset: any = offset - currentPosition
return targetOffset
}
// 浏览器 帧 事件
const requestFrame = (fn: FrameRequestCallback) => {
window.requestAnimationFrame.call(window, fn)
}
// 取值 方法
const range = (num: number, min: number, max: number) => {
return Math.min(Math.max(num, min), max)
}
// 重置首尾位置信息
const resettPosition = () => {
_swiper.current.moving = true
if (active <= -1) {
move({ pace: childCount })
}
if (active >= childCount) {
move({ pace: -childCount })
}
}
useEffect(() => {
return () => {
stopAutoPlay()
}
}, [])
return (
<div className={`${b()} ${className || ''}`} style={style}>
{showNoticeBar && direction === 'across' ? (
......@@ -358,35 +564,66 @@ export const NoticeBar: FunctionComponent<
) : null}
</div>
) : null}
{showNoticeBar &&
scrollList.current.length > 0 &&
direction === 'vertical' ? (
{showNoticeBar && scrollList.current.length > 0 && isVertical ? (
<div
className="nut-noticebar-vertical"
style={barStyle}
ref={container}
onClick={handleClick}
>
{children ? (
<div className="horseLamp_list" style={horseLampStyle}>
{scrollList.current.map((item: string, index: number) => {
return cloneChild(item, index)
})}
{iconShow() ? (
<div
className="left-icon"
style={{ backgroundImage: `url(${iconBg() || ''})` }}
>
{!iconBg() ? (
<Icon
classPrefix={iconClassPrefix}
fontClassName={iconFontClassName}
name="notice"
size="16"
color={color}
/>
) : null}
</div>
) : null}
{children ? (
<>
<div className="nut-noticebar__inner" ref={innerRef}>
{scrollList.current.map((item: string, index: number) => {
return (
<div
className="scroll-inner "
style={itemStyle(index)}
key={index}
onClick={(e) => {
handleItemClick(e, item)
}}
>
{item}
</div>
)
})}
</div>
</>
) : (
<ul className="horseLamp_list" style={horseLampStyle}>
<div className="horseLamp_list" style={horseLampStyle}>
{scrollList.current.map((item: string, index: number) => {
return (
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
<li
<div
className="horseLamp_list_item"
style={{ height }}
key={index}
onClick={(e) => {
handleItemClick(e, item)
}}
>
{item}
</li>
</div>
)
})}
</ul>
</div>
)}
<div
className="go"
......
......@@ -5,6 +5,7 @@ import React, {
FunctionComponent,
MouseEvent,
CSSProperties,
useMemo,
} from 'react'
import classNames from 'classnames'
......@@ -33,10 +34,11 @@ export interface NoticeBarProps extends BasicComponent {
scrollable: boolean | null
speed: number
rightIcon?: HTMLElement | any
close?: (list?: any) => void
click?: (item?: any) => void
onClose?: (list?: any) => void
onClick?: (item?: any) => void
close?: (event: any) => void
click?: (event: any) => void
onClose?: (event: any) => void
onClick?: (event: any) => void
onClickItem?: (event: any, value: any) => void
}
const defaultProps = {
......@@ -84,6 +86,7 @@ export const NoticeBar: FunctionComponent<
click,
onClose,
onClick,
onClickItem,
iconClassPrefix,
iconFontClassName,
} = {
......@@ -105,19 +108,64 @@ export const NoticeBar: FunctionComponent<
const [timer, SetTimer] = useState(0)
const [isCanScroll, SetIsCanScroll] = useState<null | boolean>(null)
const isVertical = direction === 'vertical'
const [rect, setRect] = useState(null as any | null)
let active = 0
const [vLeftIcon, setLeftIcon] = useState('')
const [ready, setReady] = useState(false)
const container = useRef<any>(null)
const innerRef = useRef<any>(null)
const _swiper = useRef<any>({
moving: false,
autoplayTimer: null,
width: 0,
height: 0,
offset: 0,
size: 0,
})
const [childOffset, setChildOffset] = useState<any[]>([])
const [offset, setOffset] = useState(0)
// 获取自定义slot数据内容和总条数
const { childs, childCount } = useMemo(() => {
let childCount = 0
const childs: any = React.Children.map(children, (child) => {
if (!React.isValidElement(child)) return null
childCount++
return child
})
return {
childs,
childCount,
}
}, [children])
// 滚动消息的总高度
let trackSize = childCount * Number(height)
// 设置最小偏移量(最后一条的偏移位置)
const minOffset = (() => {
if (rect) {
const base = isVertical ? rect.height : rect.width
return base - Number(height) * childCount
}
return 0
})()
useEffect(() => {
if (direction === 'vertical') {
if (isVertical) {
// 兼容老版本无左侧Icon问题
if (leftIcon !== '') {
setLeftIcon(leftIcon)
} else {
setLeftIcon('close')
}
if (children) {
const arr: string[] | any = []
React.Children.map(children, (child) => {
arr.push((child as any).props.children)
})
scrollList.current = [].concat(arr)
scrollList.current = [].concat(childs)
} else {
scrollList.current = [].concat(list)
startRollEasy()
}
complexAm ? startRoll() : startRollEasy()
complexAm && startRoll()
} else {
initScrollWrap(text)
}
......@@ -129,18 +177,6 @@ export const NoticeBar: FunctionComponent<
}
}, [])
const cloneChild = (listItem: string, listIndex: number) => {
return React.Children.map(children, function (child: any, index: number) {
if (child && index === listIndex) {
return React.cloneElement(child, {
key: listIndex,
children: listItem,
})
}
return null
})
}
useEffect(() => {
initScrollWrap(text)
}, [text])
......@@ -192,16 +228,17 @@ export const NoticeBar: FunctionComponent<
SetAnimationClass('play-infinite')
}, 0)
}
// 滚动时间间隔
const time =
height / speed / 4 < 1
? Number((height / speed / 4).toFixed(1)) * 1000
: ~~(height / speed / 4) * 1000
/**
* 滚动方式一
* 滚动方式一,普通垂直滚动
*/
const startRollEasy = () => {
showhorseLamp()
const time =
height / speed / 4 < 1
? Number((height / speed / 4).toFixed(1)) * 1000
: ~~(height / speed / 4) * 1000
const timerCurr = window.setInterval(
showhorseLamp,
time + Number(standTime)
......@@ -210,10 +247,7 @@ export const NoticeBar: FunctionComponent<
}
const showhorseLamp = () => {
SetAnimate(true)
const time =
height / speed / 4 < 1
? Number((height / speed / 4).toFixed(1)) * 1000
: ~~(height / speed / 4) * 1000
setTimeout(() => {
scrollList.current.push(scrollList.current[0])
scrollList.current.shift()
......@@ -221,6 +255,10 @@ export const NoticeBar: FunctionComponent<
}, time)
}
/**
* 复杂滚动方式
*/
const startRoll = () => {
const timerCurr = window.setInterval(() => {
const chunk = 100
......@@ -256,7 +294,7 @@ export const NoticeBar: FunctionComponent<
}
const iconShow = () => {
if (leftIcon === 'close') {
if (leftIcon === 'close' || vLeftIcon === 'close') {
return false
}
return true
......@@ -285,15 +323,17 @@ export const NoticeBar: FunctionComponent<
const barStyle = {
color,
background,
height: direction === 'vertical' ? `${height}px` : '',
height: isVertical ? `${height}px` : '',
}
const duringTime =
height / speed / 4 < 1
? Number((height / speed / 4).toFixed(1))
: ~~(height / speed / 4)
const noDuring =
height / speed < 1 ? (height / speed).toFixed(1) : ~~(height / speed)
const horseLampStyle = {
transform: complexAm ? `translateY(${distance}px)` : '',
transition: animate
......@@ -302,6 +342,164 @@ export const NoticeBar: FunctionComponent<
marginTop: animate ? `-${height}px` : '',
}
/**
* 垂直自定义滚动方式
*/
const init = (active = +0) => {
const rects = getRect(container?.current)
const _active = Math.max(Math.min(childCount - 1, active), 0)
const _height = rects.height
trackSize = childCount * Number(_height)
const targetOffset = getOffset(_active)
_swiper.current.moving = true
if (ready) {
_swiper.current.moving = false
}
active = _active
setRect(rects)
setOffset(targetOffset)
setReady(true)
}
useEffect(() => {
if (ready) {
stopAutoPlay()
autoplay()
}
return () => {
setReady(false)
}
}, [ready])
useEffect(() => {
if (isVertical && children) {
init()
stopAutoPlay()
autoplay()
}
}, [children, container?.current])
// 清除定时器
const stopAutoPlay = () => {
clearTimeout(_swiper.current.autoplayTimer)
_swiper.current.autoplayTimer = null
}
// 定时轮播
const autoplay = () => {
if (childCount <= 1) return
stopAutoPlay()
_swiper.current.autoplayTimer = setTimeout(() => {
next()
autoplay()
}, Number(standTime) + 100 * speed)
}
// 切换方法
const move = ({ pace = 0, offset = 0 }) => {
if (childCount <= 1) return
const targetActive = getActive(pace)
// 父级容器偏移量
const targetOffset = getOffset(targetActive, offset)
// 循环滚动,调整开头结尾图片位置
if (Array.isArray(children) && children[0] && targetOffset !== minOffset) {
const rightBound = targetOffset < minOffset
childOffset[0] = rightBound ? trackSize : 0
}
if (
Array.isArray(children) &&
children[childCount - 1] &&
targetOffset !== 0
) {
const leftBound = targetOffset > 0
childOffset[childCount - 1] = leftBound ? -trackSize : 0
}
setChildOffset(childOffset)
active = targetActive
setOffset(targetOffset)
getStyle(targetOffset)
}
// 下一页
const next = () => {
resettPosition()
requestFrame(() => {
requestFrame(() => {
_swiper.current.moving = false
move({
pace: 1,
})
})
})
}
const handleItemClick = (event: MouseEvent, value: any) => {
onClickItem && onClickItem(event, value)
}
const getStyle = (moveOffset = offset) => {
const target = innerRef.current
let _offset = 0
// 容器高度-元素高度
const val = rect.height - height
_offset = moveOffset + Number(active === childCount - 1 && val / 2)
target.style.transitionDuration = `${
_swiper.current.moving ? 0 : standTime
}ms`
target.style.height = `${Number(height) * childCount}px`
target.style.transform = `translate3D(0,${_offset}px,0)`
}
// 无缝滚动第一个元素位移控制
const itemStyle = (index: any) => {
const style: any = {}
if (height) {
style.height = `${height}px`
style.lineHeight = `${height}px`
}
const offset = childOffset[index]
if (offset) {
style.transform = `translate3D(0,${offset}px,0)`
}
return style
}
// 确定当前active 元素
const getActive = (pace: number) => {
if (pace) {
const _active = active + pace
return range(_active, -1, childCount)
}
return active
}
// 计算位移
const getOffset = (active: number, offset = 0) => {
const currentPosition: any = active * Number(height)
const targetOffset: any = offset - currentPosition
return targetOffset
}
// 浏览器 帧 事件
const requestFrame = (fn: FrameRequestCallback) => {
window.requestAnimationFrame.call(window, fn)
}
// 取值 方法
const range = (num: number, min: number, max: number) => {
return Math.min(Math.max(num, min), max)
}
// 重置首尾位置信息
const resettPosition = () => {
_swiper.current.moving = true
if (active <= -1) {
move({ pace: childCount })
}
if (active >= childCount) {
move({ pace: -childCount })
}
}
const b = bem('noticebar')
const noticebarClass = classNames({
'nut-noticebar-page': true,
......@@ -310,6 +508,12 @@ export const NoticeBar: FunctionComponent<
wrapable,
})
useEffect(() => {
return () => {
stopAutoPlay()
}
}, [])
return (
<div className={`${b()} ${className || ''}`} style={style}>
{showNoticeBar && direction === 'across' ? (
......@@ -355,33 +559,66 @@ export const NoticeBar: FunctionComponent<
) : null}
</div>
) : null}
{showNoticeBar && direction === 'vertical' ? (
{showNoticeBar && scrollList.current.length > 0 && isVertical ? (
<div
className="nut-noticebar-vertical"
style={barStyle}
ref={container}
onClick={handleClick}
>
{children ? (
<div className="horseLamp_list" style={horseLampStyle}>
{scrollList.current.map((item: string, index: number) => {
return cloneChild(item, index)
})}
{iconShow() ? (
<div
className="left-icon"
style={{ backgroundImage: `url(${iconBg() || ''})` }}
>
{!iconBg() ? (
<Icon
classPrefix={iconClassPrefix}
fontClassName={iconFontClassName}
name="notice"
size="16"
color={color}
/>
) : null}
</div>
) : null}
{children ? (
<>
<div className="nut-noticebar__inner" ref={innerRef}>
{scrollList.current.map((item: string, index: number) => {
return (
<div
className="scroll-inner "
style={itemStyle(index)}
key={index}
onClick={(e) => {
handleItemClick(e, item)
}}
>
{item}
</div>
)
})}
</div>
</>
) : (
<ul className="horseLamp_list" style={horseLampStyle}>
<div className="horseLamp_list" style={horseLampStyle}>
{scrollList.current.map((item: string, index: number) => {
return (
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
<li
<div
className="horseLamp_list_item"
style={{ height }}
key={index}
onClick={(e) => {
handleItemClick(e, item)
}}
>
{item}
</li>
</div>
)
})}
</ul>
</div>
)}
<div
className="go"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册