这是为您生成的图片自适应优化脚本,它利用现代浏览器的能力,根据设备屏幕尺寸和分辨率智能加载最合适的图片版本,从而提升页面加载速度和显示效果。

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">🖼️ 智能图片自适应优化</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
background: #f4f6f9;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 2rem 1rem;
}
.container {
max-width: 1100px;
width: 100%;
background: white;
border-radius: 2rem;
box-shadow: 0 20px 40px -12px rgba(0, 0, 0, 0.1);
padding: 2rem 2rem 2.5rem;
transition: all 0.2s;
}
h1 {
font-weight: 600;
font-size: 1.9rem;
letter-spacing: -0.02em;
color: #0b1a2e;
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 0.25rem;
flex-wrap: wrap;
}
h1 small {
font-weight: 400;
font-size: 1rem;
color: #4a5b6e;
background: #eef2f6;
padding: 0.2rem 0.9rem;
border-radius: 40px;
letter-spacing: 0.3px;
}
.sub {
color: #3f4e62;
margin-bottom: 2rem;
border-left: 4px solid #3b82f6;
padding-left: 1rem;
font-weight: 400;
background: #f9fafc;
border-radius: 0 12px 12px 0;
line-height: 1.6;
}
/* 演示网格 */
.demo-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 2rem 1.5rem;
margin: 2rem 0 1rem;
}
.card {
background: #ffffff;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 8px 20px rgba(0,0,0,0.02);
transition: transform 0.15s ease, box-shadow 0.2s;
border: 1px solid #eef2f6;
}
.card:hover {
transform: translateY(-3px);
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.04);
}
.card-image {
width: 100%;
background: #e6edf4;
position: relative;
overflow: hidden;
}
.card-image img {
display: block;
width: 100%;
height: auto;
object-fit: cover;
aspect-ratio: 16 / 10; /* 统一比例,演示自适应 */
background: #eef2f7;
transition: opacity 0.35s ease, filter 0.2s;
}
/* 模糊占位效果 (可选) */
img[data-src] {
filter: blur(6px);
opacity: 0.8;
transform: scale(1.02);
}
img.loaded {
filter: blur(0);
opacity: 1;
transform: scale(1);
}
.card-body {
padding: 1rem 1.2rem 1.4rem;
}
.card-title {
font-weight: 600;
font-size: 1.2rem;
margin-bottom: 6px;
color: #11212e;
}
.card-desc {
font-size: 0.9rem;
color: #4f5e6e;
line-height: 1.5;
display: flex;
justify-content: space-between;
align-items: center;
}
.badge {
background: #dbeafe;
color: #1e4b8c;
font-size: 0.7rem;
padding: 0.25rem 0.9rem;
border-radius: 20px;
font-weight: 500;
letter-spacing: 0.3px;
}
.badge.green {
background: #d1fae5;
color: #0b5e3c;
}
.info-panel {
background: #f1f5f9;
border-radius: 18px;
padding: 1.25rem 1.8rem;
margin-top: 2.2rem;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 1rem 1.5rem;
font-size: 0.95rem;
border: 1px solid #e0e8f0;
}
.info-item {
display: flex;
align-items: baseline;
gap: 6px;
flex-wrap: wrap;
}
.info-item span:first-child {
color: #455a6e;
font-weight: 450;
}
.info-item .value {
background: white;
padding: 0.1rem 0.9rem;
border-radius: 40px;
font-weight: 500;
color: #0b1f33;
border: 1px solid #dae2ec;
}
#refreshInfo {
background: #ffffff;
border: 1px solid #ced8e4;
padding: 0.5rem 1.4rem;
border-radius: 40px;
font-weight: 500;
color: #1e2c41;
cursor: pointer;
transition: 0.15s;
font-size: 0.9rem;
display: inline-flex;
align-items: center;
gap: 6px;
}
#refreshInfo:hover {
background: #e8edf5;
border-color: #9aaec7;
}
.footnote {
text-align: center;
margin-top: 1.8rem;
color: #64748b;
font-size: 0.85rem;
border-top: 1px solid #e9edf3;
padding-top: 1.8rem;
}
@media (max-width: 600px) {
.container { padding: 1.5rem; }
h1 { font-size: 1.5rem; }
.info-panel { flex-direction: column; align-items: stretch; }
}
</style>
</head>
<body>
<div class="container">
<h1>
📐 图片自适应优化
<small>srcset + 尺寸感知</small>
</h1>
<div class="sub">
⚡ 基于屏幕宽度 & 像素比 · 自动加载最适合的图片 · 支持延迟加载
</div>
<!-- 图片展示网格 -->
<div class="demo-grid" id="imageGrid">
<!-- 卡片 1 : 山川 -->
<div class="card">
<div class="card-image">
<img class="optimized-img"
data-src="https://picsum.photos/seed/landscape/600/375"
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='375' viewBox='0 0 600 375'%3E%3Crect fill='%23d9e2ec' width='600' height='375'/%3E%3Ctext x='50%25' y='50%25' dominant-baseline='middle' text-anchor='middle' fill='%2370849c' font-size='18' font-family='sans-serif'%3E加载中…%3C/text%3E%3C/svg%3E"
alt="自适应山川图片"
loading="lazy"
width="600" height="375">
</div>
<div class="card-body">
<div class="card-title">🏔️ 山脉</div>
<div class="card-desc">
<span>分辨率感知</span>
<span class="badge">16:10</span>
</div>
</div>
</div>
<!-- 卡片 2 : 城市 -->
<div class="card">
<div class="card-image">
<img class="optimized-img"
data-src="https://picsum.photos/seed/city/600/375"
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='375' viewBox='0 0 600 375'%3E%3Crect fill='%23cbd5e1' width='600' height='375'/%3E%3Ctext x='50%25' y='50%25' dominant-baseline='middle' text-anchor='middle' fill='%235f748c' font-size='18' font-family='sans-serif'%3E加载…%3C/text%3E%3C/svg%3E"
alt="自适应城市图片"
loading="lazy"
width="600" height="375">
</div>
<div class="card-body">
<div class="card-title">🌆 城市夜景</div>
<div class="card-desc">
<span>DPR 适配</span>
<span class="badge green">自动</span>
</div>
</div>
</div>
<!-- 卡片 3 : 海洋 -->
<div class="card">
<div class="card-image">
<img class="optimized-img"
data-src="https://picsum.photos/seed/ocean/600/375"
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='375' viewBox='0 0 600 375'%3E%3Crect fill='%23b9cadc' width='600' height='375'/%3E%3Ctext x='50%25' y='50%25' dominant-baseline='middle' text-anchor='middle' fill='%233f5a77' font-size='18' font-family='sans-serif'%3E加载…%3C/text%3E%3C/svg%3E"
alt="自适应海洋图片"
loading="lazy"
width="600" height="375">
</div>
<div class="card-body">
<div class="card-title">🌊 海洋</div>
<div class="card-desc">
<span>智能尺寸</span>
<span class="badge">webp 优先</span>
</div>
</div>
</div>
</div>
<!-- 实时信息面板 -->
<div class="info-panel" id="infoPanel">
<div class="info-item">
<span>📺 视口宽度</span>
<span class="value" id="viewportWidth">—</span>
</div>
<div class="info-item">
<span>🔍 像素比 (DPR)</span>
<span class="value" id="devicePixelRatio">—</span>
</div>
<div class="info-item">
<span>🖼️ 加载尺寸</span>
<span class="value" id="loadedResolution">—</span>
</div>
<button id="refreshInfo" aria-label="刷新信息">⟳ 重新检测</button>
</div>
<div class="footnote">
⚡ 图片使用 <strong>srcset</strong> + <strong>sizes</strong> 自适应,根据设备宽度与 DPR 加载最适合版本。<br>
支持延迟加载 (loading="lazy") 及模糊占位过渡。
</div>
</div>
<script>
(function() {
"use strict";
// ----- 核心:自适应图片优化脚本 -----
// 原理:为每个图片生成 srcset (多分辨率) + sizes,并根据屏幕宽度/DPR 加载最佳图片
// 同时支持渐进增强、占位模糊效果
// 工具:获取设备像素比 (至少 1)
const dpr = window.devicePixelRatio || 1;
// 更新信息面板
function updateInfo() {
const vw = window.innerWidth;
document.getElementById('viewportWidth').textContent = vw + 'px';
document.getElementById('devicePixelRatio').textContent = dpr.toFixed(1) + 'x';
// 显示已加载图片的实际尺寸 (取自最后一张加载完成的图片或取样)
const imgs = document.querySelectorAll('.optimized-img');
let loadedCount = 0;
let lastNatural = { width: '—', height: '—' };
imgs.forEach(img => {
if (img.complete && img.naturalWidth > 0) {
loadedCount++;
lastNatural = { width: img.naturalWidth, height: img.naturalHeight };
}
});
if (loadedCount > 0) {
document.getElementById('loadedResolution').textContent =
`${lastNatural.width} × ${lastNatural.height}`;
} else {
document.getElementById('loadedResolution').textContent = '等待加载…';
}
}
// ----- 生成响应式 srcset 和 sizes (核心优化) -----
function setupResponsiveImage(img) {
// 获取原始图片URL基础 (移除尺寸部分,以便生成多尺寸)
let src = img.getAttribute('data-src') || img.src;
// 如果是 picsum 类型的链接,我们提取种子,构建多尺寸
// 支持 picsum.photos 以及常规带尺寸的URL
let baseUrl = src;
// 针对 picsum 特殊处理: 从URL中解析种子和尺寸
// https://picsum.photos/seed/landscape/600/375
// 转换成多尺寸: 使用 ?random 或者构建 400, 800, 1200 版本
const picsumMatch = src.match(/^(https:\/\/picsum\.photos\/seed\/([^/]+))\/(\d+)\/(\d+)/i);
if (picsumMatch) {
const base = picsumMatch[1]; // https://picsum.photos/seed/landscape
const defaultW = picsumMatch[3];
const defaultH = picsumMatch[4];
// 生成多组分辨率 (宽度): 320, 640, 960, 1280, 1920,按比例保持高度
const widths = [320, 640, 960, 1280, 1920];
const ratio = defaultH / defaultW;
const srcsetParts = widths.map(w => {
const h = Math.round(w * ratio);
return `${base}/${w}/${h} ${w}w`;
}).join(', ');
img.setAttribute('srcset', srcsetParts);
// 设置 sizes 属性: 根据视口宽度决定加载哪张 (响应式)
// (max-width: 480px) 100vw, (max-width: 768px) 80vw, 50vw
// 这里简洁处理:如果屏幕<600用100vw,否则用50vw (实际根据布局)
img.setAttribute('sizes', '(max-width: 600px) 100vw, (max-width: 1024px) 50vw, 33vw');
// 默认 src 使用中等尺寸 (640×比例) 使初始加载不至于太大
const mediumW = 640;
const mediumH = Math.round(mediumW * ratio);
img.src = `${base}/${mediumW}/${mediumH}`;
// 同时更新 data-src 为原始大尺寸? 但我们已经用了srcset
// 保留原始大尺寸作为后备: 使用 1280w
img.setAttribute('data-fallback', `${base}/${1280}/${Math.round(1280 * ratio)}`);
} else {
// 对于其他图片 (非picsum),尝试从URL提取宽高并生成srcset
// 普通图片也可能是静态路径,我们默认添加2x 3x 基于尺寸
// 但此处为通用演示,如果图片不是picsum,则简单增加DPR srcset
// 大多数场景可使用 1x, 2x (基于原始尺寸)
const originalSrc = img.getAttribute('data-src') || img.src;
// 移除查询参数避免缓存
const cleanSrc = originalSrc.split('?')[0];
// 判断是否已有尺寸后缀,简单策略: 生成 @2x @3x (通过查询参数模拟)
// (真实的2x图片需服务器支持, 这里使用 ?dpr=2 模拟)
if (!cleanSrc.includes('picsum')) {
// 构造基于DPR的srcset (1x, 2x, 3x)
const src1x = cleanSrc;
const src2x = cleanSrc + (cleanSrc.includes('?') ? '&' : '?') + 'dpr=2';
const src3x = cleanSrc + (cleanSrc.includes('?') ? '&' : '?') + 'dpr=3';
img.setAttribute('srcset', `${src1x} 1x, ${src2x} 2x, ${src3x} 3x`);
// sizes 可以省略,使用x描述符时浏览器自动选择
} else {
// fallback 至少保留原始
img.setAttribute('srcset', `${cleanSrc} 1x`);
}
// 保留原始src
img.src = cleanSrc;
}
// 让浏览器应用srcset (部分浏览器需要重新触发)
// 强制重新解析srcset
if (img.srcset) {
img.offsetHeight; // 强制重绘
}
}
// ----- 模糊占位 → 加载完成后清除 -----
function handleImageLoad(img) {
img.classList.add('loaded');
// 移除模糊效果
img.style.filter = 'none';
// 更新信息面板 (取样)
updateInfo();
}
function handleImageError(img) {
// 如果加载失败,使用fallback
const fallback = img.getAttribute('data-fallback');
if (fallback && img.src !== fallback) {
img.src = fallback;
}
img.classList.add('loaded'); // 仍然移除模糊
img.style.filter = 'none';
}
// ----- 初始化所有图片 -----
function initImages() {
const images = document.querySelectorAll('.optimized-img');
images.forEach((img, index) => {
// 1. 应用响应式策略 (srcset, sizes)
setupResponsiveImage(img);
// 2. 绑定加载完成事件 (移除模糊)
// 如果已经加载完成则直接处理
if (img.complete && img.naturalWidth > 0) {
handleImageLoad(img);
} else {
// 移除之前绑定的监听避免重复
img.removeEventListener('load', onLoad);
img.removeEventListener('error', onError);
// 定义处理函数
const onLoad = () => handleImageLoad(img);
const onError = () => handleImageError(img);
img.addEventListener('load', onLoad, { once: true });
img.addEventListener('error', onError, { once: true });
}
// 3. 支持延迟加载: 如果浏览器不支持 loading=lazy,我们自己实现简单懒加载?
// 但现代浏览器都支持,使用原生 lazy,并加上 IntersectionObserver 作为降级 (可选)
if ('loading' in HTMLImageElement.prototype) {
img.setAttribute('loading', 'lazy');
} else {
// 极简懒加载 polyfill: 使用 IntersectionObserver
// 这里为了轻量,只做演示,不再额外实现
// 但为了效果,我们手动设置loading = lazy 已经支持大部分
}
});
// 首次信息更新
setTimeout(updateInfo, 200);
}
// ----- 窗口变化重新检测 (视口宽度) -----
let resizeTimer;
function handleResize() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
updateInfo();
}, 150);
}
// 刷新按钮
document.getElementById('refreshInfo').addEventListener('click', function(e) {
e.preventDefault();
updateInfo();
// 同时重新检查所有图片状态
const imgs = document.querySelectorAll('.optimized-img');
imgs.forEach(img => {
if (img.complete && img.naturalWidth > 0) {
handleImageLoad(img);
}
});
});
// ----- 启动 -----
document.addEventListener('DOMContentLoaded', function() {
initImages();
updateInfo();
window.addEventListener('resize', handleResize);
// 针对移动端旋转
window.addEventListener('orientationchange', function() {
setTimeout(updateInfo, 300);
});
});
// 如果DOM已加载完成 (脚本在底部,但以防万一)
if (document.readyState !== 'loading') {
initImages();
updateInfo();
window.addEventListener('resize', handleResize);
}
// 暴露一些信息用于调试
window.__imgOptimizer = { updateInfo, initImages };
})();
</script>
</body>
</html>
图片自适应机制说明
脚本通过组合多种现代Web技术,实现了图片的智能加载与显示,您可以把它理解为一位“图片管家”,它会在您浏览时,根据设备的实际情况,为您挑选最合适的图片。
- 分辨率与尺寸适配:脚本会读取您的屏幕宽度和像素比(DPR),并使用
srcset和sizes属性为图片生成多个版本(如320w、640w、1280w),浏览器会自动选择最匹配当前屏幕尺寸和分辨率的图片进行加载,避免加载过大或过小的图片。 - 加载性能优化:集成了延迟加载(
loading=“lazy”)特性,图片仅在即将进入可视区域时才开始加载,有效减少了初始页面的数据消耗,配合模糊占位图,在图片加载完成前会显示一个柔和过渡,避免了页面布局抖动,提升了用户体验。 - 状态监控与反馈:页面底部的信息面板会实时显示当前的视口宽度、设备像素比以及已加载图片的实际分辨率,您可以通过“重新检测”按钮手动刷新这些信息,直观地看到不同设备或窗口大小变化下的自适应效果。