Animación de relleno HTML5 SVG con CSS3 y JavaScript Vanilla

 

 

 

  • Deploy Fast. Deploy Smart
  • Advertise on Smashing Magazine

  • Índice
    1. Estructura de archivos
    2. HTML
    3. CSS
    4. Transición de relleno
    5. Aumento del valor de la nota

    En este artículo, puede aprender cómo crear la visualización de notas animadas desde el sitio web de Awwwards . Analiza el elemento circular HTML5 SVG, sus propiedades de trazo y cómo animarlos con variables CSS y JavaScript Vanilla.

     

    SVG significa Scalable V ector Graphics y es un lenguaje de marcado estándar basado en XML para gráficos vectoriales . Le permite dibujar trazados, curvas y formas determinando un conjunto de puntos en el plano 2D. Además, puede agregar propiedades de contracción en esos trazados (como trazo, color, grosor, relleno y más) para producir animaciones.

    Desde abril de 2017, el módulo de trazo y relleno de nivel 3 de CSS permite configurar colores SVG y patrones de relleno desde una hoja de estilo externa, en lugar de establecer atributos en cada elemento. En este tutorial, usaremos un color hexadecimal simple, pero las propiedades de relleno y trazo también aceptan patrones, degradados e imágenes como valores.

    Nota : Al visitar el sitio web de Awwwards , la visualización de notas animadas solo se puede ver con el ancho del navegador configurado en 1024 px o más.

    • Demostración: Proyecto de visualización de notas
    • Repositorio: repositorio de visualización de notas

    Estructura de archivos

    Comencemos creando los archivos en la terminal:

     mkdir note-display cd note-display touch index.html styles.css scripts.js

    HTML

    Aquí está la plantilla inicial que vincula ambos cssarchivos js:

    htmlhead meta charset="UTF-8" titleNote Display/title link rel="stylesheet" href="./styles.css"/headbody script src="./scripts.js"/script/body/html

    Cada elemento de nota consta de un elemento de lista: lique contiene el circle, el notevalor y su label.

    Elemento de elemento de lista y sus hijos directos .circle: .percenty .label. ( Vista previa grande )

     

    Es .circle_svgun elemento SVG que envuelve dos elementos circle . El primero es el camino a llenar mientras que el segundo es el relleno que se animará.

    Elementos SVG. Envoltura SVG y etiquetas circulares. ( Vista previa grande )

    Está noteseparado en números enteros y decimales para que se les puedan aplicar diferentes tamaños de fuente. Es labelun sencillo span. Entonces, juntando todo esto queda así:

    li div svg circle cx="41" cy="41" r="38"/circle circle cx="41" cy="41" r="38"/circle /svg div span0./span span00/span /div /div spanTransparent/span/li

    Los atributos cxy cydefinen el punto central del eje x y del eje y del círculo. El ratributo define su radio.

    Probablemente hayas notado el patrón de guión bajo/guión en los nombres de las clases. Eso es BEM , que significa blocky element. modifierEs una metodología que hace que la denominación de sus elementos sea más estructurada, organizada y semántica.

    Lectura recomendada : Una explicación de BEM y por qué lo necesita

    Para finalizar las estructuras de la plantilla, envolvamos los cuatro elementos de la lista en un elemento de lista desordenado:

    El contenedor de lista desordenada contiene cuatro lielementos secundarios ( vista previa grande )

    ul li div svg circle cx="41" cy="41" r="38"/circle circle cx="41" cy="41" r="38"/circle /svg div span0./span span00/span /div /div spanTransparent/span /li li div svg circle cx="41" cy="41" r="38"/circle circle cx="41" cy="41" r="38"/circle /svg div span0./span span00/span /div /div spanReasonable/span /li li div svg circle cx="41" cy="41" r="38"/circle circle cx="41" cy="41" r="38"/circle /svg div span0./span span00/span /div /div spanUsable/span /li li div svg circle cx="41" cy="41" r="38"/circle circle cx="41" cy="41" r="38"/circle /svg div span0./span span00/span /div /div spanExemplary/span /li/ul

    Debes preguntarte qué significan las etiquetas Transparent, Reasonabley Usable. ExemplaryCuanto más se familiarice con la programación, se dará cuenta de que escribir código no se trata solo de hacer que la aplicación sea funcional, sino también de garantizar que sea escalable y mantenible a largo plazo. Esto sólo se logra si su código es fácil de cambiar.

    "El acrónimo TRUEdebería ayudar a decidir si el código que escriba podrá adaptarse a los cambios en el futuro o no".

    Así que la próxima vez pregúntate:

    • Transparent: ¿Están claras las consecuencias de los cambios de código?
    • Reasonable: ¿Vale la pena la relación costo-beneficio?
    • Usable: ¿Podré reutilizarlo en escenarios inesperados?
    • Exemplary: ¿Presenta alta calidad como ejemplo para código futuro?

    Nota : " Diseño práctico orientado a objetos en Ruby " de Sandi Metz explica TRUEjunto con otros principios y cómo lograrlos a través de patrones de diseño. Si aún no se ha tomado el tiempo para estudiar patrones de diseño, considere agregar este libro a su lectura antes de dormir.

     

    CSS

    Importemos las fuentes y apliquemos un reinicio a todos los elementos:

    @import url('https://fonts.googleapis.com/css?family=Nixie+One|Raleway:200');* { padding: 0; margin: 0; box-sizing: border-box;}

    La box-sizing: border-boxpropiedad incluye valores de relleno y borde en el ancho y alto total de un elemento, por lo que es más fácil calcular sus dimensiones.

    Nota : Para obtener una explicación visual box-sizing, lea " Haga su vida más fácil con el tamaño de caja CSS ".

    body { height: 100vh; color: #fff; display: flex; background: #3E423A; font-family: 'Nixie One', cursive;}.display-container { margin: auto; display: flex;}

    Al combinar las reglas display: flexen bodyy margin-autoen .display-container, es posible centrar el elemento secundario tanto vertical como horizontalmente. El .display-containerelemento también será un flex-container; de esa manera, sus hijos se colocarán en la misma fila a lo largo del eje principal.

    El .note-displayelemento de la lista también será un archivo flex-container. Como hay muchos elementos secundarios para centrar, hagámoslo mediante las propiedades justify-contenty align-items. Todo flex-itemsestará centrado a lo largo del eje crossy main. Si no está seguro de cuáles son, consulte la sección de alineación en " Guía visual de fundamentos de CSS Flexbox ".

    .note-display { display: flex; flex-direction: column; align-items: center; margin: 0 25px;}

    Apliquemos un trazo a los círculos estableciendo las reglas stroke-width, stroke-opacityy stroke-linecapeso en conjunto le da estilo al trazo en vivo. A continuación, agreguemos un color a cada círculo:

    .circle__progress { fill: none; stroke-width: 3; stroke-opacity: 0.3; stroke-linecap: round;}.note-display:nth-child(1) .circle__progress { stroke: #AAFF00; }.note-display:nth-child(2) .circle__progress { stroke: #FF00AA; }.note-display:nth-child(3) .circle__progress { stroke: #AA00FF; }.note-display:nth-child(4) .circle__progress { stroke: #00AAFF; }

    Para posicionar el percentelemento de manera absoluta, es necesario saber exactamente hacia qué. El .circleelemento debe ser la referencia, así que agregémosle position: relative.

    Nota : Para obtener una explicación visual más profunda sobre el posicionamiento absoluto, lea " Cómo entender la posición absoluta de CSS de una vez por todas ".

    Otra forma de centrar elementos es combinarlos top: 50%y posicionar el centro del elemento en el centro de su padre left: 50%.transform: translate(-50%, -50%);

    .circle { position: relative;}.percent { width: 100%; top: 50%; left: 50%; position: absolute; font-weight: bold; text-align: center; line-height: 28px; transform: translate(-50%, -50%);}.percent__int { font-size: 28px; }.percent__dec { font-size: 12px; }.label { font-family: 'Raleway', serif; font-size: 14px; text-transform: uppercase; margin-top: 15px;}

    A estas alturas, la plantilla debería verse así:

     

    Elementos y estilos de plantilla terminados ( vista previa grande )

    Transición de relleno

    La animación del círculo se puede crear con la ayuda de dos propiedades SVG del círculo: stroke-dasharrayy stroke-dashoffset.

    " stroke-dasharrayDefine el patrón de espacio entre guiones en un trazo".

    Puede tomar hasta cuatro valores:

    • Cuando se establece en un único número entero ( stroke-dasharray: 10), los guiones y los espacios tienen el mismo tamaño;
    • Para dos valores ( stroke-dasharray: 10 5), el primero se aplica a guiones, el segundo a espacios;
    • Las formas tercera y cuarta ( stroke-dasharray: 10 5 2y stroke-dasharray: 10 5 2 3) generarán guiones y espacios en varios tamaños.

    La imagen de la izquierda muestra la propiedad stroke-dasharrayconfigurada de 0 a 238 px, que es la longitud de la circunferencia del círculo.

    La segunda imagen representa la stroke-dashoffsetpropiedad que desplaza el comienzo de la matriz de guiones. También se establece de 0 a la longitud de la circunferencia del círculo.

    Para producir el efecto de relleno, estableceremos la stroke-dasharraylongitud de la circunferencia, de modo que toda su longitud se llene con un guión grande y sin espacios. También lo compensaremos con el mismo valor, para que quede "oculto". Luego se stroke-dashoffsetactualizará al valor de nota correspondiente, llenando el trazo de acuerdo con la duración de la transición.

    La actualización de propiedades se realizará en los scripts a través de Variables CSS . Declaremos las variables y establezcamos las propiedades:

    .circle__progress--fill { --initialStroke: 0; --transitionDuration: 0; stroke-opacity: 1; stroke-dasharray: var(--initialStroke); stroke-dashoffset: var(--initialStroke); transition: stroke-dashoffset var(--transitionDuration) ease;}

    Para establecer el valor inicial y actualizar las variables, comencemos seleccionando todos .note-displaylos elementos con document.querySelectorAll. Se transitionDurationestablecerá en 900milisegundos.

    Luego, iteramos a través de la matriz de pantallas, la seleccionamos .circle__progress.circle__progress--filly extraemos el ratributo establecido en el HTML para calcular la longitud de la circunferencia. Con eso, podemos establecer los valores iniciales --dasharrayy --dashoffset.

    La animación se producirá cuando la --dashoffsetvariable se actualice con un setTimeout de 100 ms:

    const displays = document.querySelectorAll('.note-display');const transitionDuration = 900;displays.forEach(display = { let progress = display.querySelector('.circle__progress--fill'); let radius = progress.r.baseVal.value; let circumference = 2 * Math.PI * radius; progress.style.setProperty('--transitionDuration', `${transitionDuration}ms`); progress.style.setProperty('--initialStroke', circumference); setTimeout(() = progress.style.strokeDashoffset = 50, 100);});

    Para que la transición comience desde arriba, el .circle__svgelemento debe rotarse:

     

    .circle__svg { transform: rotate(-90deg);}

    Ahora, calculemos el dashoffsetvalor relativo a la nota. El valor de la nota se insertará en cada lielemento a través del atributo data-* . Se *puede cambiar por cualquier nombre que se adapte a sus necesidades y luego se puede recuperar en JavaScript a través del conjunto de datos del elemento: element.dataset.*.

    Nota : Puede leer más sobre el atributo data-* en MDN Web Docs .

    Nuestro atributo se llamará “ data-note”:

    ul+ li data-note="7.50" div svg circle cx="41" cy="41" r="38"/circle circle cx="41" cy="41" r="38"/circle /svg div span0./span span00/span /div /div spanTransparent/span /li+ li data-note="9.27" div svg circle cx="41" cy="41" r="38"/circle circle cx="41" cy="41" r="38"/circle /svg div span0./span span00/span /div /div spanReasonable/span /li+ li data-note="6.93" div svg circle cx="41" cy="41" r="38"/circle circle cx="41" cy="41" r="38"/circle /svg div span0./span span00/span /div /div spanUsable/span /li+ li data-note="8.72" div svg circle cx="41" cy="41" r="38"/circle circle cx="41" cy="41" r="38"/circle /svg div span0./span span00/span /div /div spanExemplary/span /li/ul

    El parseFloatmétodo convertirá la cadena devuelta por display.dataset.noteen un número de punto flotante. Representa offsetel porcentaje que falta para alcanzar la puntuación máxima. Entonces, para una 7.50nota, tendríamos (10 - 7.50) / 10 = 0.25, lo que significa que la circumferencelongitud debe compensarse con 25%su valor:

    let note = parseFloat(display.dataset.note);let offset = circumference * (10 - note) / 10;

    Actualizando el scripts.js:

    const displays = document.querySelectorAll('.note-display');const transitionDuration = 900;displays.forEach(display = { let progress = display.querySelector('.circle__progress--fill'); let radius = progress.r.baseVal.value; let circumference = 2 * Math.PI * radius;+ let note = parseFloat(display.dataset.note);+ let offset = circumference * (10 - note) / 10; progress.style.setProperty('--initialStroke', circumference); progress.style.setProperty('--transitionDuration', `${transitionDuration}ms`);+ setTimeout(() = progress.style.strokeDashoffset = offset, 100);});

    Antes de continuar, extraigamos la transición de stoke a su propio método:

    const displays = document.querySelectorAll('.note-display');const transitionDuration = 900;displays.forEach(display = {- let progress = display.querySelector('.circle__progress--fill');- let radius = progress.r.baseVal.value;- let circumference = 2 * Math.PI * radius; let note = parseFloat(display.dataset.note);- let offset = circumference * (10 - note) / 10;- progress.style.setProperty('--initialStroke', circumference);- progress.style.setProperty('--transitionDuration', `${transitionDuration}ms`);- setTimeout(() = progress.style.strokeDashoffset = offset, 100);+ strokeTransition(display, note);});+ function strokeTransition(display, note) {+ let progress = display.querySelector('.circle__progress--fill');+ let radius = progress.r.baseVal.value;+ let circumference = 2 * Math.PI * radius;+ let offset = circumference * (10 - note) / 10;+ progress.style.setProperty('--initialStroke', circumference);+ progress.style.setProperty('--transitionDuration', `${transitionDuration}ms`);+ setTimeout(() = progress.style.strokeDashoffset = offset, 100);+ }

    Aumento del valor de la nota

    Todavía queda 0.00por crear la transición de la nota al valor de la nota. Lo primero que debemos hacer es separar los valores enteros y decimales. Usaremos el método de cadena split()(toma un argumento que determina dónde se dividirá la cadena y devuelve una matriz que contiene ambas cadenas rotas). Estos se convertirán en números y se pasarán como argumentos a la increaseNumber()función, junto con el displayelemento y una bandera que indica si es un número entero o un decimal.

     

    const displays = document.querySelectorAll('.note-display');const transitionDuration = 900;displays.forEach(display = { let note = parseFloat(display.dataset.note);+ let [int, dec] = display.dataset.note.split('.');+ [int, dec] = [Number(int), Number(dec)]; strokeTransition(display, note);+ increaseNumber(display, int, 'int');+ increaseNumber(display, dec, 'dec');});

    En la increaseNumber()función, seleccionamos el elemento .percent__into .percent__dec, dependiendo de className, y también en caso de que la salida contenga un punto decimal o no. Hemos configurado nuestro transitionDurationen 900ms. Ahora, para animar un número del 0 al 7, por ejemplo, hay que dividir la duración por la nota 900 / 7 = 128.57ms. El resultado representa cuánto tiempo llevará cada iteración de aumento. Esto significa que setIntervaldispararemos cada 128.57ms.

    Con esas variables configuradas, definamos el setInterval. La countervariable se agregará al elemento como texto y se aumentará en cada iteración:

    function increaseNumber(display, number, className) { let element = display.querySelector(`.percent__${className}`), decPoint = className === 'int' ? '.' : '', interval = transitionDuration / number, counter = 0; let increaseInterval = setInterval(() = { element.textContent = counter + decPoint; counter++; }, interval);}

    ¡Fresco! Aumenta los valores, pero lo hace para siempre. Necesitamos borrar el setIntervalmomento en que las notas alcanzan el valor que queremos. Eso se hace con clearIntervalla función:

    function increaseNumber(display, number, className) { let element = display.querySelector(`.percent__${className}`), decPoint = className === 'int' ? '.' : '', interval = transitionDuration / number, counter = 0; let increaseInterval = setInterval(() = {+ if (counter === number) { window.clearInterval(increaseInterval); } element.textContent = counter + decPoint; counter++; }, interval);}

    Ahora el número se actualiza hasta el valor de la nota y se borra con clearInterval()la función.

    Eso es todo por este tutorial. ¡Espero que lo hayan disfrutado!

    Si tienes ganas de crear algo un poco más interactivo, consulta mi Tutorial de juego de memoria creado con Vanilla JavaScript. Cubre conceptos básicos de HTML5, CSS3 y JavaScript, como posicionamiento, perspectiva, transiciones, Flexbox, manejo de eventos, tiempos de espera y ternarios.

    ¡Feliz codificación!

    (dm, ra, il)Explora más en

    • javascript
    • CSS
    • HTML
    • SVG
    • Animación





    Tal vez te puede interesar:

    1. ¿Deberían abrirse los enlaces en ventanas nuevas?
    2. 24 excelentes tutoriales de AJAX
    3. 70 técnicas nuevas y útiles de AJAX y JavaScript
    4. Más de 45 excelentes recursos y repositorios de fragmentos de código

    Animación de relleno HTML5 SVG con CSS3 y JavaScript Vanilla

    Animación de relleno HTML5 SVG con CSS3 y JavaScript Vanilla

    Deploy Fast. Deploy Smart Advertise on Smashing Magazine Índice Estructura de archivos

    programar

    es

    https://pseint.es/static/images/programar-animacion-de-relleno-html5-svg-con-css3-y-javascript-vanilla-961-0.jpg

    2024-04-04

     

    Animación de relleno HTML5 SVG con CSS3 y JavaScript Vanilla
    Animación de relleno HTML5 SVG con CSS3 y JavaScript Vanilla

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20