Skip to content

Multiple instances fail #19

Open
Open
@ciprianlucanu

Description

@ciprianlucanu

I tried to use the color picker with multiple instances and it didn't work.
I updated the svelte component to allow this.

Here's the updated code for reference.

script

`

import { onMount, createEventDispatcher } from "svelte";

export let startColor = "#FF0000";
export let id = "colorPicker";

onMount(() => {
    document.addEventListener("mouseup", mouseUp);
    document.addEventListener("touchend", mouseUp);
    document.addEventListener("mousemove", mouseMove);
    document.addEventListener("touchmove", touchMove);
    document.addEventListener("touchstart", killMouseEvents);
    document.addEventListener("mousedown", killTouchEvents);
    setStartColor();
});

Number.prototype.mod = function (n) {
    return ((this % n) + n) % n;
};
const dispatch = createEventDispatcher();
let tracked;
let h = 1;
let s = 1;
let v = 1;
let a = 1;
let r = 255;
let g = 0;
let b = 0;
let hexValue = "#FF0000";

function setStartColor() {
    let hex = startColor.replace("#", "");
    if (hex.length !== 6 && hex.length !== 3 && !hex.match(/([^A-F0-9])/gi)) {
        alert("Invalid property value (startColor)");
        return;
    }
    let hexFiltered = "";
    if (hex.length === 3)
        hex.split("").forEach((c) => {
            hexFiltered += c + c;
        });
    else hexFiltered = hex;
    hexValue = hexFiltered;
    r = parseInt(hexFiltered.substring(0, 2), 16);
    g = parseInt(hexFiltered.substring(2, 4), 16);
    b = parseInt(hexFiltered.substring(4, 6), 16);
    rgbToHSV(r, g, b, true);
    updateCsPicker();
    updateHuePicker();
}

function removeEventListenerFromElement(elementId, eventName, listenerCallback) {
    let element = document.querySelector(elementId);
    if (element) element.removeEventListener(eventName, listenerCallback);
}

function killMouseEvents() {
    removeEventListenerFromElement(`#alpha-event-${id}`, "mousedown", alphaDown);
    removeEventListenerFromElement(`#colorsquare-event-${id}`, "mousedown", csDown);
    removeEventListenerFromElement(`#hue-event-${id}`, "mousedown", hueDown);
    document.removeEventListener("mouseup", mouseUp);
    document.removeEventListener("mousemove", mouseMove);
    document.removeEventListener("touchstart", killMouseEvents);
    document.removeEventListener("mousedown", killTouchEvents);
}

function killTouchEvents() {
    removeEventListenerFromElement(`#alpha-event-${id}`, "touchstart", alphaDownTouch);
    removeEventListenerFromElement(`#colorsquare-event-${id}`, "touchstart", csDownTouch);
    removeEventListenerFromElement(`#hue-event-${id}`, "touchstart", hueDownTouch);
    document.removeEventListener("touchend", mouseUp);
    document.removeEventListener("touchmove", touchMove);
    document.removeEventListener("touchstart", killMouseEvents);
    document.removeEventListener("mousedown", killTouchEvents);
}

function updateCsPicker() {
    let csPicker = document.querySelector(`#colorsquare-picker-${id}`);
    let xPercentage = s * 100;
    let yPercentage = (1 - v) * 100;
    csPicker.style.top = yPercentage + "%";
    csPicker.style.left = xPercentage + "%";
}

function updateHuePicker() {
    let huePicker = document.querySelector(`#hue-picker-${id}`);
    let xPercentage = h * 100;
    huePicker.style.left = xPercentage + "%";
}

function colorChangeCallback() {
    dispatch("colorChange", {
        r: r,
        g: g,
        b: b,
        a: a,
    });
}

function mouseMove(event) {
    if (tracked) {
        let mouseX = event.clientX;
        let mouseY = event.clientY;
        let trackedPos = tracked.getBoundingClientRect();
        let xPercentage, yPercentage, picker;
        switch (tracked.id) {
            case `colorsquare-event-${id}`:
                xPercentage = ((mouseX - trackedPos.x) / 240) * 100;
                yPercentage = ((mouseY - trackedPos.y) / 160) * 100;
                xPercentage > 100 ? (xPercentage = 100) : xPercentage < 0 ? (xPercentage = 0) : null;
                yPercentage > 100 ? (yPercentage = 100) : yPercentage < 0 ? (yPercentage = 0) : null;
                picker = document.querySelector(`#colorsquare-picker-${id}`);
                yPercentage = yPercentage.toFixed(2);
                xPercentage = xPercentage.toFixed(2);
                picker.style.top = yPercentage + "%";
                picker.style.left = xPercentage + "%";
                s = xPercentage / 100;
                v = 1 - yPercentage / 100;
                colorChange();
                break;
            case `hue-event-${id}`:
                xPercentage = ((mouseX - 10 - trackedPos.x) / 220) * 100;
                xPercentage > 100 ? (xPercentage = 100) : xPercentage < 0 ? (xPercentage = 0) : null;
                xPercentage = xPercentage.toFixed(2);
                picker = document.querySelector(`#hue-picker-${id}`);
                picker.style.left = xPercentage + "%";
                h = xPercentage / 100;
                hueChange();
                break;
            case `alpha-event-${id}`:
                xPercentage = ((mouseX - 10 - trackedPos.x) / 220) * 100;
                xPercentage > 100 ? (xPercentage = 100) : xPercentage < 0 ? (xPercentage = 0) : null;
                xPercentage = xPercentage.toFixed(2);
                picker = document.querySelector(`#alpha-picker-${id}`);
                picker.style.left = xPercentage + "%";
                a = xPercentage / 100;
                colorChange();
                break;
        }
    }
}

function touchMove(event) {
    if (tracked) {
        let mouseX = event.touches[0].clientX;
        let mouseY = event.touches[0].clientY;
        let trackedPos = tracked.getBoundingClientRect();
        let xPercentage, yPercentage, picker;
        switch (tracked.id) {
            case `colorsquare-event-${id}`:
                xPercentage = ((mouseX - trackedPos.x) / 240) * 100;
                yPercentage = ((mouseY - trackedPos.y) / 160) * 100;
                xPercentage > 100 ? (xPercentage = 100) : xPercentage < 0 ? (xPercentage = 0) : null;
                yPercentage > 100 ? (yPercentage = 100) : yPercentage < 0 ? (yPercentage = 0) : null;
                picker = document.querySelector(`#colorsquare-picker-${id}`);
                yPercentage = yPercentage.toFixed(2);
                xPercentage = xPercentage.toFixed(2);
                picker.style.top = yPercentage + "%";
                picker.style.left = xPercentage + "%";
                s = xPercentage / 100;
                v = 1 - yPercentage / 100;
                colorChange();
                break;
            case `hue-event-${id}`:
                xPercentage = ((mouseX - 10 - trackedPos.x) / 220) * 100;
                xPercentage > 100 ? (xPercentage = 100) : xPercentage < 0 ? (xPercentage = 0) : null;
                xPercentage = xPercentage.toFixed(2);
                picker = document.querySelector(`#hue-picker-${id}`);
                picker.style.left = xPercentage + "%";
                h = xPercentage / 100;
                hueChange();
                break;
            case `alpha-event-${id}`:
                xPercentage = ((mouseX - 10 - trackedPos.x) / 220) * 100;
                xPercentage > 100 ? (xPercentage = 100) : xPercentage < 0 ? (xPercentage = 0) : null;
                xPercentage = xPercentage.toFixed(2);
                picker = document.querySelector(`#alpha-picker-${id}`);
                picker.style.left = xPercentage + "%";
                a = xPercentage / 100;
                colorChange();
                break;
        }
    }
}

