react-lazy-img-observer 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +278 -80
- package/dist/cjs/ImageLazy.js +14 -2
- package/dist/cjs/ImageLazy.min.js +1 -1
- package/dist/esm/ImageLazy.js +15 -3
- package/dist/esm/ImageLazy.min.js +1 -1
- package/dist/types/ImageLazy.d.ts +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
# 📷 ImageLazy (v1.
|
|
1
|
+
# 📷 ImageLazy (v1.5.0)
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
---
|
|
2
8
|
|
|
3
9
|
**ImageLazy** is a lightweight and customizable React component for lazy loading images using the Intersection Observer API. It includes fallback handling, visual effects, and support for custom loaders and transitions — perfect for performance-oriented applications.
|
|
4
10
|
|
|
@@ -6,12 +12,70 @@
|
|
|
6
12
|
|
|
7
13
|
---
|
|
8
14
|
|
|
15
|
+
## 📚 Contenido
|
|
16
|
+
|
|
17
|
+
- [📦 Instalación](#-installation--instalación)
|
|
18
|
+
- [🚀 Uso Básico](#-usage--uso-básico)
|
|
19
|
+
- [📃 Tabla de Props](#-props-table--tabla-de-props)
|
|
20
|
+
- [🎨 Ejemplos Avanzados](#-advanced-example-with-spinner-and-fallback--ejemplo-avanzado-con-spinner-y-fallback)
|
|
21
|
+
- [💡 Uso de Props](#-prop-usage-with-examples--uso-de-props-con-ejemplos)
|
|
22
|
+
- [🔁 Efectos Visuales](#-effects--efectos-visuales)
|
|
23
|
+
- [🔧 Cómo funciona](#-how-it-works--cómo-funciona)
|
|
24
|
+
- [❓ FAQ](#-faq)
|
|
25
|
+
- [📊 Comparación](#-comparison-with-react-lazy-load-image-component--comparación-con-react-lazy-load-image-component)
|
|
26
|
+
- [📄 Licencia](#-license--licencia)
|
|
27
|
+
- [👤 Autor](#-author--autor)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
9
31
|
## 📦 Installation / Instalación
|
|
10
32
|
|
|
11
33
|
```bash
|
|
12
34
|
npm install react-lazy-img-observer
|
|
13
35
|
```
|
|
14
36
|
|
|
37
|
+
## 📃 Props Table / Tabla de Props
|
|
38
|
+
|
|
39
|
+
| Prop | Tipo | Por Defecto | Descripción (ES) / Description (EN) |
|
|
40
|
+
| -------------------- | ----------------------------------------- | ----------- | --------------------------------------------------------------- |
|
|
41
|
+
| `src` | `string` | – | URL de la imagen / Image URL |
|
|
42
|
+
| `alt` | `string` | – | Texto alternativo / Alt text |
|
|
43
|
+
| `srcSet` | `string` | – | Set de imágenes responsive / Responsive image set |
|
|
44
|
+
| `sizes` | `string` | – | Tamaños para srcSet / Responsive sizes |
|
|
45
|
+
| `width` | `number` | – | Ancho fijo / Fixed width |
|
|
46
|
+
| `height` | `number` | – | Alto fijo / Fixed height |
|
|
47
|
+
| `id` | `string \| number` | – | ID del elemento / Element ID |
|
|
48
|
+
| `className` | `string` | – | Clase CSS personalizada / Custom CSS class |
|
|
49
|
+
| `title` | `string` | – | Atributo title del tag img / Tooltip text |
|
|
50
|
+
| `useTitleFromAlt` | `boolean` | `false` | Usa el alt como title / Use `alt` value as `title` |
|
|
51
|
+
| `extraData` | `ImgHTMLAttributes<HTMLImageElement>` | – | Props adicionales para img / Extra native props |
|
|
52
|
+
| `viewTransitionName` | `string` | – | Nombre para View Transitions API |
|
|
53
|
+
| `style` | `CSSProperties` | – | Estilos inline / Inline styles |
|
|
54
|
+
| `backgroundColor` | `string` | – | Color de fondo mientras carga / Background while loading |
|
|
55
|
+
| `animationDuration` | `string` | `0.9s` | Duración de la transición / Animation duration |
|
|
56
|
+
| `blurAmount` | `string` | `20px` | Desenfoque inicial / Initial blur effect |
|
|
57
|
+
| `fallbackSrc` | `string` | – | Imagen fallback si falla / Fallback image if loading fails |
|
|
58
|
+
| `threshold` | `number` | `0.5` | Umbral de visibilidad / Intersection threshold |
|
|
59
|
+
| `transitionType` | `'blur' \| 'fade' \| 'scale' \| 'custom'` | `'blur'` | Tipo de transición visual / Type of transition |
|
|
60
|
+
| `onLoadComplete` | `() => void` | – | Callback al cargar / Callback after image is fully loaded |
|
|
61
|
+
| `visibleByDefault` | `boolean` | `false` | Cargar sin lazy / Load immediately without waiting for viewport |
|
|
62
|
+
| `loadingComponent` | `ReactNode` | – | Componente mientras carga / Custom loader component |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 🔁 Effects / Efectos Visuales
|
|
69
|
+
|
|
70
|
+
| Effect | Description |
|
|
71
|
+
| -------- | ----------------------------------------------------------------------------------------- |
|
|
72
|
+
| `blur` | Apply blur and remove it after load / Aplicar desenfoque y eliminarlo después de cargar |
|
|
73
|
+
| `fade` | Fade in the image / Desvanecimiento de la imagen |
|
|
74
|
+
| `scale` | Slight zoom effect / Ligero efecto de zoom |
|
|
75
|
+
| `custom` | Use your own CSS (add class to image) / Utilice su propio CSS (agregue clase a la imagen) |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
15
79
|
---
|
|
16
80
|
|
|
17
81
|
## 🚀 Usage / Uso Básico
|
|
@@ -31,6 +95,12 @@ function App() {
|
|
|
31
95
|
}
|
|
32
96
|
```
|
|
33
97
|
|
|
98
|
+
### ✅ Explicación:
|
|
99
|
+
|
|
100
|
+
- `src`: URL de la imagen / Image URL.
|
|
101
|
+
- `alt`: Texto descriptivo para accesibilidad y SEO / Descriptive text for accessibility and SEO.
|
|
102
|
+
- `width` / `height`: Definen tamaño fijo (opcional pero recomendable para evitar reflow / Define fixed size (optional but recommended to avoid reflow).
|
|
103
|
+
|
|
34
104
|
---
|
|
35
105
|
|
|
36
106
|
## 🎨 Advanced Example with Spinner and Fallback / Ejemplo Avanzado con Spinner y Fallback
|
|
@@ -60,142 +130,271 @@ function GalleryImage() {
|
|
|
60
130
|
|
|
61
131
|
---
|
|
62
132
|
|
|
63
|
-
##
|
|
133
|
+
## 💡 Prop Usage with Examples / Uso de Props con Ejemplos
|
|
134
|
+
|
|
135
|
+
### 📍 `fallbackSrc`
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
<ImageLazy
|
|
139
|
+
src="/img/main.jpg"
|
|
140
|
+
fallbackSrc="/img/fallback.jpg"
|
|
141
|
+
alt="Imagen principal"
|
|
142
|
+
/>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
👉 Si la imagen original falla, se muestra automáticamente la alternativa / If the original image fails, the alternative is automatically displayed.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
### 🎨 `backgroundColor`
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
<ImageLazy
|
|
153
|
+
src="/img/pic.jpg"
|
|
154
|
+
alt="Imagen con fondo gris"
|
|
155
|
+
backgroundColor="#f0f0f0"
|
|
156
|
+
/>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
👉 Color visible mientras la imagen carga (mejora UX) / Color visible while image loads (UX improvement).
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
### 💫 `blurAmount` y `animationDuration`
|
|
64
164
|
|
|
65
165
|
```tsx
|
|
66
166
|
<ImageLazy
|
|
67
|
-
src="/img/
|
|
68
|
-
alt="
|
|
69
|
-
|
|
70
|
-
|
|
167
|
+
src="/img/blurred.jpg"
|
|
168
|
+
alt="Efecto blur personalizado"
|
|
169
|
+
blurAmount="5px"
|
|
170
|
+
animationDuration="2s"
|
|
71
171
|
/>
|
|
72
172
|
```
|
|
73
173
|
|
|
74
|
-
|
|
174
|
+
👉 Controla el desenfoque inicial y duración del efecto / Controls the initial blur and duration of the effect.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
### 🌀 `transitionType`
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
<ImageLazy src="/img/pic.jpg" alt="Fade in" transitionType="fade" />
|
|
182
|
+
<ImageLazy src="/img/pic.jpg" alt="Scale in" transitionType="scale" />
|
|
183
|
+
<ImageLazy src="/img/pic.jpg" alt="Custom CSS" transitionType="custom" className="img-fx" />
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**CSS (ejemplo avanzado):**
|
|
75
187
|
|
|
76
188
|
```css
|
|
77
|
-
.
|
|
189
|
+
.img-fx {
|
|
78
190
|
opacity: 0;
|
|
79
|
-
transform:
|
|
80
|
-
transition: all 0.8s ease
|
|
191
|
+
transform: scale(0.8) rotate(-2deg);
|
|
192
|
+
transition: all 0.8s ease;
|
|
81
193
|
}
|
|
82
|
-
|
|
83
|
-
.my-custom-transition.loaded {
|
|
194
|
+
.img-fx.loaded {
|
|
84
195
|
opacity: 1;
|
|
85
|
-
transform:
|
|
196
|
+
transform: scale(1) rotate(0deg);
|
|
86
197
|
}
|
|
87
198
|
```
|
|
88
199
|
|
|
200
|
+
👉 Usa `custom` para controlar completamente la animación con tu propio CSS / Use `custom` to fully control the animation with your own CSS.
|
|
201
|
+
|
|
89
202
|
---
|
|
90
203
|
|
|
91
|
-
|
|
204
|
+
### 🔁 `visibleByDefault`
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
<ImageLazy src="/img/pic.jpg" alt="Carga inmediata" visibleByDefault={true} />
|
|
208
|
+
```
|
|
92
209
|
|
|
93
|
-
|
|
94
|
-
| -------------------- | ----------------------------------- | ------- | ---------------------------------------------------------- |
|
|
95
|
-
| `src` | `string` | - | Image URL / URL de imagen ✅ |
|
|
96
|
-
| `alt` | `string` | - | Alt text / Texto alternativo ✅ |
|
|
97
|
-
| `width` | `number` | - | Width / Ancho |
|
|
98
|
-
| `height` | `number` | - | Height / Alto |
|
|
99
|
-
| `srcSet` | `string` | - | Responsive image set |
|
|
100
|
-
| `sizes` | `string` | - | Responsive sizes |
|
|
101
|
-
| `fallbackSrc` | `string` | - | Fallback if image fails / Imagen alternativa si falla |
|
|
102
|
-
| `backgroundColor` | `string` | - | Background color before load |
|
|
103
|
-
| `blurAmount` | `string` | `20px` | Initial blur / Desenfoque inicial |
|
|
104
|
-
| `animationDuration` | `string` | `0.9s` | Transition duration / Duración de transición |
|
|
105
|
-
| `threshold` | `number` | `0.5` | Intersection threshold / Umbral de visibilidad |
|
|
106
|
-
| `transitionType` | `"blur"\|"fade"\|"scale"\|"custom"` | `blur` | Type of transition / Tipo de transición |
|
|
107
|
-
| `loadingComponent` | `ReactNode` | - | Custom loader / Componente de carga personalizado ✅ |
|
|
108
|
-
| `onLoadComplete` | `() => void` | - | Callback when loaded / Al terminar de cargar |
|
|
109
|
-
| `visibleByDefault` | `boolean` | `false` | Skip lazy load / Saltar carga diferida si ya está en caché |
|
|
110
|
-
| `viewTransitionName` | `string` | - | View Transitions API |
|
|
111
|
-
| `extraData` | `ImgHTMLAttributes` | - | Additional attributes |
|
|
112
|
-
| `style` | `CSSProperties` | - | Inline styles |
|
|
113
|
-
| `className` | `string` | - | Custom CSS class |
|
|
114
|
-
| `id` | `string`\|`number` | - | Element ID |
|
|
210
|
+
👉 Útil cuando no deseas esperar a que la imagen entre al viewport (ej: SSR o cuando ya está precargada / Useful when you don't want to wait for the image to enter the viewport (e.g. SSR or when it is already pre-loaded).
|
|
115
211
|
|
|
116
212
|
---
|
|
117
213
|
|
|
118
214
|
## 🔧 How it works / ¿Cómo funciona?
|
|
119
215
|
|
|
120
|
-
- When `visibleByDefault` is `false` (default), the image loads only when it's in the viewport (IntersectionObserver).
|
|
121
|
-
- If `visibleByDefault` is `true`, the image is loaded immediately (useful for SSR or cached images).
|
|
122
|
-
- You can add a custom CSS transition, fallback, background color, and even a loading spinner.
|
|
216
|
+
- When `visibleByDefault` is `false` (default), the image loads only when it's in the viewport (IntersectionObserver)/Cuando `visibleByDefault` es `false` (predeterminado), la imagen se carga solo cuando está en la ventana gráfica (IntersectionObserver).
|
|
217
|
+
- If `visibleByDefault` is `true`, the image is loaded immediately (useful for SSR or cached images)/Si `visibleByDefault` es `true`, la imagen se carga inmediatamente (útil para SSR o imágenes almacenadas en caché).
|
|
218
|
+
- You can add a custom CSS transition, fallback, background color, and even a loading spinner/Puede agregar una transición CSS personalizada, una alternativa, un color de fondo e incluso un indicador de carga.
|
|
123
219
|
|
|
124
220
|
---
|
|
125
221
|
|
|
126
|
-
|
|
222
|
+
### 🧭 `loadingComponent`
|
|
223
|
+
|
|
224
|
+
```tsx
|
|
225
|
+
<ImageLazy
|
|
226
|
+
src="/img/loading.jpg"
|
|
227
|
+
alt="Con spinner"
|
|
228
|
+
loadingComponent={<div className="spinner">Cargando...</div>}
|
|
229
|
+
/>
|
|
230
|
+
```
|
|
127
231
|
|
|
128
|
-
|
|
129
|
-
| -------- | ------------------------------------- |
|
|
130
|
-
| `blur` | Apply blur and remove it after load |
|
|
131
|
-
| `fade` | Fade in the image |
|
|
132
|
-
| `scale` | Slight zoom effect |
|
|
133
|
-
| `custom` | Use your own CSS (add class to image) |
|
|
232
|
+
👉 Muestra un componente personalizado mientras se carga la imagen / Display a custom component while the image is loading.
|
|
134
233
|
|
|
135
234
|
---
|
|
136
235
|
|
|
137
|
-
|
|
236
|
+
### 🧪 `onLoadComplete`
|
|
138
237
|
|
|
139
|
-
|
|
238
|
+
```tsx
|
|
239
|
+
function handleLoad() {
|
|
240
|
+
console.log("¡Imagen completamente cargada!");
|
|
241
|
+
}
|
|
140
242
|
|
|
141
|
-
ImageLazy
|
|
243
|
+
<ImageLazy
|
|
244
|
+
src="/img/event.jpg"
|
|
245
|
+
alt="Imagen con evento"
|
|
246
|
+
onLoadComplete={handleLoad}
|
|
247
|
+
/>;
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 🔁 `onLoadComplete` con lógica avanzada / with advanced logic
|
|
251
|
+
|
|
252
|
+
```tsx
|
|
253
|
+
const [estado, setEstado] = useState("cargando");
|
|
254
|
+
|
|
255
|
+
<ImageLazy
|
|
256
|
+
src="/img/camara.jpg"
|
|
257
|
+
alt="Avanzado"
|
|
258
|
+
onLoadComplete={() => setEstado("cargada")}
|
|
259
|
+
/>
|
|
260
|
+
<p>Estado de imagen: {estado}</p>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### ✅ `onLoadComplete` con múltiples imágenes / with multiple images
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
const [cargas, setCargas] = useState(0);
|
|
267
|
+
|
|
268
|
+
const incrementar = () => setCargas((prev) => prev + 1);
|
|
269
|
+
|
|
270
|
+
<ImageLazy src="/img/uno.jpg" alt="uno" onLoadComplete={incrementar} />
|
|
271
|
+
<ImageLazy src="/img/dos.jpg" alt="dos" onLoadComplete={incrementar} />
|
|
272
|
+
<p>Imágenes cargadas: {cargas}</p>
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
### 🧷 `useTitleFromAlt`
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
<ImageLazy
|
|
281
|
+
src="/img/title.jpg"
|
|
282
|
+
alt="Una imagen informativa"
|
|
283
|
+
useTitleFromAlt={true}
|
|
284
|
+
/>
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
👉 Usa el valor de `alt` también como atributo `title` automáticamente. Útil para tooltips nativos / Automatically uses the `alt` value as the `title` attribute as well. Useful for native tooltips..
|
|
142
288
|
|
|
143
289
|
---
|
|
144
290
|
|
|
145
|
-
|
|
291
|
+
### 🖼 `viewTransitionName`
|
|
292
|
+
|
|
293
|
+
```tsx
|
|
294
|
+
<ImageLazy
|
|
295
|
+
src="/img/view.jpg"
|
|
296
|
+
alt="Transición de vista"
|
|
297
|
+
viewTransitionName="fade-image"
|
|
298
|
+
/>
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
👉 Para integrar con la API de `view-transition` (Chrome ≥ 111) y hacer animaciones entre páginas con `<ViewTransition>` / To integrate with the `view-transition` API (Chrome ≥ 111) and make animations between pages with `<ViewTransition>`.
|
|
302
|
+
|
|
303
|
+
---
|
|
146
304
|
|
|
147
|
-
|
|
305
|
+
### 🏷️ `id` (uso en scroll o testing)
|
|
148
306
|
|
|
149
307
|
```tsx
|
|
150
308
|
<ImageLazy
|
|
151
|
-
src="/img/
|
|
152
|
-
alt="
|
|
153
|
-
|
|
309
|
+
src="/img/anchor.jpg"
|
|
310
|
+
alt="Imagen anclada"
|
|
311
|
+
id="fotoPrincipal"
|
|
154
312
|
/>
|
|
313
|
+
<a href="#fotoPrincipal">Ir a la imagen</a>
|
|
155
314
|
```
|
|
156
315
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
316
|
+
👉 Permite identificar la imagen fácilmente para navegación, testing u operaciones DOM / Allows you to easily identify the image for navigation, testing, or DOM operations..
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
### 📐 `srcSet` y `sizes`
|
|
321
|
+
|
|
322
|
+
```tsx
|
|
323
|
+
<ImageLazy
|
|
324
|
+
src="/img/400.jpg"
|
|
325
|
+
srcSet="/img/400.jpg 400w, /img/800.jpg 800w"
|
|
326
|
+
sizes="(max-width: 600px) 400px, 800px"
|
|
327
|
+
alt="Imagen responsive"
|
|
328
|
+
/>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
👉 Mejora el rendimiento en dispositivos móviles cargando imágenes más ligeras según el ancho de pantalla / Improves performance on mobile devices by loading images smaller based on screen width.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
### 🧩 `extraData`, `className`, `style`
|
|
336
|
+
|
|
337
|
+
```tsx
|
|
338
|
+
<ImageLazy
|
|
339
|
+
src="/img/extra.jpg"
|
|
340
|
+
alt="Con extras"
|
|
341
|
+
className="rounded shadow"
|
|
342
|
+
style={{ objectFit: "cover" }}
|
|
343
|
+
extraData={{ draggable: false, crossOrigin: "anonymous" }}
|
|
344
|
+
/>
|
|
169
345
|
```
|
|
170
346
|
|
|
347
|
+
👉 Puedes personalizar todo el comportamiento nativo de `<img>` sin limitaciones / You can customize all native behavior of `<img>` without limitations.
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## 🌐 SSR Compatible / Compatible con SSR
|
|
352
|
+
|
|
353
|
+
ImageLazy is safe to use in SSR environments (like Next.js). It checks for `window` and disables IntersectionObserver logic when rendering on the server.
|
|
354
|
+
|
|
355
|
+
ImageLazy es seguro para entornos con renderizado del lado del servidor como Next.js. Detecta si `window` está disponible antes de usar IntersectionObserver.
|
|
356
|
+
|
|
171
357
|
---
|
|
172
358
|
|
|
173
359
|
## 📊 Comparison with react-lazy-load-image-component / Comparación con react-lazy-load-image-component
|
|
174
360
|
|
|
175
|
-
| Feature / Característica
|
|
176
|
-
|
|
177
|
-
| Lightweight & no external dependencies
|
|
178
|
-
| SSR Friendly (Next.js compatible)
|
|
179
|
-
| Custom transitions via CSS (`custom`)
|
|
180
|
-
| Native spinner support (`loadingComponent`)
|
|
181
|
-
| Fallback support (`fallbackSrc`)
|
|
182
|
-
| Responsive images (`srcSet` and `sizes`)
|
|
183
|
-
| Load other elements lazily
|
|
184
|
-
| Built-in effects
|
|
185
|
-
| Transition customization
|
|
361
|
+
| Feature / Característica | `ImageLazy` ✅ | `react-lazy-load-image-component` ❌ |
|
|
362
|
+
| ------------------------------------------- | ---------------------------------- | ---------------------------------------------- |
|
|
363
|
+
| Lightweight & no external dependencies | ✅ Yes / Sí | ❌ No (más pesado y con múltiples componentes) |
|
|
364
|
+
| SSR Friendly (Next.js compatible) | ✅ Yes / Sí | ✅ Yes / Sí |
|
|
365
|
+
| Custom transitions via CSS (`custom`) | ✅ Yes / Sí | ❌ No |
|
|
366
|
+
| Native spinner support (`loadingComponent`) | ✅ Yes / Sí | ❌ No (solo con workarounds) |
|
|
367
|
+
| Fallback support (`fallbackSrc`) | ✅ Yes / Sí | ✅ Yes / Sí |
|
|
368
|
+
| Responsive images (`srcSet` and `sizes`) | ✅ Yes / Sí | ✅ Yes / Sí |
|
|
369
|
+
| Load other elements lazily | ❌ Images only / Solo imágenes | ✅ Yes (via `LazyLoadComponent`) |
|
|
370
|
+
| Built-in effects | ✅ `blur`, `fade`, `scale`, custom | ✅ `blur`, `opacity`, `bw` |
|
|
371
|
+
| Transition customization | ✅ Total freedom with CSS | ❌ Limited |
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## ❓ FAQ
|
|
376
|
+
|
|
377
|
+
**¿Por qué mi imagen no se muestra?** / **Why isn't my image showing?**
|
|
378
|
+
Asegúrate de que `src` no sea `null`, y revisa si `visibleByDefault` está en `true` / Make sure `src` is not `null`, and check if `visibleByDefault` is set to `true`..
|
|
379
|
+
|
|
380
|
+
**¿Puedo usarlo en SSR como Next.js?** / **Can I use it in SSR like Next.js?**
|
|
381
|
+
Sí. Internamente revisa si `window` existe antes de ejecutar el observer / Yes. Internally it checks if `window` exists before running the observer.
|
|
382
|
+
|
|
383
|
+
**¿Cómo puedo aplicar una animación personalizada?** / **How can I apply a custom animation?**
|
|
384
|
+
Usa `transitionType="custom"` y define tu clase con efectos CSS / Use `transitionType="custom"` and define your class with CSS effects.
|
|
186
385
|
|
|
187
386
|
---
|
|
188
387
|
|
|
189
388
|
## 📄 License / Licencia
|
|
190
389
|
|
|
191
|
-
[
|
|
390
|
+
[MIT License](./LICENSE)
|
|
192
391
|
|
|
193
392
|
---
|
|
194
393
|
|
|
195
394
|
## 👤 Author / Autor
|
|
196
395
|
|
|
197
396
|
**Percy Chuzon**\
|
|
198
|
-
📧 [contacto@percychuzon.com](mailto
|
|
397
|
+
📧 [contacto@percychuzon.com](mailto:contacto@percychuzon.com)\
|
|
199
398
|
🌐 [https://percychuzon.com](https://percychuzon.com)
|
|
200
399
|
|
|
201
400
|
---
|
|
@@ -209,4 +408,3 @@ Si deseas más control, usa `transitionType="custom"` y aplica tus estilos con C
|
|
|
209
408
|
---
|
|
210
409
|
|
|
211
410
|
Happy loading! 🎉 / ¡Carga feliz! 🎉
|
|
212
|
-
|
package/dist/cjs/ImageLazy.js
CHANGED
|
@@ -3,12 +3,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ImageLazy = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_1 = require("react");
|
|
6
|
-
const ImageLazy = ({ src, alt, srcSet, sizes, width, height, className, id, extraData, title, viewTransitionName, style, backgroundColor, animationDuration = "0.9s", blurAmount = "20px", fallbackSrc, threshold = 0.5, transitionType = "blur", onLoadComplete, visibleByDefault = false, loadingComponent, }) => {
|
|
6
|
+
const ImageLazy = ({ src, alt, srcSet, sizes, width, height, className, id, extraData, title, useTitleFromAlt = false, viewTransitionName, style, backgroundColor, animationDuration = "0.9s", blurAmount = "20px", fallbackSrc, threshold = 0.5, transitionType = "blur", onLoadComplete, visibleByDefault = false, loadingComponent, }) => {
|
|
7
7
|
const imageRef = (0, react_1.useRef)(null);
|
|
8
8
|
const [realSrc, setRealSrc] = (0, react_1.useState)(visibleByDefault ? src : null);
|
|
9
9
|
const [loaded, setLoaded] = (0, react_1.useState)(false);
|
|
10
10
|
const [hasError, setHasError] = (0, react_1.useState)(false);
|
|
11
11
|
(0, react_1.useEffect)(() => {
|
|
12
|
+
const isDev = typeof window !== "undefined" &&
|
|
13
|
+
window?.location?.hostname?.includes("localhost");
|
|
14
|
+
if (isDev) {
|
|
15
|
+
if (!alt) {
|
|
16
|
+
console.warn("[ImageLazy] ⚠️ Se recomienda definir el atributo 'alt' por accesibilidad.");
|
|
17
|
+
}
|
|
18
|
+
if (!src) {
|
|
19
|
+
console.warn("[ImageLazy] ⚠️ La prop 'src' está vacía o nula. La imagen no se cargará.");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}, [alt, src]);
|
|
23
|
+
(0, react_1.useLayoutEffect)(() => {
|
|
12
24
|
if (typeof window === "undefined" || visibleByDefault)
|
|
13
25
|
return;
|
|
14
26
|
if (typeof IntersectionObserver === "undefined" || !imageRef.current)
|
|
@@ -54,7 +66,7 @@ const ImageLazy = ({ src, alt, srcSet, sizes, width, height, className, id, extr
|
|
|
54
66
|
filter: loaded ? "none" : `blur(${blurAmount})`,
|
|
55
67
|
transition: `filter ${animationDuration}`,
|
|
56
68
|
};
|
|
57
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [!loaded && loadingComponent, !hasError && ((0, jsx_runtime_1.jsx)("img", { ref: imageRef, src: realSrc ??
|
|
69
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [!loaded && loadingComponent, !hasError && ((0, jsx_runtime_1.jsx)("img", { ref: imageRef, src: realSrc ?? undefined, alt: alt, title: title ?? (useTitleFromAlt ? alt : undefined), className: className, width: width, height: height, loading: "lazy", id: id?.toString(), onLoad: handleLoad, onError: handleError, srcSet: realSrc ? srcSet : undefined, sizes: realSrc ? sizes : undefined, style: {
|
|
58
70
|
backgroundColor: !loaded && backgroundColor ? backgroundColor : undefined,
|
|
59
71
|
backgroundSize: "cover",
|
|
60
72
|
backgroundPosition: "center",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ImageLazy=void 0;const jsx_runtime_1=require("react/jsx-runtime"),react_1=require("react"),ImageLazy=({src:e,alt:t,srcSet:r,sizes:
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ImageLazy=void 0;const jsx_runtime_1=require("react/jsx-runtime"),react_1=require("react"),ImageLazy=({src:e,alt:t,srcSet:r,sizes:a,width:o,height:n,className:i,id:s,extraData:c,title:l,useTitleFromAlt:u=!1,viewTransitionName:d,style:m,backgroundColor:f,animationDuration:g="0.9s",blurAmount:y="20px",fallbackSrc:p,threshold:_=.5,transitionType:b="blur",onLoadComplete:v,visibleByDefault:h=!1,loadingComponent:x})=>{const w=(0,react_1.useRef)(null),[L,z]=(0,react_1.useState)(h?e:null),[I,S]=(0,react_1.useState)(!1),[j,k]=(0,react_1.useState)(!1);(0,react_1.useEffect)((()=>{"undefined"!=typeof window&&window?.location?.hostname?.includes("localhost")&&(t||console.warn("[ImageLazy] ⚠️ Se recomienda definir el atributo 'alt' por accesibilidad."),e||console.warn("[ImageLazy] ⚠️ La prop 'src' está vacía o nula. La imagen no se cargará."))}),[t,e]),(0,react_1.useLayoutEffect)((()=>{if("undefined"==typeof window||h)return;if("undefined"==typeof IntersectionObserver||!w.current)return;const t=new IntersectionObserver((r=>{r.forEach((r=>{r.isIntersecting&&(z(e),t.unobserve(r.target))}))}),{root:null,rootMargin:"0px",threshold:_});return t.observe(w.current),()=>t.disconnect()}),[e,_,h]);const $="custom"===b?{}:"fade"===b?{opacity:I?1:0,transition:`opacity ${g}`}:"scale"===b?{transform:I?"scale(1)":"scale(1.05)",opacity:I?1:0,transition:`transform ${g}, opacity ${g}`}:{filter:I?"none":`blur(${y})`,transition:`filter ${g}`};return(0,jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment,{children:[!I&&x,!j&&(0,jsx_runtime_1.jsx)("img",{ref:w,src:L??void 0,alt:t,title:l??(u?t:void 0),className:i,width:o,height:n,loading:"lazy",id:s?.toString(),onLoad:()=>{S(!0),v?.()},onError:()=>{p&&L!==p?z(p):k(!0)},srcSet:L?r:void 0,sizes:L?a:void 0,style:{backgroundColor:!I&&f?f:void 0,backgroundSize:"cover",backgroundPosition:"center",...d?{viewTransitionName:d}:{},...$,...m},...c})]})};exports.ImageLazy=ImageLazy,exports.default=ImageLazy;
|
package/dist/esm/ImageLazy.js
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
const ImageLazy = ({ src, alt, srcSet, sizes, width, height, className, id, extraData, title, viewTransitionName, style, backgroundColor, animationDuration = "0.9s", blurAmount = "20px", fallbackSrc, threshold = 0.5, transitionType = "blur", onLoadComplete, visibleByDefault = false, loadingComponent, }) => {
|
|
2
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
const ImageLazy = ({ src, alt, srcSet, sizes, width, height, className, id, extraData, title, useTitleFromAlt = false, viewTransitionName, style, backgroundColor, animationDuration = "0.9s", blurAmount = "20px", fallbackSrc, threshold = 0.5, transitionType = "blur", onLoadComplete, visibleByDefault = false, loadingComponent, }) => {
|
|
4
4
|
const imageRef = useRef(null);
|
|
5
5
|
const [realSrc, setRealSrc] = useState(visibleByDefault ? src : null);
|
|
6
6
|
const [loaded, setLoaded] = useState(false);
|
|
7
7
|
const [hasError, setHasError] = useState(false);
|
|
8
8
|
useEffect(() => {
|
|
9
|
+
const isDev = typeof window !== "undefined" &&
|
|
10
|
+
window?.location?.hostname?.includes("localhost");
|
|
11
|
+
if (isDev) {
|
|
12
|
+
if (!alt) {
|
|
13
|
+
console.warn("[ImageLazy] ⚠️ Se recomienda definir el atributo 'alt' por accesibilidad.");
|
|
14
|
+
}
|
|
15
|
+
if (!src) {
|
|
16
|
+
console.warn("[ImageLazy] ⚠️ La prop 'src' está vacía o nula. La imagen no se cargará.");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}, [alt, src]);
|
|
20
|
+
useLayoutEffect(() => {
|
|
9
21
|
if (typeof window === "undefined" || visibleByDefault)
|
|
10
22
|
return;
|
|
11
23
|
if (typeof IntersectionObserver === "undefined" || !imageRef.current)
|
|
@@ -51,7 +63,7 @@ const ImageLazy = ({ src, alt, srcSet, sizes, width, height, className, id, extr
|
|
|
51
63
|
filter: loaded ? "none" : `blur(${blurAmount})`,
|
|
52
64
|
transition: `filter ${animationDuration}`,
|
|
53
65
|
};
|
|
54
|
-
return (_jsxs(_Fragment, { children: [!loaded && loadingComponent, !hasError && (_jsx("img", { ref: imageRef, src: realSrc ??
|
|
66
|
+
return (_jsxs(_Fragment, { children: [!loaded && loadingComponent, !hasError && (_jsx("img", { ref: imageRef, src: realSrc ?? undefined, alt: alt, title: title ?? (useTitleFromAlt ? alt : undefined), className: className, width: width, height: height, loading: "lazy", id: id?.toString(), onLoad: handleLoad, onError: handleError, srcSet: realSrc ? srcSet : undefined, sizes: realSrc ? sizes : undefined, style: {
|
|
55
67
|
backgroundColor: !loaded && backgroundColor ? backgroundColor : undefined,
|
|
56
68
|
backgroundSize: "cover",
|
|
57
69
|
backgroundPosition: "center",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as _jsx,Fragment as _Fragment,jsxs as _jsxs}from"react/jsx-runtime";import{useEffect,useRef,useState}from"react";const ImageLazy=({src:e,alt:t,srcSet:
|
|
1
|
+
import{jsx as _jsx,Fragment as _Fragment,jsxs as _jsxs}from"react/jsx-runtime";import{useEffect,useLayoutEffect,useRef,useState}from"react";const ImageLazy=({src:e,alt:t,srcSet:o,sizes:a,width:r,height:n,className:s,id:i,extraData:c,title:l,useTitleFromAlt:u=!1,viewTransitionName:d,style:f,backgroundColor:m,animationDuration:g="0.9s",blurAmount:p="20px",fallbackSrc:y,threshold:b=.5,transitionType:h="blur",onLoadComplete:v,visibleByDefault:w=!1,loadingComponent:x})=>{const L=useRef(null),[S,z]=useState(w?e:null),[I,j]=useState(!1),[E,_]=useState(!1);useEffect((()=>{"undefined"!=typeof window&&window?.location?.hostname?.includes("localhost")&&(t||console.warn("[ImageLazy] ⚠️ Se recomienda definir el atributo 'alt' por accesibilidad."),e||console.warn("[ImageLazy] ⚠️ La prop 'src' está vacía o nula. La imagen no se cargará."))}),[t,e]),useLayoutEffect((()=>{if("undefined"==typeof window||w)return;if("undefined"==typeof IntersectionObserver||!L.current)return;const t=new IntersectionObserver((o=>{o.forEach((o=>{o.isIntersecting&&(z(e),t.unobserve(o.target))}))}),{root:null,rootMargin:"0px",threshold:b});return t.observe(L.current),()=>t.disconnect()}),[e,b,w]);const k="custom"===h?{}:"fade"===h?{opacity:I?1:0,transition:`opacity ${g}`}:"scale"===h?{transform:I?"scale(1)":"scale(1.05)",opacity:I?1:0,transition:`transform ${g}, opacity ${g}`}:{filter:I?"none":`blur(${p})`,transition:`filter ${g}`};return _jsxs(_Fragment,{children:[!I&&x,!E&&_jsx("img",{ref:L,src:S??void 0,alt:t,title:l??(u?t:void 0),className:s,width:r,height:n,loading:"lazy",id:i?.toString(),onLoad:()=>{j(!0),v?.()},onError:()=>{y&&S!==y?z(y):_(!0)},srcSet:S?o:void 0,sizes:S?a:void 0,style:{backgroundColor:!I&&m?m:void 0,backgroundSize:"cover",backgroundPosition:"center",...d?{viewTransitionName:d}:{},...k,...f},...c})]})};export default ImageLazy;export{ImageLazy};
|
|
@@ -8,6 +8,7 @@ export type ImagesLazy = {
|
|
|
8
8
|
id?: number | string;
|
|
9
9
|
className?: string;
|
|
10
10
|
title?: string;
|
|
11
|
+
useTitleFromAlt?: boolean;
|
|
11
12
|
extraData?: React.ImgHTMLAttributes<HTMLImageElement>;
|
|
12
13
|
viewTransitionName?: string;
|
|
13
14
|
style?: React.CSSProperties;
|
|
@@ -21,6 +22,6 @@ export type ImagesLazy = {
|
|
|
21
22
|
visibleByDefault?: boolean;
|
|
22
23
|
loadingComponent?: React.ReactNode;
|
|
23
24
|
};
|
|
24
|
-
declare const ImageLazy: ({ src, alt, srcSet, sizes, width, height, className, id, extraData, title, viewTransitionName, style, backgroundColor, animationDuration, blurAmount, fallbackSrc, threshold, transitionType, onLoadComplete, visibleByDefault, loadingComponent, }: ImagesLazy) => import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
declare const ImageLazy: ({ src, alt, srcSet, sizes, width, height, className, id, extraData, title, useTitleFromAlt, viewTransitionName, style, backgroundColor, animationDuration, blurAmount, fallbackSrc, threshold, transitionType, onLoadComplete, visibleByDefault, loadingComponent, }: ImagesLazy) => import("react/jsx-runtime").JSX.Element;
|
|
25
26
|
export default ImageLazy;
|
|
26
27
|
export { ImageLazy };
|
package/package.json
CHANGED