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

Relative lighten/darken #90

Open
michal-kurz opened this issue May 11, 2022 · 6 comments
Open

Relative lighten/darken #90

michal-kurz opened this issue May 11, 2022 · 6 comments

Comments

@michal-kurz
Copy link

michal-kurz commented May 11, 2022

I replaced color with colord in my project, but found out I don't get the same results, because they treat lighten() and darken() differently: While color shifts color by a fraction of itself, while colord shifts color by a fraction of whole spectrum.

I prototyped this wrapper to implement this functionality:

import { AnyColor, Colord, HslaColor } from 'colord'
import { colord as nativeColord } from 'colord'

export const clamp = (number: number, min = 0, max = 1): number => {
  return number > max ? max : number > min ? number : min
}

export const lightenRelative = (color: AnyColor | Colord, amount: number): HslaColor => {
  const hsla = nativeColord(color).toHsl()

  return {
    h: hsla.h,
    s: hsla.s,
    l: clamp(hsla.l * (1 + amount), 0, 100),
    a: hsla.a
  }
}

class ExtendedColord extends Colord {
  public lightenRelative(amount: number = 0.1) {
    return extendedColord(lightenRelative(this.rgba, amount))
  }

  public darkenRelative(amount: number = 0.1) {
    return extendedColord(lightenRelative(this.rgba, -amount))
  }
}

export const extendedColord = (input: AnyColor | Colord): ExtendedColord => {
  if (input instanceof ExtendedColord) return input
  return new ExtendedColord(input as any)
}

and I will use it for now, but I'd prefer to merge it into the library itself - are you interested in this functionality? I would love to make a PR if so.

Thank you 🙏

@michal-kurz michal-kurz changed the title Feature request: reative darken/lighten PLACEHOLDER - accidentally submitted the issue prematurely May 11, 2022
@michal-kurz michal-kurz changed the title PLACEHOLDER - accidentally submitted the issue prematurely Relative lighten/darken May 11, 2022
@EricRovell
Copy link
Contributor

EricRovell commented Jun 5, 2022

@michal-kurz, thank you for the idea, I think this is a valid point.
I have implemented it via optional relative: Boolean flag,
Now we have to wait for the library owner for code review.

@Nantris
Copy link

Nantris commented Aug 5, 2022

@EricRovell thanks for the great PR!

@omgovich thanks for your incredible work! Do you have an interest in merging that PR? (#91)? We're also moving from Color to colord and this is a really big sticking point for us - it would be days of work to overhaul our project to work without a relative lighten feature.

@Nantris
Copy link

Nantris commented Aug 6, 2022

I think a relative saturate feature would also be useful. Perhaps this is not necessarily best practice in color manipulation or something, but converting from Qix's Color library - the outputs of saturate() vary wildly and I'm not sure how we could easily transition to colord as a result.

Qix's library does the math like this:

	saturate(ratio) {
		const hsl = this.hsl();
		hsl.color[1] += hsl.color[1] * ratio;
		return hsl;
	},

Assuming I did my math right, the outputs vary as below:

image

I really want to switch to colord but it didn't really make sense for us to put the time into switching to begin with. Nonetheless I decided to undertake the task, and now I'm hoping I'll be able to complete it via an addition of a relative saturate function, or maybe someone has advice for an alternative method for us to get similar outputs?

I know colord is more accurate in its outputs so some of our colors vary by like 1% from our previous values, but colors affected by saturate aren't even close due to relative vs absolute.

@EricRovell
Copy link
Contributor

@slapbox the author is quite busy these days, unfortunately. I think if you need this PR you can extend the Colord class and override the functionality for your needs🤔

@Nantris
Copy link

Nantris commented Aug 9, 2022

@EricRovell makes sense! It would be really great to get your PR merged soon, but short of that, maybe consider releasing a plugin for your PR. If I make a plugin for relative saturate, I'll do the same. Even just a gist of our code would surely be a big help to other devs if PRs can't be merged for an extended time.

@Nantris
Copy link

Nantris commented Aug 10, 2022

Here's a very basic solution for anyone who needs it. It possibly has some mistake in it, as our app still doesn't look as expected, but perhaps that's some other unrelated issue.

import { Colord } from 'colord';

// Copied from colord utils
export const clamp = (number: number, min = 0, max = 1): number =>
  number > max ? max : number > min ? number : min;

// Copied from https://github.com/Qix-/color/blob/master/index.js
function saturateRelative(hslColor, ratio) {
  const { s: saturation } = hslColor;
  const newSaturation = saturation + saturation * ratio;
  return { ...hslColor, s: newSaturation };
}

// Copied from https://github.com/Qix-/color/blob/master/index.js
function desaturateRelative(hslColor, ratio) {
  const { s: saturation } = hslColor;
  const newSaturation = saturation - saturation * ratio;
  return { ...hslColor, s: newSaturation };
}

const relativeSaturatePlugin = ColordClass => {
  ColordClass.prototype.saturate = function (ratio = 1) {
    const mixture = saturateRelative(this.toHsl(), ratio);
    return new ColordClass(mixture);
  };
  ColordClass.prototype.desaturate = function (ratio = 1) {
    const mixture = desaturateRelative(this.toHsl(), ratio);
    return new ColordClass(mixture);
  };
};

function lightenDarken(hslColor, amount, relative) {
  const l = relative ? hslColor.l * (1 + amount) : hslColor.l + amount * 100;
  return { ...hslColor, l: clamp(l, 0, 100) };
}

const relativeLightenDarkenPlugin = ColordClass => {
  ColordClass.prototype.lightenRel = function (amount, relative = true) {
    const adjusted = lightenDarken(this.toHsl(), amount, relative);
    return new ColordClass(adjusted);
  };

  ColordClass.prototype.darkenRel = function (amount, relative = true) {
    const adjusted = lightenDarken(this.toHsl(), 0 - amount, relative);
    return new ColordClass(adjusted);
  };
};

export { relativeSaturatePlugin, relativeLightenDarkenPlugin };

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

No branches or pull requests

3 participants