Carlos Eduardo Gomez García
Sebastián Buitrago
bro porque no agregas la rama de gh-pages
Celia Cruz
Yo borre las imagenes asi ...
deleteButton.addEventListener('click', deleteImages) const deleteImages = () => mountNode.innerHTML = ''
Carlos Eduardo Gomez García
Sebastián Buitrago
bro porque no agregas la rama de gh-pages
Celia Cruz
Yo borre las imagenes asi ...
deleteButton.addEventListener('click', deleteImages) const deleteImages = () => mountNode.innerHTML = ''
Camilo Gonzalez
Les comparto mi proyecto 😁 El curso ha estado genial!!! sitio: https://findafox.netlify.app/ repo: https://github.com/jmilo13/findFox/
David Rolando Balderramos Mendoza
José Zárate Vera
Compañero, falta reiniciar el contador de imágenes cargadas al darle clic en limpiar. Saludos :D
William Santiago Alzate Barriga
Como adicional al primer reto, seria esencial agregar un icono de cargando al recuadro gris, esto le dará una mejor percepción al usuario de lo que esta sucediendo con la imagen. En la siguiente url podrán encontrar un ejemplo de cómo crear este icono con solo html y css
Jose Anibal Garcia Giraldo
Les comparto mi solución al Reto 🎉 Aquí el repo y aquí el resultado 😁
John Ruiz
Wow,
Muy bien te quedo, tengo muchas dudas con JS para poder desarrollar este reto, pero voy a estudiar tu repo. Muchas gracias
Max Andy Diaz Neyra
Max Andy Diaz Neyra
Les dejo mi sitio lazy-fox: https://modozen.github.io/lazy-fox/
Marco Marin
Ahí va mi Lazy Loading. Tuve que ver el repositorio del profesor para el tema de la imagen gris, pero entendí el funcionamiento.
Fernando Enrique Zepeda Castellanos
Bryan Hernández
Amigo, estuvo buenísimo. Me inspiré en tu aporte para hacer el mío, me retó mucho porque nunca había usado clases en Javascript. Me costó mucho pero al final, con dedicación, paciencia e investigación lo logré hacer por mí mismo jeje Salvo que yo usé métodos y propiedades estáticas para no hacer instancia de la clase a utilizar, desconozco si fue buena práctica lo tuyo o lo mío jeje. Saluditos y sigue así!
Diego Alexander Guatibonza Bello
Index.js
lazy
Roger Colquehuanca
Como lograr este efecto de un loading para cada imagen de una lista api json con fetch?
quería lograr un loading a cada Imagen el texto carga rápido pero la imagen demora un poco mas.
Vi que lo hacen con imágenes estáticas pero quiero lograrlo en un api json.
Jonathan 🦑 Alvarez
ProfesorHola hola!
Los loading state consisten en lo siguiente:
Así que en efecto se hace con una imágen estática que ya esté en el proyecto, o con CSS si es una animacíon simple: https://loading.io/css/
¿Tienes otra cosa en mente para tu animación?
Roger Colquehuanca
Gracias @jonalvarezz Espero lograr este efecto en web
Los datos es de un Web Api json y hay un loading previo por cada imagen(como un placeholder) hasta que este listo la imagen original
Daniel Alberto Esquinazi
Como generaste la imagen en base64?
const image = document.createElement("img"); image.className = "mx-auto rounded-md bg-gray-300"; image.src = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIwIiBoZWlnaHQ9IjMyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjEiLz4=";
Jonathan 🦑 Alvarez
ProfesorAh pero que buena pregunta! Todo está inspirado en como Next.js lo hace: https://github.com/vercel/next.js/blob/canary/packages/next/client/image.tsx#L391
De allí lo saque y también la técnica de usar la imagen base64. (aunque veo que ya actualizaron el valor y cambió)
Miguel Angel Reyes Moreno
Lorenzo Peñalver
Me gustó tu forma de contar las imágenes!
Brandon Argel Verdeja Domínguez
Carlos Iván Ochoa Gómez
Asi quedo
Luis Alejandro Vera Hernandez
Joaquin Ariel Tejerina
Juan Diego Loaiza Martinez
Christian Boffill
Aquí les dejo mi taller: https://christbm.github.io/workshop2/
Jorge luis castro marchan
Mi solución, para eliminar las imagenes hice lo siguiente:
const cleanImages = () => { const nodeElementsImages = mountNode.children const listaElements = [...nodeElementsImages] listaElements.forEach((element) => element.remove()) } //Agregamos el evento cleanButton.addEventListener('click', cleanImages)
Para agregar el fondo gris :
const imagen = h('img.mx-auto', { width: 320, height : 300, style: { background:'grey' }, "data-src" : `https://randomfox.ca/images/${random()}.jpg`, })
Oscar J. Carabali
Tengo una pregunta, como hago para hacer este efecto en una imagen de background. He estado intentandolo, creo la imagen background desde js pero me sale error y quiero saber como hacerlo.
Vale, para agregar el fondo gris lo que hice fue crear un wrapper que encerrara a la imagen y que se adaptara al tamaño de la misma con un mínimo de 100px, y este wrapper tiene el fondo gris, así cuando se inserta sale con fondo gris:
... imagen.width = "320"; imagen.dataset.src = `https://randomfox.ca/images/${random()}.jpg` const imageWrapper = document.createElement("div"); imageWrapper.className = "bg-gray-300"; imageWrapper.style.minHeight = "100px"; imageWrapper.style.display = "inline-block"; imageWrapper.appendChild(imagen); container.appendChild(imageWrapper); ...
Para eliminar las imagenes, le puse id a los botones:
<button class="p-3" id="add">Agregar Imagen</button> <button class="p-3" id="clean">Limpiar</button>
En JavaScript los selecciono y les pongo sus eventos, para eliminarlos simplemente recorro todos los nodos hijos de mountNode, los recorro y los elimino:
const cleanImages = () => { console.log(mountNode.childNodes); [...mountNode.childNodes].forEach(child => { child.remove(); }) } addButton.addEventListener("click", addImage) cleanButton.addEventListener("click", cleanImages)
Y para imprimir los logs, en el HTML agregue variables y una función super globales para tener acceso desde index.js y desde lazy.js:
<script> let appendedImages = 0; let loadedImages = 0; const printLog = () => { console.log(`⚪ Se han agregado ${appendedImages} imágenes`); console.log(`🟣 Se han cargado ${loadedImages} imágenes`); console.log("---------------------------------------"); } </script> <script type="module" src="%PUBLIC_URL%/_dist_/index.js"></script>
Entonces, en la función createImageNode antes de retornar el container, aumento uno a las imagenes que se han agregado y llamo a la función:
... imageWrapper.appendChild(imagen); container.appendChild(imageWrapper); appendedImages++; printLog(); return container; ...
Y dentro de lazy.js en loadImage antes de dejar de observar hago lo mismo, pero aquí incremento las imágenes cargadas:
const loadImage = entry => { const container = entry.target; const imagen = container.firstChild; const imagen = container.querySelector("img"); const url = imagen.dataset.src; imagen.src = url; loadedImages++; printLog(); observer.unobserve(container) }
Y listo :D . El código completo está aquí por si quieren darle un vistazo: . Resolución del reto
Hola camaradas, les dejo mi solución de los retos: ++index.js++
import { registerImage, resetLogState } from "./lazy"; import { createImageNode } from "./utils"; const API = 'https://randomfox.ca/floof/'; const mountNode = document.querySelector('#images') const btnAdd = document.querySelector('button[type="submit"]'); const btnClean = document.querySelector('button[type="reset"]'); const addImage = async ()=>{ const [node, Image] = await createImageNode(API) registerImage(Image); mountNode.append(node); } btnAdd.addEventListener('click', addImage); btnClean.addEventListener('click', ()=>{ mountNode.innerHTML = ""; resetLogState(); })
++utils.js++
const fetchUrl = async (API)=> { const response = await fetch(API); // const data = await response.json(); // const url = data.image; const { image : url } = await response.json(); return url; } export const createImageNode = async (API)=>{ const container = document.createElement("div"); container.classList.add('p-4'); const image = document.createElement("img"); image.dataset.src = await fetchUrl(API); // image.classList.add('mx-auto'); image.className = "mx-auto rounded-md bg-gray-300"; image.width = 320; container.append(image); return [container,image] }
++lazy.js++
let appendedImages = 0; let loadedImages = 0; const printLog = () => { console.log(`⚪ Se han agregado ${appendedImages} imágenes`); console.log(`🟣 Se han cargado ${loadedImages} imágenes`); console.log("---------------------------------------"); } const isIntersecting = (entry)=> entry.isIntersecting // true si esta dentro de la pantalla const loadImage = (entry)=>{ loadedImages++; printLog() const img = entry.target; img.src = img.dataset.src; observer.unobserve(img); } const observer = new IntersectionObserver((entries)=>{ const entry = entries[0]; if(isIntersecting(entry)) loadImage(entry) }); export const registerImage = (image) =>{ // IntersectactionObservador -> observer(image) appendedImages++; printLog() observer.observe(image); } export const resetLogState = ()=>{ appendedImages = 0; loadedImages = 0; }
El uso de clases para el pintado de los estatus de las imágenes me ha funcionado, aquí les comparto:
Creamos la clase, la inicializamos y la exportamos (lazy.js).
La clase cuenta con métodos públicos y privados para su encapsulamiento y los métodos "getAppended o getLoaded" pueden o no ser públicos dependiendo de la necesidad.
El método "clear" en mi caso aplique una limpieza en consola.
class ImageLoaded { #appendImages = 0; #loadedImages = 0; #getAppended(){ return this.#appendImages; } #getLoaded(){ return this.#loadedImages; } addAppend(){ this.#appendImages += 1; } addLoad(){ this.#loadedImages += 1; } clear(){ console.clear(); this.#appendImages = 0; this.#loadedImages = 0; } printLog() { console.log(` ⚪ Se han agregado ${this.#getAppended()} imágenes 🟣 Se han cargado ${this.#getLoaded()} imágenes ---------------------------------------`); } } export const imageStatus = new ImageLoaded();
//imageStatus const accion = ({ target }) => { const imagen = (target.firstChild).firstChild; imagen.src = imagen.dataset.src; imageStatus.addLoad(); imageStatus.printLog(); observer.unobserve(target) }
import { registerImage, imageStatus } from './lazy'; //... const addButton = document.querySelector('.btn-add'); const cleanButton = document.querySelector('.btn-clean'); const addImage = () => { const newImage = createImageNode(); container.appendChild(newImage) registerImage(newImage) imageStatus.addAppend(); imageStatus.printLog() }; const cleanImage = () =>{ const allImages = [...container.querySelectorAll('div')]; allImages.forEach(img => img.remove()) imageStatus.clear(); } addButton.addEventListener('click', addImage); cleanButton.addEventListener('click', cleanImage);
Bien, esto fue lo que yo hice :D index.html :
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="description" content="Web site created using create-snowpack-app" /> <!-- Snowpack will replace %PUBLIC_URL% with the value you have for `baseUrl` in `snowpack.config.js`. Make sure to update it with your GitHub Page URL. https://<your-username>.github.io/<your-repo-name> More about config options: - https://www.snowpack.dev/reference/configuration#buildoptions.baseurl --> <link rel="stylesheet" type="text/css" href="%PUBLIC_URL%/_dist_/index.css" /> <title>Snowpack & Tailwind App</title> </head> <body> <!-- This HTML file is a template. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. Start modyfing from here. --> <div class="py-10 max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="text-center"> <p class="text-base leading-6 text-indigo-600 font-semibold tracking-wide uppercase" > Platzi </p> <h3 class="mt-2 text-3xl leading-8 font-extrabold tracking-tight text-gray-900 sm:text-4xl sm:leading-10" > Fox API </h4> <p class="mt-4 max-w-2xl text-xl leading-7 text-gray-500 mx-auto"> Coleccionando imágenes de Zorros </p> <button class="button" id="buttonFox">Click me to see a new fox!</button> <button id="deleteFoxes" class="buttonClear">Clean images</button> <div id="app"></div> </div> </div> <!-- Add more scripts here --> <script type="module" src="%PUBLIC_URL%/_dist_/index.js"></script> </body> </html>
index.css :
@import 'tailwindcss/dist/base.css'; @import 'tailwindcss/dist/components.css'; @import 'tailwindcss/dist/utilities.css'; .button, .buttonClear{ color: white; padding: 5px; border-radius: 5px; } .button{ background-color: orange; } .buttonClear{ background-color: rgb(255, 81, 0); }
index.js :
import {registerImage} from './lazy'; const maximum = 122; const minimum = 0; const random = () => Math.floor(Math.random() * (maximum - minimum) + minimum); const createImageNode = () => { const container = document.createElement('div'); container.className = "p-4 bg-gray-200 mt-5"; const image = document.createElement('img'); image.className = 'mx-auto'; image.width = '320'; image.dataset.src = `https://randomfox.ca/images/${random()}.jpg`; container.append(image); return container; } const mountNode = document.getElementById('app'); const buttonFox = document.getElementById('buttonFox'); const buttonClear = document.getElementById('deleteFoxes'); var imagenesCargadas = 0; const addImage = () => { const newImage = createImageNode(); mountNode.append(newImage); registerImage(newImage); imagenesCargadas += 1 console.log(`Se han cargado ${imagenesCargadas} fotos`) } buttonFox.addEventListener('click' , addImage); buttonClear.addEventListener('click' , () => { let containerWithImage = document.querySelectorAll('#app .p-4'); //!Obtiene nodeList containerWithImage = Array.from(containerWithImage); //!Convierte a Array containerWithImage.forEach(element => { element.remove(); }); }) //*Las líneas de abajo ya muestran 1 imagen por defecto //const nuevaImagen = createImageNode(); //mountNode.append(nuevaImagen);
lazy.js :
const isIntersecting = entry => { return entry.isIntersecting //* true si está dentro de la pantalla del navegador } var imagenesMostradas = 0; const loadImage = (entry) => { const container = entry.target; //* div const image = container.firstChild; const url = image.dataset.src; //*carga imagen image.src = url; observer.unobserve(container); imagenesMostradas += 1; console.log(`Se han mostrado ${imagenesMostradas} fotos a la vista del navegador`) } const observer = new IntersectionObserver((allEntries) => { allEntries .filter(isIntersecting) .forEach(loadImage) }) export const registerImage = image => { observer.observe(image) }
Fondo gris a las imágenes sólo se lo agregué con una clase de tailwind:
img.className = "mx-auto my-16 bg-gray-200";
Para el botón de borrar lo agregué desde js:
const cleanImages = () => { const gallery = document.getElementById("gallery"); const nodes = gallery.childNodes; if (!nodes.length > 0) return; [...nodes].forEach((node) => { gallery.removeChild(node); }); resetImages(); }; const clean = document.createElement("button"); clean.className = "mx-auto my-4 block bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded transition duration-300 ease-in-out"; clean.textContent = "Borrar imágenes"; clean.type = "reset"; clean.addEventListener("click", cleanImages); const gallery = document.createElement("div"); gallery.id = "gallery"; container.append(button, clean, gallery);
Y para el conteo de imágenes:
let totalImages = 0; let loadedImages = 0; const isIntersecting = (entry) => entry.isIntersecting; const observer = new IntersectionObserver((entries) => { entries.filter(isIntersecting).forEach((entry) => { const imgNode = entry.target; imgNode.src = imgNode.dataset.src; imgNode.onload = () => { loadedImages++; logState(); } observer.unobserve(entry.target); }); }); export const registerImage = (image) => { observer.observe(image); totalImages++; logState(); }; export const resetImages = () => { totalImages = 0; loadedImages = 0; logState(); }; function logState() { console.group("Lazy loading"); console.log(`⚪️ Total Imágenes: ${totalImages}`); console.log(`🟣 Imágenes cargadas: ${loadedImages}`); console.log(`🟡 Imágenes por cargar: ${totalImages - loadedImages}`); console.groupEnd(); }
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="description" content="Web site created using create-snowpack-app" /> <!-- Snowpack will replace %PUBLIC_URL% with the value you have for `baseUrl` in `snowpack.config.js`. Make sure to update it with your GitHub Page URL. https://<your-username>.github.io/<your-repo-name> More about config options: - https://www.snowpack.dev/reference/configuration#buildoptions.baseurl --> <link rel="stylesheet" type="text/css" href="%PUBLIC_URL%/_dist_/index.css" /> <title>Random fox</title> </head> <body> <!-- This HTML file is a template. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. Start modyfing from here. --> <div class="py-10 max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="text-center"> <p class="text-base leading-6 text-indigo-600 font-semibold tracking-wide uppercase" > Manipulando el Dom </p> <h3 class="mt-2 text-3xl leading-8 font-extrabold tracking-tight text-gray-900 sm:text-4xl sm:leading-10" > Random Fox </h4> <p class="m-4 max-w-2xl text-xl leading-7 text-gray-500 mx-auto"> Manipulacion del DOM con API de <span> <a href="https://randomfox.ca/" target="_blank">RandomFox</a></span> </p> <button id="addButton" class="p-3">Agregar Imagen</button> <button id="removeButton" class="p-3">Remover Imagenes</button> <div id="images"> </div> </div> </div> <!-- Add more scripts here --> <script type="module" src="%PUBLIC_URL%/_dist_/index.js"></script> </body> </html>
index.js
/** * This file is just a silly example to show everything working in the browser. * When you're ready to start on your site, clear the file. Happy hacking! **/ import { registerImage } from "./lazy"; const URL = "https://randomfox.ca/images"; const maximum=122; const minimum= 1; //crea un numero aleatorio para traer las imagenes const random = () => Math.floor(Math.random() * (maximum - minimum)) + minimum // reporte en consola de cuantos argumentos han traido y estan cargados const reportImgs = { imgLoaded: 0, totalImg: 0 } const stopContentLoader = (event) => { const img = event.target; // img.parentElement.lastChild.remove(); img.style.width = '320px'; img.style.height= '320px'; reportImgs.imgLoaded++; // debugger; showReportImg(); } const showReportImg = () => { console.log('----------------------------------------') console.log(`%c⚪Total Imgs: ${reportImgs.totalImg}`, 'color: white; font-size: 0.8rem'); console.log(`%c🟣Total Cargadas: ${reportImgs.imgLoaded}`, 'color: hotpink; font-size: 0.8rem'); console.log('----------------------------------------') } const allElementsDeleted = () => { console.log(`⚠ Todas las imagenes fueron eliminadas`); } const createImageNode = () => { const container = document.createElement('div'); container.className = 'flex flex-col items-center p-4 mx-auto'; const img = document.createElement('img'); img.className = 'mx-auto rounded bg-gray-300'; img.width = "320"; img.style.minHeight = '300' img.dataset.src = `${URL}/${random()}.jpg`; // TODO img.onload = stopContentLoader; //onload llama una funcion container.appendChild(img); return container; }; const nuevaImagen = createImageNode(); const addImage = () => { const newImage = createImageNode(); mountNode.append(newImage); registerImage(newImage); reportImgs.totalImg++; showReportImg(); } const removeAllImg = () =>{ mountNode.innerHTML = ""; reportImgs.imgLoaded = 0; reportImgs.totalImg = 0; allElementsDeleted() } //captura la etiqueta img del html const mountNode = document.getElementById('images') //captura el button del html const addButton = document.querySelector('#addButton'); addButton.className = 'text-white px-3 py-2 rounded-lg bg-indigo-600 focus:outline-none'; const removeButton = document.querySelector('#removeButton') removeButton.className ='rounded-lg mt-5 px-3 py-2 text-indigo-600 border border-indigo-600 focus:outline-none'; addButton.addEventListener('click', addImage); removeButton.addEventListener('click', removeAllImg);
lazy.js
const isIntersecting = (entry) => entry.isIntersecting; //true (dentro de la pantalla) const loadImage = (entry) => { const container = entry.target; //regresa un container (DIV) const imagen = container.firstChild; const url = imagen.dataset.src //cargar la imagen. imagen.src=url // console.log(entry); //desregistrar el evento observer.unobserve(container); }; const observer = new IntersectionObserver((entries)=>{ entries.filter(isIntersecting).forEach(loadImage); }); export const registerImage = (imagen) => { //intersectationObservador -> observer(imagen) observer.observe(imagen) }
Esta muy, buena tu solución para mostrar las imagenes totales y las imagenes cargadas,yo copie la parte del index.js, le agregue al de lazy.js Gracias por la solución, yo no sabia muy biien como manejarlo, sos un grande!!
lazy.js
import { reportImgs, showReportImg } from'./index' const isIntersecting = (entry) => { // console.log("entry de isIntersecting: ",entry) // console.log("entry.target.isConnected de isIntersecting: ",entry.target.isConnected); // SI esta a 200px de la pantalla hacer x o y cosa return entry.isIntersecting // true ( dentro de la pantalla) } const loadImage = (entry) => { const container = entry.target; // div que tiene adentro a la imagen const imagen = container.firstChild; const url = imagen.dataset.src; //load image imagen.src = url; // unobserve observer.unobserve(container) reportImgs.imgLoaded++; showReportImg(); } const observer = new IntersectionObserver((entries) => { // console.log("entries de: IntersectionObserver: ",entries); entries.filter(isIntersecting).forEach(loadImage) }) export const registerImage = (imanen) => { // IntersectionObserver => observer(imagen) observer.observe(imanen) };