vue中通过JavaScript实现web端鼠标横向滑动触控板滑动效果-demo

news2024/9/28 19:21:22

JavaScript实现web端鼠标横向滑动&触控板滑动效果 

支持鼠标拖动滑动&触控板滑动效果

web端实现滑动,就是对鼠标按下、鼠标松开、鼠标移动事件进行监听

效果图 

 代码

结构代码

<template>
  <div class="swiper">
    <div class="container" ref="container">
      <!-- 在这里添加要滑动的横向元素 -->
      <div class="box" v-for="num in 40" :key="num">
        <img src="@/assets/logo.png" style="width: 58px; height: 58px" />
        <span>滑动元素{{ num }}</span>
      </div>
    </div>
  </div>
</template>

样式代码

<style lang="scss" scoped>
.swiper {
  .container {
    width: 90%;
    margin: 0 auto;
    display: flex;
    /* white-space: nowrap; //避免子元素在父元素内换行 */
    overflow-x: auto; //显示横向滚动条
    /* scroll-snap-type: x mandatory; //自动吸附 */
    /* scroll-behavior: smooth; //平滑滚动的效果 */
    .box {
      width: 100px;
      min-height: 100px;
      padding: 0 20px;
      /* scroll-snap-align: start; //自动吸附 */
    }
  }
  ::-webkit-scrollbar {
    width: 0 !important;
  }
  ::-webkit-scrollbar {
    width: 0 !important;
    height: 0;
  }
}
</style>

业务逻辑代码

<script setup>
import { ref, reactive, onMounted } from 'vue';
const container = ref(null);
// console.log(container);

const control = reactive({
  isDown: false, // 是否按下鼠标
  startX: 0, // 鼠标起始位置
  scrollLeft: 0 // 滚动条位置
});

onMounted(() => {
  console.log('dom', container.value);
  // 总结web端实现滑动,就是对鼠标按下、鼠标松开、鼠标移动事件进行监听
  container.value.addEventListener('mousedown', (e) => {
    control.isDown = true;
    control.startX = e.pageX - container.value.offsetLeft;
    control.scrollLeft = container.value.scrollLeft;
  });

  container.value.addEventListener('mouseup', (e) => {
    control.isDown = false;

    // // 找到最接近的滑动元素
    // const children = container.value.getElementsByClassName('box');
    // let closestElement;
    // let closestDistance = Infinity;
    // const containerCenterX = container.value.offsetWidth / 2;

    // for (let i = 0; i < children.length; i++) {
    //   const box = children[i];
    //   const boxCenterX = box.offsetLeft + box.offsetWidth / 2;
    //   const distance = Math.abs(boxCenterX - containerCenterX);

    //   if (distance < closestDistance) {
    //     closestDistance = distance;
    //     closestElement = box;
    //   }
    // }

    // // 计算滚动到最接近的滑动元素的位置
    // const scrollLeft =
    //   closestElement.offsetLeft -
    //   container.value.offsetWidth / 2 +
    //   closestElement.offsetWidth / 2;
    // container.value.scrollTo({ left: scrollLeft, behavior: 'smooth' });

    // 找到最接近的滑动元素
    // const children = container.value.getElementsByClassName('box');
    // let closestElement;
    // let closestDistance = Infinity;
    // const containerCenterX = container.value.offsetWidth / 2;

    // for (let i = 0; i < children.length; i++) {
    //   const box = children[i];
    //   const boxCenterX = box.offsetLeft + box.offsetWidth / 2;
    //   const distance = Math.abs(boxCenterX - containerCenterX);

    //   if (distance < closestDistance) {
    //     closestDistance = distance;
    //     closestElement = box;
    //   }
    // }

    // // 计算滚动到最接近的滑动元素的位置
    // // const scrollLeft = closestElement.offsetLeft - container.value.offsetLeft;
    // const scrollLeft =
    //   closestElement.offsetLeft -
    //   container.value.offsetLeft -
    //   closestElement.offsetWidth;
    // container.value.scrollTo({ left: scrollLeft, behavior: 'smooth' });
  });

  container.value.addEventListener('mousemove', (e) => {
    if (!control.isDown) return;
    e.preventDefault();
    const x = e.pageX - container.value.offsetLeft;
    const walk = (x - control.startX) * 2; // 滑动距离
    container.value.scrollLeft = control.scrollLeft - walk;
  });

  // 元素的吸附效果
  //   container.value.addEventListener('scroll', () => {
  //     // 找到最接近的滑动元素
  //     const children = container.value.getElementsByClassName('box');
  //     let closestElement;
  //     let closestDistance = Infinity;
  //     const containerCenterX = container.value.offsetWidth / 2;

  //     for (let i = 0; i < children.length; i++) {
  //       const box = children[i];
  //       const boxCenterX = box.offsetLeft + box.offsetWidth / 2;
  //       const distance = Math.abs(boxCenterX - containerCenterX);

  //       if (distance < closestDistance) {
  //         closestDistance = distance;
  //         closestElement = box;
  //       }
  //     }

  //     // 计算滚动到最接近的滑动元素的位置
  //     const scrollLeft = closestElement.offsetLeft - container.value.offsetLeft;
  //     container.value.scrollLeft = scrollLeft;
  //   });
});
</script>

