Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How dynamically trigger move of selected point in color-picker:sv element - n points up, left, down or right? #71

Open
bongobongo opened this issue Oct 7, 2021 · 36 comments

Comments

@bongobongo
Copy link

I would like to make it possible, in custom javascript, to move the selected round point in color-picker:sv element - using e.g. the up, down, left and right arrows (and/or using the I, M, J and L keys) on the keyboard.
And I would like to make it so the selected point moves n points in a given direction on each press of the arrow -
where the value of n is in relation to how many points there are in total horisontally (and vertically).

How do i get number of points in the color-picker:sv element - horisontally and vertically?
How do I move the selected point n points up, right, left or down - and trigger the same events that are triggered when I left click to select a color?

@taufik-nurrohman
Copy link
Owner

taufik-nurrohman commented Oct 7, 2021

Good idea. Currently the method for manually move the pointer of X and Y axis is not exposed, but may be useful in the future. I won’t be integrating this feature into the core, but will probably provide it as another tweak.

I assume that you will not use the mouse at all. What do you expect when you press the Tab key? What to do with the hue and alpha slider?

@bongobongo
Copy link
Author

bongobongo commented Oct 7, 2021

Well, I was thinking to use the mouse to click on a starting point (starting color).
After that use the arrow keys to get a set of n hex values - to be used in a palette.
Controlling direction using arrow keys to move selection n points in a given direction - will make it possible to create palettes in a consistent manner.
Another similar feature would be to move selection n points diagonally (45 degrees or another selected angle) with some other keys.

"What to do with the hue and alpha slider?"
Personally I have no use for the alpha slider.
The hue - well for now I just click on it. But in relation to what I want now, it could also be practical to see what position it has, so it is possible to select (exactly) same position laiter - if one want to tweak earlier palette color values.

@taufik-nurrohman
Copy link
Owner

taufik-nurrohman commented Oct 7, 2021

So, I just took out the P2RGB() and RGB2HSV() functions from the core, and this seems to work.

const picker = new CP(document.querySelector('input'));

