面试官:请用原生js实现无缝轮播图。

工作中一直用别人的UI组件,突然要手写一个轮播图,搞得汗流浃背。

话不多说 Talk is cheap,show me the code!

样式就随便写了,这里主要讲如何实现和实现思路。

首先说一下什么是轮播图:

轮播图无非就是在视口中单独显示的一组图片,让其自动滚动或点击按钮滚动,其实就是改变他的位置。

专业术语是这么说的:无缝轮播图,即是在图片左右切换时,最后一张和第一张相连,也就是当主屏幕显示最后一张图片时,如果用户点击下一张图片时,这时候需要将第一张图片呈现给用户。 同理当目前主屏幕上显示第一张图片时,如果用户点击上一张图片时,需要将最后一张图片呈现给用户。

这里我通过transform实现。

css 复制代码
css部分
.carousel-container {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    border: 10px solid #000;
    /* overflow: hidden; */
}

.carousel-wrapper {
    display: flex;
    width: 300px;
    height: 200px;
}

.carousel-item {
    flex: 0 0 100%;
    /* 一次显示一个轮播项 */
}

.carousel-item img {
    width: 100%;
    height: 100%;
    object-fit: cover
}
ini 复制代码
HTML部分
<div class="carousel-container">
    <div class="carousel-wrapper">
        <div class="carousel-item">
            <img src="./images/3.png" alt="">
        </div>
        <div class="carousel-item">
            <img src="./images/1.png" alt="">
        </div>
        <div class="carousel-item">
            <img src="./images/2.jpg" alt="">
        </div>
        <div class="carousel-item">
            <img src="./images/3.png" alt="">
        </div>
        <div class="carousel-item">
            <img src="./images/1.png" alt="">
        </div>
    </div>
    <button class="prev-button">Previous</button>
    <button class="next-button">Next</button>
</div>

为了更好地看到效果,这里先把overflow: hidden;注释掉。 结构如图所示

接下来就是点击按钮让图片滚动了

ini 复制代码
js部分
<script>
    // 这里获取到需要操作的dom
    const carouselWrapper = document.querySelector('.carousel-wrapper')
    const carouselItems = document.querySelectorAll('.carousel-item')
    const preButton = document.querySelector('.prev-button');
    const nextButton = document.querySelector('.next-button');
    
    // 一张轮播图的宽度
    const itemWidth = carouselItems[0].clientWidth;
    
    // 标志着当前显示第几张图片
    let curIndex = 0;

    // 初始化轮播图
    function initCarousel() {
        preButton.addEventListener('click', preButtonClick)
        nextButton.addEventListener('click', nextButtonClick)
    }
    
    // 给pre按钮绑定click事件
    function preButtonClick() {
        // 往前一张滚动 自然是减-
        curIndex = curIndex - 1;
        // 过渡样式
        carouselWrapper.style.transition = 'transform 0.3s ease-in-out'
        
        // 当前一张没有图片时,我们直接让他定位到最后一张去,实现无缝轮播效果。
        if (curIndex < 0) {
            curIndex = carouselItems.length - 1;
            // 这里把过渡样式设置为none,原因是滚动到最后一张时,会出现其他图片,设为none,就是一瞬间的事。
            carouselWrapper.style.transition = 'none';
        } 
        // 进行滚动
        showItem(curIndex)
    }
    
    // next按钮其实逻辑上没什么区别
    function nextButtonClick() {
        curIndex = curIndex + 1;
        carouselWrapper.style.transition = 'transform 0.3s ease-in-out'
        
        // 如果是最后一张,就让他滚到第一张去。
        if (curIndex === carouselItems.length) {
            curIndex = 0
            carouselWrapper.style.transition = 'none'
        } 
        showItem(curIndex)
    }


    function showItem(index) {
        carouselWrapper.style.transform = `translateX(-${itemWidth * index}px)`
    }

    initCarousel()
</script>

好 现在看下效果。(gif图看着很卡,不知道什么情况)

效果是实现了,但是大家应该也发现了,正常滚动是有过渡动画的,可第一张点击pre或者是最后一张next到第一张,是瞬间完成的,这是因为我们把transition设置为none的原因。

我觉得不太行,有点生硬,应该是都有过渡效果才对。

代码改造

xml 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
    .carousel-container {
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        border: 10px solid #000;
        /* overflow: hidden; */
    }

    .carousel-wrapper {
        display: flex;
        width: 300px;
        height: 200px;
    }

    .carousel-item {
        flex: 0 0 100%;
        /* 一次显示一个轮播项 */
    }

    .carousel-item img {
        width: 100%;
        height: 100%;
        object-fit: cover
    }
</style>

<body>
    <div class="carousel-container">
        <div class="carousel-wrapper">
            <div class="carousel-item">
                <img src="./images/3.png" alt="">
            </div>
            <div class="carousel-item">
                <img src="./images/1.png" alt="">
            </div>
            <div class="carousel-item">
                <img src="./images/2.jpg" alt="">
            </div>
            <div class="carousel-item">
                <img src="./images/3.png" alt="">
            </div>
            <div class="carousel-item">
                <img src="./images/1.png" alt="">
            </div>
        </div>
        <button class="prev-button">Previous</button>
        <button class="next-button">Next</button>
    </div>
    <script>
        const carouselWrapper = document.querySelector('.carousel-wrapper')
        const carouselItems = document.querySelectorAll('.carousel-item')
        const preButton = document.querySelector('.prev-button');
        const nextButton = document.querySelector('.next-button');

        const itemWidth = carouselItems[0].clientWidth;

        let curIndex = 0;

        // 初始化轮播图
        function initCarousel() {
            preButton.addEventListener('click', preButtonClick)
            nextButton.addEventListener('click', nextButtonClick)
            showItem(++curIndex)
        }
        
        function preButtonClick() {
            curIndex = curIndex - 1;
            carouselWrapper.style.transition = 'transform 0.3s ease-in-out';
            
            if (curIndex === 0) {
                setTimeout(() => {
                    carouselWrapper.replaceChildren(...carouselItems);
                    carouselWrapper.style.transition = 'none';

                    curIndex = carouselItems.length - 2
                    showItem(curIndex)
                }, 300);
            } 
            showItem(curIndex)
        }

        function nextButtonClick() {
            curIndex = curIndex + 1;
            carouselWrapper.style.transition = 'transform 0.3s ease-in-out';
            
            if (curIndex === carouselItems.length - 1) {
                setTimeout(() => {
                    carouselWrapper.replaceChildren(...carouselItems);
                    carouselWrapper.style.transition = 'none';
                    
                    curIndex = 1
                    showItem(curIndex)
                }, 300);
            } 

            showItem(curIndex)
        }


        function showItem(index) {
            carouselWrapper.style.transform = `translateX(-${itemWidth * index}px)`
        }

        initCarousel()
    </script>
</body>
</html>

这里直接给上所有代码,差异如下:

1.首先样式是不用变得

2.先看结构,头和尾分别先放置了尾和头。

3.舒适化轮播图initCarousel,多了一行代码showItem(++curIndex),为的就是像原来一样正常显示第一张(因为我们在头部加了一张尾部图片嘛,所以 +1 )

4.在按钮监听事件,我挑一个说(preButtonClick),点击后上一张是尾图时进行判断(可以理解为进来直接点pre按钮,这里主要做了三件事

复制代码
1.替换页面元素, 这里建议自己画下图好理解
2.清除过渡效果
3.移动轮播图位置

这三件事,用户是感知不到的,下面看下最终效果

这动图好卡啊 - -!

ok现在已经都有过渡效果了。

相关推荐
codingandsleeping4 小时前
重读《你不知道的JavaScript》(上)- 作用域和闭包
前端·javascript
前端风云志6 小时前
TypeScript实用类型之Omit
javascript
烛阴6 小时前
Puppeteer入门指南:掌控浏览器,开启自动化新时代
前端·javascript
芝士加8 小时前
Playwright vs MidScene:自动化工具“双雄”谁更适合你?
前端·javascript
Carlos_sam9 小时前
OpenLayers:封装一个自定义罗盘控件
前端·javascript
前端南玖9 小时前
深入Vue3响应式:手写实现reactive与ref
前端·javascript·vue.js
Yueyanc10 小时前
LobeHub桌面应用的IPC通信方案解析
前端·javascript
麦当_11 小时前
基于 Shadcn 的可配置表单解决方案
前端·javascript·面试
Cutey91612 小时前
使用Canvas实现实时视频处理:从黑白滤镜到高级特效
前端·javascript
前端大卫12 小时前
前端调试太痛苦?这 6 个技巧直接解决 90% 问题!
前端·javascript