1// УбиÑÐ°ÐµÑ LQIP-плейÑÑ Ð¾Ð»Ð´ÐµÑ ([data-lqip]) поÑле загÑÑзки наÑÑоÑÑей каÑÑинки над ним. 2// ÐоÑле загÑÑзки он полноÑÑÑÑ Ð¿ÐµÑекÑÑÑ, но его ÑазмÑÑÑй ÑвелиÑеннÑй биÑмап вÑÑ 3// Ñавно живÑÑ Ð¾ÑделÑнÑм композиÑнÑм Ñлоем на каждÑÑ ÐºÐ°ÑÑоÑÐºÑ â на ÑлабÑÑ GPU 4// Safari из-за ÑÑого не ÑÑÐ¿ÐµÐ²Ð°ÐµÑ ÑаÑÑеÑизоваÑÑ ÑÐ°Ð¹Ð»Ñ Ð¿Ñи ÑкÑолле и показÑÐ²Ð°ÐµÑ 5// пÑÑÑÑе блоки. 6(function () { 7 const isLoaded = (img) => img.complete && img.naturalWidth > 0; 8 9 // ÐаÑÑоÑÑÐ°Ñ ÐºÐ°ÑÑинка â пеÑвÑй <img> в Ñом же конÑейнеÑе, не внÑÑÑи плейÑÑ Ð¾Ð»Ð´ÐµÑа. 10 const realImageFor = (placeholder) => { 11 const box = placeholder.parentElement; 12 if (!box) return null; 13 for (const img of box.querySelectorAll("img")) { 14 if (!placeholder.contains(img)) return img; 15 } 16 return null; 17 }; 18 19 // ÐбÑаÑнÑй поиÑк: плейÑÑ Ð¾Ð»Ð´ÐµÑ â пÑÑмой ÑебÑнок одного из ближайÑÐ¸Ñ Ð¿Ñедков каÑÑинки. 20 const placeholderFor = (img) => { 21 let node = img.parentElement; 22 for (let depth = 0; node && depth < 4; depth++) { 23 const found = node.querySelector(":scope > [data-lqip]"); 24 if (found) return found; 25 node = node.parentElement; 26 } 27 return null; 28 }; 29 30 // Two rAFs = wait for a real completed paint. On an overloaded compositor 31 // decode() alone can resolve before the frame it decoded for has actually 32 // been drawn to the screen (the first rAF just gets us to "next frame is 33 // scheduled", not "previous frame landed") â one more round trip forces us 34 // to wait until that paint has actually happened before we pull the 35 // placeholder out from under it. 36 const nextPaint = () => 37 new Promise((resolve) => 38 requestAnimationFrame(() => requestAnimationFrame(resolve)), 39 ); 40 41 // img.complete goes true as soon as the bytes are downloaded, not once the 42 // pixels are actually decoded and paintable â on a slow device those can be 43 // seconds apart. Removing the placeholder on `complete` alone left the 44 // not-yet-decoded rows unblurred (visible as bare stripes while the image 45 // paints in). img.decode() resolves once it's ready to draw, but on a 46 // sufficiently backed-up compositor even that can land before the actual 47 // paint â nextPaint() covers that last gap. 48 const settle = async (placeholder) => { 49 const img = realImageFor(placeholder); 50 if (!img || !isLoaded(img)) return; 51 if (img.decode) { 52 try { 53 await img.decode(); 54 } catch { 55 // decode() rejects if the image errors or is swapped mid-flight â 56 // still safe to drop the placeholder, there's nothing left to blur. 57 } 58 } 59 await nextPaint(); 60 placeholder.remove(); 61 }; 62 63 // ÐаÑÑинки, догÑÑзивÑиеÑÑ Ð¿Ð¾Ð·Ð¶Ðµ (lazy, htmx-вÑÑавки): load не вÑплÑваеÑ, но 64 // ловиÑÑÑ Ð½Ð° capture-Ñазе. 65 document.addEventListener( 66 "load", 67 (e) => { 68 if (!(e.target instanceof HTMLImageElement)) return; 69 const placeholder = placeholderFor(e.target); 70 if (placeholder) settle(placeholder); 71 }, 72 true, 73 ); 74 75 // ÐаÑÑинки, ÑÑпевÑие загÑÑзиÑÑÑÑ Ð¸Ð· кеÑа до Ñого, как ÑкÑÐ¸Ð¿Ñ Ð¾ÑÑабоÑал. 76 document.querySelectorAll("[data-lqip]").forEach(settle); 77})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.