今天给小伙伴们分享一个全新开发的React自定义对话框最近RLayer。
rlayer 基于react.js开发的PC桌面端交互式弹出框组件。融合了Dialog、Message、Notification、ActionSheet、Toast、Popover、Pop/confirm/i等多种功能。
看名称就能联想到前端界有名的弹窗layer.js,其实在设计开发之初就有借鉴layer插件实现思想。
功能 提供函数式调用方法 rlayer({…}) 12+弹框类型 (toast | footer | actionsheet | actionsheetPicker | android/ios | contextmenu | drawer | iframe | message | notify | popover) 7+种弹窗动画 (scaleIn | fadeIn | footer | fadeInUp | fadeInDown | fadeInLeft | fadeInRight) 引入组件在需要使用组件的页面引入rlayer组件。
// 引入弹框组件RLayerimport rlayer from \\\'./components/rlayer\\\'
快速使用 引入后即可通过函数rlayer({…})来调用即可。
支持超过30+个参数自由搭配,快速实现定制化的各种效果。
// msg消息const showMsg = () => { rlayer({ content: "这是一条msg消息提示", shadeClose: false, xclose: false, time: 2 })}// /confirm/i询问框const showConfirm = () => { let $el = rlayer({ title: \\\'询问标题\\\', content: "<div style=\\\'color:#0070f3;padding:30px;\\\'>确认框(这里是确认框提示信息,这里确认框提示信息,这里是确认框提示信息)</div>", shadeClose: false, zIndex: 1001, lockScroll: false, resize: true, dragOut: true, btns: [ { text: \\\'取消\\\', click: () => { $el.close() } }, { text: \\\'确定\\\', style: {color: \\\'#61dafb\\\'}, click: () => { handleInfo() } } ] })}
RLayer弹框模板
class RLayerComponent extends React.Component { // ... render() { let opt = this.state return ( <> <div className={domUtils.classNames(\\\'rui__layer\\\', {\\\'rui__layer-closed\\\': opt.closeCls})} id={opt.id} style={{display: opt.opened?\\\'block\\\':\\\'none\\\'}}> {} { opt.shade && <div className="rlayer__overlay" onClick={this.shadeClicked} style={{opacity: opt.opacity}}></div> } {} <div className={domUtils.classNames(\\\'rlayer__wrap\\\', opt.anim&&\\\'anim-\\\'+opt.anim, opt.type&&\\\'popui__\\\'+opt.type, opt.drawer&&\\\'popui__drawer-\\\'+opt.drawer, opt.xclose&&\\\'rlayer-closable\\\', opt.tipArrow)} style={{...opt.layerStyle}}> { opt.title && <div className=\\\'rlayer__wrap-tit\\\' dangerouslySetInnerHTML={{__html: opt.title}}></div> } { opt.type == \\\'toast\\\' && opt.icon ? <div className={domUtils.classNames(\\\'rlayer__toast-icon\\\', \\\'rlayer__toast-\\\'+opt.icon)} dangerouslySetInnerHTML={{__html: opt.toastIcon[opt.icon]}}></div> : null } <div className=\\\'rlayer__wrap-cntbox\\\'> { opt.content ? <> { opt.type == \\\'iframe\\\' ? ( <iframe scrolling=\\\'auto\\\' allowtransparency=\\\'true\\\' frameBorder=\\\'0\\\' src=https://www.haocat.cn/aimg/bk41/mmr0enwi9xfum8jsywe6w511b8kvd5lx4xwbxr36-0.webp ) : (opt.type == \\\'message\\\' || opt.type == \\\'notify\\\' || opt.type == \\\'popover\\\') ? ( <div className=\\\'rlayer__wrap-cnt\\\'> { opt.icon && <i className={domUtils.classNames(\\\'rlayer-msg__icon\\\', opt.icon)} dangerouslySetInnerHTML={{__html: opt.messageIcon[opt.icon]}}></i> } <div className=\\\'rlayer-msg__group\\\'> { opt.title && <div className=\\\'rlayer-msg__title\\\' dangerouslySetInnerHTML={{__html: opt.title}}></div> } { typeof opt.content == \\\'string\\\' ? <div className=\\\'rlayer-msg__content\\\' dangerouslySetInnerHTML={{__html: opt.content}}></div> : <div className=\\\'rlayer-msg__content\\\'>{opt.content}</div> } </div> </div> ) : ( typeof opt.content == \\\'string\\\' ? (<div className=\\\'rlayer__wrap-cnt\\\' dangerouslySetInnerHTML={{__html: opt.content}}></div>) : opt.content ) } </> : null } </div> {} { opt.btns && <div className=\\\'rlayer__wrap-btns\\\'> { opt.btns.map((btn, index) => { return <span className={domUtils.classNames(\\\'btn\\\', {\\\'btn-disabled\\\': btn.disabled})} key={index} style={{...btn.style}} dangerouslySetInnerHTML={{__html: btn.text}} onClick={this.btnClicked.bind(this, index)}></span> }) } </div> } { opt.xclose && <span className={domUtils.classNames(\\\'rlayer__xclose\\\', !opt.maximize&&opt.xposition)} style={{color: opt.xcolor}} onClick={this.close}></span> } { opt.maximize && <span className=\\\'rlayer__maximize\\\' onClick={this.maximizeClicked}></span> } { opt.resize && <span className=\\\'rlayer__resize\\\'></span> } </div> {} <div className=\\\'rlayer__dragfix\\\'></div> </div> </> ) }}
默认参数配置
class RLayerComponent extends React.Component { static defaultProps = { // 参数 id: \\\'\\\', // {string} 控制弹层唯一标识,相同id共享一个实例 title: \\\'\\\', // {string} 标题 content: \\\'\\\', // {string|element} 内容(支持字符串或组件) type: \\\'\\\', // {string} 弹框类型 layerStyle: \\\'\\\', // {object} 自定义弹框样式 icon: \\\'\\\', // {string} Toast图标(loading|success|fail) shade: true, // {bool} 是否显示遮罩层 shadeClose: true, // {bool} 是否点击遮罩层关闭弹框 lockScroll: true, // {bool} 是否弹框显示时将body滚动锁定 opacity: \\\'\\\', // {number|string} 遮罩层透明度 xclose: true, // {bool} 是否显示关闭图标 xposition: \\\'right\\\', // {string} 关闭图标位置(top|right|bottom|left) xcolor: \\\'#333\\\', // {string} 关闭图标颜色 anim: \\\'scaleIn\\\', // {string} 弹框动画 position: \\\'auto\\\', // {string|array} 弹框位置 drawer: \\\'\\\', // {string} 抽屉弹框(top|right|bottom|left) follow: null, // {string|array} 跟随定位弹框 time: 0, // {number} 弹框自动关闭秒数(1|2|3...) zIndex: 8090, // {number} 弹框层叠 topmost: false, // {bool} 是否置顶当前弹框 area: \\\'auto\\\', // {string|array} 弹框宽高 maxWidth: 375, // {number} 弹框最大宽度(只有当area:\\\'auto\\\'时设定才有效) maximize: false, // {bool} 是否显示最大化按钮 fullscreen: false, // {bool} 是否全屏弹框 fixed: true, // {bool} 是否固定弹框 drag: \\\'.rlayer__wrap-tit\\\', // {string|bool} 拖拽元素 dragOut: false, // {bool} 是否允许拖拽到浏览器外 lockAxis: null, // {string} 限制拖拽方向可选: v 垂直、h 水平,默认不限制 resize: false, // {bool} 是否允许拉伸弹框 btns: null, // {array} 弹框按钮(参数:text|style|disabled|click) // 事件 success: null, // {func} 层弹出后回调 end: null, // {func} 层销毁后回调 }// ...}
import React from \\\'react\\\'import ReactDOM from \\\'react-dom\\\'// 引入操作类import domUtils from \\\'./utils/dom\\\'let $index = 0, $lockCount = 0, $timer = {}class RLayerComponent extends React.Component { constructor(props) { super(props) this.state = { // ... } this.closeTimer = null } componentDidMount() { window.addEventListener(\\\'resize\\\', this.autopos, false) } componentWillUnmount() { window.removeEventListener(\\\'resize\\\', this.autopos, false) clearTimeout(this.closeTimer) } open = (options) => { options.id = options.id || `rlayer-${domUtils.generateId()}` this.setState({ ...this.props, ...options, opened: true, }, () => { const { success } = this.state typeof success === \\\'function\\\' && success.call(this) this.auto() this.callback() }) } close = () => { const { opened, time, end, remove, rlayerOpts, action } = this.state if(!opened) return this.setState({ closeCls: true }) clearTimeout(this.closeTimer) this.closeTimer = setTimeout(() => { this.setState({ closeCls: false, opened: false, }) if(rlayerOpts.lockScroll) { $lockCount-- if(!$lockCount) { document.body.style.paddingRight = \\\'\\\' document.body.classList.remove(\\\'rc-overflow-hidden\\\') } } if(time) { $index-- } if(action == \\\'update\\\') { document.body.style.paddingRight = \\\'\\\' document.body.classList.remove(\\\'rc-overflow-hidden\\\') } rlayerOpts.isBodyOverflow && (document.body.style.overflow = \\\'\\\') remove() typeof end === \\\'function\\\' && end.call(this) }, 200); } // 弹框位置 auto = () => { // ... } autopos = () => { const { opened, id, fixed, follow, position } = this.state if(!opened) return let oL, oT let dom = document.querySelector(\\\'#\\\' + id) let rlayero = dom.querySelector(\\\'.rlayer__wrap\\\') if(!fixed || follow) { rlayero.style.position = \\\'absolute\\\' } let area = [domUtils.client(\\\'width\\\'), domUtils.client(\\\'height\\\'), rlayero.offsetWidth, rlayero.offsetHeight] oL = (area[0] - area[2]) / 2 oT = (area[1] - area[3]) / 2 if(follow) { this.offset() } else { typeof position === \\\'object\\\' ? ( oL = parseFloat(position[0]) || 0, oT = parseFloat(position[1]) || 0 ) : ( position == \\\'t\\\' ? oT = 0 : position == \\\'r\\\' ? oL = area[0] - area[2] : position == \\\'b\\\' ? oT = area[1] - area[3] : position == \\\'l\\\' ? oL = 0 : position == \\\'lt\\\' ? (oL = 0, oT = 0) : position == \\\'rt\\\' ? (oL = area[0] - area[2], oT = 0) : position == \\\'lb\\\' ? (oL = 0, oT = area[1] - area[3]) : position == \\\'rb\\\' ? (oL = area[0] - area[2], oT = area[1] - area[3]) : null ) rlayero.style.left = parseFloat(fixed ? oL : domUtils.scroll(\\\'left\\\') + oL) + \\\'px\\\' rlayero.style.top = parseFloat(fixed ? oT : domUtils.scroll(\\\'top\\\') + oT) + \\\'px\\\' } } // 跟随元素定位 offset = () => { const { id, follow } = this.state let oW, oH, pS let dom = document.querySelector(\\\'#\\\' + id) let rlayero = dom.querySelector(\\\'.rlayer__wrap\\\') oW = rlayero.offsetWidth oH = rlayero.offsetHeight pS = domUtils.getFollowRect(follow, oW, oH) this.setState({ tipArrow: pS[2] }) rlayero.style.left = pS[0] + \\\'px\\\' rlayero.style.top = pS[1] + \\\'px\\\' } // 最大化弹框 full = () => { // ... } // 恢复弹框 restore = () => { const { id, maximize, rlayerOpts } = this.state let dom = document.querySelector(\\\'#\\\' + id) let rlayero = dom.querySelector(\\\'.rlayer__wrap\\\') let otit = dom.querySelector(\\\'.rlayer__wrap-tit\\\') let ocnt = dom.querySelector(\\\'.rlayer__wrap-cntbox\\\') let obtn = dom.querySelector(\\\'.rlayer__wrap-btns\\\') let omax = dom.querySelector(\\\'.rlayer__maximize\\\') let t = otit ? otit.offsetHeight : 0 let b = obtn ? obtn.offsetHeight : 0 if(!rlayerOpts.lockScroll) { rlayerOpts.isBodyOverflow = false this.setState({rlayerOpts}) document.body.style.overflow = \\\'\\\' } maximize && omax.classList.remove(\\\'maximized\\\') rlayero.style.left = parseFloat(rlayerOpts.rect[0]) + \\\'px\\\' rlayero.style.top = parseFloat(rlayerOpts.rect[1]) + \\\'px\\\' rlayero.style.width = parseFloat(rlayerOpts.rect[2]) + \\\'px\\\' rlayero.style.height = parseFloat(rlayerOpts.rect[3]) + \\\'px\\\' ocnt.style.height = parseFloat(rlayerOpts.rect[3] - t - b) + \\\'px\\\' } // 拖拽|缩放弹框 move = () => { // ... } // 事件处理 callback = () => { const { time } = this.state // 倒计时关闭弹框 if(time) { $index++ // 防止重复计数 if($timer[$index] != null) clearTimeout($timer[$index]) $timer[$index] = setTimeout(() => { this.close() }, parseInt(time) * 1000); } } // 点击最大化按钮 maximizeClicked = (e) => { let o = e.target if(o.classList.contains(\\\'maximized\\\')) { // 恢复 this.restore() } else { // 最大化 this.full() } } // 点击遮罩层 shadeClicked = () => { if(this.state.shadeClose) { this.close() } } // 按钮事件 btnClicked = (index, e) => { let btn = this.state.btns[index] if(!btn.disabled) { typeof btn.click === \\\'function\\\' && btn.click(e) } }}
好了,以上就是基于React.js实现PC端弹出框组件,希望对大家有所帮助哈!