在Vue中实现鼠标横向滑动&触控板滑动效果可以通过以下步骤实现:

  1. 首先在Vue中创建一个父组件,在该组件中引入子组件或者使用slot插入内容。

  2. 在父组件中创建一个div容器,用来包裹滑动内容。

  3. 在该div容器中绑定一个事件监听器,用来监听用户的鼠标或触摸板滑动事件。

  4. 在事件监听器中使用JavaScript获取鼠标或触摸板的滑动距离,并根据该距离计算出需要滑动的内容距离。

  5. 使用Vue的ref属性获取div容器,然后通过Vue的$refs属性访问该容器,并修改其left属性值,实现滑动效果。

下面是一个简单的示例代码:

<template>
  <div class="scroll-container" @mousemove="handleMouseMove" 
       @touchmove="handleTouchMove">
     <div class="scroll-content" ref="content">
       <slot></slot>
     </div>
  </div>
</template>

<script>
export default {
  methods: {
    handleMouseMove(event) {
      const scrollContent = this.$refs.content;
      const scrollSpeed = 5; // 滑动速度
      scrollContent.style.left = `${-event.pageX / scrollSpeed}px`;
    },
    handleTouchMove(event) {
      const scrollContent = this.$refs.content;
      const scrollSpeed = 5; // 滑动速度
      scrollContent.style.left = `${-event.touches[0].pageX / scrollSpeed}px`;
    },
  },
};
</script>

<style>
.scroll-container {
  width: 100%;
  height: 200px;
  overflow-x: scroll;
}

.scroll-content {
  position: relative;
  width: 2000px;
  height: 200px;
  white-space: nowrap;
  font-size: 0;
}
</style>

在上述示例代码中,我们创建了一个scroll-container容器并绑定了鼠标和触摸板的滑动事件监听器,然后使用JavaScript计算出需要滑动的距离,并修改了scroll-content容器的left属性值实现了滑动效果。需要注意的是,由于父组件的高度限制了滑动内容的高度,因此我们在scroll-content中使用了white-space: nowrap和font-size: 0两个CSS样式属性,防止滑动内容换行和出现空白间隙。


在Vue中实现横向滑动效果可以通过以下步骤实现:

  1. 在Vue组件中定义一个容器div,并设置其样式为横向滚动条,例如:
<div class="scroll-container" ref="scrollContainer">
   <div class="scroll-content">
      <!-- 滚动内容 -->
   </div>
</div>

<style>
   .scroll-container {
      overflow-x: auto;
      white-space: nowrap;
      -webkit-overflow-scrolling: touch; // 兼容iOS平滑滚动
   }

   .scroll-content {
      display: inline-block;
   }
</style>

在组件的created生命周期中,注册滑动事件监听器,并记录开始滑动时的鼠标位置或触摸位置:

