😎 Give your xaringan slides some style
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
4.8KB

  1. #' @title Generate lighter or darker version of a color
  2. #' @description Produces a linear blend of the color with white or black.
  3. #' @param color_hex A character string representing a hex color
  4. #' @param strength The "strength" of the blend with white or black,
  5. #' where 0 is entirely the original color and 1 is entirely white
  6. #' (`lighten_color()`) or black (`darken_color()`).
  7. #' @examples
  8. #' blue <- "#0e6ba8"
  9. #' blue_light <- lighten_color(blue, strength = 0.33)
  10. #' blue_dark <- darken_color(blue, strength = 0.33)
  11. #'
  12. #' if (requireNamespace("scales", quietly = TRUE)) {
  13. #' scales::show_col(c(blue_light, blue, blue_dark))
  14. #' }
  15. #'
  16. #' @name lighten_darken_color
  17. NULL
  18. #' @rdname lighten_darken_color
  19. #' @export
  20. lighten_color <- function(color_hex, strength = 0.7) {
  21. stopifnot(strength >= 0 && strength <= 1)
  22. color_rgb <- col2rgb(color_hex)[, 1]
  23. color_rgb <- (1 - strength) * color_rgb + strength * 255
  24. rgb(color_rgb[1], color_rgb[2], color_rgb[3], maxColorValue = 255)
  25. }
  26. #' @rdname lighten_darken_color
  27. #' @export
  28. darken_color <- function(color_hex, strength = 0.8) {
  29. stopifnot(strength >= 0 && strength <= 1)
  30. color_rgb <- col2rgb(color_hex)[, 1]
  31. color_rgb <- (1 - strength) * color_rgb
  32. rgb(color_rgb[1], color_rgb[2], color_rgb[3], maxColorValue = 255)
  33. }
  34. #' @title Add alpha to hex color
  35. #' @description Applies alpha (or opacity) to a color in hexadecimal form by
  36. #' converting opacity in the `[0, 1]` range to hex in the `[0, 255]` range
  37. #' and appending to the hex color.
  38. #' @inheritParams lighten_darken_color
  39. #' @param opacity Desired opacity of the output color
  40. #' @examples
  41. #' blue <- "#0e6ba8"
  42. #' blue_transparent <- apply_alpha(blue)
  43. #'
  44. #' if (requireNamespace("scales", quietly = TRUE)) {
  45. #' scales::show_col(c(blue, blue_transparent))
  46. #' }
  47. #'
  48. #' @export
  49. apply_alpha <- function(color_hex, opacity = 0.5) {
  50. paste0(color_hex, as.hexmode(round(255 * opacity, 0)))
  51. }
  52. adjust_value_color <- function(color_hex, strength = 0.5) {
  53. color_hsv <- rgb2hsv(col2rgb(color_hex))[, 1]
  54. color_hsv["v"] <- strength
  55. hsv(color_hsv[1], color_hsv[2], color_hsv[3])
  56. }
  57. #' Choose dark or light color
  58. #'
  59. #' Takes a color input as `x` and returns either the black or white color (or
  60. #' expression) if dark or light text should be used over the input color for
  61. #' best contrast. Follows W3C Recommendations.
  62. #'
  63. #' @references <https://stackoverflow.com/a/3943023/2022615>
  64. #' @param x The background color (hex)
  65. #' @param black Text or foreground color, e.g. "#222" or
  66. #' `substitute(darken_color(x, 0.8))`, if black text provides the best contrast.
  67. #' @param white Text or foreground color or expression, e.g. "#EEE" or
  68. #' `substitute(lighten_color(x, 0.8))`, if white text provides the best contrast.
  69. #' @examples
  70. #' light_green <- "#c4d6b0"
  71. #' contrast_green <- choose_dark_or_light(light_green)
  72. #' dark_purple <- "#381d2a"
  73. #' contrast_purple <- choose_dark_or_light(dark_purple)
  74. #'
  75. #' if (requireNamespace("scales", quietly = TRUE)) {
  76. #' scales::show_col(c(light_green, contrast_green, dark_purple, contrast_purple))
  77. #' }
  78. #'
  79. #' @export
  80. choose_dark_or_light <- function(x, black = "#000000", white = "#FFFFFF") {
  81. if (is_light_color(x)) eval(black) else eval(white)
  82. }
  83. is_light_color <- function(x) {
  84. # this function returns TRUE if the given color
  85. # is light-colored and requires dark text
  86. color_rgb <- col2rgb(x)[, 1]
  87. # from https://stackoverflow.com/a/3943023/2022615
  88. color_rgb <- color_rgb / 255
  89. color_rgb[color_rgb <= 0.03928] <- color_rgb[color_rgb <= 0.03928] / 12.92
  90. color_rgb[color_rgb > 0.03928] <- ((color_rgb[color_rgb > 0.03928] + 0.055) / 1.055)^2.4
  91. lum <- t(c(0.2126, 0.7152, 0.0722)) %*% color_rgb
  92. lum[1, 1] > 0.179
  93. }
  94. prepare_colors <- function(colors = NULL) {
  95. if (is.null(colors) || length(colors) < 1) return(NULL)
  96. if (is.null(names(colors))) {
  97. stop(
  98. "`colors` must have names corresponding to valid CSS classes",
  99. call. = FALSE
  100. )
  101. }
  102. if (any(grepl("\\s", names(colors)))) {
  103. stop(
  104. "Color names in `colors` must be valid CSS classes",
  105. " and cannot contain spaces",
  106. call. = FALSE)
  107. }
  108. if (any(grepl("[^[:alpha:]_-]", names(colors)))) {
  109. stop("Color names in `colors` must be valid CSS classes", call. = FALSE)
  110. }
  111. whisker::iteratelist(colors, "color_name")
  112. }
  113. full_length_hex <- function(x) {
  114. varname <- substitute(x)
  115. stop_not_hex <- function() {
  116. stop(str_wrap(
  117. "`", deparse(varname), "` is not a hexadecimal color: ", x, ". ",
  118. "If you used valid CSS colors in your xaringan theme, please convert ",
  119. "these colors to hexadecimal form, as this is the format required by ",
  120. "ggplot2."
  121. ), call. = FALSE)
  122. }
  123. if (!grepl("^#", x) || grepl("[^#0-9a-fA-F]", x)) {
  124. stop_not_hex()
  125. }
  126. x <- sub("^#", "", x)
  127. if (nchar(x) == 3) {
  128. x <- strsplit(x, character(0))[[1]]
  129. x <- rep(x, each = 2)
  130. x <- paste(x, collapse = "")
  131. } else if (nchar(x) != 6) {
  132. stop_not_hex()
  133. }
  134. paste0("#", x)
  135. }