perf: lazy async image decoding + max-height to prevent scroll freeze
ROOT CAUSE: Articles from sites like onemileatatime.com serve WebP
images with .jpeg extensions. WebKit attempts JPEG decode, fails,
retries as WebP — the retry loop blocks the compositor thread during
scroll, causing ~1s freezes per image.
FIX 1 — Image attributes (ArticleHTMLBuilder.optimizeImages):
Regex injects loading="lazy" decoding="async" on all <img> tags
that don't already have loading= set. This tells WebKit to:
- decode images off the main/compositor thread (decoding=async)
- only decode when approaching viewport (loading=lazy)
FIX 2 — CSS max-height:
img { max-height: 600px; object-fit: contain; }
Limits decoded image buffer size. A 1200x900 image still displays
at full width but WebKit doesn't need to composite oversized tiles.
Both fixes are content-rendering optimizations only — no changes
to WKWebView architecture, scrolling model, or layout system.
Also marked Atmos Rewards articles as unread for testing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -156,7 +156,7 @@ enum ArticleHTMLBuilder {
|
|||||||
.article-meta {
|
.article-meta {
|
||||||
font-size: 13px; color: #8B6914; line-height: 1.4;
|
font-size: 13px; color: #8B6914; line-height: 1.4;
|
||||||
}
|
}
|
||||||
img { max-width: 100%; height: auto; border-radius: 8px; margin: 12px 0; }
|
img { max-width: 100%; height: auto; max-height: 600px; object-fit: contain; border-radius: 8px; margin: 12px 0; }
|
||||||
a { color: #8B6914; }
|
a { color: #8B6914; }
|
||||||
h1, h2, h3, h4 { line-height: 1.3; margin-top: 24px; margin-bottom: 8px; }
|
h1, h2, h3, h4 { line-height: 1.3; margin-top: 24px; margin-bottom: 8px; }
|
||||||
pre, code { background: #f5f0e8; border-radius: 6px; padding: 2px 6px; font-size: 15px; overflow-x: auto; }
|
pre, code { background: #f5f0e8; border-radius: 6px; padding: 2px 6px; font-size: 15px; overflow-x: auto; }
|
||||||
@@ -214,9 +214,27 @@ enum ArticleHTMLBuilder {
|
|||||||
<h1 class="article-title">\(titleHTML)</h1>
|
<h1 class="article-title">\(titleHTML)</h1>
|
||||||
<div class="article-meta">\(meta)</div>
|
<div class="article-meta">\(meta)</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="article-body">\(body)</div>
|
<div id="article-body">\(optimizeImages(body))</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add loading="lazy" and decoding="async" to all <img> tags.
|
||||||
|
/// This tells WebKit to decode images off the main thread and
|
||||||
|
/// only when they approach the viewport, preventing scroll freezes
|
||||||
|
/// from large/mismatched-type images (e.g. WebP served as .jpeg).
|
||||||
|
private static let imgTagRegex = try! NSRegularExpression(
|
||||||
|
pattern: #"<img(?![^>]*loading=)([^>]*)>"#,
|
||||||
|
options: .caseInsensitive
|
||||||
|
)
|
||||||
|
|
||||||
|
private static func optimizeImages(_ html: String) -> String {
|
||||||
|
let range = NSRange(html.startIndex..., in: html)
|
||||||
|
return imgTagRegex.stringByReplacingMatches(
|
||||||
|
in: html,
|
||||||
|
range: range,
|
||||||
|
withTemplate: #"<img loading="lazy" decoding="async"$1>"#
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user