点击在线浏览,体验更好 | 链接 |
---|---|
古代JavaScript高级小册 | 链接 |
深入浅出Dart | 链接 |
古代TypeScript高级小册 | 链接 |
jcode
咱们先从最根本的实现开始,而后逐渐增加更多的性能,如手风琴模式、自定义箭头、禁用状态、暗藏时是否渲染DOM构造
组件接口定义
Collapse
属性 | 阐明 | 类型 | 默认值 | |
---|---|---|---|---|
accordion | 是否开启手风琴模式 | boolean | false | |
activeKey | 以后开展面板的 key | 手风琴模式:string \ | null 非手风琴模式:string[] | - |
arrow | 自定义箭头,如果是 ReactNode,那么 antd-mobile 会主动为你减少旋转动画成果 | ReactNode \ | ((active: boolean) => React.ReactNode) | - |
defaultActiveKey | 默认开展面板的 key | 手风琴模式:string \ | null 非手风琴模式:string[] | - |
onChange | 切换面板时触发 | 手风琴模式:(activeKey: string \ | null) => void 非手风琴模式:(activeKey: string[]) => void | - |
Collapse.Panel
属性 | 阐明 | 类型 | 默认值 | |
---|---|---|---|---|
arrow | 自定义箭头 | ReactNode \ | ((active: boolean) => React.ReactNode) | - |
destroyOnClose | 不可见时卸载内容 | boolean | false | |
disabled | 是否为禁用状态 | boolean | false | |
forceRender | 被暗藏时是否渲染 DOM 构造 | boolean | false | |
key | 惟一标识符 | string | - | |
onClick | 标题栏的点击事件 | (event: React.MouseEvent<Element, MouseEvent>) => void | - | |
title | 标题栏左侧内容 | ReactNode | - |
创立根底Collapse组件
咱们创立一个根底的Collapse组件。这个组件须要有一个状态来追踪它是否被开展
import React, { useState } from 'react';const Collapse = ({ children }) => { const [isOpen, setIsOpen] = useState(false); return ( <div> <button onClick={() => setIsOpen(!isOpen)}> {isOpen ? 'Collapse' : 'Expand'} </button> {isOpen && <div>{children}</div>} </div> );};export default Collapse;
拓展Collapse组件其它属性
accordion
:如果设置为true,咱们将启用手风琴模式。在这种模式下,只有一个面板能够被开展。当一个新的面板被开展时,之前开展的面板将被敞开。activeKey
:这是以后开展面板的key。如果咱们处于手风琴模式,这将是一个字符串或null。如果咱们不在手风琴模式,这将是一个字符串数组。arrow
:这是一个自定义的箭头。如果这是一个React节点,antd-mobile将主动为你增加旋转动画成果。如果这是一个函数,它将接管一个参数,示意面板是否被开展,并返回一个React节点。defaultActiveKey
:这是默认开展面板的key。它的类型与activeKey雷同。onChange
:这是一个函数,它在面板切换时被触发。它接管一个参数,示意以后开展面板的key。它的类型与activeKey雷同。
import React, { useState, useEffect } from 'react';const Collapse = ({ children, forceRender, accordion, activeKey, arrow, defaultActiveKey, onChange }) => { const [isOpen, setIsOpen] = useState(false); const [currentActiveKey, setCurrentActiveKey] = useState(defaultActiveKey); useEffect(() => { setCurrentActiveKey(activeKey); }, [activeKey]); const handleClick = () => { setIsOpen(!isOpen); if (accordion) { setCurrentActiveKey(isOpen ? null : activeKey); } onChange && onChange(isOpen ? null : activeKey); }; const renderArrow = () => { if (typeof arrow === 'function') { return arrow(isOpen); } return arrow; }; return ( <div> <button onClick={handleClick}> {isOpen ? 'Collapse' : 'Expand'} {renderArrow()} </button> <div style={{ display: isOpen || forceRender ? 'block' : 'none' }}> {children} </div> </div> );};export default Collapse;
实现Panel
咱们创立一个名为Collapse.Panel
的子组件来反对这些新的属性。这个子组件将作为Collapse
组件的一部分,用于示意一个可折叠的面板。
arrow
:这是一个自定义的箭头。如果这是一个React节点,antd-mobile将主动为你增加旋转动画成果。如果这是一个函数,它将接管一个参数,示意面板是否被开展,并返回一个React节点。destroyOnClose
:如果设置为true,咱们将在面板敞开时销毁它的内容。disabled
:如果设置为true,咱们将禁用面板,使其不能被关上或敞开。forceRender
:如果设置为true,咱们将在面板敞开时依然渲染它的DOM构造。key
:这是面板的惟一标识符。onClick
:这是一个函数,它在面板的标题栏被点击时被触发。它接管一个参数,示意点击事件。title
:这是面板标题栏的内容。
import React, { useState, useEffect } from 'react';const Panel = ({ children, arrow, destroyOnClose, disabled, forceRender, key, onClick, title }) => { const [isOpen, setIsOpen] = useState(false); const handleClick = (event) => { if (disabled) return; setIsOpen(!isOpen); onClick && onClick(event); }; const renderArrow = () => { if (typeof arrow === 'function') { return arrow(isOpen); } return arrow; }; useEffect(() => { if (destroyOnClose && !isOpen) { children = null; } }, [isOpen]); return ( <div key={key}> <button onClick={handleClick}> {title} {renderArrow()} </button> <div style={{ display: isOpen || forceRender ? 'block' : 'none' }}> {children} </div> </div> );};const Collapse = ({ children, accordion, activeKey, defaultActiveKey, onChange }) => {};Collapse.Panel = Panel;export default Collapse;
forceRender属性
咱们要增加一个名为forceRender的属性。如果这个属性被设置为true,咱们会在组件暗藏时依然渲染DOM构造,如果面板渲染的数据量比拟大,这个属性特地有用,不会造成关上的时候会卡顿一下
import React, { useState } from 'react';const Collapse = ({ children, forceRender }) => { const [isOpen, setIsOpen] = useState(false); return ( <div> <button onClick={() => setIsOpen(!isOpen)}> {isOpen ? 'Collapse' : 'Expand'} </button> <div style={{ display: isOpen || forceRender ? 'block' : 'none' }}> {children} </div> </div> );};export default Collapse;
实现折叠面板动画
height形式实现
.collapse-panel { border: 1px solid #ddd; border-radius: 4px; margin-bottom: 10px; overflow: hidden;}.collapse-panel-button { background-color: #f5f5f5; color: #333; cursor: pointer; padding: 10px 15px; width: 100%; text-align: left; border: none; outline: none;}.collapse-panel-content { padding: 10px 15px; background-color: white; overflow: hidden; max-height: 0; transition: max-height 0.2s ease-out;}.collapse-panel-content.open { max-height: 100vh;}
import React, { useState, useEffect, useRef } from 'react';const Panel = ({ children, arrow, destroyOnClose, disabled, forceRender, key, onClick, title }) => { const [isOpen, setIsOpen] = useState(false); const contentRef = useRef(null); const handleClick = (event) => { if (disabled) return; setIsOpen(!isOpen); onClick && onClick(event); }; const renderArrow = () => { if (typeof arrow === 'function') { return arrow(isOpen); } return arrow; }; useEffect(() => { if (destroyOnClose && !isOpen) { children = null; } }, [isOpen]); useEffect(() => { contentRef.current.style.maxHeight = isOpen ? `${contentRef.current.scrollHeight}px` : '0'; }, [isOpen]); return ( <div key={key} className="collapse-panel"> <button onClick={handleClick} className="collapse-panel-button"> {title} {renderArrow()} </button> <div ref={contentRef} className={`collapse-panel-content ${isOpen ? 'open' : ''}`}> {children} </div> </div> );};// ...
残缺成果:
jcode
其它形式
下面手风琴成果是利用height的实现,这种实现会触发重排,所以感兴趣的同学能够思考其它形式优化一下
- 基于scaleY
- 应用FLIP技术增加动画优化