created() {
   this.$refs.scrollContainer.addEventListener('mousedown', this.handleMouseDown);
   this.$refs.scrollContainer.addEventListener('touchstart', this.handleTouchStart, { passive: true });
},

methods: {
   handleMouseDown(event) {
      this.startX = event.clientX;
   },

   handleTouchStart(event) {
      this.startX = event.touches[0].clientX;
      this.touching = true;
   }
}

在组件的mounted生命周期中,注册滑动事件监听器,并根据滑动方向和滑动距离计算出滚动距离,并通过JavaScript控制滚动条的位置:

mounted() {
   this.$refs.scrollContainer.addEventListener('mousemove', this.handleMouseMove);
   this.$refs.scrollContainer.addEventListener('touchmove', this.handleTouchMove, { passive: false });
   this.$refs.scrollContainer.addEventListener('mouseup', this.handleMouseUp);
   this.$refs.scrollContainer.addEventListener('touchend', this.handleTouchEnd);
},

methods: {
   handleMouseMove(event) {
      if (this.startX) {
         const distance = event.clientX - this.startX;
         this.$refs.scrollContainer.scrollLeft -= distance;
         this.startX = event.clientX;
      }
   },

   handleTouchMove(event) {
      if (this.startX) {
         const distance = event.touches[0].clientX - this.startX;
         this.$refs.scrollContainer.scrollLeft -= distance;
         this.startX = event.touches[0].clientX;
      }
   },

   handleMouseUp() {
      this.startX = null;
   },

   handleTouchEnd() {
      this.startX = null;
   }
}

通过以上步骤,就可以在Vue中实现横向滑动效果,并且支持鼠标和触摸操作。如果需要支持惯性滑动效果,可以在滑动结束时根据滑动速度和滑动方向计算出滚动距离,并通过requestAnimationFrame动画函数实现平滑滚动效果。


可以通过以下步骤实现该功能:

绑定事件监听器,在vue组件的mounted生命周期函数或者created生命周期函数中,通过addEventListener方法绑定document的mousemove事件和touchmove事件。

mounted() {
  document.addEventListener('mousemove', this.handleMouseMove);
  document.addEventListener('touchmove', this.handleMouseMove);
},
methods: {
  handleMouseMove(event) {
    // 处理鼠标或者触摸板的移动事件
  }
}

在处理移动事件的函数中,根据元素的滑动距离和滑动方向,通过style属性的left或者transform属性改变元素的位置。其中,可以通过computed属性或者ref获取需要滑动的元素的位置和宽度。

handleMouseMove(event) {
  const delta = event.clientX - this.lastX;
  const direction = delta > 0 ? -1 : 1;
  const absDelta = Math.abs(delta);
  const containerLeft = this.$refs.container.offsetLeft;
  const containerWidth = this.$refs.container.offsetWidth;
  const contentWidth = this.$refs.content.offsetWidth;
  const maxDelta = contentWidth - containerWidth - containerLeft;
  const currentDelta = containerLeft + delta;

  if (absDelta > 5 && (currentDelta >= 0 || currentDelta <= maxDelta)) {
    // 通过transform改变滑动距离,可以有更好的性能表现
    this.$refs.container.style.transform = `translateX(${currentDelta}px)`;
    this.lastX = event.clientX;
  }
}

在滑动停止时,判断靠近开始的元素,通过计算元素到左端的距离来确定元素的位置,通过定位或者transform改变元素的位置。

