Skip to content

Commit 0e67a9b

Browse files
committed
Improve split multiple backgrounds
1 parent 6ac4fda commit 0e67a9b

4 files changed

Lines changed: 44 additions & 44 deletions

File tree

src/api/preCache.js

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,13 @@
1-
import { precacheCommonTags } from "../utils/cssTools";
2-
import { fetchImage } from "../utils/helpers";
3-
import { extractURL } from "../utils/helpers";
4-
import { embedCustomFonts } from "../modules/fonts";
5-
import { imageCache, bgCache, resourceCache, baseCSSCache, computedStyleCache } from "../core/cache";
1+
import { getStyle, inlineSingleBackgroundEntry, fetchImage, splitBackgroundImage } from '../utils/helpers.js';
2+
import { embedCustomFonts } from '../modules/fonts.js';
3+
import { precacheCommonTags } from '../utils/cssTools.js';
4+
import { imageCache, bgCache, resourceCache, baseCSSCache } from '../core/cache.js';
65

76
/**
87
* Preloads images, background images, and optionally fonts into cache before DOM capture.
9-
* This helps avoid delays or missing resources during the capture process.
108
*
11-
* - If `reset` is true, all caches are cleared and the function returns immediately.
12-
* - If `embedFonts` is true, custom fonts are embedded (icon fonts are always embedded).
13-
* - If `preWarm` is true, common tag styles are pre-cached.
14-
*
15-
* @export
16-
* @param {Document|Element} [root=document] - The root node to search for resources (defaults to the whole document)
9+
* @param {Document|Element} [root=document] - The root node to search for resources
1710
* @param {Object} [options={}] - Pre-caching options
18-
* @param {boolean} [options.embedFonts=true] - Whether to embed custom fonts
19-
* @param {boolean} [options.reset=false] - Whether to clear all caches before pre-caching
20-
* @param {boolean} [options.preWarm=true] - Whether to pre-cache common tag styles
21-
* @param {Function} [options.crossOrigin] - Function that returns CORS mode for each image URL
2211
* @returns {Promise<void>} Resolves when all resources are pre-cached
2312
*/
2413

@@ -29,7 +18,7 @@ export async function preCache(root = document, options = {}) {
2918
bgCache.clear();
3019
resourceCache.clear();
3120
baseCSSCache.clear();
32-
computedStyleCache.clear();
21+
// computedStyleCache.clear(); Not necessary to clear
3322
return;
3423
}
3524

