js弹出框自定义(js实现弹出框的插件)

今天给小伙伴们分享一个全新开发的React自定义对话框最近RLayer。

超赞 React.js 桌面端自定义弹窗组件RLayer超赞 React.js 桌面端自定义弹窗组件RLayer

rlayer 基于react.js开发的PC桌面端交互式弹出框组件。融合了Dialog、Message、Notification、ActionSheet、Toast、Popover、Popconfirm等多种功能。

超赞 React.js 桌面端自定义弹窗组件RLayer超赞 React.js 桌面端自定义弹窗组件RLayer

看名称就能联想到前端界有名的弹窗layer.js,其实在设计开发之初就有借鉴layer插件实现思想。

超赞 React.js 桌面端自定义弹窗组件RLayer超赞 React.js 桌面端自定义弹窗组件RLayer

功能

  • 提供函数式调用方法 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()
                }
            }
        ]
    })
}
超赞 React.js 桌面端自定义弹窗组件RLayer超赞 React.js 桌面端自定义弹窗组件RLayer
超赞 React.js 桌面端自定义弹窗组件RLayer超赞 React.js 桌面端自定义弹窗组件RLayer
超赞 React.js 桌面端自定义弹窗组件RLayer超赞 React.js 桌面端自定义弹窗组件RLayer
超赞 React.js 桌面端自定义弹窗组件RLayer超赞 React.js 桌面端自定义弹窗组件RLayer
超赞 React.js 桌面端自定义弹窗组件RLayer超赞 React.js 桌面端自定义弹窗组件RLayer
超赞 React.js 桌面端自定义弹窗组件RLayer超赞 React.js 桌面端自定义弹窗组件RLayer

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 桌面端自定义弹窗组件RLayer超赞 React.js 桌面端自定义弹窗组件RLayer
超赞 React.js 桌面端自定义弹窗组件RLayer超赞 React.js 桌面端自定义弹窗组件RLayer

好了,以上就是基于React.js实现PC端弹出框组件,希望对大家有所帮助哈!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请发送邮件至624739273@qq.com举报,一经查实,本站将立刻删除。
Like (0)
柳的头像

相关推荐

发表回复

Please Login to Comment
微信
微信
SHARE
TOP
要想花得少,就用购宝。话费电费9折起,官方公众号:购宝