赞
踩
用antd自带菜单导航,点击菜单项的时候并没有什么特别cool的效果,那我们就自己动手加上吧。
在其基础上我补全了功能,使其能够解决我所遇到的问题:
首先菜单项是多项并且单项点击切换的,大佬的实现并不能很好完成这一点,当存在多个具备该特效的节点时,一旦刷新,所有的节点都会触发刷新,那就是满屏的波纹,这一点必须解决。
首先做成一个高阶组件,这样就可以使用ta来对其他组件进行包装,使其具有点击特效,方便使用。
组件命名为withMaterialHoc
,先贴出组件的实现:
withMaterialHoc/index.js
import React from 'react'; import { Ripple, calcEventRelativePos } from './ripple'; let withMaterialHoc = (WrappedComponent) => { return class WithMaterial extends React.Component { constructor(props) { super(props) this.isSwitchIntl = false; this.state = { spawnData: "", clickCount: 0, isSwitchIntl: false } } handleClick = (event) => { this.setState({ spawnData: calcEventRelativePos(event), time: Date.now(), clickCount: this.state.clickCount + 1 }); } shouldComponentUpdate(nextProps, nextState) { if (nextProps.intlData != this.props.intlData) { return true; } if (nextState.clickCount && (nextState.clickCount - this.state.clickCount === 1)) { return true } else { return false } } handleRippleEnd = () => { let value = this.state.clickCount - 1 if (value < 0) { value = 0 } this.setState({ clickCount: value }) } render() { const { spawnData } = this.state; const { className, style } = this.props; return ( <div className={`g-btn ${className || ' '}`} onClick={this.handleClick} style={style} > <WrappedComponent {...this.props} /> <Ripple handleRippleEnd={this.handleRippleEnd} spawnData={spawnData} /> </div> ); } }; } export default withMaterialHoc;
ripple.js
:
import React, { useState, useEffect, useRef, Fragment,memo } from 'react'; import './index.css'; import { useSpring, animated } from 'react-spring'; function calcEventRelativePos(event) { const rect = event.target.getBoundingClientRect(); return { x: event.clientX - rect.left, y: event.clientY - rect.top, }; } function Ripple(props) { const [data, setData] = useState({ top: 0, left: 0, width: 0, height: 0 }); const isInit = useRef(true); const rippleEl = useRef(null); const { spawnData, handleRippleEnd} = props; const rippleAnim = useSpring({ from: { ...props.style, ...data, transform: 'scale(0)', opacity: 1, }, to: !isInit.current ? { opacity: 0, transform: 'scale(2)' } : {}, config: { duration: props.duration || 300, }, onRest: () => { handleRippleEnd() }, reset: true }); useEffect(() => { if (isInit.current) { isInit.current = false; } else { const parentEl = rippleEl.current.parentElement; const size = Math.max(parentEl.offsetWidth, parentEl.offsetHeight); setData({ width: size, height: size, top: spawnData.y - size / 2 || 0, left: spawnData.x - size / 2+50 || 50 }); } }, [spawnData]); return ( <animated.span className="g-ripple" style={rippleAnim} ref={rippleEl} ></animated.span> ); } Ripple = memo(Ripple) export { Ripple, calcEventRelativePos };
因为ripple
是一个函数组件,每次父组件刷新,子组件就会刷新,从而出发了动画(实现用的是react-spring
库,有兴趣可以深入了解)。
那么解决这个问题的关键就是,我点击哪个节点,哪个节点触发刷新,其他的节点不刷新。
React.memo 为高阶组件。它与 React.PureComponent 非常相似,但它适用于函数组件,但不适用于 class 组件。
首先如果父组件传入子组件的props
不改变或者是引用的地址不变(浅比较),那么被memo
包装过的函数组件就不会触发刷新,so,问题就真么愉快的解决了。
效果就是宁缺毋滥的,有时恰到好处的点缀,会起到意想不到的效果,内外兼修,才是最佳的实现,赶紧加上试试。
集成了该功能的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。