UNPKG

jab-image-editor

Version:
457 lines (404 loc) 16.8 kB
import snippet from 'tui-code-snippet'; import Colorpicker from './tools/colorpicker'; import Range from './tools/range'; import Submenu from './submenuBase'; import templateHtml from './template/submenu/filter'; import {toInteger, toCamelCase, assignmentForDestroy} from '../util'; import {defaultFilterRangeValus as FILTER_RANGE} from '../consts'; const PICKER_CONTROL_HEIGHT = '130px'; const BLEND_OPTIONS = ['add', 'diff', 'subtract', 'multiply', 'screen', 'lighten', 'darken']; const FILTER_OPTIONS = [ 'grayscale', 'invert', 'sepia', 'vintage', 'blur', 'sharpen', 'emboss', 'remove-white', 'brightness', 'noise', 'pixelate', 'color-filter', 'tint', 'multiply', 'blend' ]; const filterNameMap = { grayscale: 'grayscale', invert: 'invert', sepia: 'sepia', blur: 'blur', sharpen: 'sharpen', emboss: 'emboss', removeWhite: 'removeColor', brightness: 'brightness', contrast: 'contrast', saturation: 'saturation', vintage: 'vintage', polaroid: 'polaroid', noise: 'noise', pixelate: 'pixelate', colorFilter: 'removeColor', tint: 'blendColor', multiply: 'blendColor', blend: 'blendColor', hue: 'hue', gamma: 'gamma' }; const RANGE_INSTANCE_NAMES = [ 'removewhiteDistanceRange', 'colorfilterThresholeRange', 'pixelateRange', 'noiseRange', 'brightnessRange', 'tintOpacity' ]; const COLORPICKER_INSTANCE_NAMES = [ 'filterBlendColor', 'filterMultiplyColor', 'filterTintColor' ]; /** * Filter ui class * @class * @ignore */ class Filter extends Submenu { constructor(subMenuElement, {locale, menuBarPosition, usageStatistics}) { super(subMenuElement, { locale, name: 'filter', menuBarPosition, templateHtml, usageStatistics }); this.selectBoxShow = false; this.checkedMap = {}; this._makeControlElement(); } /** * Destroys the instance. */ destroy() { this._removeEvent(); this._destroyToolInstance(); assignmentForDestroy(this); } /** * Remove event for filter */ _removeEvent() { snippet.forEach(FILTER_OPTIONS, filter => { const filterCheckElement = this.selector(`.tie-${filter}`); const filterNameCamelCase = toCamelCase(filter); filterCheckElement.removeEventListener('change', this.eventHandler[filterNameCamelCase]); }); snippet.forEach([...RANGE_INSTANCE_NAMES, ...COLORPICKER_INSTANCE_NAMES], instanceName => { this._els[instanceName].off(); }); this._els.blendType.removeEventListener('change', this.eventHandler.changeBlendFilter); this._els.blendType.removeEventListener('click', this.eventHandler.changeBlendFilter); } _destroyToolInstance() { snippet.forEach([...RANGE_INSTANCE_NAMES, ...COLORPICKER_INSTANCE_NAMES], instanceName => { this._els[instanceName].destroy(); }); } /** * Add event for filter * @param {Object} actions - actions for crop * @param {Function} actions.applyFilter - apply filter option */ addEvent({applyFilter}) { const changeFilterState = filterName => this._changeFilterState.bind(this, applyFilter, filterName); const changeFilterStateForRange = filterName => (value, isLast) => this._changeFilterState(applyFilter, filterName, isLast); this.eventHandler = { changeBlendFilter: changeFilterState('blend'), blandTypeClick: event => event.stopPropagation() }; snippet.forEach(FILTER_OPTIONS, filter => { const filterCheckElement = this.selector(`.tie-${filter}`); const filterNameCamelCase = toCamelCase(filter); this.checkedMap[filterNameCamelCase] = filterCheckElement; this.eventHandler[filterNameCamelCase] = changeFilterState(filterNameCamelCase); filterCheckElement.addEventListener('change', this.eventHandler[filterNameCamelCase]); }); this._els.removewhiteDistanceRange.on('change', changeFilterStateForRange('removeWhite')); this._els.colorfilterThresholeRange.on('change', changeFilterStateForRange('colorFilter')); this._els.pixelateRange.on('change', changeFilterStateForRange('pixelate')); this._els.noiseRange.on('change', changeFilterStateForRange('noise')); this._els.brightnessRange.on('change', changeFilterStateForRange('brightness')); this._els.filterBlendColor.on('change', this.eventHandler.changeBlendFilter); this._els.filterMultiplyColor.on('change', changeFilterState('multiply')); this._els.filterTintColor.on('change', changeFilterState('tint')); this._els.tintOpacity.on('change', changeFilterStateForRange('tint')); this._els.filterMultiplyColor.on('changeShow', this.colorPickerChangeShow.bind(this)); this._els.filterTintColor.on('changeShow', this.colorPickerChangeShow.bind(this)); this._els.filterBlendColor.on('changeShow', this.colorPickerChangeShow.bind(this)); this._els.blendType.addEventListener('change', this.eventHandler.changeBlendFilter); this._els.blendType.addEventListener('click', this.eventHandler.blandTypeClick); } /** * Set filter for undo changed * @param {Object} chagedFilterInfos - changed command infos * @param {string} type - filter type * @param {string} action - add or remove * @param {Object} options - filter options */ setFilterState(chagedFilterInfos) { const {type, options, action} = chagedFilterInfos; const filterName = this._getFilterNameFromOptions(type, options); const isRemove = action === 'remove'; if (!isRemove) { this._setFilterState(filterName, options); } this.checkedMap[filterName].checked = !isRemove; } /** * Set filter for undo changed * @param {string} filterName - filter name * @param {Object} options - filter options * @private */ _setFilterState(filterName, options) { // eslint-disable-line if (filterName === 'colorFilter') { this._els.colorfilterThresholeRange.value = options.distance; } else if (filterName === 'removeWhite') { this._els.removewhiteDistanceRange.value = options.distance; } else if (filterName === 'pixelate') { this._els.pixelateRange.value = options.blocksize; } else if (filterName === 'brightness') { this._els.brightnessRange.value = options.brightness; } else if (filterName === 'noise') { this._els.noiseRange.value = options.noise; } else if (filterName === 'tint') { this._els.tintOpacity.value = options.alpha; this._els.filterTintColor.color = options.color; } else if (filterName === 'blend') { this._els.filterBlendColor.color = options.color; } else if (filterName === 'multiply') { this._els.filterMultiplyColor.color = options.color; } } /** * Get filter name * @param {string} type - filter type * @param {Object} options - filter options * @returns {string} filter name * @private */ _getFilterNameFromOptions(type, options) { let filterName = type; if (type === 'removeColor') { filterName = snippet.isExisty(options.useAlpha) ? 'removeWhite' : 'colorFilter'; } else if (type === 'blendColor') { filterName = { add: 'blend', multiply: 'multiply', tint: 'tint' }[options.mode]; } return filterName; } /** * Add event for filter * @param {Function} applyFilter - actions for firter * @param {string} filterName - filter name * @param {boolean} [isLast] - Is last change */ _changeFilterState(applyFilter, filterName, isLast = true) { const apply = this.checkedMap[filterName].checked; const type = filterNameMap[filterName]; const checkboxGroup = this.checkedMap[filterName].closest('.tui-image-editor-checkbox-group'); if (checkboxGroup) { if (apply) { checkboxGroup.classList.remove('tui-image-editor-disabled'); } else { checkboxGroup.classList.add('tui-image-editor-disabled'); } } applyFilter(apply, type, this._getFilterOption(filterName), !isLast); } /** * Get filter option * @param {String} type - filter type * @returns {Object} filter option object * @private */ _getFilterOption(type) { // eslint-disable-line const option = {}; switch (type) { case 'removeWhite': option.color = '#FFFFFF'; option.useAlpha = false; option.distance = parseFloat(this._els.removewhiteDistanceRange.value); break; case 'colorFilter': option.color = '#FFFFFF'; option.distance = parseFloat(this._els.colorfilterThresholeRange.value); break; case 'pixelate': option.blocksize = toInteger(this._els.pixelateRange.value); break; case 'noise': option.noise = toInteger(this._els.noiseRange.value); break; case 'brightness': option.brightness = parseFloat(this._els.brightnessRange.value); break; case 'blend': option.mode = 'add'; option.color = this._els.filterBlendColor.color; option.mode = this._els.blendType.value; break; case 'multiply': option.mode = 'multiply'; option.color = this._els.filterMultiplyColor.color; break; case 'tint': option.mode = 'tint'; option.color = this._els.filterTintColor.color; option.alpha = this._els.tintOpacity.value; break; default: break; } return option; } /** * Make submenu range and colorpicker control * @private */ _makeControlElement() { this._els = { removewhiteDistanceRange: new Range( {slider: this.selector('.tie-removewhite-distance-range')}, FILTER_RANGE.removewhiteDistanceRange ), brightnessRange: new Range( {slider: this.selector('.tie-brightness-range')}, FILTER_RANGE.brightnessRange ), noiseRange: new Range( {slider: this.selector('.tie-noise-range')}, FILTER_RANGE.noiseRange ), pixelateRange: new Range( {slider: this.selector('.tie-pixelate-range')}, FILTER_RANGE.pixelateRange ), colorfilterThresholeRange: new Range( {slider: this.selector('.tie-colorfilter-threshole-range')}, FILTER_RANGE.colorfilterThresholeRange ), filterTintColor: new Colorpicker( this.selector('.tie-filter-tint-color'), '#03bd9e', this.toggleDirection, this.usageStatistics ), filterMultiplyColor: new Colorpicker( this.selector('.tie-filter-multiply-color'), '#515ce6', this.toggleDirection, this.usageStatistics ), filterBlendColor: new Colorpicker( this.selector('.tie-filter-blend-color'), '#ffbb3b', this.toggleDirection, this.usageStatistics ) }; this._els.tintOpacity = this._pickerWithRange(this._els.filterTintColor.pickerControl); this._els.blendType = this._pickerWithSelectbox(this._els.filterBlendColor.pickerControl); this.colorPickerControls.push(this._els.filterTintColor); this.colorPickerControls.push(this._els.filterMultiplyColor); this.colorPickerControls.push(this._els.filterBlendColor); } /** * Make submenu control for picker & range mixin * @param {HTMLElement} pickerControl - pickerControl dom element * @returns {Range} * @private */ _pickerWithRange(pickerControl) { const rangeWrap = document.createElement('div'); const rangelabel = document.createElement('label'); const slider = document.createElement('div'); slider.id = 'tie-filter-tint-opacity'; rangelabel.innerHTML = 'Opacity'; rangeWrap.appendChild(rangelabel); rangeWrap.appendChild(slider); pickerControl.appendChild(rangeWrap); pickerControl.style.height = PICKER_CONTROL_HEIGHT; return new Range({slider}, FILTER_RANGE.tintOpacityRange); } /** * Make submenu control for picker & selectbox * @param {HTMLElement} pickerControl - pickerControl dom element * @returns {HTMLElement} * @private */ _pickerWithSelectbox(pickerControl) { const selectlistWrap = document.createElement('div'); const selectlist = document.createElement('select'); const optionlist = document.createElement('ul'); selectlistWrap.className = 'tui-image-editor-selectlist-wrap'; optionlist.className = 'tui-image-editor-selectlist'; selectlistWrap.appendChild(selectlist); selectlistWrap.appendChild(optionlist); this._makeSelectOptionList(selectlist); pickerControl.appendChild(selectlistWrap); pickerControl.style.height = PICKER_CONTROL_HEIGHT; this._drawSelectOptionList(selectlist, optionlist); this._pickerWithSelectboxForAddEvent(selectlist, optionlist); return selectlist; } /** * Make selectbox option list custom style * @param {HTMLElement} selectlist - selectbox element * @param {HTMLElement} optionlist - custom option list item element * @private */ _drawSelectOptionList(selectlist, optionlist) { const options = selectlist.querySelectorAll('option'); snippet.forEach(options, option => { const optionElement = document.createElement('li'); optionElement.innerHTML = option.innerHTML; optionElement.setAttribute('data-item', option.value); optionlist.appendChild(optionElement); }); } /** * custome selectbox custom event * @param {HTMLElement} selectlist - selectbox element * @param {HTMLElement} optionlist - custom option list item element * @private */ _pickerWithSelectboxForAddEvent(selectlist, optionlist) { optionlist.addEventListener('click', event => { const optionValue = event.target.getAttribute('data-item'); const fireEvent = document.createEvent('HTMLEvents'); selectlist.querySelector(`[value="${optionValue}"]`).selected = true; fireEvent.initEvent('change', true, true); selectlist.dispatchEvent(fireEvent); this.selectBoxShow = false; optionlist.style.display = 'none'; }); selectlist.addEventListener('mousedown', event => { event.preventDefault(); this.selectBoxShow = !this.selectBoxShow; optionlist.style.display = this.selectBoxShow ? 'block' : 'none'; optionlist.setAttribute('data-selectitem', selectlist.value); optionlist.querySelector(`[data-item='${selectlist.value}']`).classList.add('active'); }); } /** * Make option list for select control * @param {HTMLElement} selectlist - blend option select list element * @private */ _makeSelectOptionList(selectlist) { snippet.forEach(BLEND_OPTIONS, option => { const selectOption = document.createElement('option'); selectOption.setAttribute('value', option); selectOption.innerHTML = option.replace(/^[a-z]/, $0 => $0.toUpperCase()); selectlist.appendChild(selectOption); }); } } export default Filter;