如何用react封装一款ColorPicker 颜色拾色器组件

1,201 阅读2分钟

这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

TIP 👉 呜呼!楚虽三户能亡秦,岂有堂堂中国空无人!____陆游《金错刀行》

前言

Web Component是前端界一直非常热衷的一个领域,用第三方组件化的框架去实现的话,你需要依赖框架本身很多东西,很多时候我们只是简单的几个组件,不是很大,也不是很多,所以为了保证组件的`轻量,简单`,其实这个时候我们并不想采用第三方的框架。

ColorPicker 颜色拾色器

import

import ColorPicker from '@/components/ColorPicker/ColorPicker';

Props

1. value
  • 类型:string
  • 默认值:"#999"
  • 说明:颜色值
2. onChange
  • 类型:func (必填)
  • 默认值:无
  • 说明:拾色器中点击"确定"按钮后的回调函数,入参:
    • {string} color 选中的颜色值
3. className
  • 类型:string | array | object(key: 样式名,value: bool)
  • 默认值:无
  • 说明:最外层元素样式
<ColorPicker className = {'my-class'}></ColorPicker>
<ColorPicker className = {['my-class1', 'my-class2']}></ColorPicker>
<ColorPicker className = {{'my-class1': true}}></ColorPicker>

实现ColorPicker.js

/**
 *  颜色拾色器组件
 */
import React from 'react';
import PropTypes from 'prop-types';
import { SketchPicker } from 'react-color';
import Button from "@/components/Button/Button.js";
// 为保证测试用例运行,不使用webpack内置utils模块,单独引入
import utils from '../../js/common/utils/index.js';
import './colorPicker.scss';

class ColorPicker extends React.Component {
    // 入参类型检查
    static propTypes = {
        // 颜色值,如:'#666666'
        value: PropTypes.string,
        /**
         * 拾色器中点击"确定"按钮后的回调函数
         * @param {string} color 选中的颜色值
         */
        onChange: PropTypes.func.isRequired,
        // 最外层元素样式
        className: PropTypes.oneOfType([
            PropTypes.string,
            PropTypes.array,
            PropTypes.objectOf(PropTypes.bool)
        ])
    }

    // 入参默认值
    static defaultProps = {
        value: '#999'
    }

    constructor(props) {
        super(props);
        this.state = {
            color: props.value || '#999',
            // 是否显示拾色器浮层
            isShow : false,
            // 是否开启拾色器浮层动效
            animate: false
        }
    }

    componentDidUpdate(prevProps, prevState) {
        if (this.props.value && prevProps.value !== this.props.value) {
            this.setState({ color: this.props.value})
        }
    }

    // 切换显示状态
    toogleShow (isShow) {
        if (isShow === undefined) {
            isShow = !this.state.isShow
        }

        this.setState({ isShow, animate: true});

        if (isShow) {
            setTimeout(() => this.autoSetPickerPosition(), 350)
        }

    };

    // 为保证拾色器全部显示在窗口内,自动设置拾色器浮层的位置
    autoSetPickerPosition () {
        const colorPicker = this.refs.colorPicker;
        const pickerBox = this.refs.pickerBox;

        const winWidth = window.document.body.clientWidth;
        const winHeight = window.document.body.clientHeight;

        const colorPickerRect = colorPicker.getBoundingClientRect();
        const colorPickerX = colorPickerRect.x;
        const colorPickerY = colorPickerRect.y;

        const pickerBoxRect = pickerBox.getBoundingClientRect();
        const pickerBoxWidth = pickerBoxRect.width;
        const pickerBoxheight = pickerBoxRect.height;

        // console.log(`winWidth=${winWidth}, winHeight=${winHeight}`)
        // console.log(`colorPickerX=${colorPickerX}, colorPickerY=${colorPickerY}`)
        // console.log(`pickerBoxWidth=${pickerBoxWidth}, pickerBoxheight=${pickerBoxheight}`)
        if (colorPickerY + pickerBoxheight > winHeight) {
            pickerBox.style.top = `${winHeight - pickerBoxheight - colorPickerY - 5}px`;
        }
        if (colorPickerX + 20 + 5 + pickerBoxWidth > winWidth) {
            pickerBox.style.left = 'auto';
            pickerBox.style.right = `${20 + 5}px`;
        }
    }

    // 处理颜色改变事件
    handleChange = (value)=>{
        this.setState({ color: value.hex });
    };

    // 处理点击"确定"事件
    handleOK = ()=>{
        this.toogleShow(false);
        this.props.onChange && this.props.onChange(this.state.color);
    };

    /**
     * 选择框最外层需要的class
     * @returns {string}
     */
    getClassName(){
        return utils.clasx(this.props.className, {
            'color-picker': true
        });
    }

    /**
     * 拾色器浮层的样式
     * @return {string}
     */
    getPickerBoxClassName () {
        return utils.clasx({
            'picker-box': true,
            'picker-box-show': this.state.isShow,
            'picker-box-hide': !this.state.isShow,
            'show-animate': this.state.isShow && this.state.animate,
            'hide-animate': !this.state.isShow && this.state.animate
        });
    }

    render() {
        return (
            <div className={this.getClassName()} ref="colorPicker">
                <div
                    className="color-block"
                    style={ { background: this.props.value } }
                    onClick={ () => this.toogleShow() }
                />
                <div className={ this.getPickerBoxClassName() } ref="pickerBox">
                    <SketchPicker
                        disableAlpha={true}
                        presetColors={[]}
                        color={this.state.color}
                        onChange={this.handleChange} />
                    <div className="buttons-bar">
                        <Button onClick={() => this.toogleShow(false)}>取消</Button>
                        <Button type="primary" onClick={this.handleOK}>确定</Button>
                    </div>
                </div>
            </div>
        );
    }
}

export default ColorPicker;

样式这块就先不放了

「欢迎在评论区讨论」

希望看完的朋友可以给个赞,鼓励一下