function csDown(event) {
    tracked = event.currentTarget;
    let xPercentage = ((event.offsetX + 1) / 240) * 100;
    let yPercentage = ((event.offsetY + 1) / 160) * 100;
    yPercentage = yPercentage.toFixed(2);
    xPercentage = xPercentage.toFixed(2);
    let picker = document.querySelector(`#colorsquare-picker-${id}`);
    picker.style.top = yPercentage + "%";
    picker.style.left = xPercentage + "%";
    s = xPercentage / 100;
    v = 1 - yPercentage / 100;
    colorChange();
}

function csDownTouch(event) {
    tracked = event.currentTarget;
    let rect = event.target.getBoundingClientRect();
    let offsetX = event.targetTouches[0].clientX - rect.left;
    let offsetY = event.targetTouches[0].clientY - rect.top;
    let xPercentage = ((offsetX + 1) / 240) * 100;
    let yPercentage = ((offsetY + 1) / 160) * 100;
    yPercentage = yPercentage.toFixed(2);
    xPercentage = xPercentage.toFixed(2);
    let picker = document.querySelector(`#colorsquare-picker-${id}`);
    picker.style.top = yPercentage + "%";
    picker.style.left = xPercentage + "%";
    s = xPercentage / 100;
    v = 1 - yPercentage / 100;
    colorChange();
}

function mouseUp(event) {
    tracked = null;
}

function hueDown(event) {
    tracked = event.currentTarget;
    let xPercentage = ((event.offsetX - 9) / 220) * 100;
    xPercentage = xPercentage.toFixed(2);
    let picker = document.querySelector(`#hue-picker-${id}`);
    picker.style.left = xPercentage + "%";
    h = xPercentage / 100;
    hueChange();
}

function hueDownTouch(event) {
    tracked = event.currentTarget;
    let rect = event.target.getBoundingClientRect();
    let offsetX = event.targetTouches[0].clientX - rect.left;
    let xPercentage = ((offsetX - 9) / 220) * 100;
    xPercentage = xPercentage.toFixed(2);
    let picker = document.querySelector(`#hue-picker-${id}`);
    picker.style.left = xPercentage + "%";
    h = xPercentage / 100;
    hueChange();
}

function hueChange() {
    let rgb = hsvToRgb(h, 1, 1);
    let colorsquare = document.querySelector(`#colorsquare-${id}`);
    colorsquare.style.background = `rgba(${rgb[0]},${rgb[1]},${rgb[2]},1)`;
    colorChange();
}

function colorChange() {
    let rgb = hsvToRgb(h, s, v);
    r = rgb[0];
    g = rgb[1];
    b = rgb[2];
    hexValue = RGBAToHex();
    let pickedColor = document.querySelector(`#color-picked-${id}`);
    pickedColor.style.background = `rgba(${rgb[0]},${rgb[1]},${rgb[2]},${a})`;
    colorChangeCallback();
}

function alphaDown(event) {
    tracked = event.currentTarget;
    let xPercentage = ((event.offsetX - 9) / 220) * 100;
    xPercentage = xPercentage.toFixed(2);
    let picker = document.querySelector(`#alpha-picker-${id}`);
    picker.style.left = xPercentage + "%";
    a = xPercentage / 100;
    colorChange();
}

function alphaDownTouch(event) {
    tracked = event.currentTarget;
    let rect = event.target.getBoundingClientRect();
    let offsetX = event.targetTouches[0].clientX - rect.left;
    let xPercentage = ((offsetX - 9) / 220) * 100;
    xPercentage = xPercentage.toFixed(2);
    let picker = document.querySelector(`#alpha-picker-${id}`);
    picker.style.left = xPercentage + "%";
    a = xPercentage / 100;
    colorChange();
}

//Math algorithms
function hsvToRgb(h, s, v) {
    var r, g, b;

    var i = Math.floor(h * 6);
    var f = h * 6 - i;
    var p = v * (1 - s);
    var q = v * (1 - f * s);
    var t = v * (1 - (1 - f) * s);

    switch (i % 6) {
        case 0:
            (r = v), (g = t), (b = p);
            break;
        case 1:
            (r = q), (g = v), (b = p);
            break;
        case 2:
            (r = p), (g = v), (b = t);
            break;
        case 3:
            (r = p), (g = q), (b = v);
            break;
        case 4:
            (r = t), (g = p), (b = v);
            break;
        case 5:
            (r = v), (g = p), (b = q);
            break;
    }

    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}

