aico-image-editor
Version:
Combine multiple image into and create single combined image
360 lines (318 loc) • 12.9 kB
JavaScript
// import '../../scss/main.scss';
// import '../../scss/custom.scss';
// Load your styles using style-loader
//import Alpine from 'alpinejs';
import chroma from "chroma-js";
const loadColorPickerHTML = () => import(/* webpackMode: "eager" */'./colorPicker.html');
// initialize custom made styles and html loading modules//
// inspired by alpine js component//
import initStyles from '../../initStyles';
import initHTML from '../../initHTML';
initHTML('color-picker',loadColorPickerHTML, ['eventname']);
// const container = document.getElementById('canvas-holder');
// const circle = document.getElementById('colorpicker-circle');
// const canvas = document.getElementById('colorpicker');
import { ColorUtils } from './color-utilities';
export default () => ({
get componentKey() {
return this.$el.getRootNode().host.getAttribute('key')
},
container: null,
circle: null,
canvas: null,
hue: 0,
saturation: 0,
lightness: 0,
updateOutputPosition() {
document.documentElement.style.setProperty('--hue', this.hue);
},
updateRGBFromHue() {
this.updateOutputPosition();
let [h,s,l] = ColorUtils.rgbToHSL(this.red, this.green, this.blue);
const newHue = this.hue;
const [r,g,b] = ColorUtils.hslToRGB(newHue,s,l);
this.red = r;
this.green = g;
this.blue = b;
// this.saturation = s = Math.round(s);
// this.lightness = l = Math.round(l);
this.saturation = s;
this.lightness = l;
this.updateColorsInOtherModes('rgb');
//console.log('rgb updated')
},
updateColorsInOtherModes(mode) {
//this alternate syntax can be used in place of get/set
//el._x_model.set(Math.min(255, Math.max(0, el._x_model.get())));
let self = this;
if(mode === 'rgb') {
const [h,s,l] = ColorUtils.rgbToHSL(this.red, this.green, this.blue);
// this.hue = h = Math.round(h);
// this.lightness = l = Math.round(l);
// this.saturation = s = Math.round(s);
this.hue = h;
this.lightness = l;
this.saturation = s;
this.updateOutputPosition();
//console.log('hsl updated');
const [c,m,y,k] = ColorUtils.rgbToCMYK(this.red, this.green, this.blue)
this.cyan = c;
this.magenta = m;
this.yellow = y;
this.key = k;
const hex = ColorUtils.rgbToHEX(this.red, this.green, this.blue)
this.hex = hex;
} else if (mode === 'cmyk') {
const [h,s,l] = ColorUtils.cmykToHSL(this.cyan, this.magenta, this.yellow,this.key);
this.hue = h;
this.lightness = l;
this.saturation = s;
this.updateOutputPosition();
//console.log('hsl updated');
const [r,g,b] = ColorUtils.cmykToRGB(this.cyan, this.magenta, this.yellow,this.key);
this.red = r;
this.green = g;
this.blue = b;
const hex = ColorUtils.cmykToHEX(this.cyan, this.magenta, this.yellow,this.key);
this.hex = hex;
} else if (mode === 'hex') {
if(this.isHexCode(this.hex)) {
const [h,s,l] = ColorUtils.hexToHSL(this.hex);
this.hue = h;
this.lightness = l;
this.saturation = s;
this.updateOutputPosition();
//console.log('hsl updated');
const [c,m,y,k] = ColorUtils.hexToCMYK(this.hex);
this.cyan = c;
this.magenta = m;
this.yellow = y;
this.key = k;
const [r,g,b] = ColorUtils.hexToRGB(this.hex);
this.red = r;
this.green = g;
this.blue = b;
}
}
},
_red: 0,
get red() {
return this._red;
},
set red(val) {
this._red = Math.min(255, Math.max(0, val))
},
_green: 0,
get green() {
return this._green;
},
set green(val) {
this._green = Math.min(255, Math.max(0, val))
},
_blue: 0,
get blue() {
return this._blue;
},
set blue(val) {
this._blue = Math.min(255, Math.max(0, val))
},
_cyan: 0.0000,
get cyan() {
return this._cyan;
},
set cyan(val) {
this._cyan = Math.min(100, Math.max(0, val))
},
_magenta: 0.0000,
get magenta() {
return this._magenta;
},
set magenta(val) {
this._magenta = Math.min(100, Math.max(0, val))
},
_yellow: 0.0000,
get yellow() {
return this._yellow;
},
set yellow(val) {
this._yellow = Math.min(100, Math.max(0, val))
},
_key: 1.0000,
get key() {
return this._key;
},
set key(val) {
this._key = Math.min(100, Math.max(0, val))
},
_hex: '#000000',
get hex() {
return this._hex;
},
set hex(val) {
this._hex = val;
},
hexCheck() {
this.hex = this.isHexCode(this.hex) ? this.hex : '#000000';
this.updateColorsInOtherModes('hex');
},
isHexCode(hexcode) {
return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(hexcode)
},
triggerSelectedColorEvent(initTime) {
// eventName is kept to identify component (in case of multiple used) and dispatch event based on eventname attribute set while consuming component
// e.g. event name will be "gradientcolor-updated-in-colorpicker" in case of gradientcolor
this.$dispatch(`${this.eventName}-updated-in-colorpicker`, {colorObj: {id: 'tempcolor', color:this.selectedColor, initTime: initTime}})
},
get selectedColor() {
let rgbColor = `rgb(${this.red},${this.green},${this.blue})`;
//this.$store.canvas.setLabelColor(color);
let color = chroma(rgbColor).hex();
//this.activeColorObj = {};
return color;
},
colordatawrapper: {
//get r,g and b color data from event emitted by nested elements
// amd set them back to r, g and b values here for selectedcolor
['@color-selected'](event) {
this.red = event.detail.r;
this.green = event.detail.g;
this.blue = event.detail.b;
this.updateColorsInOtherModes('rgb')
},
['@color-updated-from-outer-component.window'](event) {
if(this.componentKey === event.detail.componentKey) {
this.hex = event.detail.hex
this.updateColorsInOtherModes('hex')
// update circle position
this.$dispatch('color-updated',{
h: this.hue,
s: this.saturation,
l: this.lightness,
componentKey: this.componentKey
})
}
}
},
//spectrum data starts here
isMouseDown: false,
init() {
initStyles(this.$el.shadowRoot);
this.container = this.$refs.canvasholder;
this.circle = this.$refs.colorpickercircle;
this.canvas = this.$refs.colorpicker;
this.initColorPicker();
},
initColorPicker() {
const [width, height] = [200, 205];
[this.canvas.width, this.canvas.height] = [width, height];
this.drawColors(this.canvas);
// init this event at init time when initTime is true along with event detail
this.triggerSelectedColorEvent(true)
// and also keep watch of selectedColor and retrigger the same event as above
this.$watch('selectedColor', () => {
this.triggerSelectedColorEvent(false)
})
},
drawColors(canvas) {
const context = canvas.getContext('2d',{ willReadFrequently: true });
const {width, height} = canvas;
//Colors - horizontal gradient
const gradientH = context.createLinearGradient(0, 0, width, 0);
gradientH.addColorStop(0, "rgb(255, 0, 0)"); // red
gradientH.addColorStop(1/6, "rgb(255, 255, 0)"); // yellow
gradientH.addColorStop(2/6, "rgb(0, 255, 0)"); // green
gradientH.addColorStop(3/6, "rgb(0, 255, 255)");
gradientH.addColorStop(4/6, "rgb(0, 0, 255)"); // blue
gradientH.addColorStop(5/6, "rgb(255, 0, 255)");
gradientH.addColorStop(1, "rgb(255, 0, 0)"); // red
context.fillStyle = gradientH;
context.fillRect(0, 0, width, height);
//Shades - vertical gradient
const gradientV = context.createLinearGradient(0, 0, 0, height);
gradientV.addColorStop(0, "rgba(255, 255, 255, 1)");
gradientV.addColorStop(0.5, "rgba(255, 255, 255, 0)");
gradientV.addColorStop(0.5, "rgba(0, 0, 0, 0)");
gradientV.addColorStop(1, "rgba(0, 0, 0, 1)");
context.fillStyle = gradientV;
context.fillRect(0, 0, width, height);
},
getCanvasPointCoordinates(event,canvas) {
const rect = canvas.getBoundingClientRect();
let x = event.clientX - rect.left; //x position within the element.
let y = event.clientY - rect.top; //y position within the element.
//on clicking on last co-ordinate of canvas r,g and b are 0 and
//hence incorrect data was returned so maximum is it's 1px less than width
x = Math.min(canvas.width, Math.max(0,x));
y = Math.min(canvas.height, Math.max(0,y));
return {x,y};
},
setCircleBorderColor(lightness) {
const txtColor = lightness < 50 ? '#FFF' : '#000';
this.circle.style.borderColor = txtColor;
},
pickColor(event, canvas, circle) {
const context = canvas.getContext('2d',{ willReadFrequently: true });
const {x, y} = this.getCanvasPointCoordinates(event,canvas)
const imgData = context.getImageData(x, y, 1, 1);
let [r, g, b] = imgData.data;
const [h, s, l] = ColorUtils.rgbToHSL(r, g, b);
this.setCircleBorderColor(l);
canvas.dispatchEvent(new CustomEvent('color-selected', {
bubbles: true, detail: {r, g, b, h, s, l}
}));
},
updateCirclePosition(event,canvas,circle) {
const {x, y} = this.getCanvasPointCoordinates(event,canvas)
circle.style.top = (y - 0) + 'px';
circle.style.left = (x - 0) + 'px';
},
getWrapperConfig() {
return {
['@mousedown'](event) {
this.isMouseDown = true;
},
['@mouseup'](event) {
this.isMouseDown = false;
this.updateCirclePosition(event, this.canvas, this.circle);
this.pickColor(event, this.canvas, this.circle)
},
['@mousemove'](event) {
if(this.isMouseDown) {
this.updateCirclePosition(event, this.canvas, this.circle)
}
},
['@mouseup.outside'](event) {
if(this.isMouseDown) {
this.isMouseDown = false;
this.updateCirclePosition(event, this.canvas, this.circle)
this.pickColor(event, this.canvas, this.circle)
}
},
['@color-updated.window'](event) {
if(event.detail.componentKey === this.componentKey) {
const [h,s,l] = [event.detail.h,event.detail.s, event.detail.l];
const leftpx = ((h * this.canvas.width) / 360) + 'px',
toppx = ((100- l) * (this.canvas.height/100)) + 'px';
this.setCircleBorderColor(l);
//console.log(leftpx, toppx)
this.circle.style.left = leftpx;
this.circle.style.top = toppx;
}
},
[` -${this.componentKey}-changed.window`](event) {
// eventName will be set here when attribute is first assigned when its html loads
// at that time attributeChangedCallback will fire and that will dispatch
// eventName like `${attr}-${key}-changed` where attr is "eventname" and key is
// kept to fully isolate two components even when eventname is same because key can never be the same.
// in this case eventname is observed attribute so event is "eventname-1-changed" dispatched which is received here
// key must be differnt for two components e.g. 1, 2 and so on....
// and thus, eventName prop here will be set to whatever attribute is passed while consuming component (gradientcolor or textcolor)
this.eventName = event.detail.eventName;
}
}
},
eventName: '',
getChromaHex(selectedColor) {
return chroma(selectedColor).hex();
}
})