preface

Picker picker is familiar to mobile developers. You do wechat small program development has its own picker component, do public number development can use weUI to provide the picker component. In addition, there are numerous open source picker components available for use. But if you were asked to implement one yourself, how would you do it? I spent some time writing a simple demo, hoping to provide an idea for those who want to implement a picker picker themselves but have no idea how to do it.

implementation

CSS

* {
    margin: 0;
    padding: 0;
}
.btn {
    height: 32px;
    padding: 0 15px;
    text-align: center;
    font-size: 14px;
    line-height: 32px;
    color: #FFF;
    border: none;
    background: #1890ff;
    border-radius: 2px;
    cursor: pointer;
}
.mask {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 999;
    background: rgba(0.0.0.6.);
    animation: fadeIn .3s forwards;
}
.slide-box {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 15px;
    border-radius: 10px 10px 0 0;
    background: #FFF;
    user-select: none;
}
.fade-in {
    animation: fadeIn .3s forwards;
}
@keyframes fadeIn {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}
.fade-out {
    animation: fadeOut .3s forwards;
}
@keyframes fadeOut {
    from {
        opacity: 10;
    }
    to {
        opacity: 0;
    }
}
.slide-up {
    animation: slideUp .3s forwards;
}
@keyframes slideUp {
    from {
        transform: translate3d(0.100%, 0);
    }
    to {
        transform: translate3d(0.0.0);
    }
}

.slide-down {
    animation: slideDown .3s forwards;
}
@keyframes slideDown {
    from {
        transform: translate3d(0.0.0);
    }
    to {
        transform: translate3d(0.100%, 0);
    }
}
h4 {
    height: 24px;
    margin-bottom: 16px;
    font-size: 16px;
    line-height: 24px;
    text-align: center;
}
.picker-group {
    display: flex;
}
.picker-column {
    position: relative;
    flex: 1;
    height: 200px;
    margin: 0 auto;
    overflow: hidden;
    touch-action: none;
}
.picker-column::before {
    content: ' ';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 1;
    height: 79px;
    border-bottom: 1px solid #ebebeb;
    background: linear-gradient(to bottom, rgba(255.255.255.9.), rgba(255.255.255.6.));
}
.picker-column::after {
    content: ' ';
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1;
    height: 79px;
    border-top: 1px solid #ebebeb;
    background: linear-gradient(to bottom, rgba(255.255.255.6.), rgba(255.255.255.9.));
}
li {
    list-style: none;
    font-size: 14px;
    line-height: 40px;
    text-align: center;
}
.btn-sure {
    display: block;
    margin: 15px auto 0;
}
Copy the code

HTML

<button class="btn btn-open" type="button"</button><div hidden class="mask">
    <div class="slide-box">
        <h4>Time selector</h4>
        <div class="picker-group">
            <div class="picker-column">
                <ul class="picker-content"></ul>
            </div>
            <div class="picker-column">
                <ul class="picker-content"></ul>
            </div>
        </div>
        <button class="btn btn-sure" type="button">determine</button>
    </div>
</div>
Copy the code

js

class Picker {
    constructor(options) {
        this.options = Object.assign({}, options);
        this.isPointerdown = false;
        this.itemHeight = 40; // List item height
        this.maxY = this.itemHeight * 2;
        this.minY = this.itemHeight * (3 - this.options.list.length);
        this.lastY = 0;
        this.diffY = 0;
        this.translateY = 0; // The current position
        this.friction = 0.95; // Friction coefficient
        this.distanceY = 0; // Slide distance
        this.result = this.options.list[0];
        this.render();
        this.bindEventListener();
    }
    render() {
        let html = ' ';
        for (const item of this.options.list) {
            html += '<li>' + item + '</li>';
        }
        this.options.pickerContent.innerHTML = html;
        this.options.pickerContent.style.transform = 'translate3d(0px, ' + this.maxY + 'px, 0px)';
    }
    handlePointerdown(e) {
        // If the mouse clicks, only the left button responds
        if (e.pointerType === 'mouse'&& e.button ! = =0) {
            return;
        }
        this.options.pickerColumn.setPointerCapture(e.pointerId);
        this.isPointerdown = true;
        this.lastY = e.clientY;
        this.diffY = 0;
        this.distanceY = 0;
        this.getTransform();
        this.options.pickerContent.style.transform = 'translate3d(0px, ' + this.translateY + 'px, 0px)';
        this.options.pickerContent.style.transition = 'none';
    }
    handlePointermove(e) {
        if (this.isPointerdown) {
            this.diffY = e.clientY - this.lastY;
            this.translateY += this.diffY;
            this.lastY = e.clientY;
            this.options.pickerContent.style.transform = 'translate3d(0px, ' + this.translateY + 'px, 0px)'; }}handlePointerup(e) {
        if (this.isPointerdown) {
            this.isPointerdown = false;
            this.getTranslateY();
            // The sliding distance is proportional to the time span and the minimum time is 300ms
            const duration = Math.max(Math.abs(this.distanceY) * 1.5.300);
            this.options.pickerContent.style.transition = 'transform ' + duration + 'ms ease';
            this.options.pickerContent.style.transform = 'translate3d(0px, ' + this.translateY + 'px, 0px)'; }}handlePointercancel(e) {
        if (this.isPointerdown) {
            this.isPointerdown = false; }}bindEventListener() {
        this.handlePointerdown = this.handlePointerdown.bind(this);
        this.handlePointermove = this.handlePointermove.bind(this);
        this.handlePointerup = this.handlePointerup.bind(this);
        this.handlePointercancel = this.handlePointercancel.bind(this);
        this.options.pickerColumn.addEventListener('pointerdown'.this.handlePointerdown);
        this.options.pickerColumn.addEventListener('pointermove'.this.handlePointermove);
        this.options.pickerColumn.addEventListener('pointerup'.this.handlePointerup);
        this.options.pickerColumn.addEventListener('pointercancel'.this.handlePointercancel);
    }
    getTransform() {
        const transform = window.getComputedStyle(this.options.pickerContent).getPropertyValue('transform');
        this.translateY = parseFloat(transform.split(', ') [5]);
    }
    getTranslateY() {
        let speed = this.diffY;
        while (Math.abs(speed) > 1) {
            speed *= this.friction;
            this.distanceY += speed;
        }
        // boundary judgment
        let y = this.translateY + this.distanceY;
        if (y > this.maxY) {
            this.translateY = this.maxY;
            this.distanceY = this.maxY - this.translateY;
        } else if (y < this.minY) {
            this.translateY = this.minY;
            this.distanceY = this.minY - this.translateY;
        } else {
            this.translateY = y;
        }
        // Calculates the stop position to be an integer multiple of itemHeight
        let i = Math.round(this.translateY / this.itemHeight);
        this.translateY = i * this.itemHeight;
        this.result = this.options.list[2 - this.translateY / this.itemHeight]; }}// Call mode
function createList(start, end) {
    const list = [];
    for (i = start; i < end; i++) {
        list[i] = i < 10 ? '0' + i : ' ' + i;
    }
    return list;
}
const hours = createList(0.24), minutes = createList(0.60);
const pickerColumns = document.querySelectorAll('.picker-column');
const pickerContents = document.querySelectorAll('.picker-content');
const hourPicker = new Picker({
    pickerColumn: pickerColumns[0].pickerContent: pickerContents[0].list: hours
});
const minutePicker = new Picker({
    pickerColumn: pickerColumns[1].pickerContent: pickerContents[1].list: minutes
});
Copy the code

Demo: jsdemo. Codeman. Top/HTML/picker…

conclusion

At this point, a simple picker selector is implemented. If you want to realize the function of dynamically loading the data in the next column according to the previous column (such as the provincial selector), you also need to implement it on the basis of this code.