<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #f4f5f7; display: flex; justify-content: center; padding: 40px; }
.ant-card {
width: 320px; background: #ffffff; border-radius: 8px;
box-shadow: 0 1px 2px -2px rgba(0,0,0,.16), 0 3px 6px 0 rgba(0,0,0,.12);
overflow: hidden;
}
.ant-card-head { padding: 0 16px; height: 48px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #f0f0f0; }
.ant-card-head-title { font-size: 16px; font-weight: 600; color: rgba(0,0,0,0.88); }
.ant-card-extra a { font-size: 14px; color: #1677ff; text-decoration: none; }
.ant-carousel { padding: 16px; }
.swiper { width: 100%; border-radius: 8px; overflow: hidden; }
.carousel-item {
position: relative; display: block; width: 100%; height: 170px;
text-decoration: none; transform: translateZ(0);
}
.carousel-img {
width: 100%; height: 100%; object-fit: cover;
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.carousel-item:hover .carousel-img { transform: scale(1.05); }
/* 底部深色渐变遮罩 */
.carousel-item::after {
content: ''; position: absolute; inset: 0;
background: linear-gradient(to top, rgba(0, 0, 0, 0.95) 0%, rgba(0, 0, 0, 0.4) 45%, transparent 100%);
pointer-events: none; z-index: 1;
}
/* ⭐ 极简显目的单行标题 */
.carousel-title {
position: absolute; bottom: 12px; left: 12px; right: 65px; /* 给进度条留空间 */
color: #ffffff; font-size: 15px; font-weight: 600; line-height: 1.4;
z-index: 2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
/* 极致阴影,保证在任何图片上都清晰可见 */
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9), 0 2px 6px rgba(0, 0, 0, 0.5);
}
/* 分页器 */
.custom-pagination {
position: absolute; bottom: 16px; right: 16px;
display: flex; gap: 6px; z-index: 10;
}
.custom-bullet {
position: relative; width: 4px; height: 4px;
background: rgba(255, 255, 255, 0.4); border-radius: 4px;
transition: width 0.3s, background 0.3s; cursor: pointer; overflow: hidden;
}
.custom-bullet.active { width: 24px; background: rgba(255, 255, 255, 0.3); }
.custom-bullet .progress {
position: absolute; top: 0; left: 0; height: 100%; width: 100%;
background: #ffffff; border-radius: 4px;
transform-origin: left center; transform: scaleX(0); will-change: transform;
}
.custom-bullet:not(.active) .progress { transition: transform 0.3s ease; }
.custom-bullet.active .progress { transition: none; }
</style>
<div class="ant-card">
<div class="ant-card-head">
<div class="ant-card-head-title">推荐阅读</div>
<div class="ant-card-extra"><a href="#">更多 ></a></div>
</div>
<div class="ant-carousel">
<div class="swiper mySwiper">
<div class="swiper-wrapper">
<div class="swiper-slide">
<a href="#" class="carousel-item">
<img src="https://picsum.photos/400/200?random=1" class="carousel-img" alt="">
<!-- 测试长标题:如果太长会自动显示省略号 -->
<div class="carousel-title">这是一个超长的标题测试:前端性能优化的 10 个核心技巧,包含所有细节</div>
</a>
</div>
<div class="swiper-slide">
<a href="#" class="carousel-item">
<img src="https://picsum.photos/400/200?random=2" class="carousel-img" alt="">
<div class="carousel-title">React 19 新特性深度解析</div>
</a>
</div>
<div class="swiper-slide">
<a href="#" class="carousel-item">
<img src="https://picsum.photos/400/200?random=3" class="carousel-img" alt="">
<div class="carousel-title">Ant Design 设计语言指南</div>
</a>
</div>
</div>
<div class="custom-pagination"></div>
</div>
</div>
</div>
<script>
var swiper = new Swiper(".mySwiper", {
loop: true, autoplay: { delay: 4000, disableOnInteraction: false, pauseOnMouseEnter: true },
speed: 600, watchSlidesProgress: true,
on: {
init: function () {
const total = document.querySelectorAll('.mySwiper .swiper-slide:not(.swiper-slide-duplicate)').length;
const $pagination = document.querySelector('.custom-pagination');
$pagination.innerHTML = '';
for (let i = 0; i < total; i++) {
const $dot = document.createElement('div');
$dot.className = 'custom-bullet' + (i === 0 ? ' active' : '');
$dot.innerHTML = '<div class="progress"></div>';
$dot.addEventListener('click', () => this.slideToLoop(i));
$pagination.appendChild($dot);
}
},
slideChange: function () {
const realIndex = this.realIndex;
document.querySelectorAll('.custom-bullet').forEach((dot, index) => {
if (index === realIndex) {
dot.classList.add('active');
dot.querySelector('.progress').style.transform = 'scaleX(0)';
} else {
dot.classList.remove('active');
}
});
},
autoplayTimeLeft: function (s, time, progress) {
const realIndex = s.realIndex;
const $activeDot = document.querySelectorAll('.custom-bullet')[realIndex];
if ($activeDot) {
$activeDot.querySelector('.progress').style.transform = `scaleX(${1 - progress})`;
}
}
}
});
</script>
引入其他库:
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />