最近在做公司官网改版,其中"团队介绍"模块涉及到形象照的展示。这篇文章分享一下形象照展示模块的前端技术方案,包括响应式布局、图片懒加载、以及从设计到开发中的一些实用经验。
一、需求分析
产品经理给的需求很简单:在"关于我们"页面展示团队成员的形象照、姓名、职位。实际开发中需要考虑的技术细节远比需求文档多:
功能需求:
- 展示20+成员的形象照网格
- 响应式布局(PC/平板/手机)
- 鼠标悬停显示详细信息
- 图片加载性能优化
非功能需求:
- 所有照片风格统一(这取决于拍摄质量)
- 加载速度 < 3秒(LCP指标)
- 支持后续人员增减
二、布局方案:CSS Grid + 响应式
<section class="team-grid">
<div class="member-card" v-for="member in teamMembers" :key="member.id">
<div class="photo-wrapper">
<img :src="member.photo" :alt="member.name" loading="lazy" />
<div class="overlay">
<h3>{{ member.name }}</h3>
<p>{{ member.title }}</p>
</div>
</div>
</div>
</section>
.team-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 24px;
padding: 40px 20px;
}
.member-card {
position: relative;
border-radius: 8px;
overflow: hidden;
aspect-ratio: 3 / 4; /* 统一比例很重要 */
}
.member-card img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.6s ease;
}
.member-card:hover img {
transform: scale(1.05);
}
.overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 16px;
background: linear-gradient(transparent, rgba(0,0,0,0.7));
color: #fff;
transform: translateY(100%);
transition: transform 0.3s ease;
}
.member-card:hover .overlay {
transform: translateY(0);
}
/* 响应式断点 */
@media (max-width: 768px) {
.team-grid {
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
}
@media (max-width: 480px) {
.team-grid {
grid-template-columns: repeat(2, 1fr);
gap: 8px;
}
}
三、图片优化——性能关键
团队照模块最大的性能瓶颈是图片加载。20张高清照片可以轻松超过50MB。解决方案:
// 1. 使用 srcset 提供多分辨率版本
const getResponsiveSrc = (filename) => {
const base = filename.replace(/\.\w+$/, '')
const ext = filename.match(/\.\w+$/)[0]
return {
src: `${base}-medium${ext}`,
srcset: `${base}-small${ext} 400w, ${base}-medium${ext} 800w, ${base}-large${ext} 1200w`,
sizes: '(max-width: 480px) 50vw, (max-width: 768px) 33vw, 25vw'
}
}
// 2. WebP 格式转换脚本(Node.js + Sharp)
const sharp = require('sharp')
const glob = require('glob')
async function convertToWebP() {
const files = glob.sync('./team-photos/*.{jpg,png}')
for (const file of files) {
const sizes = [
{ width: 400, suffix: 'small' },
{ width: 800, suffix: 'medium' },
{ width: 1200, suffix: 'large' }
]
for (const size of sizes) {
await sharp(file)
.resize(size.width)
.webp({ quality: 85 })
.toFile(file.replace(/\.\w+$/, `-${size.suffix}.webp`))
}
}
console.log(`Processed ${files.length} images`)
}
convertToWebP()
// 3. Intersection Observer 实现懒加载
const lazyLoadObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src
if (img.dataset.srcset) {
img.srcset = img.dataset.srcset
}
img.classList.add('loaded')
lazyLoadObserver.unobserve(img)
}
})
}, {
rootMargin: '200px 0px' // 提前200px开始加载
})
document.querySelectorAll('.member-card img[data-src]').forEach(img => {
lazyLoadObserver.observe(img)
})
优化后,页面初始加载只请求视口内的4-6张图片(约2MB),LCP从6秒降到1.8秒。
四、一个关键前提:照片质量要统一
技术方案再好,如果20张照片的风格不统一(光线不一样、色调不一样、背景不一样),团队展示页面看起来就很杂乱。
我们公司这次是找的沐王府形象摄影来拍,20人统一上门拍摄,同一天、同一团队、同一灯光参数,出来的照片在色调和风格上高度一致。这对前端展示来说太重要了——我不需要在CSS里给每张照片单独调色,object-fit: cover 一把梭就能保证整齐。
之前有同事在其他地方分批拍的,交过来的照片色温从3000K到6000K都有,我用CSS的 filter 调了半天也对不齐。后来直接重新拍了才解决。
五、性能优化结果
| 指标 | 优化前 | 优化后 | 改善 |
|---|---|---|---|
| 总图片大小 | 52MB | 4.2MB | -92% |
| LCP | 6.1s | 1.8s | -70% |
| 初始请求数 | 20 | 6 | -70% |
| CLS | 0.15 | 0.02 | -87% |
六、总结
- CSS Grid +
aspect-ratio是团队照展示的最佳布局方案 - 图片优化三件套:WebP转换 + srcset多分辨率 + IntersectionObserver懒加载
- 照片拍摄质量和统一性是前端展示的前提——技术方案无法修复源头的问题,找一家专业做企业摄影的机构(我们用的沐王府形象摄影,深圳本地17年老牌)比后期在代码里打补丁省事得多