UNPKG

jab-image-editor

Version:
363 lines (306 loc) 11.6 kB
import snippet from 'tui-code-snippet'; import {toInteger, clamp} from '../../util'; import {keyCodes} from '../../consts'; const INPUT_FILTER_REGEXP = /(-?)([0-9]*)[^0-9]*([0-9]*)/g; /** * Range control class * @class * @ignore */ class Range { /** * @constructor * @extends {View} * @param {Object} rangeElements - Html resources for creating sliders * @param {HTMLElement} rangeElements.slider - b * @param {HTMLElement} [rangeElements.input] - c * @param {Object} options - Slider make options * @param {number} options.min - min value * @param {number} options.max - max value * @param {number} options.value - default value * @param {number} [options.useDecimal] - Decimal point processing. * @param {number} [options.realTimeEvent] - Reflect live events. */ constructor(rangeElements, options = {}) { this._value = options.value || 0; this.rangeElement = rangeElements.slider; this.rangeInputElement = rangeElements.input; this._drawRangeElement(); this.rangeWidth = this._getRangeWidth(); this._min = options.min || 0; this._max = options.max || 100; this._useDecimal = options.useDecimal; this._absMax = (this._min * -1) + this._max; this.realTimeEvent = options.realTimeEvent || false; this.eventHandler = { startChangingSlide: this._startChangingSlide.bind(this), stopChangingSlide: this._stopChangingSlide.bind(this), changeSlide: this._changeSlide.bind(this), changeSlideFinally: this._changeSlideFinally.bind(this), changeInput: this._changeValueWithInput.bind(this, false), changeInputFinally: this._changeValueWithInput.bind(this, true), changeInputWithArrow: this._changeValueWithInputKeyEvent.bind(this) }; this._addClickEvent(); this._addDragEvent(); this._addInputEvent(); this.value = options.value; this.trigger('change'); } /** * Destroys the instance. */ destroy() { this._removeClickEvent(); this._removeDragEvent(); this._removeInputEvent(); this.rangeElement.innerHTML = ''; snippet.forEach(this, (value, key) => { this[key] = null; }); } /** * Set range max value and re position cursor * @param {number} maxValue - max value */ set max(maxValue) { this._max = maxValue; this._absMax = (this._min * -1) + this._max; this.value = this._value; } get max() { return this._max; } /** * Get range value * @returns {Number} range value */ get value() { return this._value; } /** * Set range value * @param {Number} value range value * @param {Boolean} fire whether fire custom event or not */ set value(value) { value = this._useDecimal ? value : toInteger(value); const absValue = value - this._min; let leftPosition = (absValue * this.rangeWidth) / this._absMax; if (this.rangeWidth < leftPosition) { leftPosition = this.rangeWidth; } this.pointer.style.left = `${leftPosition}px`; this.subbar.style.right = `${this.rangeWidth - leftPosition}px`; this._value = value; if (this.rangeInputElement) { this.rangeInputElement.value = value; } } /** * event tirigger * @param {string} type - type */ trigger(type) { this.fire(type, this._value); } /** * Calculate slider width * @returns {number} - slider width */ _getRangeWidth() { const getElementWidth = element => toInteger(window.getComputedStyle(element, null).width); return getElementWidth(this.rangeElement) - getElementWidth(this.pointer); } /** * Make range element * @private */ _drawRangeElement() { this.rangeElement.classList.add('tui-image-editor-range'); this.bar = document.createElement('div'); this.bar.className = 'tui-image-editor-virtual-range-bar'; this.subbar = document.createElement('div'); this.subbar.className = 'tui-image-editor-virtual-range-subbar'; this.pointer = document.createElement('div'); this.pointer.className = 'tui-image-editor-virtual-range-pointer'; this.bar.appendChild(this.subbar); this.bar.appendChild(this.pointer); this.rangeElement.appendChild(this.bar); } /** * Add range input editing event * @private */ _addInputEvent() { if (this.rangeInputElement) { this.rangeInputElement.addEventListener('keydown', this.eventHandler.changeInputWithArrow); this.rangeInputElement.addEventListener('keyup', this.eventHandler.changeInput); this.rangeInputElement.addEventListener('blur', this.eventHandler.changeInputFinally); } } /** * Remove range input editing event * @private */ _removeInputEvent() { if (this.rangeInputElement) { this.rangeInputElement.removeEventListener('keydown', this.eventHandler.changeInputWithArrow); this.rangeInputElement.removeEventListener('keyup', this.eventHandler.changeInput); this.rangeInputElement.removeEventListener('blur', this.eventHandler.changeInputFinally); } } /** * change angle event * @param {object} event - key event * @private */ _changeValueWithInputKeyEvent(event) { const {keyCode, target} = event; if ([keyCodes.ARROW_UP, keyCodes.ARROW_DOWN].indexOf(keyCode) < 0) { return; } let value = Number(target.value); value = this._valueUpDownForKeyEvent(value, keyCode); const unChanged = value < this._min || value > this._max; if (!unChanged) { const clampValue = clamp(value, this._min, this.max); this.value = clampValue; this.fire('change', clampValue, false); } } /** * value up down for input * @param {number} value - original value number * @param {number} keyCode - input event key code * @returns {number} value - changed value * @private */ _valueUpDownForKeyEvent(value, keyCode) { const step = this._useDecimal ? 0.1 : 1; if (keyCode === keyCodes.ARROW_UP) { value += step; } else if (keyCode === keyCodes.ARROW_DOWN) { value -= step; } return value; } /** * change angle event * @param {boolean} isLast - Is last change * @param {object} event - key event * @private */ _changeValueWithInput(isLast, event) { const {keyCode, target} = event; if ([keyCodes.ARROW_UP, keyCodes.ARROW_DOWN].indexOf(keyCode) >= 0) { return; } const stringValue = this._filterForInputText(target.value); const waitForChange = !stringValue || isNaN(stringValue); target.value = stringValue; if (!waitForChange) { let value = this._useDecimal ? Number(stringValue) : toInteger(stringValue); value = clamp(value, this._min, this.max); this.value = value; this.fire('change', value, isLast); } } /** * Add Range click event * @private */ _addClickEvent() { this.rangeElement.addEventListener('click', this.eventHandler.changeSlideFinally); } /** * Remove Range click event * @private */ _removeClickEvent() { this.rangeElement.removeEventListener('click', this.eventHandler.changeSlideFinally); } /** * Add Range drag event * @private */ _addDragEvent() { this.pointer.addEventListener('mousedown', this.eventHandler.startChangingSlide); this.pointer.addEventListener('touchstart', this.eventHandler.startChangingSlide); } /** * Remove Range drag event * @private */ _removeDragEvent() { this.pointer.removeEventListener('mousedown', this.eventHandler.startChangingSlide); this.pointer.removeEventListener('touchstart', this.eventHandler.startChangingSlide); } /** * change angle event * @param {object} event - change event * @private */ _changeSlide(event) { const changePosition = event.screenX ? event.screenX : event.changedTouches[0].screenX; const diffPosition = changePosition - this.firstPosition; let touchPx = this.firstLeft + diffPosition; touchPx = touchPx > this.rangeWidth ? this.rangeWidth : touchPx; touchPx = touchPx < 0 ? 0 : touchPx; this.pointer.style.left = `${touchPx}px`; this.subbar.style.right = `${this.rangeWidth - touchPx}px`; const ratio = touchPx / this.rangeWidth; const resultValue = (this._absMax * ratio) + this._min; const value = this._useDecimal ? resultValue : toInteger(resultValue); const isValueChanged = this.value !== value; if (isValueChanged) { this.value = value; if (this.realTimeEvent) { this.fire('change', this._value, false); } } } _changeSlideFinally(event) { event.stopPropagation(); if (event.target.className !== 'tui-image-editor-range') { return; } const touchPx = event.offsetX; const ratio = touchPx / this.rangeWidth; const value = (this._absMax * ratio) + this._min; this.pointer.style.left = `${ratio * this.rangeWidth}px`; this.subbar.style.right = `${(1 - ratio) * this.rangeWidth}px`; this.value = value; this.fire('change', value, true); } _startChangingSlide(event) { this.firstPosition = event.screenX ? event.screenX : event.changedTouches[0].screenX; this.firstLeft = toInteger(this.pointer.style.left) || 0; document.addEventListener('mousemove', this.eventHandler.changeSlide); document.addEventListener('touchmove', this.eventHandler.changeSlide); document.addEventListener('mouseup', this.eventHandler.stopChangingSlide); document.addEventListener('touchend', this.eventHandler.stopChangingSlide); } /** * stop change angle event * @private */ _stopChangingSlide() { this.fire('change', this._value, true); document.removeEventListener('mousemove', this.eventHandler.changeSlide); document.removeEventListener('touchmove', this.eventHandler.changeSlide); document.removeEventListener('mouseup', this.eventHandler.stopChangingSlide); document.removeEventListener('touchend', this.eventHandler.stopChangingSlide); } /** * Unnecessary string filtering. * @param {string} inputValue - origin string of input * @returns {string} filtered string * @private */ _filterForInputText(inputValue) { return inputValue.replace(INPUT_FILTER_REGEXP, '$1$2$3'); } } snippet.CustomEvents.mixin(Range); export default Range;