handleMouseMove(event) {
  // ...
  this.timer = setTimeout(() => {
    const containerLeft = this.$refs.container.offsetLeft;
    const containerWidth = this.$refs.container.offsetWidth;
    const contentWidth = this.$refs.content.offsetWidth;
    const maxDelta = contentWidth - containerWidth - containerLeft;

    if (containerLeft < 0) {
      this.$refs.container.style.transform = `translateX(0)`;
      return;
    }

    if (maxDelta < 0) {
      this.$refs.container.style.transform = `translateX(${-maxDelta}px)`;
      return;
    }

    const items = this.$refs.content.children;
    let minDelta = Infinity;
    let index = 0;

    for (let i = 0; i < items.length; i++) {
      const itemLeft = items[i].offsetLeft;
      const delta = Math.abs(itemLeft - containerLeft);
      if (delta < minDelta) {
        minDelta = delta;
        index = i;
      }
    }

    const itemLeft = items[index].offsetLeft;
    const delta = itemLeft - containerLeft;
    this.$refs.container.style.transform = `translateX(${-delta}px)`;
  }, 100);
}

注意在组件销毁时,需要清除事件监听器和定时器。

beforeDestroy() {
  document.removeEventListener('mousemove', this.handleMouseMove);
  document.removeEventListener('touchmove', this.handleMouseMove);
  clearTimeout(this.timer);
}

完整的vue组件代码如下:

<template>
  <div class="container" ref="container">
    <div class="content" ref="content">
      <div class="item" v-for="(item, index) in items" :key="index">{{item}}</div>
    </div>
  </div>
</template>

<script>
export default {
  name: "HorizontalScroll",
  data() {
    return {
      items: ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7", "Item 8", "Item 9", "Item 10"],
      lastX: 0,
      timer: null,
    };
  },
  mounted() {
    document.addEventListener('mousemove', this.handleMouseMove);
    document.addEventListener('touchmove', this.handleMouseMove);
  },
  beforeDestroy() {
    document.removeEventListener('mousemove', this.handleMouseMove);
    document.removeEventListener('touchmove', this.handleMouseMove);
    clearTimeout(this.timer);
  },
  methods: {
    handleMouseMove(event) {
      const delta = event.clientX - this.lastX;
      const direction = delta > 0 ? -1 : 1;
      const absDelta = Math.abs(delta);
      const containerLeft = this.$refs.container.offsetLeft;
      const containerWidth = this.$refs.container.offsetWidth;
      const contentWidth = this.$refs.content.offsetWidth;
      const maxDelta = contentWidth - containerWidth - containerLeft;
      const currentDelta = containerLeft + delta;

      if (absDelta > 5 && (currentDelta >= 0 || currentDelta <= maxDelta)) {
        this.$refs.container.style.transform = `translateX(${currentDelta}px)`;
        this.lastX = event.clientX;
      }

      clearTimeout(this.timer);
      this.timer = setTimeout(() => {
        const containerLeft = this.$refs.container.offsetLeft;
        const containerWidth = this.$refs.container.offsetWidth;
        const contentWidth = this.$refs.content.offsetWidth;
        const maxDelta = contentWidth - containerWidth - containerLeft;

        if (containerLeft < 0) {
          this.$refs.container.style.transform = `translateX(0)`;
          return;
        }

        if (maxDelta < 0) {
          this.$refs.container.style.transform = `translateX(${-maxDelta}px)`;
          return;
        }

        const items = this.$refs.content.children;
        let minDelta = Infinity;
        let index = 0;

        for (let i = 0; i < items.length; i++) {
          const itemLeft = items[i].offsetLeft;
          const delta = Math.abs(itemLeft - containerLeft);
          if (delta < minDelta) {
            minDelta = delta;
            index = i;
          }
        }

        const itemLeft = items[index].offsetLeft;
        const delta = itemLeft - containerLeft;
        this.$refs.container.style.transform = `translateX(${-delta}px)`;
      }, 100);
    },
  },
};
</script>

<style scoped>
.container {
  position: relative;
  width: 100%;
  height: 80px;
  overflow: hidden;
  margin: 0 auto;
  padding: 0 20px;
}

.content {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  white-space: nowrap;
}

.item {
  display: inline-block;
  height: 100%;
  line-height: 80px;
  padding: 0 10px;
  font-size: 16px;
  color: #333;
  background-color: #ddd;
  border-radius: 5px;
  margin-right: 10px;
}
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

财报解读:谷歌成功绝地反击?厮杀尚未真正开始!

在经历了一轮激烈的攻防战之后&#xff0c;谷歌、微软同一天发布了财报&#xff0c;从数据来看&#xff0c;谷歌成功抵御了微软携OpenAI发起的挑战&#xff0c;业绩表现全面超预期&#xff0c;而微软的业绩虽然整体也超预期&#xff0c;但相比其四面出击的高调则黯淡了许多。 巨…

GoogleLeNet V2 V3 —— Batch Normalization

文章目录 Batch Normalizationinternal covariate shift激活层的作用BN执行的位置数据白化网络中的BN层训练过程 BN的实验效果MNIST与GoogleLeNet V1比较 GoogleLeNet出来之后&#xff0c;Google在这个基础上又演进了几个版本&#xff0c;一般来说是说有4个版本&#xff0c;之前…

12-1_Qt 5.9 C++开发指南_自定义插件和库-自定义Widget组件(提升法(promotion)创建自定义定制化组件)

当UI设计器提供的界面组件不满足实际设计需求时&#xff0c;可以从 QWidget 继承自定义界面组件。 有两种方法使用自定义界面组件&#xff1a; 一种是提升法(promotion)&#xff0c;例如在8.3 节将一个QGraphicsView组件提升为自定义的 QWGraphicsView 类&#xff0c;提升法用…

python将UTC +8 时间 转换为 UTC 时间

因为在工作的时候&#xff0c;有时候经常使用 UTC 时间&#xff0c;因为北京时间是 UTC 8&#xff0c;有时候要自己换算一下&#xff0c;或者 时间戳转换的时候有问题&#xff0c;所以就写了这个。 import time from datetime import datetime import pytz# 输入时间字符串 # …

LLM当前状态和潜在影响;谷歌Brain2Music读取大脑活动生成音乐

&#x1f989; AI新闻 &#x1f680; 谷歌Brain2Music利用AI读取大脑活动生成音乐 摘要&#xff1a;谷歌发布了名为Brain2Music的论文&#xff0c;通过人工智能和脑部成像技术生成个性化音乐。他们招募了5名志愿者&#xff0c;记录他们在听不同音乐类型时的大脑活动数据。通过…

刷完阿里 P8 面试官推荐的 Java 高并发核心编程文档后终拿蚂蚁 offer

前言 学完阿里 P8 面试官推荐的 Java 高并发核心编程文档后终于拿到了蚂蚁 p6 的 offer&#xff0c;这份文档包含的内容有点多。 主要包含的内容&#xff1a;Java NIO、Reactor 模式、高性能通信框架 Netty、分布式锁、分布式 ID、分布式缓存、高并发架构、多线程、线程池、内…

C语言IO篇(一) 输出百分号

1.百分号输出问题是什么&#xff1f; C语言中无法直接打印单个的%。 2.怎么解决百分号输出问题&#xff1f; 在C语言中&#xff0c;如何输出百分号呢&#xff1f; 1.在printf中用2个连续 %% 输出百分号。 2.将内容写入到字符串后打印 3.为什么出现百分号输出问题&#xff1f; …

install wxwidgets and wxPython on Linux

安装wxwidgets https://wiki.wxwidgets.org/Compiling_and_getting_startedhttps://wiki.wxwidgets.org/Compiling_and_getting_started 安装wxPython pip install wxPython 安装wxformbuilderhttps://github.com/wxFormBuilder/wxFormBuilder/releaseshttps://github.com/wx…

通达信赫尔均线 (HMA) 指标公式设置及原理详解

我们知道传统的均线存在短周期均线&#xff08;如5日均线&#xff09;灵敏但不够平滑&#xff0c;大周期均线&#xff08;如120日均线&#xff09;平滑但反应滞后、无法及时反映当前行情变化的缺点。&#xff08;如下图&#xff09;赫尔均线 (HMA) 正是为了解决这样的问题&…

AtcoderABC229场

A - First GridA - First Grid 题目大意 要求判断是否可以从每个黑色方块到达其他所有黑色方块&#xff0c;只能经过黑色方块&#xff0c;并且黑色方块之间必须相连&#xff08;共享一条边&#xff09;。 思路分析 据题意&#xff0c;不能的只有以下两种情况 .# #. #. .#…

交互式AI技术与模型部署:bert-base-chinese模型交互式问答界面设置

使用Gradio实现Question Answering交互式问答界面&#xff0c;首先你需要有一个已经训练好的Question Answering模型&#xff0c;这里你提到要使用bert-base-chinese模型。 Gradio支持PyTorch和TensorFlow模型&#xff0c;所以你需要将bert-base-chinese模型转换成PyTorch或Te…

双击start.bat文件闪退,运行报错“unable to access jarfile”

问题&#xff1a;电脑运行“start.bat”文件&#xff0c;无反应&#xff0c;闪退&#xff0c;管理员身份运行报错“unable to access jarfile” 解决思路&#xff1a; 1、由于该项目运行需要jdk环境&#xff0c;检查jdk版本需要是1.8.0_251版本 通过在 cmd 命令行输入java -v…

unittest 数据驱动DDT应用

前言 一般进行接口测试时&#xff0c;每个接口的传参都不止一种情况&#xff0c;一般会考虑正向、逆向等多种组合。所以在测试一个接口时通常会编写多条case&#xff0c;而这些case除了传参不同外&#xff0c;其实并没什么区别。 这个时候就可以利用ddt来管理测试数据&#xf…

智慧城市环境污染数据采集远程监控方案4G工业路由器应用

随着科技水平的发展和人民生活水平的提高&#xff0c;城市环境污染问题日渐严峻&#xff0c;尤其是在发展迅速的国家&#xff0c;环境污染问题便更为突出。许多发达国家将重污染工厂搬到发展中国家&#xff0c;这导致发展中国家的环境污染日益严重。严重的环境污染也带来了一系…

青海师范大学迎来首个虚拟IP“卓玛”,这是要怎么赋能数字教育?

虚拟数字人作为虚拟世界和现实世界之间的链接 是人们最先了解元宇宙概念的一个表达形式 相较于传统的高校吉祥物IP 在数字教育时代 爆火的虚拟数字人 会给高校带来怎样的新体验、新机遇 虚拟IP对高校的必要性 新潮化、高互动化赋能数字教育 1. 以可视化虚拟IP&#xff0c…

vue计时器

//将秒转化为时分秒 const resultTime ref();const formateSeconds function (endTime) {let secondTime parseInt(endTime); //将传入的秒的值转化为Numberlet min 0; // 初始化分let h 0; // 初始化小时// let result "";if (secondTime > 60) {//如果秒数…

Qtday4作业

思维导图 2.手动完成服务器的实现 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpServer> // 服务器类 #include<QTcpSocket> // 客户端类 #include<QDebug> // 信息调试类 #include<QMessageBox> …

Chapter 8: Files | Python for Everybody 讲义笔记_En

文章目录 Python for Everybody课程简介FilesPersistenceOpening filesText files and linesReading filesSearching through a fileLetting the user choose the file nameUsing try, except, and openWriting filesDebuggingGlossary Python for Everybody Exploring Data Us…

利用vscode--sftp,将本地项目/文件上传到远程服务器中详细教程

1、首先在 vscode 中下载 sftp&#xff1a; 2、然后在 vscode 中打开本地将要上传的项目或文件&#xff1a; 3、安装完后&#xff0c;使用快捷键 ctrlshiftP 打开指令窗口&#xff0c;输入 sftp:config &#xff0c;回车&#xff0c;在当前目录中会自动生成 .vscode 文件夹及 s…

如何拥有一个自己的小程序商城?

在今天的移动互联网时代&#xff0c;拥有一个自己的小程序商城已经成为了很多企业和个人的追求。它不仅可以帮助企业提升品牌形象和销售额&#xff0c;还能够提供更好的用户体验和更高的用户粘性。那么&#xff0c;如何拥有自己的小程序商城呢&#xff1f; 第一步&#xff1a;选…