function P2RGB(a) {
    let h = +a[0],
        s = +a[1],
        v = +a[2],
        r,
        g,
        b,
        i,
        f,
        p,
        q,
        t;
    i = Math.floor(h * 6);
    f = h * 6 - i;
    p = v * (1 - s);
    q = v * (1 - f * s);
    t = v * (1 - (1 - f) * s);
    i = i || 0;
    q = q || 0;
    t = t || 0;
    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 RGB2HSV(a) {
    let r = +a[0] / 255,
        g = +a[1] / 255,
        b = +a[2] / 255,
        max = Math.max(r, g, b),
        min = Math.min(r, g, b),
        h,
        s,
        v = max,
        d = max - min;
    s = max === 0 ? 0 : d / max;
    if (max === min) {
        h = 0; // Achromatic
    } else {
        switch (max) {
            case r:
                h = (g - b) / d + (g < b ? 6 : 0);
                break;
            case g:
                h = (b - r) / d + 2;
                break;
            case b:
                h = (r - g) / d + 4;
                break;
        }
        h /= 6;
    }
    return [h, s, v];
}

picker.on('change', function(r, g, b, a) {
    this.source.value = this.color(r, g, b, a);
});

const step = .05; // Adjust the color step here!

picker.source.addEventListener('keydown', function(e) {
    let key = e.key,
        color = picker.get();
    let a = color.pop(); // Detach alpha value from the color list
    let [h, s, v] = RGB2HSV(color);
    if ('ArrowDown' === key) {
        v -= step;
        v = v < 0 ? .01 : v; // Cannot use `0` because it will be normalized to red :(
        picker.set(...[...P2RGB([h, s, v]), a]);
        e.preventDefault();
    } else if ('ArrowLeft' === key) {
        s -= step;
        s = s < 0 ? .01 : s; // Cannot use `0` because it will be normalized to red :(
        picker.set(...[...P2RGB([h, s, v]), a]);
        e.preventDefault();
    } else if ('ArrowRight' === key) {
        s += step;
        s = s > 1 ? 1 : s;
        picker.set(...[...P2RGB([h, s, v]), a]);
        e.preventDefault();
    } else if ('ArrowUp' === key) {
        v += step;
        v = v > 1 ? 1 : v;
        picker.set(...[...P2RGB([h, s, v]), a]);
        e.preventDefault();
    }
}, false);

No need to add special method to move the cursor by arrow keys. Changing the saturation and value is enough.

@bongobongo
Copy link
Author

That was quick!!!
Excellent work.
Just changed this:
v -= .05;
to
v -= step;
And similar change for the other three directions, and it worked with the step value.

Now I can probably write the code for moving it diagonally myself.

Much appreciated!!!!

@bongobongo
Copy link
Author

bongobongo commented Oct 8, 2021

Is it possible to link the eventlistener keydown to both the i tag in the color-picker:sv and i tag in color-picker:h (instead of picker.source) ?
So, if i tag of sv has focus, then react to all four directions in that pane, and if i tag of h has focus - then react to up and down directions in hue pane.
This way it is possible to control more presicely the stepping of the color-picker:h,
It would also be cool if possible to enter an exact number for setting the hue (startingpoint), in some way or another.

@taufik-nurrohman
Copy link
Owner

taufik-nurrohman commented Oct 8, 2021

That <i> tag movement was faked and does not have any event listener attached to it. Instead, the event listeners were attached to the container. The <i> tag then moved along based on the pointer offset relative to the container. That’s why you can just click anywhere in the container and expect the <i> tag to immediately moved to the clicked position.

It was only for presentational purpose.

@taufik-nurrohman
Copy link
Owner

By the way, here is the final result. It is still buggy because of the color value rounding that occurs. Maybe it will be more precise if the pointer can be moved directly without any rounding and normalization. The rounding process can then be done in the color value. People usually don’t notice that. But the pointer position seems to be easily noticed.

/* Fake focus state */
.color-picker.h-is-selected .color-picker\:h div,
.color-picker.sv-is-selected .color-picker\:sv div,
.color-picker.a-is-selected .color-picker\:a div {
  outline: 2px solid rgba(0, 0, 255, .5);
  outline-offset: -2px;
}
const picker = new CP(document.querySelector('input'));

function P2RGB(a) {
    let h = +a[0],
        s = +a[1],
        v = +a[2],
        r,
        g,
        b,
        i,
        f,
        p,
        q,
        t;
    i = Math.floor(h * 6);
    f = h * 6 - i;
    p = v * (1 - s);
    q = v * (1 - f * s);
    t = v * (1 - (1 - f) * s);
    i = i || 0;
    q = q || 0;
    t = t || 0;
    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 [r * 255, g * 255, b * 255];
}

function RGB2HSV(a) {
    let r = +a[0] / 255,
        g = +a[1] / 255,
        b = +a[2] / 255,
        max = Math.max(r, g, b),
        min = Math.min(r, g, b),
        h,
        s,
        v = max,
        d = max - min;
    s = max === 0 ? 0 : d / max;
    if (max === min) {
        h = 0; // Achromatic
    } else {
        switch (max) {
            case r:
                h = (g - b) / d + (g < b ? 6 : 0);
                break;
            case g:
                h = (b - r) / d + 2;
                break;
            case b:
                h = (r - g) / d + 4;
                break;
        }
        h /= 6;
    }
    return [h, s, v];
}

picker.on('change', function(r, g, b, a) {
    this.source.value = this.color(r, g, b, a);
});

const step = .05; // Adjust the color step here!

let tabIndex = 0; // 0: sv, 1: h, 2: a

// Reset tab index on focus
picker.source.addEventListener('focus', function() {
    tabIndex = 0;
    picker.self.classList.add('sv-is-selected');
    picker.self.classList.remove('a-is-selected');
    picker.self.classList.remove('h-is-selected');
});

picker.source.addEventListener('keydown', function(e) {
    let key = e.key,
        stop = false,
        update = false;
    if (picker.visible) {
        // Press `Escape` to close the color picker panel
        if ('Escape' === key) {
            picker.exit();
            picker.source.focus();
            picker.source.select();
            stop = true;
        // Press `Tab` or `Shift+Tab` to cycle between color picker controls
        } else if ('Tab' === key) {
            if (e.shiftKey) {
                tabIndex = tabIndex < 1 ? 2 : tabIndex - 1;
            } else {
                tabIndex = tabIndex > 1 ? 0 : tabIndex + 1;
            }
            // console.info(tabIndex);
            picker.self.classList.remove('a-is-selected');
            picker.self.classList.remove('h-is-selected');
            picker.self.classList.remove('sv-is-selected');
            picker.self.classList.add(['sv', 'h', 'a'][tabIndex] + '-is-selected');
            stop = true;
        }
        if (stop) {
            e.preventDefault();
            return;
        }
    }
    let [r, g, b, a] = picker.get();
    let [h, s, v] = RGB2HSV([r, g, b]);
    // Saturation/Value
    if (0 === tabIndex) {
        if ('ArrowDown' === key) {
            v -= step;
            v = v < 0 ? .01 : v; // Cannot use `0` because it will be normalized to red :(
            stop = update = true;
        } else if ('ArrowLeft' === key) {
            s -= step;
            s = s < 0 ? .01 : s; // Cannot use `0` because it will be normalized to red :(
            stop = update = true;
        } else if ('ArrowRight' === key) {
            s += step;
            s = s > 1 ? 1 : s;
            stop = update = true;
        } else if ('ArrowUp' === key) {
            v += step;
            v = v > 1 ? 1 : v;
            stop = update = true;
        }
    // Hue
    } else if (1 === tabIndex) {
        if ('ArrowDown' === key) {
            h -= step;
            h = h < 0 ? 0 : h;
            stop = update = true;
        } else if ('ArrowUp' === key) {
            h += step;
            h = h > 1 ? 1 : h;
            stop = update = true;
        }
    // Alpha
    } else if (2 === tabIndex) {
        if ('ArrowDown' === key) {
            a -= .01;
            a = a < 0 ? 0 : a;
            stop = update = true;
        } else if ('ArrowUp' === key) {
            a += .01;
            a = a > 1 ? 1 : a;
            stop = update = true;
        }
    }
    stop && e.preventDefault();
    update && picker.enter().set(...[...P2RGB([h, s, v]), a]);
}, false);

@bongobongo
Copy link
Author

bongobongo commented Oct 8, 2021

UPDATE: added a third and fourth issue: Kind of working - but found some issues that I hope can be fixed:

  1. If I press-release an arrow key - if saturation or hue has focus, the whole picker blink.
    If I hold one of the arrow keys down, the whole picker is hidden as long as the key is down.
    How do I get it to work without the hide and show of the picker?

  2. I also notice that if the hue has focus and respond to up down arrows, and I then click one of the predefined colors below the picker, then hue indicator is moved to correct place, but then hue is no longer responding to up and down arrows. Have to shift focus to saturation and back again to hue to make it work with up and down keys again.

  3. Click somewhere in the middele of the saturation area. Then use arrow keys to go downward. Then notice how hue changes on the last step that takes you below lowest point of the saturation area. The hue should not be changed.

    Same change in hue can be seen if clicking e.g. in the middle of the saturation area, and using left key to move left. When you get close to left edge and/or passed it - then hue may change.

  4. Doing the same as in issue 3, but clicking closer to the right border of the saturation area, above the middle, and then using down key, the down direction is changed towards right. More to the right the lower you get. Ideally it should stay on the same vertical line.

@bongobongo
Copy link
Author

Done some more testing and found a couple of more issues.
Added them to the issue comment above - so it's all in same spot.

@taufik-nurrohman
Copy link
Owner

taufik-nurrohman commented Oct 8, 2021

@bongobongo Try this one. It moves the pointer by pixels → https://taufik-nurrohman.js.org/color-picker/tweak/keyboard.html

@bongobongo
Copy link
Author

Just tested it.
Found one issue:

  1. click inside hue.
  2. Move indicator up with arrow key - to see color change in saturation area.
  3. then, when click inside saturation area, hue is reset to where it was on last click in hue area.
    It should stay put.

and this was missing:
if (tabIndex == 2) // this is when alpha pane is removed
tabIndex = 0;

Except for this - it really works very well

@taufik-nurrohman
Copy link
Owner

taufik-nurrohman commented Oct 10, 2021

  • This should fix the hue/opacity bug.
  • This should fix the hidden alpha control.

@bongobongo
Copy link
Author

bongobongo commented Oct 10, 2021

Tested it again.
Last fix introduced two issues:
After changing hue with up or down key -

  1. when using arrow keys in saturation to move indcator vertically, the movement is no longer allways on a straight line.
  2. when using arrow keys in saturation to move indicator vertically, the indikator in hue is sometimes moved up or down (not much), but should stay put.

The issues can be seen here:
https://taufik-nurrohman.js.org/color-picker/tweak/keyboard.html

@taufik-nurrohman
Copy link
Owner

taufik-nurrohman commented Oct 10, 2021

That’s because of the color data rounding done by picker.set() method on every key-up, caused the pointers to be re-positioned. What browser are you using? I am using Firefox and the difference is not that much.

Actually, I can fix this using debounce function but I am afraid that this solution won’t last long.

Can you confirm that the movement will be straight on the line when you keep pressing the arrow key?

@bongobongo
Copy link
Author

bongobongo commented Oct 10, 2021

If I keep the down arrow pressed, it keeps on a straight line. When it reaches bottom of saturation - the marker is moved to bottom left.
I now notice that this eratic movement (when using up/down arrow) happens regardless of using arrows to change hue, or if I click inside the hue.
I see now that it happens regardless of changing the hue or not.
The eratic movement is more visible the lower you get in saturation area.
I'm testing in Chrome.

UPDATE: Now also tested in Firefox.
See same issues there.
Now I also notice that when moving inside saturation horizontally - it looks like it is on a straight line, but I somtimes see small eratic movements of the hue indicator (which should not move).

@taufik-nurrohman
Copy link
Owner

When it reaches bottom of saturation - the marker is moved to bottom left.

Yes, because there is no point in moving the pointer to the left or right at the bottom of the saturation. It will always be black.

@bongobongo
Copy link
Author

bongobongo commented Oct 10, 2021

"....there is no point in moving the pointer to the left or right at the bottom of the saturation. It will always be black..."
There is actually a point to do that.
If you want to make a palette of colors. Then one nice way to do this is to start up somewhere, and to left:
Then use arrow right to move towards right border of saturation area. Then at some point you may start to move down. For me I would probably not go all the way down to the black area - because I want shades of whatever hue I'm in, not white and not black.
But the fact that the marker does not follow a stright line up or down, regardless if it has hit black, make me think that this should be fixed.
I'm using the color-picker at font-size 40px. When the picker has this size, then the issues I have mentioned here is more visible as well.

Anyway... before this last "fix" - using arrow down, did not make marker change direction nor to pop to a bottom corner. It is counterinutitive - and not very functional - in my opinion.

@taufik-nurrohman
Copy link
Owner

taufik-nurrohman commented Oct 10, 2021

I’m sure the source that causes that eratic movement comes from RGB2HSV function. But picker.set() function only look at the provided RGB input. And this kind of input does not have any information about the direction in the UI.

Including the hue direction: both 0 and 100% distance from top have the same red color. Because hue is basically a circle and so 0° is equal to 360°. If we, for example put the hue slider to the top to get a red color and assuming that normally the default red placement is at the bottom, then if we refresh the color picker, it is likely the pointer will be placed at bottom because the color converter only understand about the input color. And input color does not have directions.

@taufik-nurrohman
Copy link
Owner

I am afraid that I cannot fix the buggy pointer position at the bottom unless I use some hack to store the exact pointer position somewhere. But I can try to find a better “RGB to HSV” converter function.

By the way, if you ever know other JavaScript color picker application that has keyboard navigation support, please let me know. Maybe I can implement some feature from there for this tweak.

@taufik-nurrohman
Copy link
Owner

before this last "fix" - using arrow down, did not make marker change direction nor to pop to a bottom corner.

If you remove the source.addEventListener('keyup', function(e) {}) part, the movement bug will not happen, but the internal color value will not update.

@bongobongo
Copy link
Author

bongobongo commented Oct 10, 2021

"...I am afraid that I cannot fix the buggy pointer position at the bottom unless I use some hack to store the exact pointer position somewhere. .."
But can you fix that the pointer do not follow a straight line down or up, regardless of beeing at bottom of saturation? And that the hue indicator is moving up, or down (little) - when you use arrow up or down?
These are the main issues now.

@bongobongo
Copy link
Author

bongobongo commented Oct 10, 2021

Also want to mention that when I get the issue (without this last "fix" implemented, as of today),
that is - when I move the hue using up/down arrow, and then give focus to saturation - and then click in saturation and hue get changed.
If I move hue e.g. up with arrow key, and then click on that last hue position - (click on the hue where indicator is) and then shift focus to saturation and click in saturation - the hue is not changed.
So... is it possible dynamically to trigger click on the hue - on the exact location of the hue after each move of hue with up/down arrow. If thats possible, then that could perhaps fix the issue. Then the fix of today is no longer needed.

Regardless if that is possible or not:
There seem to be running other code (in relation to update the state of the picker) when moving hue indicator with a click and drag, and moving hue indicator by arrow keys. It looks that something is happening when doing a click and drag, that is not happening when doing arrow up/down.

@taufik-nurrohman
Copy link
Owner

taufik-nurrohman commented Oct 10, 2021

Ended up storing the pointer position before picker.set() and then restore it.

Not the cleanest solution, and sometimes there still small movement in the SV control when doing mixed keyboard and mouse interaction. But at least I can keep the pointer position now.

4c9f1fc#diff-6b752c354cc55775f0bc979cbd9fc9f2716158d743fddd3515dd15f7f8fa796dR230

You will still, however, get the color normalization fired when you hide the color picker element and then show it again.

@bongobongo
Copy link
Author

bongobongo commented Oct 10, 2021

Did a quick test in Chrome.
I use font-size 40 for the picker container.

  1. If I click inside saturation area. And move down 3 steps, and back up 3 steps again. Then I'm not back at the same color. Is it not possible to make it so it goes back to where it startet?

  2. I have focus on saturation area. Then I mouse-click in hue area, away from current indicator position and below current indicator position.
    First time I now use arrow key, then it pops away in the hue area to position before first arrow step.

UPDATE: I also notice that after the behaviour ref. #2 above, if I click somewhere in saturation area, and use arrow up - it moves first time much longer than it should.

I tested this on your testpage.
But when I test using the default font-size (the smaller version of the picker) - I did not see these issues:
https://taufik-nurrohman.js.org/color-picker/tweak/keyboard.html

@bongobongo
Copy link
Author

bongobongo commented Oct 10, 2021

I wrote this some comments back, after you introduced an improved version:
"Just tested it.
Found one issue:

click inside hue.
Move indicator up with arrow key - to see color change in saturation area.
then, when click inside saturation area, hue is reset to where it was on last click in hue area.
It should stay put..."

I looked a bit more on this version now, and for me - the most imporant thing is that it is "pixel perfect" in the saturation area - when you use the arrow keys.
That version accomplish that goal.
The only issue with that version was that navigating inside the hue area using arrows, caused a colorshift in saturation when saturation was cllicked.
But using mouseclick or mousedrag in hue area - does not cause any problems at all.

But after testing this feature, I realize that it's not very important to be able to use arrow keys to select the hue. If I want to select a specific hue, then I can allways enter a hex code in the input field and hit enter (after adding an onenter event to input field).

So - perhaps - remove arrow navigation for hue and alpha for that version - and voila - there it is - a fully functional - and "pixel perfect" color picker with arrow navigation.

@taufik-nurrohman
Copy link
Owner

taufik-nurrohman commented Oct 11, 2021

Are you sure you are not testing on the cached version of the page? I suggest you to clone this repository and test it locally via file:// protocol. Mine works just fine.

If I click inside saturation area. And move down 3 steps, and back up 3 steps again. Then I'm not back at the same color. Is it not possible to make it so it goes back to where it started?

Peek.2021-10-11.06-44.mp4
Peek.2021-10-11.06-57.mp4

I have focus on saturation area. Then I mouse-click in hue area, away from current indicator position and below current indicator position. First time I now use arrow key, then it pops away in the hue area to position before first arrow step.

Peek.2021-10-11.06-50.mp4
Peek.2021-10-11.06-49.mp4
Peek.2021-10-11.06-59.mp4

@taufik-nurrohman
Copy link
Owner

Here with exact click event.

Peek.2021-10-11.07-18.mp4

@taufik-nurrohman
Copy link
Owner

Sigh! Found the issue after dragging.

Peek.2021-10-11.07-26.mp4

@bongobongo
Copy link
Author

bongobongo commented Oct 11, 2021

As noted in my previous comment above. After testing this kind of navigation for a while now, I do not think there is a real need for using arrows to navigate in hue and opacity area. Using click and click-drag, is more than good enough for me. And if one need a specific hue, one could allways get that by entering a specific color in the input field + enter (with onenter event enabled), or e.g. click a predefined color. So if there is problems getting this to work - consistently - why not just drop those two features? The most important thing for me is that it's possible to repeat previous steps (using arrow navigation in saturation) - with excact color accuracy - when using the arrow keys in saturation area.

@taufik-nurrohman
Copy link
Owner

taufik-nurrohman commented Oct 11, 2021

@bongobongo If your goal is to make a specific palette composition, you can always hook the change event to the pre-defined color palletes tweak so that it will generate solid tint/shade color list which you can click.

You will need some tint/shade function to convert the current color into array of tint/shade color pallete. Like this one. I can make an example for that.

But this “keyboard accessible color picker” thing is a good idea in my opinion.

@bongobongo
Copy link
Author

bongobongo commented Oct 11, 2021

"...tint/shade function to convert the current color into array of tint/shade color pallete..."
Great...
An example of that would be useful.

@taufik-nurrohman
Copy link
Owner

2021-10-12-20:29:39

HTML

<input type="text">

CSS

.color-pallete {
  border: 1px solid #000;
  border-top-width: 0;
  display: flex;
}

.color-pallete .color {
  cursor: pointer;
  flex: 1;
  height: 1.5em;
}

.color-pallete .color:first-child {
  border-right: 1px solid #000;
  min-width: 1.5em;
}

.color-pallete .color:last-child {
  border-left: 1px solid #000;
  min-width: 1.5em;
}

JS

function createColorPicker(source, onChange) {
    if (source.hasColorPallete) {
        return;
    }
    source.hasColorPallete = true;
    function createColorBlends(hex1, hex2, amt) {
        let rgb1 = hex1.match(/../g),
            rgb2 = hex2.match(/../g),
            v = i => parseInt(i, 16),
            r = Math.round(v(rgb1[0]) + (v(rgb2[0]) - v(rgb1[0])) * amt).toString(16).padStart(2, '0'),
            g = Math.round(v(rgb1[1]) + (v(rgb2[1]) - v(rgb1[1])) * amt).toString(16).padStart(2, '0'),
            b = Math.round(v(rgb1[2]) + (v(rgb2[2]) - v(rgb1[2])) * amt).toString(16).padStart(2, '0'),
            a = rgb1[3] || "";
        return r + g + b + a;
    }
    function createColorPallete(picker, hex1, hex2, count) {
        const pallete = document.createElement('span');
        pallete.className = 'color-pallete';
        for (let color, i = 0; i < count; ++i) {
            color = document.createElement('span');
            color.addEventListener('click', function(e) {
                let value = this.title;
                picker.source.value = value;
                // picker.set.apply(picker, CP.HEX(value));
                onChange.apply(picker, CP.HEX(value));
                e.preventDefault();
                e.stopPropagation();
            }, false);
            color.className = 'color';
            color.style.backgroundColor = color.title = '#' + createColorBlends(hex1, hex2, i / (count - 1));
            pallete.appendChild(color);
        }
        picker.self.appendChild(pallete);
        return pallete.children;
    }
    const picker = new CP(source);
    let color = picker.source.value.slice(1); // HEX color code without the `#` part
    let mix1 = createColorPallete(picker, color, '808080', 12),
        mix2 = createColorPallete(picker, color, 'ffffff', 12),
        mix3 = createColorPallete(picker, color, '000000', 12);
    function updateColorPallete(value) {
        for (let i = 0, j = mix1.length; i < j; ++i) {
            mix1[i].style.backgroundColor = mix1[i].title = '#' + createColorBlends(value.slice(1), '808080', i / (j - 1));
        }
        for (let i = 0, j = mix2.length; i < j; ++i) {
            mix2[i].style.backgroundColor = mix2[i].title = '#' + createColorBlends(value.slice(1), 'ffffff', i / (j - 1));
        }
        for (let i = 0, j = mix3.length; i < j; ++i) {
            mix3[i].style.backgroundColor = mix3[i].title = '#' + createColorBlends(value.slice(1), '000000', i / (j - 1));
        }
    }
    picker.on('change', function(r, g, b, a) {
        let value = this.color(r, g, b, a);
        this.source.value = value;
        onChange.apply(this, CP.HEX(value));
        updateColorPallete(value);
    });
    picker.on('enter', function(r, g, b, a) {
        updateColorPallete(this.color(r, g, b, a));
    });
}

createColorPicker(document.querySelector('input'), function(r, g, b, a) {
    document.documentElement.style.backgroundColor = this.color(r, g, b, a);
});

@bongobongo
Copy link
Author

bongobongo commented Oct 12, 2021

Great, but have the issues with arrow navigation been fixed?
There are still issues here:
https://taufik-nurrohman.js.org/color-picker/tweak/keyboard.html

The two most important one now after short testing:

  1. Click in saturation area. Move some steps in one direction, and same number of steps back again - does not end up on same color code.
  2. Use of arrow up or down after mousedrag in hue, make indicator and hue shift.

@taufik-nurrohman
Copy link
Owner

I will try to fix it later after #73.

@bongobongo
Copy link
Author

bongobongo commented Oct 13, 2021

Found an issue when using arrows in saturation area.
In your example here:
https://taufik-nurrohman.js.org/color-picker/tweak/keyboard.html
It takes 32 steps to get from left side to right side.
If changing fontsize to 40px
then there takes approximately 80 steps to cover the same distance.

Ideally - for me, one step, should move the same distance - relative to size of saturation area.
Example 1:
Picker size without any font-size modification (same URL as above)
Move right one step, from color: #b8b889
Takes me to color: #b8b883

Example 2:
With font-size 40px for picker container:
One step from color: #b8b889
Takes me to another color: #b8b887
In my opinion I should get to excact same color as in example 1.

@taufik-nurrohman
Copy link
Owner

It takes 32 steps to get from left side to right side.
If changing fontsize to 40px
then there takes approximately 80 steps to cover the same distance.

The step parameter in enableKeyboardControls() function determines movement distance on every key stroke.

enableKeyboardControls(picker, 5); // Move cursor by `5px`

To make it dynamic, you can measure it based on the current font size, but you can’t get it right easily.

const step = parseInt(getComputedStyle(picker.self).getPropertyValue('font-size'), 10);

enableKeyboardControls(picker, step);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants