chameleon-backgrounds 2.0.0 → 2.1.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 +25 -1
- package/dist/chameleon-backgrounds.min.js +1 -1
- package/package.json +1 -1
- package/src/chameleon-backgrounds.js +67 -8
package/README.md
CHANGED
|
@@ -24,10 +24,17 @@ Large background images slow down initial page loads. ChameleonBackgrounds **def
|
|
|
24
24
|
|
|
25
25
|
## Installation
|
|
26
26
|
|
|
27
|
-
### npm
|
|
27
|
+
### Package Managers (npm / yarn / bun)
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
+
# npm
|
|
30
31
|
npm install chameleon-backgrounds
|
|
32
|
+
|
|
33
|
+
# yarn
|
|
34
|
+
yarn add chameleon-backgrounds
|
|
35
|
+
|
|
36
|
+
# bun
|
|
37
|
+
bun add chameleon-backgrounds
|
|
31
38
|
```
|
|
32
39
|
|
|
33
40
|
### CDN / Script Tag
|
|
@@ -150,6 +157,23 @@ Stops any running slider, removes all injected DOM and styles, and restores the
|
|
|
150
157
|
|
|
151
158
|
Returns a read-only copy of the resolved options.
|
|
152
159
|
|
|
160
|
+
### `.reloadOptions(newOptions)`
|
|
161
|
+
|
|
162
|
+
Update options on the fly without destroying the instance. For example, you can change the transition duration or even convert a single background into a slider dynamically:
|
|
163
|
+
```javascript
|
|
164
|
+
bg.reloadOptions({
|
|
165
|
+
type: 'slider',
|
|
166
|
+
src: [
|
|
167
|
+
'./img/slide1.jpg',
|
|
168
|
+
'./img/slide2.jpg'
|
|
169
|
+
],
|
|
170
|
+
transitionDuration: 5000,
|
|
171
|
+
overlayColor: '#ff0000'
|
|
172
|
+
});
|
|
173
|
+
// To apply the updated options, simply reload the background
|
|
174
|
+
bg.reloadBackground();
|
|
175
|
+
```
|
|
176
|
+
|
|
153
177
|
---
|
|
154
178
|
|
|
155
179
|
## Migration from v1
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! For license information please see chameleon-backgrounds.min.js.LICENSE.txt */
|
|
2
|
-
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.ChameleonBackgrounds=t():e.ChameleonBackgrounds=t()}(this,()=>(()=>{"use strict";const e={d:(t,
|
|
2
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.ChameleonBackgrounds=t():e.ChameleonBackgrounds=t()}(this,()=>(()=>{"use strict";const e={d:(t,i)=>{if(Array.isArray(i))for(var n=0;n<i.length;){var o=i[n++],r=i[n++];e.o(t,o)?0===r&&n++:0===r?Object.defineProperty(t,o,{enumerable:!0,value:i[n++]}):Object.defineProperty(t,o,{enumerable:!0,get:r})}else for(var o in i)e.o(i,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:i[o]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};let t={};class i{static#e=Object.freeze({element:"body",type:"single",src:"",overlayColor:"#0f1e25",overlayImage:null,minOverlay:0,transitionDuration:2e3,sliderDuration:8e3,sliderLoop:!1});static#t=Object.freeze({transition_duration:"transitionDuration",slider_duration:"sliderDuration",slider_loop:"sliderLoop",min_overlay:"minOverlay",overlay_color:"overlayColor",overlay_image:"overlayImage"});#i;#n;#o;#r;#s=null;#l=null;#d=!1;constructor(e={}){const t=i.#a(e);this.#i={...i.#e,...t},this.#o=i.#h(),this.#n=i.#c(this.#i.element),this.#r=this.#n.innerHTML,this.#u()}destroy(){if(this.#d)return;this.#d=!0,null!==this.#l&&(clearInterval(this.#l),this.#l=null),this.#s?.parentNode&&(this.#s.parentNode.removeChild(this.#s),this.#s=null);const e=this.#n.querySelector(`#cbg-inner-${this.#o}`);if(e&&e.parentNode===this.#n){for(;e.firstChild;)this.#n.insertBefore(e.firstChild,e);this.#n.removeChild(e)}const t=this.#n.querySelector(`.cbg-loader-${this.#o}`);t&&t.remove(),this.#n.style.backgroundImage=""}get options(){return{...this.#i}}#u(){this.#m(),this.#p(),this.#n.matches("body")?queueMicrotask(()=>this.#y()):"complete"===document.readyState?this.#y():window.addEventListener("load",()=>this.#y(),{once:!0})}#m(){const e=this.#o,t="body"===this.#i.element||this.#n.matches("body")?"body":this.#i.element,i=this.#i.transitionDuration/1e3,n=`\n ${"string"==typeof t?t:`.cbg-host-${e}`} {\n position: relative;\n }\n\n #cbg-inner-${e} {\n z-index: 2;\n position: relative;\n }\n\n .cbg-loader-${e} {\n height: 100%;\n width: 100%;\n position: ${this.#n.matches("body")?"fixed":"absolute"};\n background-image: ${this.#i.overlayImage?`url(${this.#i.overlayImage})`:"none"};\n background-color: ${this.#i.overlayColor};\n opacity: 1;\n z-index: 1;\n transition: opacity ${i}s ease;\n top: 0;\n left: 0;\n }\n `,o=document.createElement("style");o.dataset.chameleonUid=e,o.textContent=n,document.head.appendChild(o),this.#s=o}#p(){const e=this.#o,t=document.createElement("div");for(t.id=`cbg-inner-${e}`;this.#n.firstChild;)t.appendChild(this.#n.firstChild);const i=document.createElement("div");i.classList.add(`cbg-loader-${e}`),this.#n.appendChild(t),this.#n.appendChild(i)}#y(){this.#d||("single"===this.#i.type?this.#g("string"==typeof this.#i.src?this.#i.src:this.#i.src[0]):"slider"===this.#i.type&&this.#f())}#g(e,t=!0){return this.#d?Promise.resolve():new Promise(t=>{const i=new Image;i.onload=()=>{if(this.#d)return t();this.#n.style.backgroundImage=`url(${e})`,this.#n.style.backgroundSize="cover";const i=this.#n.querySelector(`.cbg-loader-${this.#o}`);i&&(i.style.opacity=String(this.#i.minOverlay)),t()},i.onerror=()=>{console.warn(`[ChameleonBackgrounds] Failed to load image: ${e}`),t()},i.src=e})}reloadBackground(e){if(this.#d)return Promise.resolve();void 0!==e&&("slider"===this.#i.type&&"string"==typeof e?this.#i.src=e.includes(",")?e.split(",").map(e=>e.trim()):[e]:this.#i.src=e),null!==this.#l&&(clearInterval(this.#l),this.#l=null);const t=this.#n.querySelector(`.cbg-loader-${this.#o}`);return t&&(t.style.opacity="1"),new Promise(e=>{setTimeout(()=>{if(this.#d)return e();if("single"===this.#i.type){const t="string"==typeof this.#i.src?this.#i.src:this.#i.src[0];this.#g(t,!1).then(e)}else"slider"===this.#i.type&&(this.#f(),e())},this.#i.transitionDuration)})}reloadOptions(e){if(this.#d)return;const t=i.#a(e);this.#i={...this.#i,...t},this.#s?.parentNode&&(this.#s.parentNode.removeChild(this.#s),this.#s=null),this.#m()}#f(){if(this.#d)return;const e=this.#i.src;if(!Array.isArray(e)||0===e.length)return void console.warn("[ChameleonBackgrounds] Slider mode requires an array of image URLs in `src`.");let t=0;this.#g(e[t]).then(()=>{if(this.#d)return;if(t=1,1===e.length)return;const i=this.#i.sliderDuration+2*this.#i.transitionDuration;this.#l=setInterval(()=>{this.#d?clearInterval(this.#l):(this.reloadBackground(e[t]),t++,t>=e.length&&(this.#i.sliderLoop?t=0:(clearInterval(this.#l),this.#l=null)))},i)})}static#a(e){const t={};for(const[n,o]of Object.entries(e))t[i.#t[n]??n]=o;return t}static#c(e){if(e instanceof HTMLElement)return e;if("string"==typeof e){if("body"===e)return document.body;const t=document.querySelector(e);if(!t)throw new Error(`[ChameleonBackgrounds] Element not found: ${e}`);return t}throw new TypeError(`[ChameleonBackgrounds] Invalid element: ${e}`)}static#h(){return"undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID().slice(0,8):Array.from({length:8},()=>"abcdefghijklmnopqrstuvwxyz0123456789".charAt(Math.floor(36*Math.random()))).join("")}}const n=i;return e.d(t,["default",0,n]),t=t.default,t})());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chameleon-backgrounds",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "A zero-dependency JavaScript library to dynamically load background images with elegant fade-in transitions and slideshow support.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/chameleon-backgrounds.js",
|
|
@@ -119,8 +119,22 @@ class ChameleonBackgrounds {
|
|
|
119
119
|
this.#styleElement = null;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
//
|
|
123
|
-
this.#element.
|
|
122
|
+
// Unwrap the original content instead of resetting innerHTML
|
|
123
|
+
const wrapper = this.#element.querySelector(`#cbg-inner-${this.#uid}`);
|
|
124
|
+
if (wrapper && wrapper.parentNode === this.#element) {
|
|
125
|
+
while (wrapper.firstChild) {
|
|
126
|
+
this.#element.insertBefore(wrapper.firstChild, wrapper);
|
|
127
|
+
}
|
|
128
|
+
this.#element.removeChild(wrapper);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Remove loader
|
|
132
|
+
const loader = this.#element.querySelector(`.cbg-loader-${this.#uid}`);
|
|
133
|
+
if (loader) {
|
|
134
|
+
loader.remove();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Remove inline background-image
|
|
124
138
|
this.#element.style.backgroundImage = '';
|
|
125
139
|
}
|
|
126
140
|
|
|
@@ -209,17 +223,19 @@ class ChameleonBackgrounds {
|
|
|
209
223
|
*/
|
|
210
224
|
#buildDOM() {
|
|
211
225
|
const uid = this.#uid;
|
|
212
|
-
const content = this.#element.innerHTML;
|
|
213
226
|
|
|
214
227
|
const wrapper = document.createElement('div');
|
|
215
228
|
wrapper.id = `cbg-inner-${uid}`;
|
|
216
|
-
|
|
229
|
+
|
|
230
|
+
// Move all child nodes from the element into the wrapper
|
|
231
|
+
while (this.#element.firstChild) {
|
|
232
|
+
wrapper.appendChild(this.#element.firstChild);
|
|
233
|
+
}
|
|
217
234
|
|
|
218
235
|
const loader = document.createElement('div');
|
|
219
236
|
loader.classList.add(`cbg-loader-${uid}`);
|
|
220
237
|
|
|
221
|
-
//
|
|
222
|
-
this.#element.innerHTML = '';
|
|
238
|
+
// Append wrapper and loader
|
|
223
239
|
this.#element.appendChild(wrapper);
|
|
224
240
|
this.#element.appendChild(loader);
|
|
225
241
|
}
|
|
@@ -276,14 +292,30 @@ class ChameleonBackgrounds {
|
|
|
276
292
|
});
|
|
277
293
|
}
|
|
278
294
|
|
|
295
|
+
/**
|
|
279
296
|
/**
|
|
280
297
|
* Reload background with a fade-out → swap → fade-in cycle.
|
|
281
|
-
* @param {string} src -
|
|
298
|
+
* @param {string} [src] - Optional new image URL. If omitted, reloads using current options.
|
|
282
299
|
* @returns {Promise<void>}
|
|
283
300
|
*/
|
|
284
301
|
reloadBackground(src) {
|
|
285
302
|
if (this.#destroyed) return Promise.resolve();
|
|
286
303
|
|
|
304
|
+
if (src !== undefined) {
|
|
305
|
+
if (this.#options.type === 'slider' && typeof src === 'string') {
|
|
306
|
+
// If the user passes a comma-separated string, split it. Otherwise wrap it.
|
|
307
|
+
this.#options.src = src.includes(',') ? src.split(',').map(s => s.trim()) : [src];
|
|
308
|
+
} else {
|
|
309
|
+
this.#options.src = src;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Stop any running slider
|
|
314
|
+
if (this.#sliderIntervalId !== null) {
|
|
315
|
+
clearInterval(this.#sliderIntervalId);
|
|
316
|
+
this.#sliderIntervalId = null;
|
|
317
|
+
}
|
|
318
|
+
|
|
287
319
|
const loader = this.#element.querySelector(`.cbg-loader-${this.#uid}`);
|
|
288
320
|
if (loader) {
|
|
289
321
|
loader.style.opacity = '1';
|
|
@@ -292,11 +324,38 @@ class ChameleonBackgrounds {
|
|
|
292
324
|
return new Promise((resolve) => {
|
|
293
325
|
setTimeout(() => {
|
|
294
326
|
if (this.#destroyed) return resolve();
|
|
295
|
-
|
|
327
|
+
|
|
328
|
+
if (this.#options.type === 'single') {
|
|
329
|
+
const singleSrc = typeof this.#options.src === 'string' ? this.#options.src : this.#options.src[0];
|
|
330
|
+
this.#loadBackground(singleSrc, false).then(resolve);
|
|
331
|
+
} else if (this.#options.type === 'slider') {
|
|
332
|
+
this.#startSlider();
|
|
333
|
+
resolve();
|
|
334
|
+
}
|
|
296
335
|
}, this.#options.transitionDuration);
|
|
297
336
|
});
|
|
298
337
|
}
|
|
299
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Update options on the fly without destroying the instance.
|
|
341
|
+
* @param {Object} newOptions - The new options to merge.
|
|
342
|
+
*/
|
|
343
|
+
reloadOptions(newOptions) {
|
|
344
|
+
if (this.#destroyed) return;
|
|
345
|
+
|
|
346
|
+
const normalized = ChameleonBackgrounds.#normalizeOptions(newOptions);
|
|
347
|
+
this.#options = { ...this.#options, ...normalized };
|
|
348
|
+
|
|
349
|
+
// Remove old styles
|
|
350
|
+
if (this.#styleElement?.parentNode) {
|
|
351
|
+
this.#styleElement.parentNode.removeChild(this.#styleElement);
|
|
352
|
+
this.#styleElement = null;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Inject updated styles
|
|
356
|
+
this.#injectStyles();
|
|
357
|
+
}
|
|
358
|
+
|
|
300
359
|
// ---------------------------------------------------------------------------
|
|
301
360
|
// Slider
|
|
302
361
|
// ---------------------------------------------------------------------------
|