yaoningvital / blog

my blog
31 stars 4 forks source link

用scrollLeft实现水平滑动 #148

Open yaoningvital opened 5 years ago

yaoningvital commented 5 years ago

1、情景描述

最近在做的项目中有一个需求,就是有一些标签,它们水平排列,点击每一个标签,要让它滑动到合适的位置,尽量让每一次点击的标签能显示在中间的位置。如下图1所示: image

当点击“青年top10”时,要让它滑动到中间的位置来,如下图2所示: image

2、问题分析

如果把一个一个的标签定义为一个个 span 标签,包含它们的标签为class为'channel-slider'的 div,而包含在这个div外面的是class为'channel'的div,它们的html结构为:

<div class="channel">
        <div class="channel-slider">
          <span ng-repeat="item in main.bottomProductChannelData"
                ng-click="main.selectChannel(item.id)"
          >{{item.title}}</span>
        </div>
</div>

要想实现需求的效果,其实就是:

  1. 外面的 channel 盒子的宽度为屏幕可视宽度;
  2. 里面的 channel-slider 盒子包含所有的span,它的宽度可以非常宽,它可以左右滑动,超出父元素 channel 的部分会隐藏。

如下图3所示: (图3) image

上图3是 channel-slider 往右可以滑动的最远距离。 下图4是 channel-slider 往左可以滑到的最远距离: (图4) image

3、解决方案

怎么样能控制 channel-slider 实现左右的滑动呢?可以利用 channel 元素的 scrollLeft 属性。

3.1 第一步,设置 .channel 元素的css属性如下:

.channel 元素有一个固定宽度(一般为屏幕的可视宽度),这里假设为300px:

.channel{
  width:300px;
  overflow-x: scroll;
}

注意:这里最为重要的一点就是,设置overflow-x: scroll; 或者是 overflow-x: auto;

3.2 第二步,设置 .channel-slider 宽度

这里假设设它的宽度为800px:

.channel-slider{
  width: 800px;
}

到这一步,.channel 元素的 scrollLeft 属性的值就是 .channel-slider 元素向左边滚动的距离,如下图5所示: (图5) image

原生的dom元素有一个scrollLeft属性。这个属性是可读并且可写的。 当读取 scrollLeft 属性时:

let channel=document.getElementsByClassName('channel')[0]
channel.scrollLeft

它表示它内部的.channel-slider 元素向左边滑动的距离。

同时这个属性又可以设置,当设置它为某一个值时,它内部的元素将滑动到特定的位置。比如说:

let channel=document.getElementsByClassName('channel')[0]
channel.scrollLeft = 100 

那么 channel 内部的 .channel-slider 将向左移动100px。

如果说在上面的例子中,.channel-slider 最多能向左移动500px,但是设置它要向左移动600px,即:

let channel=document.getElementsByClassName('channel')[0]
channel.scrollLeft = 600 

那么,.channel-slider 将会移动到自己最多能移动到的位置,即这里的500px的位置。

同时,jquery也提供了一个scrollLeft()的方法,能实现原生的dom元素一样的功能。

$('.channel').scrollLeft()

上面的代码将读取.channel里面的滑动元素 .channel-slider 向左滑动的距离。

$('.channel').scrollLeft(100)

上面的代码将让 .channel 内部的 .channel-slider 元素向左滑动100px。

3.3 设置 .channel 的 scrollLeft 属性的值

了解了上面的东西后,要实现需求,其实就是每点击一个span,就要让.channel-slider 相应地移动一定的距离。那么要移动多少距离呢?

假设我们设当前被点击的span的中心位置距离它的父元素 .channel-slider 最左边的距离为 origin,设视框 .channel 的中心位置距离它本身的最左边的距离为 aim,因为我们想将被点击的span元素尽量移到视框 .channel 的中间位置,所以,需要移动的距离就是 origin - aim,如下图6所示: image

即:

let channel = document.getElementsByClassName('channel')[0]
channel.scrollLeft(origin - aim)

如果用jquery实现:

$('.channel').scrollLeft(origin - aim)

上面的代码可以将元素直接滑动到合适的位置,没有动画效果。 注意,如果 origin - aim的值小于0怎么办?我们知道scrollLeft 的值大于0时,slider 向左移动响应的距离,如果scrollLeft 的值小于0时,slider不会移动。

3.4 利用jquery 的 animate() 方法实现移动的动画效果

$('.channel').animate({
  scrollLeft: origin - aim
},300)

以上,可以实现移动时的动画效果。