css css3 background linear-gradients background-position

css - Usando valores porcentuales con posición de fondo en un gradiente lineal



css3 background (1)

¿Hay alguna manera de hacer que background-position tome valores de porcentaje? Actualmente, mi botón solo funciona con valores explícitos para width y background-position .

body { min-height: 100vh; display: flex; flex-direction: column; justify-content: space-around; align-items: center; } .button { display: flex; justify-content: center; align-items: center; text-decoration: none; color: white; font-weight: bold; width: 350px; height: 50px; border: 1px solid green; transition: background 0.5s; background-repeat: no-repeat; background-image: linear-gradient(to left, #2484c6, #1995c8 51%, #00bbce), linear-gradient(to right, #2484c6 0%, #1995c8 51%, #00bbce 76%); } .button-pixel { background-position: -350px 0px, 0px 0px; } .button-pixel:hover { background-position: 0px 0px, 350px 0px; } .button-percentage { background-position: -100% 0px, 0px 0px; } .button-percentage:hover { background-position: 0% 0px, 100% 0px; }

<a href="#" class="button button-pixel">In Pixel</a> <a href="#" class="button button-percentage">In Percentage</a>


TL; DR

Todos los valores de porcentaje utilizados con background-position son equivalentes cuando se usa un degradado como fondo, por lo que no verá ninguna diferencia. Debe especificar un background-size diferente del background-size del contenedor:

body { display: flex; flex-direction: column; justify-content: space-around; align-items: center; min-height:90vh; } .button { text-decoration: none; color: white; font-weight: bold; width: 350px; height: 50px; text-align:center; transition: background 0.5s; background-repeat: no-repeat; background-image: linear-gradient(to left, #2484c6, #1995c8 51%, #00bbce), linear-gradient(to right, #2484c6 0%, #1995c8 51%, #00bbce 76%); background-size:200% 100%; /*Changed this*/ } .button-pixel { background-position: -350px 0px, 0px 0px; } .button-pixel:hover { background-position: 0px 0px, 350px 0px; } .button-percentage { background-position: 100% 0px, 0px 0px; } .button-percentage:hover { background-position: 0% 0px, 100% 0px; }

<a href="#" class="button button-pixel">Pixel</a> <a href="#" class="button button-percentage">Percentage</a>

¿Cómo funciona la posición de fondo?

Usemos una imagen clásica para explicar cómo funciona la background-position .

Cuando se utilizan valores de píxeles, la referencia es la esquina superior / izquierda de la imagen, sea cual sea el tamaño. Es como usar la top / left con un elemento posicionado:

.box { width:200px; height:200px; background-image:url(https://picsum.photos/100/100?image=1069); border:1px solid; background-position:0 0; background-repeat:no-repeat; position:relative; animation:back 5s infinite linear alternate; } .box:before { content:""; position:absolute; top:0; left:0; width:10px; height:10px; background:red; z-index:1; animation:change 5s infinite linear alternate; } @keyframes back{ to{background-position:200px 200px;} } @keyframes change{ to{top:200px; left:200px;} }

<div class="box"> </div>

Cuando se usan valores de porcentaje, la referencia es diferente de cuando se usan valores de píxeles; Ya no es la esquina superior / izquierda:

.box { width:200px; height:200px; background-image:url(https://picsum.photos/100/100?image=1069); border:1px solid; background-position:0% 0%; background-repeat:no-repeat; position:relative; animation:back 5s infinite linear alternate; } .box:before { content:""; position:absolute; top:0; left:0; width:10px; height:10px; background:red; z-index:1; animation:change 5s infinite linear alternate; } @keyframes back{ to{background-position:100% 100%;} } @keyframes change{ to{top:200px; left:200px;} }

<div class="box"> </div>

En este caso, debemos considerar dos parámetros: el tamaño del contenedor Y el tamaño de la imagen. Aquí hay una ilustración de cómo funciona (tomé una background-position igual a 30% 30% ):

Primero, consideramos la imagen para encontrar el punto de referencia que usaremos para colocar la imagen. Es el punto dentro de la imagen que se coloca a 30% 30% desde la esquina superior / izquierda considerando el tamaño de la imagen (como se define con las líneas verdes). Luego, colocamos ese punto dentro del contenedor a 30% 30% de la esquina superior / izquierda considerando el tamaño del contenedor .

De esta lógica, podemos identificar claramente algunos casos triviales como

50% 50% (centro) 100% 100% (abajo a la derecha) 100% 0% (arriba a la derecha)

1 Abajo la lista completa de valores.

Ahora está claro que si el tamaño de la imagen es igual al tamaño del contenedor , nada sucederá simplemente porque todas las posiciones son equivalentes . La parte superior / izquierda de la imagen ya está en la parte superior / izquierda (0% 0%) del contenedor, el centro ya está en el centro (50% 50%), etc.

.box { width:200px; height:200px; background-image:url(https://picsum.photos/200/200?image=1069); border:1px solid; background-position:0% 0%; background-repeat:no-repeat; position:relative; animation:back 5s infinite linear alternate; } .box:before { content:""; position:absolute; top:0; left:0; width:10px; height:10px; background:red; z-index:1; animation:change 5s infinite linear alternate; } @keyframes back{ to{background-position:100% 100%;} } @keyframes change{ to{top:calc(100% - 5px); left:calc(100% - 5px);} }

<div class="box"> </div>

La lógica anterior es la misma cuando se aplica a los gradientes, ya que los gradientes se consideran imágenes y, de manera predeterminada, si no especifica un background-size un degradado será el tamaño de su contenedor, a diferencia de cuando se usa una imagen.

Si nos referimos a la specification del background-size , podemos ver cómo surge su problema:

Nota:

Si ambos valores son automáticos, entonces se debe usar el ancho y / o la altura intrínsecos de la imagen , si corresponde, la dimensión faltante (si existe) se comporta como automática como se describe anteriormente. Si la imagen no tiene un ancho intrínseco ni una altura intrínseca, su tamaño se determina como para contener .

Y:

Contiene

Escale la imagen, al tiempo que conserva su relación de aspecto intrínseca (si existe), hasta el tamaño más grande, de modo que tanto su ancho como su altura puedan caber dentro del área de posicionamiento del fondo.

Y también:

Una imagen de mapa de bits (como JPG) siempre tiene dimensiones y proporciones intrínsecas .

CSS <gradient> s no tienen dimensiones intrínsecas o proporciones intrínsecas . ref

Una imagen siempre tiene valores intrínsecos, por lo que en la mayoría de los casos no tendrá el mismo tamaño que su contenedor, por lo que background-position con unidades porcentuales tendrá un efecto. Pero los gradientes no tienen valores intrínsecos, por lo tanto, las dimensiones de un gradiente serán iguales al tamaño de su contenedor, y background-position con valores porcentuales nunca funcionará a menos que especifiquemos un background-size diferente de las dimensiones de su contenedor.


Más en profundidad

Vimos en los ejemplos anteriores cómo funciona el background-size cuando se usan valores entre 0% y 100% , pero ¿qué hay de usar valores negativos o un valor mayor que 100% ? La lógica es la misma, pero encontrar el punto de referencia será más complicado.

Valores negativos (<0%)

Supongamos que queremos colocar un fondo en -50% 0 . En este caso el punto de referencia estará fuera de la imagen. Aquí hay un ejemplo:

.box { width:200px; height:200px; border:1px solid; background:url(https://picsum.photos/100/100?image=1069) -50% 0/100px 100px no-repeat; }

<div class="box"></div>

Como podemos ver en la ilustración, primero consideramos el -50% de la imagen, que es -50px , para definir nuestro punto de referencia (es decir, -50px desde el borde izquierdo de la imagen). Luego colocamos ese punto en -50% considerando el tamaño del contenedor (-100px desde el borde izquierdo del contenedor). Luego dibujamos la imagen, y obtenemos el resultado anterior. Sólo 100px de la imagen es visible.

También podemos notar que los valores porcentuales negativos se comportarán igual que los valores fijos negativos cuando el tamaño de la imagen sea más pequeño que el tamaño del contenedor (ambos desplazarán la imagen a la izquierda). En este caso, -50% 0 es lo mismo que -50px 0 .

.box { width:200px; height:200px; display:inline-block; border:1px solid; background:url(https://picsum.photos/100/100?image=1069) -50% 0/100px 100px no-repeat; } .alt{ background:url(https://picsum.photos/100/100?image=1069) -50px 0/100px 100px no-repeat; }

<div class="box"> </div> <div class="box alt"> </div>

Si, por ejemplo, aumentamos el tamaño de la imagen a 150px 150px , -50% 0 será el mismo que -25px 0 .

Cuando hacemos el tamaño más grande que el contenedor , los valores negativos comenzarán a desplazar la imagen a la derecha (como con los valores de píxeles positivos), lo cual es lógico ya que el 50% de la imagen aumentará mientras que el 50% del contenedor seguirá siendo el mismo.

Si consideramos la ilustración anterior, es como aumentar la línea verde superior hasta que sea más grande que la inferior. Así que el signo solo no es suficiente para saber cómo se cambiará la imagen de fondo; Necesitamos también considerar el tamaño.

.box { width:200px; height:200px; border:1px solid; background:url(https://picsum.photos/300/300?image=1069) -50% 0/150px 150px no-repeat; animation:change 2s linear infinite alternate; } @keyframes change{ from {background-size:50px 50px} to {background-size:300px 300px} }

<div class="box"> </div>

Lo mismo ocurrirá lógicamente para los gradientes:

.box { width:200px; height:200px; border:1px solid; background:linear-gradient(to right,red,blue) -50% 0/150px 50px no-repeat; animation:change 2s linear infinite alternate; } @keyframes change{ from {background-size:50px 50px} to {background-size:300px 50px} }

<div class="box"> </div>

Grandes valores (> 100%)

La misma lógica que antes: si definimos un fondo en 150% 0 , entonces consideramos nuestro punto de referencia 150% desde el borde izquierdo (o 50% desde el borde derecho), luego lo colocamos 150% desde el borde izquierdo del contenedor .

.box { width:200px; height:200px; border:1px solid; background:url(https://picsum.photos/100/100?image=1069) 150% 0/100px 100px no-repeat; }

<div class="box"> </div>

En este caso, 150% 0 es equivalente a 150px 0 , y si comenzamos a aumentar el tamaño del fondo tendremos el mismo comportamiento que se demostró anteriormente:

.box { width:200px; height:200px; border:1px solid; background:url(https://picsum.photos/300/300?image=1069) 150% 0/100px 100px no-repeat; animation:change 2s infinite linear alternate; } @keyframes change { from {background-size:50px 50px} to {background-size:300px 300px} }

<div class="box"> </div>


Casos especiales

El uso de valores fuera del rango [0% 100%] nos permite ocultar la imagen de fondo, pero ¿cómo encontramos los valores exactos para ocultar completamente la imagen?

Consideremos la siguiente ilustración:

Nuestra imagen tiene un ancho Ws y el contenedor un ancho Wp y necesitamos encontrar el valor de p . De la figura podemos obtener la siguiente fórmula:

p * Wp = p * Ws + Ws <=> p = Ws/(Wp - Ws) where p in [0,1]

Si el tamaño del contenedor es 200px y la imagen es 100px entonces p es 1 así que 100% (agregamos, por supuesto, el signo negativo y es -100% ).

Podemos hacer esto más genérico si consideramos los valores porcentuales con background-size lugar de valores fijos. Supongamos que el background-size es S% . Entonces tendremos Ws = Wp * s (s in [0,1] and S=s*100%) , y la fórmula será

p = Ws/(Wp - Ws) <=> p = s / (1 - s)

Añadiendo el signo negativo será p = s / (s - 1) .

Ahora, si queremos ocultar la imagen en el lado derecho, hacemos la misma lógica a la derecha (consideramos un espejo de la ilustración anterior), pero dado que siempre consideraremos el borde izquierdo para encontrar el porcentaje que necesitamos para agregar 100% .

El nuevo porcentaje p''% es 100% + p% , y la fórmula será p'' = 1 + p --> p'' = 1 + s / (1 - s) = 1 / (1 - s) .

Aquí hay una animación para ilustrar el cálculo anterior:

.box { width:200px; height:50px; margin:5px; border:1px solid; background-image:linear-gradient(to right,red,blue); background-position:0 0; background-size:calc(var(--s) * 100%) 100%; background-repeat:no-repeat; animation:change 4s linear infinite alternate; } @keyframes change{ from { /*Hide on the left*/ background-position:calc(var(--s)/(var(--s) - 1) * 100%) } to { /*Hide on the right*/ background-position:calc(1/(1 - var(--s)) * 100%) } }

<div class="box" style="--s:0.5"> </div> <div class="box" style="--s:0.8"> </div> <div class="box" style="--s:2"> </div>

Calculemos algunos valores:

Cuando s=0.5 , tenemos un background-size igual al 50% , y los valores porcentuales serán de -100% a 200% . En este caso, comenzamos con un valor negativo y terminamos con uno positivo porque el tamaño de la imagen es más pequeño que el tamaño del contenedor . Si consideramos el último caso ( s=2 ), el background-size es igual a 200% , y los valores porcentuales serán de 200% a -100% . Comenzamos con un valor positivo y terminamos con uno negativo porque el tamaño de la imagen es más grande que el tamaño del contenedor .

Esto confirma lo que dijimos anteriormente: para desplazar una imagen a la izquierda necesitamos valores negativos si el tamaño es pequeño, pero necesitamos valores positivos si el tamaño es grande (lo mismo para la derecha).


Relación entre píxel y valores porcentuales.

Vamos a definir una manera de calcular valores porcentuales basados ​​en valores de píxeles, o viceversa (es decir, la fórmula para convertir entre ambos). Para hacer esto simplemente necesitamos considerar los puntos de referencia.

Cuando usemos valores de píxeles, consideraremos las líneas azules y tendremos background-position:XY .

Cuando usemos valores porcentuales, consideraremos las líneas verdes y tendremos background-position:Px Py .

La fórmula será como sigue: Y + Py * Ws = Py * Wp donde Ws es el ancho de la imagen y Wp es el ancho del contenedor (la misma fórmula para el eje X considerando la altura).

Tendremos Y = Py * (Wp - Ws) . De esta fórmula podemos validar dos puntos como se explicó anteriormente:

  • Cuando Wp = Ws , la fórmula ya no es válida, lo que confirma que los valores de porcentaje no tienen efecto cuando el tamaño de la imagen es el mismo que el contenedor; por lo tanto, no hay relación entre los valores de píxel y porcentaje.
  • Y y Py tendrán el mismo signo cuando Wp > Ws y tendrán el signo opuesto cuando Wp < Ws . Esto confirma que el valor del porcentaje se comporta de manera diferente dependiendo del tamaño de la imagen.

También podemos expresar la fórmula de manera diferente si consideramos el valor porcentual del background-size de background-size . Tendremos Y = Py * Wp * (1-s) .

Aquí hay una animación para ilustrar el cálculo anterior:

.box { width:200px; height:50px; margin:5px; border:1px solid; background-image:linear-gradient(to right,red,blue); background-position:0 0; background-size:calc(var(--s) * 100%) 100%; background-repeat:no-repeat; animation:percentage 2s linear infinite alternate; } .box.alt { animation-name:pixel; } @keyframes percentage{ from { background-position:-50%;} to { background-position:150%;} } @keyframes pixel{ from { background-position:calc(-0.5 * 200px * (1 - var(--s))) } to { background-position:calc(1.5 * 200px * (1 - var(--s)));} }

<div class="box" style="--s:0.5"> </div> <div class="box alt" style="--s:0.5"> </div> <div class="box" style="--s:2"> </div> <div class="box alt" style="--s:2"> </div>


Cambiando la referencia

En los cálculos anteriores, siempre consideramos la esquina superior / izquierda de la imagen y el contenedor para aplicar nuestra lógica, ya sea para los valores de píxeles o porcentajes. Esta referencia se puede cambiar agregando más valores a background-position .

Por defecto background-position: XY es equivalente a background-position: left X top Y (posición en X desde la left y en Y desde top ). Al ajustar top y / o a la left , cambiamos la referencia y la forma en que se coloca la imagen. Aquí hay unos ejemplos:

.box { width:150px; height:150px; display:inline-block; background-image:url(https://picsum.photos/70/70?image=1069); border:1px solid; background-position:0 0; background-repeat:no-repeat; position:relative; } body { margin:0; }

<div class="box"></div> <div class="box" style="background-position:left 0 bottom 0"></div> <div class="box" style="background-position:right 0 bottom 0"></div> <div class="box" style="background-position:right 0 top 0"></div> <div class="box" style="background-position:right 10% top 30%"></div> <div class="box" style="background-position:right 10% bottom 30%"></div> <div class="box" style="background-position:right 10px top 20px"></div> <div class="box" style="background-position:left 50% bottom 20px"></div>

Está claro que para el valor X solo podemos usar left y right (la posición horizontal) y con el valor Y solo podemos usar la bottom y top (la posición vertical). Con todas las diferentes combinaciones podemos obtener lógicamente las 4 esquinas diferentes.

Esta característica también es útil para optimizar algunos cálculos. En el ejemplo de la sección de casos especiales , hicimos un primer cálculo para ocultar la imagen de la izquierda y luego otro para ocultarla a la derecha. Si consideramos cambiar la referencia solo necesitamos hacer un cálculo. Tomamos la fórmula utilizada para el lado izquierdo y usamos la misma en el lado derecho.

Aquí está la nueva versión:

.box { width:200px; height:50px; margin:5px; border:1px solid; background-image:linear-gradient(to right,red,blue); background-position:0 0; background-size:calc(var(--s) * 100%) 100%; background-repeat:no-repeat; animation:change 4s linear infinite alternate; } @keyframes change{ from { /*Hide on the left*/ background-position:left calc(var(--s)/(var(--s) - 1) * 100%) top 0 } to { /*Hide on the right*/ background-position:right calc(var(--s)/(var(--s) - 1) * 100%) top 0 } }

<div class="box" style="--s:0.5"> </div> <div class="box" style="--s:0.8"> </div> <div class="box" style="--s:2"> </div>

Para s=0.5 no volveremos a animar de -100% a 200% PERO será de left -100% a right -100% .

Aquí hay otro ejemplo que usa valores de píxeles donde podemos ver claramente qué tan fácil es manejar el cálculo al cambiar la referencia:

.box { width:200px; height:200px; background-image:url(https://picsum.photos/100/100?image=1069); border:1px solid; background-position:0 0; background-repeat:no-repeat; animation:change 2s infinite linear; } @keyframes change{ 0%{background-position:left 20px top 20px;} 25%{background-position:right 20px top 20px;} 50%{background-position:right 20px bottom 20px;} 75%{background-position:left 20px bottom 20px;} 100%{background-position:left 20px top 20px;} }

<div class="box"></div>

Sería complicado lograr la misma animación manteniendo la misma referencia. Entonces, si queremos hacer una animación simétrica, hacemos nuestra lógica en un lado y usamos la misma en el otro lado cambiando la referencia.


Combinando valores de píxel y porcentaje.

En CSS3 podemos usar calc() para hacer un cálculo complejo que involucra diferentes unidades. Por ejemplo, podemos escribir el width:calc(100px + 20% + 12em) y el navegador calculará el valor calculado teniendo en cuenta cómo funciona cada unidad y terminaremos con un valor de píxel (en este caso).

¿Qué pasa background-position ? Si escribimos calc(50% + 50px) , ¿será evaluado a un valor de porcentaje o un valor de píxel? ¿Se convertirá el valor de píxel a porcentaje o al contrario?

El resultado no se convertirá en un valor de píxel o un valor de porcentaje, sino que se utilizarán ambos a la vez. background-position tiene un comportamiento especial cuando se mezclan valores de porcentaje y píxel dentro de calc() y la lógica es la siguiente:

  1. Primero usamos el valor de porcentaje para posicionar la imagen aplicando toda la lógica relacionada con los valores de porcentaje.
  2. Consideramos la posición de (1) como la referencia y usamos el valor de píxel para posicionar la imagen nuevamente aplicando toda la lógica relacionada con los valores de píxel.

Entonces, calc(50% + 50px) significa: centrar la imagen, luego desplazarla 50px hacia la izquierda.

Esta característica puede simplificar mucho cálculo. Aquí hay un ejemplo:

.box { width:200px; height:200px; display:inline-block; border:1px solid; background-image: linear-gradient(red,red), linear-gradient(red,red), linear-gradient(red,red), linear-gradient(red,red); background-size:20px 20px; background-position: calc(50% + 20px) 50%, calc(50% - 20px) 50%, 50% calc(50% - 20px), 50% calc(50% + 20px); background-repeat:no-repeat; transition:0.5s; } .box:hover { background-position:50%; }

<div class="box"></div> <div class="box" style="width:100px;height:100px;"></div>

Sería tedioso encontrar el porcentaje correcto o los valores de píxel para colocar los 4 cuadrados rojos como arriba, pero mezclando ambos usando calc() es bastante fácil.

Ahora, supongamos que tenemos algo como esto: calc(10% + 20px + 30% + -10px + 10% + 20px) . ¿Cómo manejará el navegador esto?

En tal caso, el navegador primero evaluará cada unidad para obtener el calc(X% + Ypx) la forma simplificada calc(X% + Ypx) luego aplicará la lógica anterior para posicionar la imagen.

calc(10% + 20px + 30% + -10px + 10% + 20px) calc((10% + 30% + 10%) + (20px + -10px +20px)) calc(50% + 30px)

.box { display:inline-block; width:200px; height:200px; background-image:url(https://picsum.photos/100/100?image=1069); border:1px solid; background-position:calc(10% + 20px + 30% + -10px + 10% + 20px) 0; background-repeat:no-repeat; } .alt { background-position:calc(50% + 30px) 0; }

<div class="box"></div> <div class="box alt"></div>

Cualquiera que sea la complejidad de la fórmula, el navegador siempre evaluará los valores de porcentaje y píxel por separado.


Usando mas unidades

Además de la unidad px , también podemos utilizar todas las unidades comunes dentro de la posición de fondo como em , ch , ex , rem , cm , etc. Todos se comportarán igual que los valores de píxeles.

.box { display:inline-block; width:200px; height:200px; font-size:25px; background-image:url(https://picsum.photos/100/100?image=1069); border:1px solid; background-position:50px 0; background-repeat:no-repeat; } .em { background-position:2em 0; } .ch { background-position:2ch 0; } :root { font-size:50px} .rem { background-position:1rem 0; }

<div class="box"></div> <div class="box em"></div> <div class="box ch"></div> <div class="box rem"></div>

Entonces podemos usar valores de porcentaje o valores de longitud ( px , em , ch , etc.)


Usando el origen del fondo

Aquí hay otra propiedad importante que se puede usar para alterar la posición de la imagen de fondo. Esta propiedad se basa en el modelo de caja, de modo que recibamos un recordatorio rápido sobre cómo funciona:

Cada elemento tiene 3 cuadros diferentes dentro de él: cuadro de borde, cuadro de relleno y el cuadro de contenido. background-origin especifica qué caja debemos considerar para hacer toda nuestra lógica anterior.

Aquí hay un ejemplo autoexplicativo:

.box { display:inline-block; width:200px; height:200px; font-size:25px; background-image:url(https://picsum.photos/100/100?image=1069); border:20px solid rgba(0,0,0,0.1); padding:20px; background-position:0 0; background-repeat:no-repeat; box-sizing:border-box; } .border { background-origin:border-box; } .padding { background-origin:padding-box; /*the default value*/ } .content { background-origin:content-box; }

<div class="box border"></div> <div class="box padding"></div> <div class="box content"></div>

Debería estar claro ahora que cuando no tenemos content-box relleno es equivalente a padding-box , y cuando no tenemos border-box borde de border-box es equivalente a padding-box .


1 : Aquí está la lista completa de los valores equivalentes:

  • left = left center = center left = 0 50%
  • right = right center = center right = 100% 50%
  • top = top center = center top = 50% 0
  • bottom = bottom center = center bottom = 50% 100%
  • center = center center = 50% 50%


  • top left = left top = 0 0
  • top right = right top = 100% 0
  • bottom left = left bottom = 0 100%
  • bottom right = right bottom = 100% 100%


  • top X left Y = left Y top X = YX
  • top X right Y = right Y top X
  • bottom X left Y = left Y bottom X
  • bottom X right Y = right Y bottom X

Referencia oficial: https://www.w3.org/TR/css-backgrounds-3/


En la mayoría de los casos, consideré solo un eje para la explicación / cálculo, pero las mismas reglas se aplican a ambos ejes y ambos son independientes.