- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
基于 React18 Hooks 实现手机端弹框组件 RcPop 。
react-popup 基于 react18+hook 自定义多功能弹框组件。整合了 msg/alert/dialog/toast及android/ios 弹窗效果。支持 20+ 自定义参数、 组件式+函数式 调用方式,全方位满足各种弹窗场景需求.
在需要使用弹窗的页面引入组件.
// 引入自定义组件 import RcPop, { rcpop } from './components/rcpop'
RcPop支持 组件式+函数式 两种调用方式.
组件写法 。
< RcPop visible ={visible} title ="标题" content ="弹窗内容" type ="android" shadeClose ="false" closeable :btns ="[ {text: '取消', click: () => setVisible(false)}, {text: '确认', style: {color: '#09f'}, click: handleOK}, ]" @onOpen ={handleOpen} @onClose ={handleClose} /> < div > 这里是自定义弹窗内容,优先级高于content内容。 </ div > </ RcPop >
函数写法 。
function handlePopup() { rcpop({ title: '标题' , content: ` <div style="padding:20px;"> <p>函数式调用:<em style="color:#999;">rcpop({...})</em></p> </div>`, btns: [ { text: '取消' , click: () => { // 关闭弹窗 rcpop.close() } }, { text: '确认' , style: {color: '#09f' }, click: () => { rcpop({ type: 'toast' , icon: 'loading' , content: '加载中...' , opacity: . 2 , time: 2 }) } } ] }) }
rcpop({ title: '标题' , content: ` <div style="color:#f90"> <p>显示自定义弹窗内容</p> </div>`, btns: [ { text: '稍后提示' }, { text: '取消', click: () => rcpop.close() }, { text: '立即更新' , style: {color: '#09f' }, click: () => { // ... } } ] })
< RcPop visible ={visible} closeable xposition ="top" content ="这里是内容信息" btns ={[ {text: '确认', style: {color: '#00d8ff'}, click: () = > setVisible(false)}, ]} onOpen={()=> { console.log('弹窗开启...') }} onClose={()=>{ console.log('弹窗关闭...') setVisible(false) }} > < div style ={{padding: '15px'}} > < img src ={reactLogo} width ="60" onClick ={handleContextPopup} /> < h3 style ={{color:'#f60', 'paddingTop':'10px'}} > 当 content 和 自定义插槽 内容同时存在,只显示插槽内容。 </ h3 > </ div > </ RcPop >
function handleContextPopup(e) { let points = [e.clientX, e.clientY] rcpop({ type: 'contextmenu' , follow: points, opacity: 0 , btns: [ {text: '标记备注信息' }, { text: '删除' , style: {color: '#f00' }, click: () => { rcpop.close() } } ] }) }
这次主打的是学习 React Hooks 开发自定义弹窗,之前也有开发过类似的弹层组件.
https://www.cnblogs.com/xiaoyan2017/p/14085142.html 。
https://www.cnblogs.com/xiaoyan2017/p/11589149.html 。
在components目录下新建rcpop文件夹.
rcpop支持如下参数配置 。
// 弹窗默认参数 const defaultProps = { // 是否显示弹出层 visible: false , // 弹窗唯一性标识 id: null , // 弹窗标题 title: '' , // 弹窗内容 content: '' , // 弹窗类型(toast | footer | actionsheet | actionsheetPicker | ios | android | androidSheet | contextmenu) type: '' , // toast图标(loading | success | fail) icon: '' , // 是否显示遮罩层 shade: true , // 点击遮罩层关闭 shadeClose: true , // 遮罩透明度 opacity: '' , // 自定义遮罩层样式 overlayStyle: {}, // 是否圆角 round: false , // 是否显示关闭图标 closeable: false , // 关闭图标位置(left | right | top | bottom) closePosition: 'right' , // 关闭图标颜色 closeColor: '' , // 动画类型(scaleIn | fadeIn | footer | fadeInUp | fadeInDown) anim: 'scaleIn' , // 弹窗出现位置(top | right | bottom | left) position: '' , // 长按/右键弹窗(坐标点) follow: null , // 弹窗关闭时长,单位秒 time: 0 , // 弹窗层级 zIndex: 2023 , // 弹窗按钮组(text | style | disabled | click) btns: null , // 指定挂载的节点(仅对标签组件有效) // teleport = () => document.body, teleport: null , // 弹窗打开回调 onOpen: () => {}, // 弹窗关闭回调 onClose: () => {}, // 点击遮罩层回调 onClickOverlay: () => {}, // 自定义样式 customStyle: {}, // 类名 className: null , // 默认插槽内容 children: null }
弹窗组件模板 。
const renderNode = () => { return ( < div ref ={ref} className ={classNames('rc__popup', options.className, {'rc__popup-closed': closed})} id ={options.id} style ={{'display': !opened.current ? 'none' : undefined}} > {/* 遮罩层 */} { isTrue(options.shade) && < div className ="rcpopup__overlay" onClick ={handleShadeClick} style ={{'opacity': options.opacity, 'zIndex': oIndex-1, ...options.overlayStyle}} ></ div > } {/* 窗体 */} < div className ="rcpopup__wrap" style ={{'zIndex': oIndex}} > < div ref ={childRef} className ={classNames( 'rcpopup__child', { [`anim-${options.anim}`]: options.anim, [`popupui__${options.type}`]: options.type, 'round': options.round }, options.position )} style ={popStyles} > { options.title && < div className ="rcpopup__title" > {options.title} </ div > } { (options.type == 'toast' && options.icon) && < div className ={classNames('rcpopup__toast', options.icon)} dangerouslySetInnerHTML ={{__html: ToastIcon[options.icon]}} ></ div > } {/* 内容 */} { options.children ? < div className ="rcpopup__content" > {options.children} </ div > : options.content ? < div className ="rcpopup__content" dangerouslySetInnerHTML ={{__html: options.content}} ></ div > : null } {/* 按钮组 */} { options.btns && < div className ="rcpopup__actions" > { options.btns.map((btn, index) => { return < span className ={classNames('btn', {'btn-disabled': btn.disabled})} key ={index} style ={btn.style} dangerouslySetInnerHTML ={{__html: btn.text}} onClick ={e = > handleActions(e, index)}> </ span > }) } </ div > } { isTrue(options.closeable) && < div className ={classNames('rcpopup__xclose', options.closePosition)} style ={{'color': options.closeColor}} onClick ={close} ></ div > } </ div > </ div > </ div > ) }
完整代码块 。
/* * * @title 基于react18 hooks自定义移动端弹窗组件 * @author YXY Q: 282310962 * @date 2023/07/25 */ import { useState, useEffect, createRef, useRef, forwardRef, useImperativeHandle } from 'react' import { createPortal } from 'react-dom' import { createRoot } from 'react-dom/client' // ... const RcPop = forwardRef((props, ref) => { const mergeProps = { ...defaultProps, ...props } const [options, setOptions] = useState(mergeProps) const [oIndex, setOIndex] = useState(options.zIndex) const [closed, setClosed] = useState( false ) const [followStyle, setFollowStyle] = useState({ position: 'absolute' , left: '-999px' , top: '-999px' }) const opened = useRef( false ) const childRef = useRef() const stopTimer = useRef( null ) const popStyles = options.follow ? { ...followStyle, ...options.customStyle } : { ...options.customStyle } const isTrue = (str) => /^true$/ i.test(str) const ToastIcon = { loading: '<svg viewBox="25 25 50 50"><circle fill="none" cx="50" cy="50" r="20"></circle></svg>' , success: '<svg viewBox="0 0 1024 1024"><path d="M512 85.333c235.648 0 426.667 191.019 426.667 426.667S747.648 938.667 512 938.667 85.333 747.648 85.333 512 276.352 85.333 512 85.333zm-74.965 550.4l-90.582-90.581a42.667 42.667 0 1 0-60.33 60.33l120.704 120.705a42.667 42.667 0 0 0 60.33 0L768.811 424.49a42.667 42.667 0 1 0-60.288-60.331L436.992 635.648z" /></svg>' , error: '<svg viewBox="0 0 1024 1024"><path d="M512 85.333C276.352 85.333 85.333 276.352 85.333 512S276.352 938.667 512 938.667 938.667 747.648 938.667 512 747.648 85.333 512 85.333zm128.427 606.72l-129.75-129.749-129.066 129.024a35.968 35.968 0 1 1-50.902-50.901L459.733 511.36 329.301 380.928a35.968 35.968 0 1 1 50.859-50.944l130.475 130.475 129.706-129.75a35.968 35.968 0 1 1 50.944 50.902L561.536 511.36l129.75 129.75a35.968 35.968 0 1 1-50.902 50.943z" /></svg>' , warning: '<svg viewBox="0 0 1024 1024"><path d="M512 941.12q-89.28 0-167.52-34.08t-136.32-92.16T116 678.08t-34.08-168T116 342.56t92.16-136.32 136.32-92.16T512 80t168 34.08 136.8 92.16 92.16 136.32 34.08 167.52-34.08 168-92.16 136.8T680 907.04t-168 34.08zM460.16 569.6q0 23.04 14.88 38.88T512 624.32t37.44-15.84 15.36-38.88V248q0-23.04-15.36-36.96T512 197.12t-37.44 14.4-15.36 37.44zM512 688.64q-27.84 0-47.52 19.68t-19.68 47.52 19.68 47.52T512 823.04t48-19.68 20.16-47.52T560 708.32t-48-19.68z"/></svg>' , info: '<svg viewBox="0 0 1024 1024"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm84 343.1l-87 301.4c-4.8 17.2-7.2 28.6-7.2 33.9 0 3.1 1.3 6 3.8 8.7s5.2 4 8.1 4c4.8 0 9.6-2.1 14.4-6.4 12.7-10.5 28-29.4 45.8-56.8l14.4 8.5c-42.7 74.4-88 111.6-136.1 111.6-18.4 0-33-5.2-43.9-15.5-10.9-10.3-16.3-23.4-16.3-39.2 0-10.5 2.4-23.7 7.2-39.9l58.9-202.7c5.7-19.5 8.5-34.2 8.5-44.1 0-6.2-2.7-11.7-8.1-16.5-5.4-4.8-12.7-7.2-22-7.2-4.2 0-9.3.1-15.3.4l5.5-17L570.4 407H596v.1zm17.8-88.7c-12.2 12.2-26.9 18.2-44.1 18.2-17 0-31.5-6.1-43.7-18.2-12.2-12.2-18.2-26.9-18.2-44.1s6-31.9 18-44.1c12-12.1 26.6-18.2 43.9-18.2 17.5 0 32.3 6.1 44.3 18.2 12 12.2 18 26.9 18 44.1s-6.1 31.9-18.2 44.1z"/></svg>' , } /* * * 开启弹窗 */ function open(params) { params && setOptions({ ...options, ...params }) if (options.type == 'toast' ) { options.time = options.time || 3 } if (opened.current) return opened.current = true setOIndex( ++index + options.zIndex) options.onOpen ? .() // 右键/长按菜单 if (options.follow) { setTimeout(() => { let rcpop = childRef.current let oW, oH, winW, winH, pos oW = rcpop.clientWidth oH = rcpop.clientHeight winW = window.innerWidth winH = window.innerHeight pos = getPos(options.follow[0], options.follow[1 ], oW, oH, winW, winH) setFollowStyle({ ...followStyle, left: pos[ 0 ], top: pos[ 1 ] }) }) } if (options.time) { clearTimeout(stopTimer.current) stopTimer.current = setTimeout(() => { close() }, options.time * 1000 ) } } /* * * 关闭弹窗 */ function close() { if (!opened.current) return setClosed( true ) setTimeout(() => { setClosed( false ) opened.current = false options.onClose ? .() clearTimeout(stopTimer.current) }, 200 ) } // 点击遮罩层 function handleShadeClick(e) { options.onClickOverlay ? .(e) if (isTrue(options.shadeClose)) { close() } } // 点击按钮组 function handleActions(e, index) { let btn = options.btns[index] if (! btn.disabled) { btn ?.click? .(e) } } // 抽离的React的classnames操作类 function classNames() { var hasOwn = {}.hasOwnProperty var classes = [] for ( var i = 0; i < arguments.length; i++ ) { var arg = arguments[i] if (!arg) continue var argType = typeof arg if (argType === 'string' || argType === 'number' ) { classes.push(arg) } else if (Array.isArray(arg) && arg.length) { var inner = classNames.apply( null , arg) if (inner) { classes.push(inner) } } else if (argType === 'object' ) { for ( var key in arg) { if (hasOwn.call(arg, key) && arg[key]) { classes.push(key) } } } } return classes.join(' ' ) } // 获取挂载节点 function getTeleport(getContainer) { const container = typeof getContainer == 'function' ? getContainer() : getContainer return container || document.body } // 设置挂载节点 function renderTeleport(getContainer, node) { if (getContainer) { const container = getTeleport(getContainer) return createPortal(node, container) } return node } // 获取弹窗坐标点 function getPos(x, y, ow, oh, winW, winH) { let l = (x + ow) > winW ? x - ow : x; let t = (y + oh) > winH ? y - oh : y; return [l, t]; } const renderNode = () => { return ( <div ref={ref} className={classNames('rc__popup', options.className, {'rc__popup-closed': closed})} id={options.id} style={{'display': !opened.current ? 'none' : undefined}}> { /* 遮罩层 */ } { isTrue(options.shade) && <div className="rcpopup__overlay" onClick={handleShadeClick} style={{'opacity': options.opacity, 'zIndex': oIndex-1, ...options.overlayStyle}}></div> } { /* 窗体 */ } <div className="rcpopup__wrap" style={{'zIndex': oIndex}}> < div ref = {childRef} className = {classNames( 'rcpopup__child' , { [`anim - ${options.anim}`]: options.anim, [`popupui__${options.type}`]: options.type, 'round' : options.round }, options.position )} style = {popStyles} > { options.title && <div className="rcpopup__title">{options.title}</div> } { (options.type == 'toast' && options.icon) && <div className={classNames('rcpopup__toast', options.icon)} dangerouslySetInnerHTML={{__html: ToastIcon[options.icon]}}></div> } { /* 内容 */ } { /* { (options.children || options.content) && <div className="rcpopup__content">{options.children || options.content}</div> } */ } { options.children ? <div className="rcpopup__content">{options.children}</div> : options.content ? <div className="rcpopup__content" dangerouslySetInnerHTML={{__html: options.content}}></div> : null } { /* 按钮组 */ } { options.btns && <div className="rcpopup__actions"> { options.btns.map((btn, index) => { return <span className={classNames('btn', {'btn-disabled': btn.disabled})} key={index} style={btn.style} dangerouslySetInnerHTML={{__html: btn.text}} onClick={e => handleActions(e, index)}></span> }) } </div> } { isTrue(options.closeable) && <div className={classNames('rcpopup__xclose', options.closePosition)} style={{'color': options.closeColor}} onClick={close}></div> } </div> </div> </div> ) } useEffect(() => { props.visible && open() !props.visible && close() }, [props.visible]) // 暴露指定的方法给父组件调用 useImperativeHandle(ref, () => ({ open, close })) return renderTeleport(options.teleport || mergeProps.teleport, renderNode()) })
react动态设置className,于是抽离封装了classNames函数.
// 抽离的React的classnames操作类 function classNames() { var hasOwn = {}.hasOwnProperty var classes = [] for ( var i = 0; i < arguments.length; i++ ) { var arg = arguments[i] if (!arg) continue var argType = typeof arg if (argType === 'string' || argType === 'number' ) { classes.push(arg) } else if (Array.isArray(arg) && arg.length) { var inner = classNames.apply( null , arg) if (inner) { classes.push(inner) } } else if (argType === 'object' ) { for ( var key in arg) { if (hasOwn.call(arg, key) && arg[key]) { classes.push(key) } } } } return classes.join(' ' ) }
非常方便的实现各种动态操作className类.
通过 createRoot 将弹窗组件挂载到body,实现函数式调用.
/* * * 函数式弹窗组件 * rcpop({...}) | rcpop.close() */ let popRef = createRef() function Popup(options = {}) { options.id = options.id || 'rcpopup-' + Math.floor(Math.random() * 10000 ) // 判断id唯一性 let rnode = document.querySelector(`#${options.id}`) if (options.id && rnode) return const div = document.createElement('div' ) document.body.appendChild(div) const root = createRoot(div) root.render( < RcPop ref = {popRef} visible ={ true } {...options} onClose ={() => { let node = document.querySelector(`#${options.id}`) if (!node) return root.unmount() document.body.removeChild(div) }} /> ) return popRef }
OK,以上就是react18 hook实现自定义弹窗的一些小分享,希望对大家有所帮助~~💪 。
。
最后此篇关于react18hooks自定义移动端Popup弹窗组件RcPop的文章就讲到这里了,如果你想了解更多关于react18hooks自定义移动端Popup弹窗组件RcPop的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
只是想知道 Jquery Mobile 是否足够稳定以用于实时生产企业移动应用程序。 有很多 HTML5 框架,因为我们的团队使用 JQuery 已经有一段时间了,我们更愿意使用 Jquery 移动框
关闭。这个问题需要details or clarity .它目前不接受答案。 想改进这个问题吗? 通过 editing this post 添加细节并澄清问题. 关闭 3 年前。 Improve t
所以我尝试在 JavaScript 中对元素进行拖放。我使用的视频教程在这里; https://www.youtube.com/watch?v=KTlZ4Hs5h80 。我已经按照它的说明进行了编码,
无法在移动 iOS(safari 和 chrome)上自动播放以前缓存的 mp3 音频 我正在 Angular 8 中开发一个应用程序,在该应用程序的一部分中,我试图在对象数组中缓存几个传入的音频 m
Git 基于内容而不是文件,所以我目前理解以下行为,但我想知道是否有特殊选项或 hack 来检测此类事情: git init mkdir -p foo/bar echo "test" foo/a.tx
我正在寻找语义 ui 正确的类来隐藏例如移动 View 中的 DIV。在 Bootstrap 中,我们有“visible-xs”和“hidden-xs”。 但是在语义ui上我只找到了“仅移动网格” 最
我正在使用 ubuntu 和 想要移动或复制大文件。 但是当我与其他人一起使用服务器时,我不想拥有所有内存并使其他进程几乎停止。 那么有没有办法在内存使用受限的情况下移动或复制文件? 最佳答案 如果你
这些指令有什么区别?以 ARM9 处理器为例,它不应该是: ASM: mov r0, 0 C: r0 = 0; ASM: ld r0, 0 C: r0 = 0; ? 我不知道为什么要使用一个或另一个:
我有一个文件夹,其中包含一些随机命名的文件,其中包含我需要的数据。 为了使用数据,我必须将文件移动到另一个文件夹并将文件命名为“file1.xml” 每次移动和重命名文件时,它都会替换目标文件夹中以前
我经常在 IB/Storyboard 中堆叠对象,几乎不可能拖动其他对象后面的对象而不移动前面的对象。无论如何我可以移动已经选择但位于其他对象后面的对象吗?当我尝试移动它时,它总是选择顶部的对象,还是
几个月前,我看到 Safari 7 允许推送通知,它似乎是一个非常有用的工具,除了我看到的每个示例都专注于桌面浏览,而不是移动设备。 Safari 推送通知是否可以在移动设备上运行,如果没有,是否有计
我有一个简单的 View 模型,其中包含修改后的 ObservableCollection使用 SynchronizationContext.Current.Send在 UI 线程上执行对集合的更改。
关于cassandra创建的数据文件和系统文件的位置,我需要移动在“cassandra.yaml”配置文件中设置的“commitlog_directory”、“data_file_directorie
我有这个代码 $(function() { var message = 'Dont forget us'; var original; var txt1 = ' - '; $(wind
我的客户报告说他的网站有一个奇怪的问题。该网站的 URL 是 your-montenegro.me 在 基于 Android 的浏览器 上加载时,页面底部会出现一个奇怪的空白区域。以下是屏幕截图: 华
我有这个 HTML 标记: Express 300 bsf Sign Up 我需要将元素从 DOM 上的一个
我有一个可重新排序的 TableView (UITableView 实例)。尽管我已经实现了 UITableViewDataSource 方法: tableView:moveRowAtIndexPat
我的客户报告说他的网站有一个奇怪的问题。该网站的 URL 是 your-montenegro.me 在 基于 Android 的浏览器 上加载时,页面底部会出现一个奇怪的空白区域。以下是屏幕截图: 华
我需要在拖放或复制/剪切和粘贴(复制与移动)期间获取操作类型。它是一个 Swing 应用程序,并且实现了 TransferHandle。我在操作结束时需要此信息,在 importData 方法中。 对
我编写了一个具有 add 和 get 方法的 SortedIntList 类。 我调用以下四个方法: SortedIntList mySortedIntList = new SortedIntList
我是一名优秀的程序员,十分优秀!