😎 Give your xaringan slides some style
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

168 lines
5.6KB

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