function RGBAToHex() {
    let rHex = r.toString(16);
    let gHex = g.toString(16);
    let bHex = b.toString(16);

    if (rHex.length == 1) rHex = "0" + rHex;
    if (gHex.length == 1) gHex = "0" + gHex;
    if (bHex.length == 1) bHex = "0" + bHex;

    return ("#" + rHex + gHex + bHex).toUpperCase();
}

function rgbToHSV(r, g, b, update) {
    let rperc, gperc, bperc, max, min, diff, pr, hnew, snew, vnew;
    rperc = r / 255;
    gperc = g / 255;
    bperc = b / 255;
    max = Math.max(rperc, gperc, bperc);
    min = Math.min(rperc, gperc, bperc);
    diff = max - min;

    vnew = max;
    vnew == 0 ? (snew = 0) : (snew = diff / max);

    for (let i = 0; i < 3; i++) {
        if ([rperc, gperc, bperc][i] === max) {
            pr = i;
            break;
        }
    }
    if (diff == 0) {
        hnew = 0;
        if (update) {
            h = hnew;
            s = snew;
            v = vnew;
            hueChange();
            return;
        } else {
            return { h: hnew, s: snew, v: vnew };
        }
    } else {
        switch (pr) {
            case 0:
                hnew = (60 * (((gperc - bperc) / diff) % 6)) / 360;
                break;
            case 1:
                hnew = (60 * ((bperc - rperc) / diff + 2)) / 360;
                break;
            case 2:
                hnew = (60 * ((rperc - gperc) / diff + 4)) / 360;
                break;
        }
        if (hnew < 0) hnew += 6;
    }

    if (update) {
        h = hnew;
        s = snew;
        v = vnew;
        hueChange();
    } else {
        return { h: hnew, s: snew, v: vnew };
    }
}

**html**

<div class="hue-selector">
    <div id="hue-picker-{id}" class="hue-picker" />
    <div id="hue-event-{id}" class="hue-event" on:mousedown={hueDown} on:touchstart={hueDownTouch} />
</div>

<div class="alpha-selector">
    <div class="alpha-value" />
    <div id="alpha-picker-{id}" class="alpha-picker" />
    <div id="alpha-event-{id}" class="alpha-event" on:mousedown={alphaDown} on:touchstart={alphaDownTouch} />
</div>

<div class="color-info-box">
    <div class="color-picked-bg">
        <div id="color-picked-{id}" class="color-picked" />
    </div>

    <div class="hex-text-block">
        <p class="text">{hexValue}</p>
    </div>

    <div class="rgb-text-div">
        <div class="rgb-text-block">
            <p class="text">{r}</p>
            <p class="text-label">R</p>
        </div>

        <div class="rgb-text-block">
            <p class="text">{g}</p>
            <p class="text-label">G</p>
        </div>

        <div class="rgb-text-block">
            <p class="text">{b}</p>
            <p class="text-label">B</p>
        </div>
    </div>
</div>
` **style** `
.main-container {
    width: 240px;
    height: 265px;
    background: #f2f2f2;
    border-radius: 1px;
    -webkit-box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.51);
    -moz-box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.51);
    box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.51);
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

.saturation-gradient {
    background: linear-gradient(to right, rgb(255, 255, 255), rgba(255, 255, 255, 0));
    width: 240px;
    height: 160px;
}

.value-gradient {
    background: linear-gradient(to top, rgb(0, 0, 0), rgba(0, 0, 0, 0));
    overflow: hidden;
    width: 240px;
    height: 160px;
}

.hue-selector {
    background: linear-gradient(
        to right,
        #ff0000 0%,
        #ffff00 17%,
        #00ff00 33%,
        #00ffff 50%,
        #0000ff 67%,
        #ff00ff 83%,
        #ff0000 100%
    );
    margin: 15px 10px 10px 10px;
    border-radius: 10px;
    height: 10px;
}

.hue-selector .hue-picker {
    background: #fff;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    left: 0%;
    position: relative;
    cursor: default;
    transform: translate(-5px, -1px);
    -webkit-box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.67);
    -moz-box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.67);
    box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.67);
}

.hue-selector .hue-event {
    width: 236px;
    height: 14px;
    transform: translate(-8px, -14px);
    cursor: default;
    touch-action: none;
}

.alpha-selector {
    background-image: linear-gradient(45deg, #808080 25%, transparent 25%),
        linear-gradient(-45deg, #808080 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #808080 75%),
        linear-gradient(-45deg, transparent 75%, #808080 75%);
    background-size: 10px 10px;
    background-position: 0 0, 0 5px, 5px -5px, -5px 0px;
    margin: 10px 10px;
    border-radius: 10px;
    height: 10px;
    position: relative;
}

.alpha-selector .alpha-picker {
    background: #fff;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    left: 100%;
    position: relative;
    cursor: default;
    transform: translate(-5px, -11px);
    -webkit-box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.67);
    -moz-box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.67);
    box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.67);
}

.alpha-selector .alpha-event {
    width: 236px;
    height: 14px;
    transform: translate(-8px, -24px);
    cursor: default;
    touch-action: none;
}

.alpha-value {
    background: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1));
    width: 100%;
    height: 100%;
    border-radius: 10px;
}

.colorsquare {
    background: rgb(255, 0, 0);
}

.value-gradient .colorsquare-picker {
    margin: 0;
    padding: 0;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    border: 2px solid #fffb;
    position: relative;
    transform: translate(-9px, -9px);
    left: 100%;
}

.value-gradient .colorsquare-event {
    width: 100%;
    height: 100%;
    position: relative;
    transform: translate(0, -16px);
    touch-action: none;
}

.color-info-box {
    margin: 10px;
    width: 100%;
    height: 22px;
    vertical-align: middle;
    position: relative;
}

.color-picked {
    width: 18px;
    height: 18px;
    border-radius: 2px;
    background: rgba(255, 0, 0, 1);
    display: inline-block;
}

.color-picked-bg {
    background-image: linear-gradient(45deg, #808080 25%, transparent 25%),
        linear-gradient(-45deg, #808080 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #808080 75%),
        linear-gradient(-45deg, transparent 75%, #808080 75%);
    background-size: 10px 10px;
    background-position: 0 0, 0 5px, 5px -5px, -5px 0px;
    border: 2px solid #fff;
    border-radius: 4px;
    width: 18px;
    height: 18px;
    color: #fff;
    display: inline-block;
}

.hex-text-block {
    display: inline-block;
    background: white;
    border-radius: 2px;
    padding: 2px;
    border: 1px solid #e3e3e3;
    height: 16px;
    width: 54px;
    vertical-align: top;
    text-align: center;
}

.rgb-text-block {
    display: inline-block;
    background: white;
    border-radius: 2px;
    padding: 2px;
    margin: 0 1px;
    border: 1px solid #dcdcdc;
    height: 16px;
    width: 23px;
    vertical-align: top;
    text-align: center;
}

.rgb-text-div {
    right: 10%;
    display: inline-block;
    vertical-align: top;
    position: absolute;
}

.text-label {
    position: relative;
    top: -12px;
    font-family: sans-serif;
    font-size: small;
    color: #888;
}

.text {
    display: inline;
    font-family: sans-serif;
    margin: 0;
    display: inline-block;
    font-size: 12px;
    font-size-adjust: 0.5;
    position: relative;
    top: -1px;
    vertical-align: middle;
    -webkit-touch-callout: all;
    -webkit-user-select: all;
    -khtml-user-select: all;
    -moz-user-select: all;
    -ms-user-select: all;
    user-select: all;
}

`

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions