JavaScript练手小技巧:利用鼠标滚轮控制图片轮播

近日,在浏览网站的时候,发现了一个有意思的效果:一个图片轮播,通过上下滚动鼠标滚轮控制图片的上下切换。

于是就有了自己做一个的想法,顺带复习下鼠标滚轮事件。

鼠标滚轮事件,参考这篇文章:鼠标滚轮事件-CSDN博客

一、HTML和CSS

无论怎么样的滚动,首先要制作图片轮播的结构和样式。

HTML:

<div class="box" id="box">
    <ul class="list" id="list">
        <li><img src="../images/pic1.jpg" alt=""></li>
        <li><img src="../images/pic2.jpg" alt=""></li>
        <li><img src="../images/pic3.jpg" alt=""></li>
        <li><img src="../images/pic4.jpg" alt=""></li>
        <li><img src="../images/pic5.jpg" alt=""></li>
    </ul>   
    <div class="dots" id="dots"></div>
</div>

HTML结构很简单,就是一个 div 里面套了两个结构:图片区和控制点区。

  • 图片区 ul#list,就是一个 ul,里面有多个 li 嵌套了图片。
  • 控制点区 div#dots 没有内容,这是因为控制点要根据图片的数量(ul 的 li 个数)动态生成。

CSS:

*{
    margin: 0;
    padding: 0;
}
ul,li,ol{
    list-style: none;
}
.box{
    width: 600px;
    height: 399px;
    border:20px #000 solid;
    margin-left: auto;
    margin-right: auto;
    overflow: hidden;
    position: relative;
    margin-top: 100px;
}
.list{
    position: relative;
}
.list img{
    display: block;
}
.list li{
    width: 600px;
    height: 399px;
    overflow: hidden;
}
.box .dots{
    position: absolute;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    background: rgba(255,255,255,0.5);
    padding: 5px;
    border-radius: 30px;
}
.box .dots span{
    display: block;
    width: 15px;
    height: 15px;
    background: #fff;
    border-radius: 50%;
    margin-left: 5px;
    margin-right: 5px;
    cursor: pointer;
}
.box .dots span.active{
    background: #f30;
}

CSS代码如上,就不分析了,反正就这样。强调两点:

  • 整个  div.box 要相对定位,约束绝对定位的控制点区 div#dots
  • ul.list 也要相对定位,因为要复制第一个图,放到最后;复制最后一个图,放到第一图的前面。复制出来的图都是绝对定位的。

二、JS

1. 为了防止重复执行滚动事件,写一个 flag,当为 true 的时候,就不执行滚动事件。默认值为 false。

let isScroll = false;

2. 根据图片的个数动态生成点(span)。为了防止过度操作 DOM,使用了  createDocumentFragment 缓存生成的span 标签。

let li = list.querySelectorAll("li");
let li_len = li.length;
let index = 0;
let wrap = document.createDocumentFragment();
// 初始化工作
for(let i = 0; i < li_len; i++){
    let span = document.createElement("span");
    span.dataset.index = i;
    if(i == 0){
        span.classList.add("active");
    }
    wrap.appendChild(span);
    span.addEventListener("click",function(){
        index = this.dataset.index;
        changePic(index);
    } );
}
dots.appendChild(wrap);

3. 上下移动图片,就是在控制图片的 transform 的 translateY 属性。

// 图片切换函数:index++
function nextPic(){
    index++;
    if( index >= li_len ){
        index = 0;
        list.style.transform = "translateY(399px)"; // 调整list位置
        document.body.offsetHeight; // 强制重绘HTML
    }
    changePic(index);
}

// 图片切换函数:index--
function prevPic(){
    index--;
    if( index < 0 ){
        index = li_len - 1;
        list.style.transform = "translateY(" + (-399*li_len) + "px)";
        document.body.offsetHeight; // 强制重绘HTML
    }
    changePic(index);
}

 4. 切换图片,就是在换图,已经更改控制点的样式。

// 切换图片
function changePic(index){
    // 点的切换
    let dotsBros = findeBros(dots_span[index]);
    dots_span[index].classList.add("active");
    dotsBros.forEach(function(item){
        item.classList.remove("active");
    });
    
    // 图片切换
    isScroll = true;
    list.style.transform = "translateY(" + (-index * 399) + "px)";
    list.style.transition = "transform 0.5s";
    document.body.offsetHeight; // 强制重绘HTML
}

5. 当动画结束的时候,就要恢复 flag 变量的值为 flase,并且去掉图片的过渡动画。

// 动画结束时,恢复初始状态
list.addEventListener("transitionend",function(){
            isScroll = false;
            list.style.transition = "none";
 });

6. 滚动事件,判断滚轮值的正负,选择上还是下滚动图片。

// 鼠标滚轮事件
box.addEventListener("wheel",function(e){
    e.preventDefault();
    let delta = e.deltaY;
    if(delta > 0 && isScroll == false ){
        nextPic();
    }else if(delta < 0 && isScroll == false){
        prevPic();
    }
},{
    passive: false
});

 完整JS代码:

let box = document.getElementById("box");
let list = document.getElementById("list");
let dots = document.getElementById("dots");
let isScroll = false;

let li = list.querySelectorAll("li");
let li_len = li.length;
let index = 0;
let wrap = document.createDocumentFragment();
// 初始化工作
for(let i = 0; i < li_len; i++){
    let span = document.createElement("span");
    span.dataset.index = i;
    if(i == 0){
        span.classList.add("active");
    }
    wrap.appendChild(span);
    span.addEventListener("click",function(){
        index = this.dataset.index;
        changePic(index);
    } );
}
dots.appendChild(wrap);
let dots_span = dots.children;
// 初始图片
let liFirst = li[0];
let liLast = li[li_len - 1];
let newLiFirst = liFirst.cloneNode(true);
let newLiLast = liLast.cloneNode(true);
list.appendChild(newLiFirst);
list.appendChild(newLiLast);
newLiLast.style.position = "absolute";
newLiLast.style.top = "-399px";
newLiLast.style.left = "0";
// 工具函数:获取兄弟节点
function findeBros(tag){
    let bros = [];
    let parent = tag.parentNode;
    for(let i = 0; i < parent.children.length; i++){
        if(parent.children[i] !== tag){
            bros.push(parent.children[i]);
        }
    }
    return bros;
}
// 图片切换函数:index++
function nextPic(){
    index++;
    if( index >= li_len ){
        index = 0;
        list.style.transform = "translateY(399px)"; // 调整list位置
        document.body.offsetHeight; // 强制重绘HTML
    }
    changePic(index);
}

    // 图片切换函数:index--
    function prevPic(){
    index--;
    if( index < 0 ){
        index = li_len - 1;
        list.style.transform = "translateY(" + (-399*li_len) + "px)";
        document.body.offsetHeight; // 强制重绘HTML
    }
    changePic(index);
}

// 切换图片
function changePic(index){
    // 点的切换
    let dotsBros = findeBros(dots_span[index]);
    dots_span[index].classList.add("active");
    dotsBros.forEach(function(item){
        item.classList.remove("active");
    });
    
    // 图片切换
    isScroll = true;
    list.style.transform = "translateY(" + (-index * 399) + "px)";
    list.style.transition = "transform 0.5s";
    document.body.offsetHeight; // 强制重绘HTML
}
// 动画结束时,恢复初始状态
list.addEventListener("transitionend",function(){
    isScroll = false;
    list.style.transition = "none";
});

// 鼠标滚轮事件
box.addEventListener("wheel",function(e){
    e.preventDefault();
    let delta = e.deltaY;
    if(delta > 0 && isScroll == false ){
        nextPic();
    }else if(delta < 0 && isScroll == false){
        prevPic();
    }
},{
    passive: false
});

                                                                                                                                                                                                                                                                                                                                                                                                                                                                            

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/874058.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【人工智能学习笔记】4_2 深度学习基础之多层感知机

感知机概述 感知机是人工智能最早的模型,是一种有监督的算法,本质上是一个二分类问题,是神经网络和支持向量机的基础缺点:感知机智能解决单纯的线性问题 感知机的过程 多层感知机的层级结构 多层感知机的层级结构主要包括输入层、隐藏层和输出层、可以用于拟合非线性函数。…

初中生台灯哪个牌子的质量好?五款真的有护眼效果的台灯

现在市面上形形色色的打着“护眼”口号的台灯太多了&#xff0c;因为眼睛对于我们来说很重要&#xff0c;我们看到美丽的事物都因为有他&#xff0c;所以大家一听到护眼就会选择购买。初中生台灯哪个牌子的质量好&#xff1f;很多商家为了赚钱&#xff0c;随便贴个标签就说护眼…

【Mysql】系统服务启动访问报错问题处理:this is incompatible with sql_mode=only_full_group_by

一、背景&#xff1a; 本来已经正常运行的平台&#xff0c;突然有一天由于对服务器进行部分操作迁移&#xff0c;发现jar可以正常启动&#xff0c;但是访问功能一直报错&#xff0c;监控后台日志后&#xff0c;发现了问题&#xff1a; 报错的具体信息如下&#xff1a; Caused…

安全产品概述

防火墙 防火墙的核心功能是过滤掉有害的流量&#xff0c;在专用网络和公共网络之间建立保护屏障。防火墙过滤通常基于一系列规则&#xff0c;如 IP 地址、域名、协议、端口号、关键字等&#xff0c;对入站和出站的流量进行过滤。这些规则也称为访问控制列表&#xff08;ACCESS…

JSON格式

JSON&#xff08;JavaScript Object Notation, JS对象简谱&#xff09;是一种轻量级的数据交换格式。JSON 就是一种字符串格式,这种格式无论是在前端还是在后端,都可以很容易的转换成对象,所以常用于前后端数据传递 说明&#xff1a; JSON的语法 ​ var obj“{‘属性名’:‘…

Vue3.0项目实战(三)——大事件管理系统首页 layout 架子与文章分类的实现

目录 1. 首页 layout 架子 [element-plus 菜单] 1.1 基本架子拆解 2. 登录访问拦截 2.1 需求 2.2 vue3 和 vue2 中的 Vue-Router 区别 3. 用户基本信息获取&渲染 4. 退出功能 [element-plus 确认框] 5. 文章分类页面 - [element-plus 表格] 5.1 基本架子 - PageCo…

Tomcat Request Cookie 丢失问题

优质博文&#xff1a;IT-BLOG-CN 一、问题描述 生产环境偶尔(涉及到多线程处理)出现"前端传递Cookie为空"的告警&#xff0c;导致前端请求丢失&#xff0c;出现请求失败问题。告警内容如下 前端传递Cookie为空 告警内容&#xff1a;服务端获取request Cookie为空&…

大屏地图区域显示、复选框多选打点,自定义窗体信息(vue3+TS)

效果图&#xff1a; NPM 安装 Loader&#xff1a; npm i amap/amap-jsapi-loader --save 并设置 key 和安全密钥&#xff1a; import AMapLoader from amap/amap-jsapi-loader;//引入高德地图window._AMapSecurityConfig {securityJsCode: "「你申请的安全密钥」"…

[数据集][目标检测]百事可乐可口可乐瓶子检测数据集VOC+YOLO格式195张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;195 标注数量(xml文件个数)&#xff1a;195 标注数量(txt文件个数)&#xff1a;195 标注类别…

前端登录鉴权——以若依Ruoyi前后端分离项目为例解读

权限模型 Ruoyi框架学习——权限管理_若依框架权限-CSDN博客 用户-角色-菜单&#xff08;User-Role-Menu&#xff09;模型是一种常用于权限管理的设计模式&#xff0c;用于实现系统中的用户权限控制。该模型主要包含以下几个要素&#xff1a; 用户&#xff08;User&#xff09;…

【VSCode v1.93.0】手动配置远程remote-ssh

开发环境 VS Code版本&#xff1a;1.93.0 (Windows) Ubuntu版本&#xff1a;20.04 使用VS Code 插件remote-ssh远程访问Ubuntu服务器中的代码&#xff0c;若Ubuntu无法联网&#xff0c;在连接的时候会报错&#xff1a; Could not establish connection to "xxxx": F…

店匠科技携手Stripe共谋电商支付新篇章

在全球电商行业蓬勃发展的背景下,支付环节作为交易闭环的核心,其重要性日益凸显。随着消费者对支付体验要求的不断提高,以及跨境电商的迅猛发展,支付市场正经历着前所未有的变革与挑战。在这一充满机遇与竞争的领域,店匠科技(Shoplazza)凭借其创新的嵌入式支付解决方案—— Sho…

【Obsidian】同步插件 Remotely Save 的应用介绍

背景 引言&#xff0c;大多数的笔记软件数据都存储在云端&#xff0c;这样如果是没网络&#xff0c;或者笔记服务器崩溃故障&#xff0c;那么你就无法访问自己的笔记了&#xff0c;前段时间的语雀系统崩溃&#xff0c;不知道你有没有了解&#xff1f; 所以笔记在自己手里&…

第四届“长城杯”网络安全大赛 暨京津冀网络安全技能竞赛(初赛) 全方向 题解WriteUp

战队名称&#xff1a;TeamGipsy 战队排名&#xff1a;18 SQLUP 题目描述&#xff1a;a website developed by a novice developer. 开题&#xff0c;是个登录界面。 账号admin&#xff0c;随便什么密码都能登录 点击头像可以进行文件上传 先简单上传个木马试试 测一下&…

ComfyUI-图片表情控制工作流整合包,相关软件包及工作流均已打包好,一键启动即可

前言 本期给大家带来基于LivePortrait的ComfyUI图片表情控制工作流&#xff0c;还可以通过控制表情变化及参考视频生成动态肖像。 此工作流操作简单&#xff0c;相关ComfyUI软件包、模型、节点、工作流均已打包在内&#xff0c;一键启动即可。 # 配置要求&#xff1a; 建议电…

基于 TiDB 资源管控 + TiCDC 实现多业务融合容灾测试

导读 随着金融行业的不断发展&#xff0c;多个业务系统的整合成为了趋势&#xff0c;分布式数据库的应用也愈发广泛。为了应对多业务融合带来的复杂性&#xff0c;金融机构需要在保障各业务系统高效运行的同时&#xff0c;确保 IT 系统的高可用性和稳定性。本文将介绍 TiDB 如…

媒体服务器软件BUG说明及改进方案

媒体服务器软件BUG说明及改进方案 一、BUG描述二、问题分析三、改进方案四、实施计划五、预期效果六、总结一、BUG描述 在当前版本的媒体服务器中,存在一个关于静音媒体流处理的问题。具体表现为:当主叫连续发送静音帧到媒体服务器时,媒体服务器并未将这些静音帧转发给被叫…

2. Python之注释及缩进以及print和input函数使用

一. Python代码中的注释及缩进 Python中注释有单行注释&#xff0c;多行注释&#xff0c;声明注释 单行注释使用符号#&#xff0c;从#开始&#xff0c;后面到本行末尾都是注释内容。多行注释可以使用’‘’ ‘’三个引号&#xff0c;三个引号内的内容都是注释声明注释&#x…

相机内存卡格式化了照片怎么恢复?格式化恢复详解

摄影爱好者们都知道&#xff0c;相机内存卡是记录我们美好瞬间的重要媒介。然而&#xff0c;在使用过程中&#xff0c;有时我们会因操作不当或设备故障&#xff0c;不小心格式化了内存卡&#xff0c;从而导致珍贵的照片丢失。面对这种情况&#xff0c;我们该如何恢复这些被格式…

深入FastAPI:掌握使用多个关联模型的高级用法[Union类型]

在FastAPI中&#xff0c;响应模型可以声明为Union类型&#xff0c;这允许你为同一个端点定义多种可能的响应模型。这种灵活性使得API可以根据不同的情况返回不同类型的数据结构。 例如&#xff0c;根据请求中的查询参数或数据库中的数据&#xff0c;一个API端点可能有时返回一…