今天給小夥伴們分享一個全新開發的React自定義對話框最近RLayer。

rlayer 基於react.js開發的PC桌面端互動式彈出框組件。融合了Dialog、Message、Notification、ActionSheet、Toast、Popover、Popconfirm等多種功能。

看名稱就能聯想到前端界有名的彈窗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組件。
// 引入彈框組件RLayer
import rlayer from './components/rlayer'快速使用
引入後即可通過函數rlayer({…})來調用即可。
支持超過30+個參數自由搭配,快速實現定製化的各種效果。
// msg消息
const showMsg = () => {
rlayer({
content: "這是一條msg消息提示",
shadeClose: false,
xclose: false,
time: 2
})
}
// confirm詢問框
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={opt.content}></iframe>
)
:
(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>
{/* btns */}
{ 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} 層銷毀後回調
}
// ...
}/**
* ReactJs|Next.js彈出框組件RLayer
*/
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端彈出框組件,希望對大家有所幫助哈!
原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/203576.html
微信掃一掃
支付寶掃一掃