Veamos cómo obtener la posición del mouse del usuario y asignarla a las propiedades personalizadas de CSS: --positionX
y --positionY
.
Podríamos hacer esto en JavaScript. Si lo hiciéramos, podríamos hacer cosas como hacer que un elemento se pueda arrastrar o mover un fondo. Pero en realidad, todavía podemos hacer cosas similares, ¡pero sin usar JavaScript!
He usado este método como parte de una demostración que hice para obtener un efecto de ‘Hacer clic y arrastrar’ con CSS puro. Usé el perspective
consejos de mi artículo anterior. Es un efecto bastante bueno hacer esto completamente en CSS, que podría tener una utilidad más amplia que mi demostración, así que echemos un vistazo.
La puesta en marcha
Nuestra primera demostración utilizará --positionX
y --positionY
propiedades personalizadas para establecer el width
y height
de un elemento.
Tenga en cuenta que solo usamos SCSS aquí por brevedad, pero todo esto se puede hacer en CSS puro.
Este es nuestro estado inicial. Tenemos aquí un ‘envoltorio’ <div>
con un .content
clase de eso se extiende a la anchura y la altura del cuerpo. Esta <div>
albergará el contenido de nuestro proyecto, y los elementos que queremos controlar usando la posición del mouse – en este caso, el .square
elemento.
También hemos agregado las dos propiedades personalizadas al content
. Usaremos la posición del mouse para establecer el valor de estas propiedades, y luego las usaremos para establecer el .square
elementos width
y height
respectivamente.
Una vez que hayamos mapeado las propiedades personalizadas para la posición del mouse, podemos usarlas para hacer prácticamente cualquier cosa que queramos. Por ejemplo, podríamos usarlos para configurar el top
/ left
propiedades de un absolute
elemento posicionado, controlar un transform
propiedad, establezca la background-position
, manipular colores o incluso establecer el contenido de un pseudoelemento. Veremos algunas de estas demostraciones adicionales al final del artículo.
La cuadrícula
El objetivo es crear una rejilla invisible en la pantalla y utilice el :hover
pseudo-clase para mapear cada ‘celda’ a un conjunto de valores que asignaremos a las propiedades personalizadas. Entonces, cuando el cursor del mouse se mueve hacia el lado derecho de la pantalla, el valor del --positionX
será mayor y cuando se mueve hacia la izquierda, baja. Haremos lo mismo con --positionY
: el valor será menor cuando el cursor se mueva hacia arriba y mayor cuando se mueva hacia abajo.
Algunas palabras sobre el tamaño de la cuadrícula que estamos usando: De hecho, podemos hacer la cuadrícula del tamaño que queramos. Cuanto más grande sea, más precisos serán los valores de nuestras propiedades personalizadas. Pero eso también significa que tendremos más celdas, lo que puede generar problemas de rendimiento. Es importante mantener un equilibrio adecuado y ajustar el tamaño de la cuadrícula a las necesidades específicas de cada proyecto.
Por ahora, digamos que queremos una cuadrícula de 10 × 10 para un total de 100 celdas en nuestro marcado. (Y sí, está bien usar Pug para eso, aunque no lo haré en este ejemplo).
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<!-- 97 more cells -->
<div class="content">
<div class="square"></div>
</div>
Probablemente te estés preguntando por qué .cell
los elementos vienen antes del .content
. Eso es por la cascada.
Queremos usar el .cell
clase para controlar el .square
, y la forma en que funciona la cascada (por ahora) es que un elemento solo puede controlar a sus hijos (o descendientes) y sus hermanos (o sus descendientes), pero solo mientras el hermano esté detrás del elemento controlador.
Eso significa dos cosas:
- Cada
.cell
debe ir antes del elemento que queremos controlar (en este caso, el.square
). - No podemos poner esos
.cell
s en un recipiente, porque si lo hacemos, el.content
ya no es su hermano.
Colocando las celdas
Hay algunas formas de colocar el .cell
s. podemos position: absolute
ellos y compensarlos con el top
y left
propiedades. O podemos translate
colocarlos en posición usando transform
. Pero la opción más fácil probablemente sea usar display: grid
.
body {
background-color: #000;
height: 100vh;
display: grid;
grid-template: repeat(10, 1fr) / repeat(10, 1fr);
}
.cell {
width: 100%;
height: 100%;
border: 1px solid gray;
z-index: 2;
}
- El
border
es solo temporal, por lo que podríamos ver elcell
s en la pantalla. lo eliminaremos más adelante. - el
z-index
es importante, porque queremos elcell
s estar frente alcontent
.
Esto es lo que tenemos hasta ahora:
Añadiendo valores
Queremos usar .cell
para configurar el --positionX
y --positionY
valores.
Cuando pasamos el cursor sobre un .cell
que está en la primera columna (izquierda), el valor de --positionX
debiera ser 0
. Cuando pasamos el cursor sobre un .cell
en la segunda columna, el valor debe ser 1
. Debería ser 2
en la tercera columna, y así sucesivamente.
Lo mismo ocurre con el eje y. Cuando pasamos el cursor sobre un .cell
que está en la primera fila (superior), --positionY
debiera ser 0
, y cuando pasamos el cursor sobre un cell
en la segunda fila, el valor debe ser 1
, y así.
Los números de esta imagen representan los números de los elementos de la celda en la cuadrícula. Si tomamos solo una .cell
como ejemplo, digamos la celda 42, podemos seleccionarla usando :nth-child()
:
.cell:nth-child(42) { }
Pero debemos recordar un par de cosas:
- Solo queremos que este selector funcione cuando pasamos el cursor sobre la celda, por lo que adjuntaremos
:hover
lo. - Queremos seleccionar el
.content
en lugar de la celda en sí, por lo que usaremos el General Sibling Combinator (~
) Para hacer eso.
Así que ahora, para establecer --positionX
a 1
y --positionY
a 3
por .content
cuando el 42 cell
está suspendido, debemos hacer algo como esto:
.cell:nth-child(42):hover ~ .content {
--positionX: 1;
--positionY: 3;
}
¿¡Pero quién quiere hacer eso 100 veces !? Hay algunos enfoques para facilitar las cosas:
- Usa un descaro
@for
bucle para pasar por las 100 celdas y hacer algunos cálculos matemáticos para establecer el--positionX
y--positionY
valores para cada iteración. - Separe los ejes xey para seleccionar cada fila y cada columna individualmente con
:nth-child
notación funcional de. - Combine esos dos enfoques y use un Sass
@for
bucle y:nth-child
notación funcional.
Pensé mucho sobre cuál sería el mejor y más fácil enfoque de tomar, y aunque todos tienen pros y contras, aterricé en el tercer enfoque. La cantidad de código para escribir, la calidad del código compilado y la complejidad matemática entraron en mi pensamiento. ¿No estás de acuerdo? ¡Dime por qué en los comentarios!
Establecer valores con un @for
círculo
@for $i from 0 to 10 {
.cell:nth-child(???):hover ~ .content {
--positionX: #{$i};
}
.cell:nth-child(???):hover ~ .content {
--positionY: #{$i};
}
}
Este es el ciclo básico. Lo repasaremos 10 veces ya que tenemos 10 filas y 10 columnas. Y hemos separado los ejes xey para establecer --positionX
para cada columna, y el --positionY
por cada fila. Todo lo que tenemos que hacer ahora es reemplazar esos ???
cosas con la notación adecuada para seleccionar cada fila y columna.
Comencemos con el eje x
Volviendo a nuestra imagen de cuadrícula (la que tiene números), podemos ver que los números de todas las celdas en la segunda columna son múltiplos de 10, más 2. Las celdas en la tercera columna son múltiplos de 10 más 3. Y así sobre.
Ahora vamos a ‘traducirlo’ al :nth-child
notación funcional. Así es como se ve en la segunda columna:
:nth-child(10n + 2)
10n
selecciona cada múltiplo de 10.2
es el número de columna.
Y para nuestro ciclo, reemplazaremos el número de columna con #{$i + 1}
para iterar secuencialmente:
.cell:nth-child(10n + #{$i + 1}):hover ~ .content {
--positionX: #{$i};
}
Ahora tratemos con el eje y
Mire nuevamente la imagen de la cuadrícula y enfóquese en la cuarta fila. Los números de celda están entre 41 y 50. Las celdas de la quinta fila están entre 51 y 60, y así sucesivamente. Para seleccionar cada fila, necesitaremos definir su rango. Por ejemplo, el rango de la cuarta fila es:
.cell:nth-child(n + 41):nth-child(-n + 50)
(n + 41)
es el inicio del rango.(-n + 50)
es el final del rango.
Ahora reemplazaremos los números con algunas matemáticas en el $i
valor. Para el inicio de la gama, obtenemos (n + #{10 * $i + 1})
, y para el final del rango obtenemos (-n + #{10 * ($i + 1)})
.
Entonces el final @for
bucle es:
@for $i from 0 to 10 {
.cell:nth-child(10n + #{$i + 1}):hover ~ .content {
--positionX: #{$i};
}
.cell:nth-child(n + #{10 * $i + 1}):nth-child(-n + #{10 * ($i + 1)}):hover ~ .content {
--positionY: #{$i};
}
}
¡El mapeo está hecho! Cuando pasamos el cursor sobre los elementos, --positionX
y --positionY
cambiar según la posición del ratón. Eso significa que podemos usarlos para controlar los elementos dentro del .content
.
Manejo de las propiedades personalizadas
Bien, ahora tenemos la posición del mouse asignada a dos propiedades personalizadas. Lo siguiente es usarlas para controlar el .square
elementos width
y height
valores.
Comencemos con el width
y decir que queremos lo mínimo width
de El .square
ser – estar 100px
(es decir, cuando el cursor del mouse está en el lado izquierdo de la pantalla), y queremos que crezca 20px
para cada paso, el cursor del mouse se mueve hacia la derecha.
Utilizando calc()
, Podemos hacerlo:
.square {
width: calc(100px + var(--positionX) * 20px);
}
Y, por supuesto, haremos lo mismo con height
, pero con --positionY
en lugar de:
.square {
width: calc(100px + var(--positionX) * 20px);
height: calc(100px + var(--positionY) * 20px);
}
¡Eso es! Ahora tenemos un simple .square
elemento, con un width
y height
que está controlado por la posición del mouse. Mueva el cursor del mouse sobre la ventana y vea cómo square
cambia su width
y height
respectivamente.
Agregué un pequeño transition
para suavizar el efecto. Eso no es obligatorio, por supuesto. Y también he comentado sobre el .cell
frontera.
Probemos un método alternativo
Puede haber un caso en el que desee ‘omitir’ --positionX
y --positionY
y establezca el valor final directamente dentro del @for
círculo. Entonces, para nuestro ejemplo se vería así:
@for $i from 0 to 10 {
.cell:nth-child(10n + #{$i + 1}):hover ~ .content {
--squareWidth: #{100 + $i * 20}px;
}
.cell:nth-child(n + #{10 * $i + 1}):nth-child(-n + #{10 * ($i + 1)}):hover ~ .content {
--squareHeight: #{100 + $i * 20}px;: #{$i};
}
}
Entonces la .square
usaría las propiedades personalizadas como esta:
.square {
width: var(--squareWidth);
height: var(--squareHeight);
}
Este método es un poco más flexible porque permite funciones matemáticas (y cadenas) de Sass más avanzadas. Dicho esto, el principio general es absolutamente el mismo que ya cubrimos.
¿Que sigue?
Bueno, el resto depende de usted, ¡y las posibilidades son infinitas! ¿Cómo crees que lo usarías? ¿Puedes llevarlo más lejos? Intente usar este truco en su CSS y comparta su trabajo en los comentarios o avísame en Twitter. Será genial ver cómo se juntan una colección de estos.
Aquí hay algunos ejemplos para hacer girar la rueda de su hámster: