我们来证明一下这个公式是正确的,首先如果我们遇到 current = 0, 那么 0 这个位置的图片的上一张就会获得 -1 这个指针,这个时候我们用 ( − 1 + 4 ) / 4 = 3 / 4 (-1 + 4) / 4 = 3 / 4 (−1+4)/4=3/4,这里 3 除以 4 的余数就是 3,而 3 刚好就是这个数组的最后一个图片。
然后我们来试试,如果当前图片就是数组里面的最后一张图,在我们的例子里面就是 3,3 + 1 = 4, 这个时候通过转换 ( 4 + 4 ) / 4 (4 + 4) / 4 (4+4)/4 余数就是 0,显然我们获得的数字就是数组的第一个图片的位置。
通过这个公式我们就可以取得上一张和下一张图片在数组里面的指针位置,这个时候我们就可以用这个指针获取到他们在节点中的对象,使用 CSSDOM 来改变他们的属性这里我们需要先把所有元素移动到当前图片的位置,然后根据 -1、0、1 这三个偏移的值对这个图片进行往左或者往右移动,最后我们要需要加上当前鼠标的拖动距离
我们已经把整个逻辑给整理了一遍,下来我们看看 mousemove 这个事件回调函数代码的应该怎么写:
let move = event => { let x = event.clientX - startX; let current = position - Math.round(x / 500); for (let offset of [-1, 0, 1]) { let pos = current + offset; // 计算图片所在 index pos = (pos + children.length) % children.length; console.log('pos', pos); children[pos].style.transition = 'none'; children[pos].style.transform = `translateX(${-pos * 500 + offset * 500 + (x % 500)}px)`; } };
讲了那么多东西,代码就那么几行,确实代码简单不等于它背后的逻辑就简单。所以写代码的程序员也可以是深不可测的。
最后还有一个小问题,在我们拖拽的时候,我们会发现上一张图和下一张有一个奇怪跳动的现象。
这个问题是我们的 Math.round(x / 500) 所导致的,因为我们在 transform 的时候,加入了 x % 500, 而在我们的 current 值的计算中没有包含这一部分的计算,所以在鼠标拖动的时候就会缺少这部分的偏移度。
我们只需要把这里的 Math.round(x / 500) 改为 (x - x % 500) / 500 即可达到同样的取整数的效果,同时还可以保留我们 x 原有的正负值。
这里其实还有比较多的问题的,我们还没有去改 mouseup 事件里面的逻辑。那么接下来我们就来看看 up 中的逻辑我们应该怎么去实现。
这里我们需要改的就是 children 中 for 循环的代码,我们要实现的是让我们拖动图片超过一定的位置就会自动轮播到对应方向的下一张图片。up 这里的逻辑其实是和 move 是基本一样的,不过这里有几个地方需要更改的:
首先我们的 transition 禁止是可以去掉了,改为 ' ' 空在 transform 中的 + x % 500 就不需要了,因为这里图片是我们鼠标松开的时候,不需要图片再跟随我们鼠标的位置了在计算 pos = current + offset的这里,我们在 up 的回调中是没有 current 的,所以我们需要把 current 改为 position因为有一个 z-index 的层次关系,我们会看到有图片在被挪动位置的时候,它在我们当前图片上飞过,但是飞过去的元素其实是我们不需要的元素,而这个飞过去的元素是来源于我们之前用的 [-1, 0, 1] 这里面的 -1 和 1 的两个元素,所以在 up 这个逻辑里面我们要把不需要的给去掉。意思就是说,如果我们鼠标是往左移动的,那么我们只需要 -1 的元素,相反就是只需要 1 的元素,另外的那边的元素就可以去掉了。首先 for of 循环是没有顺序要求的,所以我们可以把 -1 和 1 这两个数字用一个公式来代替,放在我们 0 的后面。但是怎么才能找到我们需要的是哪一边呢?其实我们需要计算的就是图片在移动的方向,所以我们要改动的就是 position = position - Math.round(x / 500) 这行代码,这个方向可以通过 Math.round(x / 500) - x 获得。而这个值就是相对当前元素的中间,他是更偏向左边(负数)还是右边(正数),其实这个数字是多少并不是最重要的,我们要的是它的符号也就是 -1 还是 1,所以这里我们就可以使用 - Math.sign(Math.round(x / 500) - x) 来取得结果中的符号,这个函数最终返回要不就是 -1, 要不就是 1 了, 正好是我们想要的。其实还有一个小 bug,当我们拖动当前图片过短的时候,图片位置的计算是不正确的。