
La siguiente es una publicación invitada de ryan scherf. Ryan encontró una forma ingeniosa de darles a los avatares un tipo de bordes ásperos, desiguales y variados. Como si hubieran sido cortados con tijeras por alguien que no era muy bueno usando tijeras. Lo bueno es que, naturalmente, es una técnica de mejora progresiva y se puede hacer solo con CSS.
Con una marca creativa y divertida como Quirky, siempre estamos pensando en formas de llevar ese ambiente a la web. En todo el sitio, hay un aspecto de “dibujado a mano” en algunos elementos. Sin el uso de muchas imágenes, es muy difícil conseguir esa vibra de dibujo a mano. Con algo de trigonometría ligera y conocimientos muy básicos de CSS’ clip-path
, podemos hacer esto con relativa facilidad y buen rendimiento.
Lo que estamos construyendo. Observe los bordes desiguales y variados en cada uno.
¿Por qué no usar máscaras de imagen?
Por ejemplo, una máscara definida en SVG:
img {
mask: url(mask.svg) top left / cover;
}
El mask
La propiedad puede hacer referencia a SVG externo o SVG definido en el documento por ID.
Pero, ¿qué pasaría si quisiera una forma única para cada avatar que se muestra, no la misma forma? Puede generar programáticamente muchas formas SVG diferentes para aplicar. Pero podemos lograr lo mismo y obtener esa generación matemática generando clip-path
s con (S)CSS.
¿Cuál es el soporte del navegador?
El soporte del navegador para clip-path
, cuando se usa con un valor de forma como polygon()
, es Chrome 24+, Safari 7+, Opera 25+, iOS 7.1+, Android 4.4+. Firefox es compatible clip-path
solo con la ruta definida en SVG (cubriremos eso). Aún no hay soporte en IE.
Tendrás que usar -webkit-clip-path
, ya que esa es la única forma en que se admite en este momento, pero probablemente sea mejor eliminar clip-path
allí también. Si IE o Firefox comienzan a admitirlo de esta manera, es probable que no tenga prefijo.
Trazados de recorte en pocas palabras
Hay algunos valores de forma diferentes que puede usar para el recorte de CSS, pero en nuestro caso, el polygon
la forma es mejor ya que nos da la mayor cantidad de puntos y flexibilidad para crear nuestro efecto de dibujo a mano.
Das polygon()
una lista de valores de puntos X, Y, como: <x0> <y0>, <x1> <y1>, ... <xn> <yn>
. Eso dibujará un camino alrededor de tus puntos. en orden y recortar cualquiera de los contenidos fuera de de la forma recién creada.
/*
This will create a Hexagon, with the first
point being the top tip of the shape
*/
.hexagon {
clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
}
Aquí está ese ejemplo simple en acción:
Vea Pen Hexagon con clip-path de Chris Coyier (@chriscoyier) en CodePen.
Matemáticas no tan aterradoras
Nuestro hexágono es muy bueno, pero aún no logra un efecto de boceto real. Es bastante rígido, muy pocas líneas. La mejor manera de pensar en una forma dibujada a mano es una serie de pequeñas líneas que conectan dos puntos. Cuantos más puntos tengamos, más líneas cortas crearemos. De hecho, con suficientes puntos, podríamos hacer una polygon
forma tan suave que imita una circle
.
Aquí hay un ejemplo del uso de 200 puntos:
Vea el Pen 200 Points de Chris Coyier (@chriscoyier) en CodePen.
¿De dónde vienen los puntos?
Aquí es donde entra un poco de matemática. ¿Quizás tomaste trigonometría en la escuela secundaria? Una de las ideas fundamentales que aprendes en esa clase es sobre el Circulo unitario. Básicamente, existe una fórmula establecida (dado pi) que puede generar cualquier número de puntos alrededor de un círculo.
El círculo unitario (a través de Wikipedia)
Si tuviéramos que conectar nuestros segmentos, obtendríamos una forma que se parece a:
¡Conecta los puntos!
Todavía un poco rígido, pero con un aspecto un poco más dibujado a mano también.
¡Más puntos!
Sabemos cómo hacer hexágonos y círculos con el clip-path: polygon()
, entonces, ¿cómo hacemos que parezca dibujado a mano?
- Ajuste el número de puntos (cuantos más haya, menor será la longitud de los segmentos)
- Agregue algo de variación X e Y (para que los segmentos no sean uniformes)
Traigamos eso a SCSS y creemos una función para que haga el trabajo sucio por nosotros. Estaremos usando:
random()
cos()
sin()
La matemática más relevante es:
/*
To generate an arbitrary points on
the unit circle at angle t
*/
$x: cos
$y: sin
Y poner eso en la sintaxis correcta se ve así:
$w: 160px // Avatar width
$n: 60; // Number of points on the circle
@function sketchAvatar() {
$points: ();
@for $i from 0 through $n {
$points: append($points, ($w / 2) * (1 + cos((2 * pi() * $i / $n))) ($w / 2) * (1 + sin((2 * pi() * $i / $n))), comma);
}
@return $points;
}
Esto es un poco peludo. Lo que sucede es que comenzamos en la mitad superior de nuestra forma y generamos una lista de conjuntos de puntos alrededor del círculo para 60 puntos espaciados uniformemente.
Reuniéndolo en conjunto con variaciones
El código anterior todavía produce polígonos bastante suaves y uniformes, por lo que tendremos que agregar una variación. Todo lo que tenemos que hacer es ajustar los puntos en cualquier dirección para dar la sensación de compensación que estamos buscando. El $lower
y $upper
los números de variación pueden ser casi cualquier cosa dependiendo de la apariencia que esté buscando.
$w: 120px; // Overall width
@function sketchAvatar() {
$n: 60; // Number of points
$lower: -80; // Lower variance
$upper: 80; // Upper variance
$points: ();
@for $i from 0 through $n {
$points: append($points, ($w / 2) * (1 + cos((2 * pi() * $i / $n))) + (rand($lower, $upper) / 100) ($w / 2) * (1 + sin((2 * pi() * $i / $n))), comma);
}
@return $points;
}
¡Lo hicimos! Avatares incompletos y únicos con CSS clip-path: polygon()
:
Vea los avatares de Pen Sketchy de Chris Coyier (@chriscoyier) en CodePen.
Haciendo que funcione en Firefox
Cris aquí! Pensé que dado que Firefox no es compatible con esto hecho de esta manera, pero sí es compatible con la sintaxis SVG, tal vez podríamos rellenarlo un poco.
.avatar {
clip-path: polygon( ... ) /* Firefox: nope */
clip-path: url(#clip); /* Firefox: yep */
}
Así que para cada avatar, yo...
- Muestra los puntos de polígono en la propiedad de contenido de un pseudo elemento (de un elemento que tiene un pseudo elemento válido como el div principal) en CSS
- Extraído ese valor con JavaScript
- Vuelva a formatear los puntos para que coincidan con el formato SVG (por ejemplo, sin "px")
- inyectado un nuevo
<svg>
en el camino con un <clipPath>
Listo para ir
$(".user").each(function(i) {
var path = window.getComputedStyle(this, ':after').getPropertyValue('content');
// clean house
svgPolygonPoints =
path
.replace(/px/g, "")
.replace(/polygon/, "")
.replace(/(/, "")
.replace(/)/, "")
.replace(/;/g, "")
.replace(/"/g, "")
.replace(/'/g, "");
// To get this to actually work, create a <div> instead with this inside, see below.
var svg = $("<svg width="0" height="0">")
.append("<defs><clipPath id='clip-" + (i+1) +"'><polygon points="" + svgPolygonPoints +"" /></clipPath></defs>");
$("body").append(svg);
});
¡No funciona! ja ja. Incluso si fuerza un repintado en los avatares, simplemente no le gusta el SVG inyectado por alguna razón. Mira la solución de Amelia
Es básicamente como:
.user:nth-child(1) {
clip-path: polygon(120.04px 60px ...);
}
se convierte en:
<svg width="0" height="0">
<defs>
<clippath id="clip-1">
<polygon points="120.04 60, ... "></polygon>
</clippath>
</defs>
</svg>