手撸一些提示类组件(例如Modal组件)

1,802 阅读2分钟

一、前言

手撸组件系列它又来了,也是今年的最后一篇文章,这篇文章的思路适用所有的提示类组件,比如Modal组件、Toast组件、Message组件等等。这次我们就以Modal组件为例,废话不多说,直接进入正题。

二、实现效果

modal1.png

三、实现思路

我们都知道Modal是提示类组件的一种,这种提示类组件都有一个共同的特点就是他们都悬浮在其他元素之上,离用户的距离最近

3.1、组件结构

react代码如下:

    <!-- modal组件容器 -->
    <div className='yonyou-modal-box'>
            <!-- 遮罩层 -->
            <div className='yonyou-modal-mask'></div>
            <!-- 遮罩层上方的元素 -->
            <div className='yonyou-modal-wrap'>
                <div className='yonyou-dialog'>
                    <!-- 提示框标题 -->
                    <div className='yonyou-title'>
                        <div className='yonyou-title-name'>Basic Modal</div>
                        <div className='yonyou-title-icon' onClick={this.clickIcon}>
                            <svg viewBox="64 64 896 896" focusable="false" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true">
                                <path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path>
                            </svg>
                        </div>
                    </div>
                </div>
            </div>
        </div>

样式如下:

.yonyou-modal-mask {
    position: fixed;
    top: 0px;
    bottom: 0px;
    left: 0px;
    right: 0px;
    z-index: 500;
    background-color: rgba(0, 0, 0, 0.45);
}
.yonyou-modal-wrap {
    position: fixed;
    top: 0px;
    bottom: 0px;
    left: 0px;
    right: 0px;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 500;
}
.yonyou-dialog {
    width: 520px;
    height: 200px;
    box-sizing: border-box;
    border-radius: 8px;
    box-shadow: 0 6px 16px 0 rgb(0 0 0 / 8%), 0 3px 6px -4px rgb(0 0 0 / 12%), 0 9px 28px 8px rgb(0 0 0 / 5%);
    background-color: #ffffff;
    padding: 20px 24px;
}
.yonyou-title {
    width: 100%;
    height: 30px;
    border-bottom: 1px solid #dcdcdc;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

现在大家如果访问文件,应该是可以看到咱们要实现的效果,可是,这就完了吗?当然没有,我们还需要考虑这个组件在DOM中存放的位置。

3.2、组件在DOM中的位置

对于这类组件的位置,业界上的处理出奇的一致,都是直接放在body下,成为body标签的子元素,而非孙元素或者增子曾孙元素,原因非常简单,这么做的话,样式上不会受其他dom结构影响。

因为我们这里使用的是react,所以我们可以使用 ReactDOM.createPortal , 这个API可以将我们的组件插入到任何位置,代码如下:

class Modal extends React.Component {
    render (){
        const modalFrame = <div className='yonyou-modal-box'>
            <div className='yonyou-modal-mask'></div>
            <div className='yonyou-modal-wrap'>
                <div className='yonyou-dialog'>
                    <div className='yonyou-title'>
                        <div className='yonyou-title-name'>Basic Modal</div>
                        <div className='yonyou-title-icon'>
                            <svg viewBox="64 64 896 896" focusable="false" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true">
                                <path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path>
                            </svg>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        return ReactDOM.createPortal(modalFrame, document.getElementsByTagName('body')[0])
    }
}

3.3、确定组件的显隐性

这里我们将显隐性的控制权交给用户,具体操作如下:

  • 定义一个visible属性,决定组件是否显示
  • 定义一个closeModal函数,让用户自己决定什么时候关闭组件

完整代码如下:

import React from 'react';
import ReactDOM from 'react-dom';
import './modal.css';

export default class Modal extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            visible: this.props.visible
        };
    }
    // 组件初始化挂载
    componentDidMount(){
    }
    click = (event) => {
        const { closeModal } = this.props;
        closeModal && closeModal();
    }
    render (){
        const { visible } = this.props;
        const modalFrame = <div className='yonyou-modal-box'>
            <div className='yonyou-modal-mask'></div>
            <div className='yonyou-modal-wrap' onClick={this.click}>
                <div className='yonyou-dialog'>
                    <div className='yonyou-title'>
                        <div className='yonyou-title-name'>Basic Modal</div>
                        <div className='yonyou-title-icon' onClick={this.clickIcon}>
                            <svg viewBox="64 64 896 896" focusable="false" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true">
                                <path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path>
                            </svg>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        return ReactDOM.createPortal(visible ? modalFrame : null, document.getElementsByTagName('body')[0])
    }
}

四、最后

好啦,2022的最后一篇文章到这里就结束啦,我们明年再见。