js實現彈出框的插件「js彈出框自定義」

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

超贊 React.js 桌面端自定義彈窗組件RLayer

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

超贊 React.js 桌面端自定義彈窗組件RLayer

看名稱就能聯想到前端界有名的彈窗layer.js,其實在設計開發之初就有借鑒layer插件實現思想。

超贊 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

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實現PC端彈出框組件,希望對大家有所幫助哈!

原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/203576.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
投稿專員的頭像投稿專員
上一篇 2024-12-07 12:09
下一篇 2024-12-07 12:09

相關推薦

發表回復

登錄後才能評論