One place for hosting & domains

      Cómo crear un efecto de paralaje de desplazamiento con Pure CSS en Chrome


      Introducción

      Modern CSS es una potente herramienta que puede usar para crear diversas funciones avanzadas de interfaz de usuario (UI). En el pasado, estas funciones dependían de las bibliotecas de JavaScript.

      En esta guía, configurará algunas líneas CSS para crear un efecto de paralaje de desplazamiento en una página web. Usará imágenes de placekitten.com como imágenes de marcador de posición en segundo plano.

      Tendrá una página web con un efecto puro de paralaje de desplazamiento de CSS una vez que haya completado el tutorial.

      Advertencia: En este artículo, se utilizan propiedades experimentales de CSS que no funcionan en los distintos navegadores. Este proyecto se probó y funciona en Chrome. Esta técnica no funciona bien en Firefox, Safari e iOS por algunas de las optimizaciones de esos navegadores.

      Paso 1: Crear un nuevo proyecto

      En este paso, utilice la línea de comandos para configurar una nueva carpeta y archivos de proyecto. Para comenzar, abra su terminal y cree una nueva carpeta de proyecto.

      Escriba el siguiente comando para crear la carpeta del proyecto:

      En este caso, nombre la carpeta css-parallax. Ahora, posiciónese en la carpeta css-parallax:

      Luego, cree un archivo index.html en su carpeta css-parallax con el comando nano:

      Pondrá todo el HTML para el proyecto en este archivo.

      En el siguiente paso, comenzará a crear la estructura de la página web.

      Paso 2: Configurar la estructura de la aplicación

      En este paso, añadirá el HTML que necesita para crear la estructura del proyecto.

      Dentro de su archivo index.html, agregue el siguiente código:

      css-parallax/index.html

      
      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <title>CSS Scrolling Parallax</title>
        </head>
        <body></body>
      </html>
      

      Esta es la estructura básica de la mayoría de las páginas web que utilizan HTML.

      Añada el siguiente código en la etiqueta <body>:

      css-parallax/index.html

      
      <body>
      ...
         <main>
            <section class="section parallax bg1">
               <h1>Cute Kitten</h1>
            </section>
            <section class="section static">
               <h1>Boring</h1>
            </section>
            <section class="section parallax bg2">
               <h1>Fluffy Kitten</h1>
            </section>
         </main>
      ...
      </body>
      
      

      Este código crea tres secciones diferentes. Dos tendrán una imagen de fondo, y una será un fondo estático y simple.

      En los siguientes pasos, añadirá los estilos para cada sección usando las clases que añadió en el HTML.

      Paso 3: Crear un archivo CSS y agregar el CSS inicial

      En este paso, creará un archivo CSS. Luego, añadirá en el CSS inicial que necesita para diseñar el sitio web y crear el efecto de paralaje.

      Primero, cree el archivo styles.css en su carpeta css-parallax con el comando nano:

      Aquí es donde pondrá todo el CSS que necesita para crear el efecto de paralaje de desplazamiento.

      Luego, comience con la clase .wrapper. Dentro de su archivo styles.css, añada el siguiente código:

      css-parallax/styles.css

      .wrapper {
        height: 100vh;
        overflow-x: hidden;
        overflow-y: auto;
        perspective: 2px;
      }
      

      La clase .wrapper establece las propiedades de perspectiva y desplazamiento para toda la página.

      La altura del wrapper debe configurarse en un valor fijo para que el efecto funcione. Puede usar la unidad del área de visualización (viewport) vh configurada a 100 para obtener la altura completa del área de visualización de la pantalla.

      Cuando escale las imágenes, se añadirá una barra de desplazamiento horizontal a la pantalla para que pueda deshabilitarla añadiendo overflow-x: hidden;. La propiedad perspective simula la distancia desde el área de visualización a los seudoelementos que creará y transformará más adelante en el CSS.

      En el siguiente paso, va a añadir más CSS para diseñar su página web.

      Paso 4: Añadir estilos a la clase .section

      En este paso, añadirá estilos a la clase .section.

      Dentro de su archivo styles.css, añada el siguiente código debajo de la clase wrapper:

      css-parallax/styles.css

      
      .wrapper {
        height: 100vh;
        overflow-x: hidden;
        perspective: 2px;
      }
      .section { 
        position: relative;
        height: 100vh;
        display: flex;
        align-items: center;
        justify-content: center;
        color: white;
        text-shadow: 0 0 5px #000;
      }
      

      La clase .section define el tamaño, la pantalla y las propiedades de texto para las secciones principales.

      Establezca una posición de relative para que el elemento secundario, .parallax::after pueda posicionarse de manera absoluta en relación con el elemento principal .section.

      Cada sección tiene un view-height(vh) de 100 para ocupar la altura completa del área de visualización. Este valor puede modificarse y configurarse a la altura que prefiera para cada sección.

      Finalmente, las propiedades de CSS restantes se utilizan para dar formato y agregar estilo al texto dentro de cada sección. Posiciona el texto en el centro de cada sección y agrega un color blanco (white).

      El siguiente paso es añadir un seudoelemento y agregarle estilo para crear el efecto de paralaje en dos de las secciones en su HTML.

      Paso 5: Añadir estilos a la clase .parallax

      En este paso, añadirá los estilos a la clase .parallax.

      Primero, añadirá un seudoelemento en la clase .parallax a la que agregará el estilo.

      Nota: Puede visitar la documentación web de MDN para obtener más información sobre los seudoelementos de CSS.

      Añada el siguiente código debajo de la clase .section:

      css-parallax/styles.css

      ...
      
      .section {
        position: relative;
        height: 100vh;
        display: flex;
        align-items: center;
        justify-content: center;
        color: white;
        text-shadow: 0 0 5px #000;
      }
      
      .parallax::after {
        content: " ";
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        transform: translateZ(-1px) scale(1.5);
        background-size: 100%;
        z-index: -1;
      }
      ...
      

      La clase .parallax añade un seudoelemento ::after a la imagen de fondo y proporciona las transformaciones necesarias para el efecto de paralaje.

      El seudoelemento es el último elemento secundario del elemento con la clase .parallax.

      La primera mitad del código muestra y posiciona el seudoelemento. La propiedad transform mueve el seudoelemento lejos de la cámara en el z-index y, luego, lo escala de vuelta para llenar el área de visualización.

      Dado que el seudoelemento está más lejos, parece moverse más lentamente.

      En el siguiente paso, añadirá las imágenes de fondo y el estilo de fondo estático.

      Paso 6: Añadir las imágenes y el fondo para cada sección

      En este paso, añadirá las propiedades finales de CSS para añadir las imágenes y el color de fondo de la sección estática.

      Primero, añada un color sólido de fondo a la sección .static con el siguiente código después de la clase .parallax::after:

      css-parallax/styles.css

      ...
      
      .parallax::after {
        content: " ";
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        transform: translateZ(-1px) scale(1.5);
        background-size: 100%;
        z-index: -1;
      }
      
      .static {
        background: red;
      }
      ...
      

      La clase .static agrega un fondo a la sección estática que no tiene ninguna imagen.

      Las dos secciones con la clase .parallax también tienen otra clase que es diferente para cada una. Utilice las clases .bg1 y .bg2 para añadir las imágenes de fondo de Kitten.

      Añada el siguiente código a la clase .static:

      css-parallax/styles.css

      ...
      
      .static {
        background: red;
      }
      .bg1::after {
        background-image: url('https://placekitten.com/g/900/700');
      }
      
      .bg2::after {
        background-image: url('https://placekitten.com/g/800/600');
      }
      
      ...
      

      Las clases .bg1, .bg2 añaden las imágenes de fondo respectivas para cada sección.

      Las imágenes son del sitio web placekitten. Es un servicio para obtener imágenes de gatitos y utilizarlas como marcadores de posición.

      Una vez que añada el código completo para el efecto de paralaje de desplazamiento, puede vincularlo al archivo styles.css en el archivo index.html.

      Paso 7: Vincular styles.css y abrir index.html en su navegador

      En este paso, vinculará el archivo styles.css y abrirá el proyecto en su navegador para ver el efecto de paralaje de desplazamiento.

      Primero, añada el siguiente código a la etiqueta <head> en el archivo index.html:

      css-parallax/index.html

       ...
      <head>
        <meta charset="UTF-8" />
        <^>
        <link rel="stylesheet" href="https://www.digitalocean.com/community/tutorials/styles.css" />
        <^>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>CSS Parallax</title>
      </head>
      
      ...
      

      Ahora, puede abrir el archivo index.html en su navegador:

      Gif animado del efecto de paralaje de desplazamiento

      Al hacer esto, habrá configurado una página web con un efecto de desplazamiento. Consulte este repositorio de GitHub para ver el código completo.

      Conclusión

      En este artículo, configuró un proyecto con un archivo index.html y styles.css, y ahora tiene una página web funcional. Modificó la estructura de su página web y creó estilos para las diversas secciones en el sitio.

      Las imágenes utilizadas o el efecto de paralaje se pueden alejar aún más para que se muevan más lentamente. Tendrá que cambiar la cantidad de píxeles en las propiedades perspective y transform. Si no desea que la imagen de fondo se desplace en absoluto, utilice background-attachment: fixed; en vez de perspective/translate/scale.



      Source link

      Cómo crear elementos de arrastrar y soltar con Vanilla JavaScript y HTML


      Introducción

      Arrastrar y soltar es una interacción de usuario común que se puede encontrar en muchas interfaces de usuario gráficas.

      Existen bibliotecas de JavaScript preexistentes para añadir la función de arrastrar y soltar a su aplicación. Sin embargo, es posible que haya situaciones en las que una biblioteca no esté disponible o que introduzca una dependencia o gasto general que su proyecto no necesita. En estas situaciones, conocer qué API están disponibles para usted en los navegadores web modernos puede ofrecer soluciones alternativas.

      La API Drag and Drop HTML se basa en el modelo de evento del DOM para obtener información sobre lo que se está arrastrando o soltando y actualizar ese elemento en arrastrar o saltar. Con los controladores de eventos de JavaScript, puede convertir cualquier elemento en un elemento que se puede arrastrar o en un elemento que se puede soltar.

      En este tutorial, crearemos un ejemplo de arrastrar y soltar usando la API Drag and Drop HTML con vanilla JavaScript para usar los controladores de eventos.

      Requisitos previos

      Para completar este tutorial, necesitará lo siguiente:

      • Un navegador web moderno compatible con la API Drag and Drop (Chrome 4+, Firefox 3.5+, Safari 3.1+, Edge 18+).

      Paso 1: Crear el proyecto y marcado inicial

      Nuestro proyecto consistirá en un contenedor con dos tipos de elementos secundarios:

      • Elementos secundarios que puede arrastrar
      • Elementos secundarios en los que se pueden soltar elementos

      Primero, abra la ventana de su terminal y cree un nuevo directorio de proyecto:

      • mkdir drag-and-drop-example

      Luego, diríjase a ese directorio:

      Después, cree un archivo index.html en ese directorio:

      Luego, añada código reutilizable para una página web HTML:

      index.html

      <!DOCTYPE html>
      <html>
        <head>
          <title>My Drag-and-Drop Example</title>
          <link rel="stylesheet" href="https://www.digitalocean.com/community/tutorials/style.css" />
        </head>
        <body>
        </body>
      </html>
      

      Y entre las etiquetas <body> añada su elemento draggable y su dropzone (objetivo para soltar):

      index.html

      <div class="example-parent">
        <div class="example-origin">
          <div
            id="draggable-1"
            class="example-draggable"
          >
            draggable
          </div>
        </div>
      
        <div
          class="example-dropzone"
        >
          dropzone
        </div>
      </div>
      

      Guarde y cierre el archivo. Después, cree un archivo style.css:

      Luego, añada estilos para los elementos en el archivo index.html:

      style.css

      .example-parent {
        border: 2px solid #DFA612;
        color: black;
        display: flex;
        font-family: sans-serif;
        font-weight: bold;
      }
      
      .example-origin {
        flex-basis: 100%;
        flex-grow: 1;
        padding: 10px;
      }
      
      .example-draggable {
        background-color: #4AAE9B;
        font-weight: normal;
        margin-bottom: 10px;
        margin-top: 10px;
        padding: 10px;
      }
      
      .example-dropzone {
        background-color: #6DB65B;
        flex-basis: 100%;
        flex-grow: 1;
        padding: 10px;
      }
      

      Esto añadirá un formato para la aplicación. Ahora puede ver index.html en el navegador y observar que esto produce un draggable <div> y un dropzone <div>.

      Captura de pantalla de los divs draggable y dropzone

      Luego, realizaremos explícitamente el primer <div> arrastrable añadiendo el atributo draggable:

      index.html

      <div class="example-parent">
        <div class="example-origin">
          <div
            id="draggable-1"
            class="example-draggable"
            draggable="true"
          >
            draggable
          </div>
        </div>
      
        <div
          class="example-dropzone"
        >
          dropzone
        </div>
      </div>
      

      Guarde y cierre el archivo.

      Finalmente, vea index.html en el navegador de nuevo. Si hacemos clic en el draggable <div> y lo arrastramos por la pantalla, debería haber una indicación visual de que se está moviendo.

      El valor predeterminado para el atributo draggable es auto. Eso significa que el comportamiento predeterminado de su navegador determinará si el elemento se puede arrastrar. Generalmente, esto significa que las selecciones, las imágenes y los enlaces se pueden arrastrar sin especificar draggable="true".

      Ahora ya tiene un archivo HTML con un elemento que se puede arrastrar. Pasaremos a añadir controladores onevent.

      Paso 2: Gestionar eventos de arrastrar y soltar con JavaScript

      Actualmente, si soltamos el ratón mientras arrastramos el elemento que se puede arrastrar, no pasará nada. Para activar una acción en arrastrar o soltar en elementos DOM, necesitaremos usar la API Drag and Drop:

      • ondragstart: Este controlador de eventos se adjuntará a nuestro elemento draggable y se accionará cuando se produzca un evento dragstart
      • ondragover: Este controlador de eventos estará adjunto a nuestro elemento dropzone y se accionará cuando se produzca un evento dragover
      • ondrop: Este controlador de eventos también estará adjunto a nuestro elemento dropzone y se accionará cuando se produzca un evento drop.

      Nota: En total hay ocho controladores de eventos: ondrag, ondragend, ondragenter, ondragexit, ondragleave, ondragover, ondragstart y ondrop. Para nuestro ejemplo, no los necesitaremos todos.

      Primero, vamos a hacer referencia a un nuevo archivo script.js en el archivo index.html:

      index.html

      <body>
        ...
        <script src="https://www.digitalocean.com/community/tutorials/script.js"></script>
      </body>
      

      Luego, cree un nuevo archivo script.js:

      El objeto DataTransfer realizará un seguimiento de la información relacionada con el arrastre actual. Para actualizar nuestro elemento en arrastrar y soltar, debemos acceder directamente al objeto DataTransfer. Para hacer esto, podemos seleccionar la propiedad dataTransfer desde el DragEvent del elemento DOM.

      Nota: El objeto DataTransfer puede realizar un seguimiento técnico de la información de varios elementos que se arrastran al mismo tiempo. Por nuestro ejemplo, nos enfocaremos en arrastrar un elemento.

      El método setData del objeto dataTransfer se puede usar para configurar la información del estado de arrastre para el elemento que arrastra actualmente. Emplea dos parámetros:

      • una cadena que declara el formato del segundo parámetro
      • los datos reales que se hayan transferido

      Nuestro objetivo es mover nuestro elemento draggable a un nuevo elemento principal. Deberíamos poder seleccionar nuestro elemento draggable con un id único. Podemos configurar el id del elemento arrastrado con el método setData para que pueda utilizarse más adelante.

      Revisemos nuestro archivo script.js y creemos una nueva función para usar setData:

      script.js

      function onDragStart(event) {
        event
          .dataTransfer
          .setData('text/plain', event.target.id);
      }
      

      Nota: Los navegadores Internet Explorer del 9 al 11 aparentemente tienen problemas para usar 'text/plain'. El formato debe 'text' para ese navegador.

      Para actualizar el estilo CSS del elemento arrastrado, podemos acceder a sus estilos usando el evento DOM de nuevo y configurando el estilo que nuestra preferencia para el currentTarget.

      Agreguemos a nuestra función y cambiemos el backgroundColor a yellow:

      script.js

      function onDragStart(event) {
        event
          .dataTransfer
          .setData('text/plain', event.target.id);
      
        event
          .currentTarget
          .style
          .backgroundColor="yellow";
      }
      

      Nota: Los estilos que cambie deberán actualizarse manualmente de nuevo al soltar si desea estilos de arrastrar solamente. Si cambia algo cuando empieza a arrastrar, el elemento arrastrado mantendrá ese nuevo estilo a menos que lo modifique de nuevo.

      Ahora ya tenemos nuestra función de JavaScript para cuando se inicia el arrastre.

      Podemos añadir ondragstart al elemento draggable en index.html:

      index.html

      <div class="example-parent">
        <div class="example-origin">
          <div
            id="draggable-1"
            class="example-draggable"
            draggable="true"
            ondragstart="onDragStart(event);"
          >
            draggable
          </div>
        </div>
      
        <div class="example-dropzone">
          dropzone
        </div>
      </div>
      

      Vea index.html en su navegador. Si intenta arrastrar su elemento ahora, se aplicará el estilo declarado en nuestra función:

      Gif animado que representa un elemento que se arrastra, pero que no se suelta

      Sin embargo, no pasará nada cuando suelte el botón del ratón.

      El siguiente controlador de eventos que se accionó en esta secuencia es ondragover.

      Normalmente, el comportamiento predeterminado de soltar para ciertos elementos DOM como <div> en navegadores no acepta la función de soltar. Este comportamiento interceptará el comportamiento que estamos tratando de implementar. Para garantizar que obtengamos el comportamiento de soltar deseado, aplicaremos preventDefault.

      Revisemos el archivo script.js y creemos una nueva función para usar preventDefault: Añada este código al final del archivo:

      script.js

      function onDragOver(event) {
        event.preventDefault();
      }
      

      Ahora, podemos añadir ondragover a nuestro elemento dropzone en index.html:

      index.html

      <div class="example-parent">
        <div class="example-origin">
          <div
            id="draggable-1"
            class="example-draggable"
            draggable="true"
            ondragstart="onDragStart(event);"
          >
            draggable
          </div>
        </div>
      
        <div
          class="example-dropzone"
          ondragover="onDragOver(event);"
        >
          dropzone
        </div>
      </div>
      

      En este momento, aún no hemos escrito el código para controlar la función real de soltar. El controlador de eventos final que se accionó en esta secuencia es ondrop.

      Volvamos a revisar nuestro archivo script.js y creemos una nueva función.

      Podemos hacer referencia a los datos que guardamos anteriormente con el método setData del objeto dataTransfer. Usaremos el método getData del objeto dataTransfer. Los datos que configuramos fueron el id, así que eso es lo que se devolverá:

      script.js

      function onDrop(event) {
        const id = event
          .dataTransfer
          .getData('text');
      }
      

      Seleccione nuestro elemento draggable con el id que recuperamos:

      script.js

      function onDrop(event) {
        // ...
      
        const draggableElement = document.getElementById(id);
      }
      

      Seleccione nuestro elemento dropzone:

      script.js

      function onDrop(event) {
        // ...
      
        const dropzone = event.target;
      }
      

      Añada nuestro elemento draggable al dropzone:

      script.js

      function onDrop(event) {
        // ...
      
        dropzone.appendChild(draggableElement);
      }
      

      Restablezca nuestro objeto dataTransfer:

      script.js

      function onDrop(event) {
        // ...
      
        event
          .dataTransfer
          .clearData();
      }
      

      Ahora, podemos añadir ondragover a nuestro elemento dropzone en index.html:

      index.html

      <div class="example-parent">
        <div class="example-origin">
          <div
            id="draggable-1"
            class="example-draggable"
            draggable="true"
            ondragstart="onDragStart(event);"
          >
            draggable
          </div>
        </div>
      
        <div
          class="example-dropzone"
          ondragover="onDragOver(event);"
          ondrop="onDrop(event);"
        >
          dropzone
        </div>
      </div>
      

      Una vez hecho esto, tendremos una función de arrastrar y soltar finalizada. Vea index.html en su navegador y arrastre el elemento draggable al dropzone.

      Gif animado que representa un elemento que se arrastra y se suelta en un objetivo para soltar

      Nuestro ejemplo gestiona el caso de un solo elemento que se puede arrastrar y un único objetivo para soltar. Puede tener varios elementos que se pueden arrastrar, varios objetivos para soltar y personalizarlos con todos los demás controladores de eventos de la API Drag and Drop

      Paso 3: Crear un ejemplo avanzado con múltiples elementos que se pueden arrastrar

      Aquí hay un ejemplo más de cómo se podría usar esta API: una lista de cosas por hacer con tareas que se pueden arrastrar de una columna "Tareas pendientes" a otra columna de "Completadas".

      Gif animado que representa múltiples tareas siendo arrastradas y soltadas en la columna Completadas

      Para crear su propia lista de tareas pendientes, añada más elementos que se pueden arrastrar con id únicos a index.html:

      index.html

      <div class="example-parent">
        <h1>To-do list</h1>
        <div class="example-origin">
          To-do
          <div
            id="draggable-1"
            class="example-draggable"
            draggable="true"
            ondragstart="onDragStart(event);"
          >
            thing 1
          </div>
          <div
            id="draggable-2"
            class="example-draggable"
            draggable="true"
            ondragstart="onDragStart(event);"
          >
            thing 2
          </div>
          <div
            id="draggable-3"
            class="example-draggable"
            draggable="true"
            ondragstart="onDragStart(event);"
          >
            thing 3
          </div>
          <div
            id="draggable-4"
            class="example-draggable"
            draggable="true"
            ondragstart="onDragStart(event);"
          >
            thing 4
          </div>
        </div>
      
        <div
          class="example-dropzone"
          ondragover="onDragOver(event);"
          ondrop="onDrop(event);"
        >
          Done
        </div>
      </div>
      

      Vea index.html en su navegador y arrastre los elementos en la columna Tareas pendientes a la columna Completadas. Ha creado una aplicación de tareas pendientes y ha probado su funcionalidad.

      Conclusión

      En este artículo, creó una app de tareas pendientes para explorar la funcionalidad de arrastrar y soltar que está disponible para los navegadores web modernos.

      La API Drag and Drop proporciona múltiples opciones para personalizar sus acciones más allá de arrastrar y soltar. Por ejemplo, puede actualizar el estilo CSS de sus elementos arrastrados. Además, en vez de mover el elemento, puede elegir copiar el elemento que se puede arrastrar de forma que se duplique al soltarse

      Tenga en cuenta que, si bien muchos navegadores web son compatibles con esta tecnología, es posible que no pueda serle útil si su audiencia tiene dispositivos que no son compatibles con esta funcionalidad.

      Para obtener más información sobre todo lo que puede soltar con la API Drag and Drop , consulte los documentos de MDN.



      Source link

      Cómo crear paginación personalizada con React


      Introducción

      A menudo tenemos que crear aplicaciones web en las que necesitamos buscar grandes conjuntos de registros de datos de un servidor remoto, API o una base de datos. Si está creando un sistema de pago, por ejemplo, podría ser buscar miles de transacciones. Si es una aplicación de redes sociales, podría ser buscar muchos comentarios de usuarios, perfiles o actividades. Sea cual sea la aplicación, existen varias soluciones para presentar los datos de una forma que no abrume al usuario final que interactúa con la aplicación.

      Un método para administrar grandes conjuntos de datos es usar la paginación. La paginación funciona de forma efectiva cuando ya conoce el tamaño del conjunto de datos (el número total de registros en la base de datos). Segundo, solo carga el lote requerido de datos del conjunto total de datos según la interacción del usuario final con el control de paginación. Esta es la técnica usada a la hora de mostrar resultados de la búsqueda en Google.

      En este tutorial, aprenderá cómo crear un componente de paginación personalizado con React para paginar grandes conjuntos de datos. Creará una vista paginada de los países del mundo, un conjunto de datos con un tamaño conocido.

      Aquí tiene una demostración de lo que va a crear en este tutorial:

      Captura de pantalla de la aplicación de demostración, que muestra los países del mundo

      Requisitos previos

      Para completar este tutorial, necesitará lo siguiente:

      Este tutorial se verificó con Node v14.2.0, npm v6.14.4, react v16.13.1 y react-scripts v3.4.1.

      Paso 1: Configurar el proyecto

      Comience una nueva aplicación React usando el comando create-react-app. Puede ponerle el nombre que desee a la aplicación, pero este tutorial la llamará react-pagination:

      • npx create-react-app react-pagination

      A continuación, instalará las dependencias necesarias para su aplicación. Primero, utilice la ventana del terminal para navegar al directorio del proyecto:

      Ejecute el siguiente comando para instalar las dependencias necesarias:

      • npm install bootstrap@4.1.0 prop-types@15.6.1 react-flags@0.1.13 countries-api@2.0.1 node-sass@4.14.1

      Esto instalará bootstrap, prop-types, react-flags, countries-api y node-sass.

      Instaló el paquete bootstrap como dependencia para su aplicación, ya que necesitará algunos estilos predeterminados. También usará los estilos del componente de pagination de Bootstrap.

      Para incluir Bootstrap en la aplicación, edite el archivo src/index.js:

      Y añada la siguiente línea antes de las demás declaraciones import:

      src/index.js

      import "bootstrap/dist/css/bootstrap.min.css";
      

      Ahora los estilos de Bootstrap estarán disponibles para su aplicación.

      También instaló react-flags como dependencia para su aplicación. Para obtener acceso a los iconos de banderas de su aplicación, necesitará copiar las imágenes de iconos al directorio public de su aplicación.

      Cree un directorio img en su directorio public:

      Copie los archivos de imágenes en flags a img:

      • cp -R node_modules/react-flags/vendor/flags public/img

      Esto proporciona una copia de todas las imágenes react-flag para su aplicación.

      Ahora que ha incluido algunas dependencias, inicie la aplicación ejecutando el siguiente comando con npm desde el directorio del proyecto react-pagination.

      Ahora que ha iniciado la aplicación, puede comenzar el desarrollo. Observe que se abrió una ventana del navegador con la funcionalidad live-reloading para mantener la sincronización con la aplicación a medida que desarrolla.

      En este momento, la vista de la aplicación debería parecerse a la siguiente captura de pantalla:

      Vista inicial - Bienvenido a la pantalla de React

      Ahora está listo para comenzar a crear componentes.

      Paso 2: Crear el componente CountryCard

      En este paso, creará el componente CountryCard. El componente CountryCard renderiza el nombre, región y bandera de un país concreto.

      Primero, vamos a crear un directorio components en el directorio src:

      A continuación, cree un nuevo archivo CountryCard.js en el directorio src/components:

      • nano src/components/CountryCard.js

      Luego, añádale el siguiente snippet:

      src/components/CountryCard.js

      import React from 'react';
      import PropTypes from 'prop-types';
      import Flag from 'react-flags';
      
      const CountryCard = props => {
        const {
          cca2: code2 = '', region = null, name = {}
        } = props.country || {};
      
        return (
          <div className="col-sm-6 col-md-4 country-card">
            <div className="country-card-container border-gray rounded border mx-2 my-3 d-flex flex-row align-items-center p-0 bg-light">
              <div className="h-100 position-relative border-gray border-right px-2 bg-white rounded-left">
                <Flag country={code2} format="png" pngSize={64} basePath="./img/flags" className="d-block h-100" />
              </div>
              <div className="px-3">
                <span className="country-name text-dark d-block font-weight-bold">{ name.common }</span>
                <span className="country-region text-secondary text-uppercase">{ region }</span>
              </div>
            </div>
          </div>
        )
      }
      
      CountryCard.propTypes = {
        country: PropTypes.shape({
          cca2: PropTypes.string.isRequired,
          region: PropTypes.string.isRequired,
          name: PropTypes.shape({
            common: PropTypes.string.isRequired
          }).isRequired
        }).isRequired
      };
      
      export default CountryCard;
      

      El componente CountryCard requiere una propiedad country que contiene los datos sobre el país que va a renderizar. Como se ha visto en los propTypes para el componente CountryCard, el objeto prop country debe contener los siguientes datos:

      • cca2: código del país de 2 dígitos
      • region: la región del país (por ejemplo, “África”)
      • name.common: el nombre común del país (por ejemplo “Nigeria”)

      Aquí hay un objetivo de país de muestra:

      {
        cca2: "NG",
        region: "Africa",
        name: {
          common: "Nigeria"
        }
      }
      

      Además, observe cómo renderiza la bandera del país usando el paquete react-flags. Puede comprobar la documentación react-flagspara obtener más información sobre los props necesarios y cómo usar el paquete.

      Ahora ha completado un componente CountryCard individual. Finalmente, usará CountryCard varias veces para mostrar diferentes banderas e información del país en su aplicación.

      En este paso, creará el componente Pagination. El componente Pagination contiene la lógica para crear, renderizar y cambiar páginas en el control de paginación.

      A continuación, cree un nuevo archivo Pagination.js en el directorio src/components:

      • nano src/components/Pagination.js

      Luego, añádale el siguiente snippet:

      src/components/Pagination.js

      import React, { Component, Fragment } from 'react';
      import PropTypes from 'prop-types';
      
      class Pagination extends Component {
        constructor(props) {
          super(props);
          const { totalRecords = null, pageLimit = 30, pageNeighbours = 0 } = props;
      
          this.pageLimit = typeof pageLimit === 'number' ? pageLimit : 30;
          this.totalRecords = typeof totalRecords === 'number' ? totalRecords : 0;
      
          // pageNeighbours can be: 0, 1 or 2
          this.pageNeighbours = typeof pageNeighbours === 'number'
            ? Math.max(0, Math.min(pageNeighbours, 2))
            : 0;
      
          this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
      
          this.state = { currentPage: 1 };
        }
      }
      
      Pagination.propTypes = {
        totalRecords: PropTypes.number.isRequired,
        pageLimit: PropTypes.number,
        pageNeighbours: PropTypes.number,
        onPageChanged: PropTypes.func
      };
      
      export default Pagination;
      

      El componente Pagination puede tomar cuatro propiedades especiales como se especifica en el objeto propTypes.

      • onPageChanged es una función invocada con datos del estado de paginación actual solo cuando la página actual cambia.
      • totalRecords indica el número total de registros que se paginarán. Es obligatorio.
      • pageLimit indica el número de registros que se mostrarán por página. Si no se especifica, vuelve al valor predeterminado de 30 como se ha definido en el constructor().
      • pageNeighbours indica el número de números de páginas adicionales para mostrar en cada lado de la página actual. El valor mínimo es 0 y el valor máximo es 2. Si no se especifica, vuelve al valor 0, como se definió en constructor().

      La siguiente imagen ilustra el efecto de los diferentes valores del prop pageNeighbours:

      Ilustración de Page Neighbours

      En la función constructor(), calcule las páginas totales de la siguiente manera:

      this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
      

      Observe que aquí utiliza Math.ceil() para garantizar que obtiene un valor entero para el número total de páginas. Esto también garantiza que el excedente de registros se captura en la última página, especialmente en los casos en que el número de registros que sobran es menor que el número de registros que se mostrarán por página.

      Finalmente, inició el estado con la propiedad currentPage configurada en 1. Necesita esta propiedad de estado para realizar un seguimiento interno de la página actualmente activa.

      A continuación, creará el método para generar los números de página.

      Tras las importaciones pero antes de la clase Pagination, añada las siguientes constantes y una función range:

      src/components/Pagination.js

      // ...
      
      const LEFT_PAGE = 'LEFT';
      const RIGHT_PAGE = 'RIGHT';
      
      /**
       * Helper method for creating a range of numbers
       * range(1, 5) => [1, 2, 3, 4, 5]
       */
      const range = (from, to, step = 1) => {
        let i = from;
        const range = [];
      
        while (i <= to) {
          range.push(i);
          i += step;
        }
      
        return range;
      }
      

      En la clase Pagination, tras el constructor, añada el siguiente método fetchPageNumbers:

      src/components/Pagination.js

      class Pagination extends Component {
        // ...
      
        /**
         * Let's say we have 10 pages and we set pageNeighbours to 2
         * Given that the current page is 6
         * The pagination control will look like the following:
         *
         * (1) < {4 5} [6] {7 8} > (10)
         *
         * (x) => terminal pages: first and last page(always visible)
         * [x] => represents current page
         * {...x} => represents page neighbours
         */
        fetchPageNumbers = () => {
          const totalPages = this.totalPages;
          const currentPage = this.state.currentPage;
          const pageNeighbours = this.pageNeighbours;
      
          /**
           * totalNumbers: the total page numbers to show on the control
           * totalBlocks: totalNumbers + 2 to cover for the left(<) and right(>) controls
           */
          const totalNumbers = (this.pageNeighbours * 2) + 3;
          const totalBlocks = totalNumbers + 2;
      
          if (totalPages > totalBlocks) {
            const startPage = Math.max(2, currentPage - pageNeighbours);
            const endPage = Math.min(totalPages - 1, currentPage + pageNeighbours);
            let pages = range(startPage, endPage);
      
            /**
             * hasLeftSpill: has hidden pages to the left
             * hasRightSpill: has hidden pages to the right
             * spillOffset: number of hidden pages either to the left or to the right
             */
            const hasLeftSpill = startPage > 2;
            const hasRightSpill = (totalPages - endPage) > 1;
            const spillOffset = totalNumbers - (pages.length + 1);
      
            switch (true) {
              // handle: (1) < {5 6} [7] {8 9} (10)
              case (hasLeftSpill && !hasRightSpill): {
                const extraPages = range(startPage - spillOffset, startPage - 1);
                pages = [LEFT_PAGE, ...extraPages, ...pages];
                break;
              }
      
              // handle: (1) {2 3} [4] {5 6} > (10)
              case (!hasLeftSpill && hasRightSpill): {
                const extraPages = range(endPage + 1, endPage + spillOffset);
                pages = [...pages, ...extraPages, RIGHT_PAGE];
                break;
              }
      
              // handle: (1) < {4 5} [6] {7 8} > (10)
              case (hasLeftSpill && hasRightSpill):
              default: {
                pages = [LEFT_PAGE, ...pages, RIGHT_PAGE];
                break;
              }
            }
      
            return [1, ...pages, totalPages];
          }
      
          return range(1, totalPages);
        }
      }
      

      Aquí, primero define dos constantes: LEFT_PAGE y RIGHT_PAGE. Estas constantes se usarán para indicar puntos en los que tiene controles de página para moverse a la izquierda y derecha respectivamente.

      También definió una función de ayuda range() que puede ayudar a generar intervalos de números.

      Nota: Si utiliza una biblioteca de utilidades como Lodash en su proyecto, puede usar la función _.range() proporcionada por Lodash. El siguiente snippet de código muestra la diferencia entre la función range() que acaba de definir y la de Lodash:

      range(1, 5); // returns [1, 2, 3, 4, 5]
      _.range(1, 5); // returns [1, 2, 3, 4]
      

      A continuación, definió el método fetchPageNumbers() en la clase Pagination. Este método gestiona la lógica principal para generar los números de página que se mostrarán en el control de paginación. Quiere que la primera página y la última página siempre estén visibles.

      Primero, ha definido un par de variables. totalNumbers representa los números de página en total que se mostrarán en el control. totalBlocks representa el número total de páginas que se mostrará más dos bloques adicionales para los indicadores de página izquierda y derecha.

      Si totalPages no es mayor que totalBlocks, devuelve un intervalo de número desde 1 a totalPages. De lo contrario, devolverá la matriz de números de página, con LEFT_PAGE y RIGHT_PAGE en los lugares donde tenga páginas que se reparten a la izquierda y la derecha, respectivamente.

      Sin embargo, observe que su control de paginación garantiza que la primera página y la última página siempre estén visibles. Los controles de página izquierda y derecha aparecen hacia dentro.

      Ahora, añadirá el método render() para permitirle renderizar el control de paginación.

      En la clase Pagination, tras el método constructor y fetchPageNumbers, añada el siguiente método render:

      src/components/Pagination.js

      class Pagination extends Component {
        // ...
      
        render() {
          if (!this.totalRecords || this.totalPages === 1) return null;
      
          const { currentPage } = this.state;
          const pages = this.fetchPageNumbers();
      
          return (
            <Fragment>
              <nav aria-label="Countries Pagination">
                <ul className="pagination">
                  { pages.map((page, index) => {
      
                    if (page === LEFT_PAGE) return (
                      <li key={index} className="page-item">
                        <a className="page-link" href="https://www.digitalocean.com/community/tutorials/#" aria-label="Previous" onClick={this.handleMoveLeft}>
                          <span aria-hidden="true">&laquo;</span>
                          <span className="sr-only">Previous</span>
                        </a>
                      </li>
                    );
      
                    if (page === RIGHT_PAGE) return (
                      <li key={index} className="page-item">
                        <a className="page-link" href="https://www.digitalocean.com/community/tutorials/#" aria-label="Next" onClick={this.handleMoveRight}>
                          <span aria-hidden="true">&raquo;</span>
                          <span className="sr-only">Next</span>
                        </a>
                      </li>
                    );
      
                    return (
                      <li key={index} className={`page-item${ currentPage === page ? ' active' : ''}`}>
                        <a className="page-link" href="https://www.digitalocean.com/community/tutorials/#" onClick={ this.handleClick(page) }>{ page }</a>
                      </li>
                    );
      
                  }) }
      
                </ul>
              </nav>
            </Fragment>
          );
        }
      }
      

      Aquí, genera la matriz de números de página invocando el método fetchPageNumbers() que creó anteriormente. A continuación renderiza cada número de página usando Array.prototype.map(). Observe que registra los controladores de eventos de clic en cada número de página renderizada para gestionar los clics.

      Además, observe que el control de paginación no se renderizará si el prop totalRecords no se pasó correctamente al componente Pagination o en los casos en que solo haya 1 página.

      Finalmente, definirá los métodos del controlador de eventos.

      En la clase Pagination, tras el constructor y el método fetchPageNumbers y el método render, añada lo siguiente:

      src/components/Pagination.js

      class Pagination extends Component {
        // ...
      
        componentDidMount() {
          this.gotoPage(1);
        }
      
        gotoPage = page => {
          const { onPageChanged = f => f } = this.props;
          const currentPage = Math.max(0, Math.min(page, this.totalPages));
          const paginationData = {
            currentPage,
            totalPages: this.totalPages,
            pageLimit: this.pageLimit,
            totalRecords: this.totalRecords
          };
      
          this.setState({ currentPage }, () => onPageChanged(paginationData));
        }
      
        handleClick = page => evt => {
          evt.preventDefault();
          this.gotoPage(page);
        }
      
        handleMoveLeft = evt => {
          evt.preventDefault();
          this.gotoPage(this.state.currentPage - (this.pageNeighbours * 2) - 1);
        }
      
        handleMoveRight = evt => {
          evt.preventDefault();
          this.gotoPage(this.state.currentPage + (this.pageNeighbours * 2) + 1);
        }
      }
      

      Define el método gotoPage() que modifica el estado y establece currentPage a la página especificada. Garantiza que el argumento page tiene un valor mínimo de 1 y un valor máximo del número total de páginas. Finalmente invoca la función onPageChanged() que se pasó como prop, con datos que indican el nuevo estado de paginación.

      Cuando se monta el componente, va a la primera página invocando this.gotoPage(1) como se muestra en el método de ciclo de vida componentDidMount().

      Observe cómo usa (this.pageNeighbours * 2) en handleMoveLeft() y handleMoveRight() para deslizar los números de página a la izquierda y a la derecha, respectivamente, según el número de página actual.

      Aquí tiene una demostración de la interacción del movimiento izquierdo a derecho.

      Movimiento izquierda-derecha de la interacción

      Ahora ha completado el componente Pagination. Los usuarios podrá interactuar con los controles de navegación en este componente para mostrar diferentes páginas de banderas.

      Paso 4: Crear el componente App

      Ahora que tiene un componente CountryCard y Pagination, puede usarlos en su componente App.

      Modifique el archivo App.js en el directorio src:

      Sustituya el contenido de App.js con las siguientes líneas de código:

      src/App.js

      import React, { Component } from 'react';
      import Countries from 'countries-api';
      import './App.css';
      import Pagination from './components/Pagination';
      import CountryCard from './components/CountryCard';
      
      class App extends Component {
        state = { allCountries: [], currentCountries: [], currentPage: null, totalPages: null }
      
        componentDidMount() {
          const { data: allCountries = [] } = Countries.findAll();
          this.setState({ allCountries });
        }
      
        onPageChanged = data => {
          const { allCountries } = this.state;
          const { currentPage, totalPages, pageLimit } = data;
          const offset = (currentPage - 1) * pageLimit;
          const currentCountries = allCountries.slice(offset, offset + pageLimit);
      
          this.setState({ currentPage, currentCountries, totalPages });
        }
      }
      
      export default App;
      

      Aquí inicia el estado del componente App con los siguientes atributos:

      • allCountries - Esta es una matriz de todos los países en su aplicación. Iniciado a una matriz vacía ([]).
      • currentCountries - Esta es una matriz de todos los países que se mostrarán en la página activa actualmente. Iniciado a una matriz vacía ([]).
      • currentPage - El número de página de la página activa actualmente. Iniciado a null.
      • totalPages - El número de páginas en total para todos los registros de país. Iniciado a null.

      A continuación, en el método de ciclo de vida componentDidMount(), busca todos los países del mundo usando el paquete countries-api invocando Countries.findAll(). A continuación, actualiza el estado de la aplicación, configurando allCountries para que contenga todos los países del mundo. Puede consultar la documentación de countries-api para obtener más información sobre el paquete.

      Finalmente, definió el método onPageChanged(), que invocaremos cada vez que navegue a una nueva página desde el control de paginación. Este método se pasará a la propiedad onPageChanged del componente Pagination.

      Hay dos líneas a las que vale la pena prestar atención en este método. La primera es esta línea:

      const offset = (currentPage - 1) * pageLimit;
      

      El valor offset indica el índice de inicio para buscar los registros para la página actual. Usar (currentPage - 1) garantiza que el offset se basa en cero. Digamos, por ejemplo, que está mostrando 25 registros por página, y actualmente está viendo la página 5. Luego el offset será (5 - 1) * 25 = 100).

      Por ejemplo, si está buscando registros bajo demanda desde una base de datos, esta es una consulta SQL de muestra para mostrarle cómo usar la posición inicial:

      SELECT * FROM `countries` LIMIT 100, 25
      

      Ya que no está buscando registros desde una base de datos o cualquier fuente externa, necesita una forma de extraer el bloque de registros requerido que se mostrará para la página actual.

      La segunda es esta línea:

      const currentCountries = allCountries.slice(offset, offset + pageLimit);
      

      Aquí utiliza el método Array.prototype.slice() para extraer el bloque de registros requerido desde allCountries pasando offset como el índica de inicio para la porción y (offset + pageLimit) como el índice antes del cual finalizar la porción.

      Nota: En este tutorial no buscó registros de una fuente externa. En una aplicación real, probablemente estará buscando registros desde una base de datos o una API. La lógica para buscar los registros puede ir al método onPageChanged() del componente App.

      Digamos que tiene un endpoint de API ficticio /api/countries?page={current_page}&limit={page_limit}. El siguiente snippet muestra cómo buscar países bajo demanda desde la API usando el paquete HTTP de axios:

      onPageChanged = data => {
        const { currentPage, totalPages, pageLimit } = data;
      
        axios.get(`/api/countries?page=${currentPage}&limit=${pageLimit}`)
          .then(response => {
            const currentCountries = response.data.countries;
            this.setState({ currentPage, currentCountries, totalPages });
          });
      }
      

      Ahora, puede terminar el componente App añadiendo el método render().

      En la clase App, pero tras componentDidMount y onPageChanged, añada el siguiente método render:

      src/App.js

      class App extends Component {
        // ... other methods here ...
      
        render() {
          const { allCountries, currentCountries, currentPage, totalPages } = this.state;
          const totalCountries = allCountries.length;
      
          if (totalCountries === 0) return null;
      
          const headerClass = ['text-dark py-2 pr-4 m-0', currentPage ? 'border-gray border-right' : ''].join(' ').trim();
      
          return (
            <div className="container mb-5">
              <div className="row d-flex flex-row py-5">
                <div className="w-100 px-4 py-5 d-flex flex-row flex-wrap align-items-center justify-content-between">
                  <div className="d-flex flex-row align-items-center">
                    <h2 className={headerClass}>
                      <strong className="text-secondary">{totalCountries}</strong> Countries
                    </h2>
                    { currentPage && (
                      <span className="current-page d-inline-block h-100 pl-4 text-secondary">
                        Page <span className="font-weight-bold">{ currentPage }</span> / <span className="font-weight-bold">{ totalPages }</span>
                      </span>
                    ) }
                  </div>
                  <div className="d-flex flex-row py-4 align-items-center">
                    <Pagination totalRecords={totalCountries} pageLimit={18} pageNeighbours={1} onPageChanged={this.onPageChanged} />
                  </div>
                </div>
                { currentCountries.map(country => <CountryCard key={country.cca3} country={country} />) }
              </div>
            </div>
          );
        }
      }
      

      En el método render(), renderiza el número total de países, la página actual, el número de páginas en total, el control <Pagination>, y luego la <CountryCard> para cada país en la página actual.

      Observe que ha pasado el método onPageChanged() que definió anteriormente para el prop onPageChanged del control <Pagination>. Esto es muy importante para capturar los cambios en la página desde el componente Pagination. También está mostrando 18 países por página.

      En este momento, la aplicación tendrá un aspecto similar a la siguiente captura de pantalla:

      Captura de pantalla de la aplicación con 248 países listados y números de página en la parte superior para avanzar por cada página

      Ahora tiene un componente App que muestra varios componentes CountryCard y un componente Pagination que desglosa los contenidos en páginas independientes. A continuación, explorará el estilo de su aplicación.

      Paso 5: Añadir estilos personalizados

      Quizá haya notado que estuvo añadiendo algunas clases personalizadas a los componentes que creó antes. Vamos a definir algunas reglas de estilo para esas clases en el archivo src/App.scss.

      El archivo App.scss tendrá el aspecto del siguiente snippet:

      src/App.scss

      /* Declare some variables */
      $base-color: #ced4da;
      $light-background: lighten(desaturate($base-color, 50%), 12.5%);
      
      .current-page {
        font-size: 1.5rem;
        vertical-align: middle;
      }
      
      .country-card-container {
        height: 60px;
        cursor: pointer;
        position: relative;
        overflow: hidden;
      }
      
      .country-name {
        font-size: 0.9rem;
      }
      
      .country-region {
        font-size: 0.7rem;
      }
      
      .current-page,
      .country-name,
      .country-region {
        line-height: 1;
      }
      
      // Override some Bootstrap pagination styles
      ul.pagination {
        margin-top: 0;
        margin-bottom: 0;
        box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
      
        li.page-item.active {
          a.page-link {
            color: saturate(darken($base-color, 50%), 5%) !important;
            background-color: saturate(lighten($base-color, 7.5%), 2.5%) !important;
            border-color: $base-color !important;
          }
        }
      
        a.page-link {
          padding: 0.75rem 1rem;
          min-width: 3.5rem;
          text-align: center;
          box-shadow: none !important;
          border-color: $base-color !important;
          color: saturate(darken($base-color, 30%), 10%);
          font-weight: 900;
          font-size: 1rem;
      
          &:hover {
            background-color: $light-background;
          }
        }
      }
      

      Modifique su archivo App.js para referenciar a App.scss en vez de a App.css.

      Nota: Para obtener más información sobre esto, consulte la documentación de Crear una aplicación React.

      src/App.js

      import React, { Component } from 'react';
      import Countries from 'countries-api';
      import './App.scss';
      import Pagination from './components/Pagination';
      import CountryCard from './components/CountryCard';
      

      Tras añadir los estilos, la aplicación ahora tendrá un aspecto similar al de la siguiente captura de pantalla:

      Captura de pantalla de la aplicación página 1 de 14 con estilos

      Ahora ha completado la aplicación con estilos personalizados adicionales. Puede usar estilos personalizados para modificar y mejorar cualquier estilo predeterminado proporcionado por las bibliotecas como Bootstrap.

      Conclusión

      En este tutorial, creó un widget de paginación personalizado en su aplicación React. Aunque no realizó invocaciones a ninguna API ni interactuar con cualquier backend en este tutorial, su aplicación puede demandar dichas interacciones. No está limitado al enfoque usado en este tutorial, puede ampliarlo como desee para adaptarse a los requisitos de su aplicación.

      Para ver el código fuente completo de este tutorial, consulte el repositorio build-react-pagination-demo en GitHub. También puede obtener una demostración en vivo de este tutorial en Code Sandbox.

      Si desea aprender más sobre React, eche un vistazo a nuestra serie Cómo crear código en React.js, o consulte nuestra página del tema React para ver ejercicios y proyectos de programación.



      Source link