@@ -55,15 +44,18 @@ export async function preCache(root = document, options = {}) {
5544
}
5645
}
5746
for (const el of allEls) {
58-
const bg = getComputedStyle(el).backgroundImage;
59-
const url = extractURL(bg);
60-
if (url && !bgCache.has(url)) {
61-
const crossOrigin = crossOriginFn ? crossOriginFn(url) : "anonymous";
62-
promises.push(
63-
fetchImage(url, 3000, crossOrigin)
64-
.then(dataURL => bgCache.set(url, dataURL))
65-
.catch(() => {})
66-
);
47+
const bg = getStyle(el).backgroundImage;
48+
if (bg && bg !== "none") {
49+
const bgSplits = splitBackgroundImage(bg);
50+
for (const entry of bgSplits) {
51+
const isUrl = entry.startsWith("url(");
52+
if (isUrl) {
53+
promises.push(
54+
inlineSingleBackgroundEntry(entry, { crossOrigin: crossOriginFn, skipInline: true })
55+
.catch(() => {})
56+
);
57+
}
58+
}
6759
}
6860
}
6961

src/modules/background.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* @module background
44
*/
55

6-
import { getStyle, inlineSingleBackgroundEntry } from '../utils/helpers.js';
6+
import { getStyle, inlineSingleBackgroundEntry, splitBackgroundImage } from '../utils/helpers.js';
77

88
/**
99
* Converts all background images in the cloned element tree to data URLs.
@@ -34,9 +34,7 @@ export async function inlineBackgroundImages(source, clone, styleCache, options
3434
continue;
3535
}
3636

37-
const bgSplits = bg
38-
.split(/,(?=(?:[^()]*\([^()]*\))*[^()]*$)/)
39-
.map(s => s.trim());
37+
const bgSplits = splitBackgroundImage(bg);
4038

4139
const newBgParts = await Promise.all(
4240
bgSplits.map(entry => inlineSingleBackgroundEntry(entry, options))

src/modules/pseudo.js

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
* @module pseudo
44
*/
55

6-
7-
import { getStyle, snapshotComputedStyle, parseContent, extractURL, safeEncodeURI, fetchImage, inlineSingleBackgroundEntry } from '../utils/helpers.js';
6+
import { getStyle, snapshotComputedStyle, parseContent, extractURL, safeEncodeURI, fetchImage, inlineSingleBackgroundEntry, splitBackgroundImage } from '../utils/helpers.js';
87
import { getStyleKey } from '../utils/cssTools.js';
98
import { iconToImage } from '../modules/fonts.js';
109

11-
1210
/**
1311
* Creates elements to represent ::before, ::after, and ::first-letter pseudo-elements, inlining their styles and content.
1412
*
@@ -18,8 +16,9 @@ import { iconToImage } from '../modules/fonts.js';
1816
* @param {WeakMap} styleCache - Cache of computed styles
1917
* @param {boolean} compress - Whether to compress style keys
2018
* @param {boolean} embedFonts - Whether to embed icon fonts as images
21-
* @returns {Promise<void>} Promise that resolves when all pseudo-elements are processed
19+
* @returns {Promise} Promise that resolves when all pseudo-elements are processed
2220
*/
21+
2322
export async function inlinePseudoElements(source, clone, styleMap, styleCache, compress, embedFonts = false) {
2423
if (!(source instanceof Element) || !(clone instanceof Element)) return;
2524
for (const pseudo of ["::before", "::after", "::first-letter"]) {
@@ -109,7 +108,7 @@ export async function inlinePseudoElements(source, clone, styleMap, styleCache,
109108

110109
if (hasBg) {
111110
try {
112-
const bgSplits = bg.split(/,(?=(?:[^()]*\([^()]*\))*[^()]*$)/).map(s => s.trim());
111+
const bgSplits = splitBackgroundImage(bg);
113112
const newBgParts = await Promise.all(
114113
bgSplits.map(entry => inlineSingleBackgroundEntry(entry))
115114
);
@@ -119,19 +118,12 @@ export async function inlinePseudoElements(source, clone, styleMap, styleCache,
119118
}
120119
}
121120

122-
if (hasBgColor) {
123-
pseudoEl.style.backgroundColor = bgColor;
124-
}
121+
if (hasBgColor) pseudoEl.style.backgroundColor = bgColor;
125122

126123
const hasContent2 = pseudoEl.childNodes.length > 0 || (pseudoEl.textContent && pseudoEl.textContent.trim() !== "");
127124
const hasVisibleBox = hasContent2 || hasBg || hasBgColor;
128125
if (!hasVisibleBox) continue;
129-
130-
if (pseudo === "::before") {
131-
clone.insertBefore(pseudoEl, clone.firstChild);
132-
} else {
133-
clone.appendChild(pseudoEl);
134-
}
126+
pseudo === "::before" ? clone.insertBefore(pseudoEl, clone.firstChild) : clone.appendChild(pseudoEl);
135127
}
136128
} catch (e) {
137129
console.warn(`[snapdom] Failed to capture ${pseudo} for`, source, e);

src/utils/helpers.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,21 @@ export function safeEncodeURI(uri) {
238238
return uri;
239239
}
240240
}
241+
242+
export function splitBackgroundImage(bg) {
243+
const parts = [];
244+
let depth = 0;
245+
let lastIndex = 0;
246+
for (let i = 0; i < bg.length; i++) {
247+
const char = bg[i];
248+
if (char === '(') depth++;
249+
if (char === ')') depth--;
250+
if (char === ',' && depth === 0) {
251+
parts.push(bg.slice(lastIndex, i).trim());
252+
lastIndex = i + 1;
253+
}
254+
}
255+
parts.push(bg.slice(lastIndex).trim());
256+
return parts;
257+
}
258+

0 commit comments

Comments
 (0)