动画:
点击标题伸缩
点击菜单项伸缩
实现:
子元素未知高度过渡:通过js获取子元素高度后,再将其隐藏,后续就可通过获取的高度进行动画过渡
参数:
App.propTypes = {
click: PropTypes.func, 点击菜单项回调,第一个参数是索引,第二个参数是菜单项内容
title:PropTypes.string, 下拉框的标题
tipTriangleWidth:PropTypes.string, 下拉框的三角提示大小
tipTriangleLeft:PropTypes.string, 下拉框的三角提示位置
tipBorderColor:PropTypes.string, 下拉框的边框色
titleColor:PropTypes.string, 标题颜色
titleSize:PropTypes.string, 标题字体大小
itemSize:PropTypes.string, 菜单项字体大小
itemColor:PropTypes.string, 菜单项字体颜色
hoverColor:PropTypes.string, 菜单项hover背景色
}
效果图:
代码示例:
使用:
import React,{useState} from 'react'
import Dropdown from './dropdown/dropdown'
import DropItem from './dropdown/dropItem/dropItem'
function App(){
const click=function (index,text) {
console.log(index);
console.log(text);
}
return(
<div>
<Dropdown
click={click}
title={'下拉内容'}
>
<DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
<DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
<DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
<DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
<DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
<DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
<DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
</Dropdown>
</div>
)
}
export default App
dropdown.jsx:
import React,{useState,useEffect,useCallback,useRef} from 'react'
import PropTypes from 'prop-types'
import './dropdown.css'
import DropItem from './dropItem/dropItem'
function App(props) {
const [panelHeight, setPanelHeight] = useState(0);
const [pop, setPop] = useState(false);
const dropf = useRef(null);
const panelf = useRef(null);
//样式处理
useEffect(() => {
_styleHandler('tipTriangleWidth');
_styleHandler('tipTriangleLeft');
_styleHandler('tipBorderColor');
_styleHandler('titleColor');
_styleHandler('titleSize');
_styleHandler('itemSize');
_styleHandler('itemColor');
_styleHandler('hoverColor');
}, [])
// 统一样式处理
const _styleHandler = useCallback((key) => {
props[key] && dropf.current.style.setProperty('--' + key, props[key])
}, [])
//初始获取子组件高度
useEffect(() => {
setPanelHeight(panelf.current.scrollHeight);
panelf.current.style.display = 'none';
panelf.current.style.height = 0 + 'px';
}, [])
//点击标题收缩展开
const _click = useCallback(function () {
if (pop) {
panelf.current.style.height = 0 + 'px';
panelf.current.style.opacity = '0';
} else {
panelf.current.style.display = 'block';
setTimeout(() => {
panelf.current.style.opacity = '1';
panelf.current.style.height = panelHeight + 'px';
},10)
}
setPop(!pop);
},[pop,panelHeight])
//展开后点击收缩
const _hide=useCallback(function (index,text) {
if (pop) {
panelf.current.style.height = 0 + 'px';
panelf.current.style.opacity = '0';
}
setPop(!pop);
props.click(index, text);
},[pop,panelHeight])
//监听过渡结束,设置none
const _transitioned = useCallback(function () {
if (!pop) {
panelf.current.style.display='none';
}
}, [pop])
return(
<div className='jf-dropdown-container' ref={dropf}>
<div className='jf-dropdown-title' onClick={_click}>
<span>{props.title }</span>
<span className='iconfont icon-xiasanjiao'></span>
</div>
<div onTransitionEnd={_transitioned} className='jf-dropdown-panel' ref={panelf}>
<div className='jf-dropdown-triangle'></div>
<ul className='jf-dropdown-cont'>
{
props.children.map((item,index)=>{
return <DropItem click={_hide} index={index} {...item.props} key={item+index}>{ item.props.children }</DropItem>
})
}
</ul>
</div>
</div>
)
}
// export const xxx=xx;
export default App;
App.propTypes = {
click: PropTypes.func,
title:PropTypes.string,
tipTriangleWidth:PropTypes.string,
tipTriangleLeft:PropTypes.string,
tipBorderColor:PropTypes.string,
titleColor:PropTypes.string,
titleSize:PropTypes.string,
itemSize:PropTypes.string,
itemColor:PropTypes.string,
hoverColor:PropTypes.string,
}
App.defaultProps = {
click:()=>{}
}
dropdown.css:
.jf-dropdown-container {
display: inline-block;
position: relative;
--tipTriangleWidth: 6px;
--tipTriangleLeft: 5%;
--tipBorderColor: #ebeef5;
--titleColor: #409eff;
--titleSize: 16px;
--itemSize: 16px;
--itemColor: black;
--hoverColor: #ecf5ff;
}
.jf-dropdown-title {
color: var(--titleColor);
cursor: pointer;
font-size: var(--titleSize);
}
.jf-dropdown-title > .iconfont {
font-size: var(--titleSize);
}
.jf-dropdown-panel {
position: absolute;
overflow: hidden;
opacity: 0;
transition: height 0.5s, opacity 0.2s 0.1s;
margin: 5px 0;
}
.jf-dropdown-cont {
white-space: nowrap;
z-index: 2000;
border: 1px solid var(--tipBorderColor, #ebeef5);
border-radius: 4px;
padding-left: 0;
color: var(--itemColor);
margin: 0;
font-size: var(--itemSize);
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.jf-dropdown-cont > li:hover {
background-color: var(--hoverColor);
}
.jf-dropdown-triangle {
position: relative;
/* top: -6px; */
width: 0;
height: 0;
left: var(--tipTriangleLeft);
box-sizing: border-box;
border-top-width: 0px;
border-right-width: var(--tipTriangleWidth, 6px);
border-bottom-width: var(--tipTriangleWidth, 6px);
border-left-width: var(--tipTriangleWidth, 6px);
border-top-color: transparent;
border-right-color: transparent;
border-bottom-color: var(--tipBorderColor, #ebeef5);
border-left-color: transparent;
border-style: solid;
}
.jf-dropdown-triangle::after {
content: " ";
box-sizing: border-box;
position: absolute;
top: 1px;
left: 0;
margin-left: calc(0px - var(--tipTriangleWidth, 6px));
border-width: var(--tipTriangleWidth, 6px);
border-right-width: var(--tipTriangleWidth, 6px);
border-bottom-width: var(--tipTriangleWidth, 6px);
border-left-width: var(--tipTriangleWidth, 6px);
border-top-width: 0;
border-bottom-color: white;
width: 0;
height: 0;
border-top-color: transparent;
border-right-color: transparent;
border-left-color: transparent;
border-style: solid;
}
dropdown/dropItem.jsx:
import React, { useState,useCallback } from 'react'
import './dropItem.css'
function App(props){
return(
<li onClick={()=>{props.click(props.index,props.children)}} className='jf-dropItem-container'>
{props.children}
</li>
)
}
export default App
dropdown/dropItem.css:
.jf-dropItem-container {
cursor: pointer;
height: 20px;
padding: 10px 5px;
list-style: none;
}
.jf-dropItem-container:hover {
}