以下是 Kevin Foley 的客座文章。Kevin 是 Squarespace 的开发者,除了其他事情外,他还利用 开发者平台 做着很酷的事情。他最近一直在做一个可滑动的图片库,他同意在这里分享他的一些工作成果!
几周前,Chris 发布了一个创建 带有滑动背景图片的滑块 的教程。几乎在同一时间,我正在做一些新的可滑动的画廊,所以 Chris 建议我写一篇关于如何在滑块中添加滑动支持的教程。它就在这里!

创建可滑动的画廊时,有两个技术(我知道的)可以选择。您可以动画化滚动位置,或者使用 `translate` 移动元素。两者各有优缺点。
使用 Translate
用 `translate` 移动滑块可以让你获得硬件加速和亚像素动画的优势。但是,在最初的触控事件中,你可能会注意到一个小的延迟(只有几十毫秒),然后滑块才会开始移动。这一点没有得到很好的记录,我只是在我的经验中注意到了它。
使用 Overflow Scroll
Overflow Scroll 对初始触控非常敏感,因为它属于浏览器的原生功能。你不需要等待 JavaScript 中的事件监听器。但是你失去了使用 translate 移动元素的所有平滑度。
在本教程中,我们将使用 `translate`,因为我认为它看起来更好。
HTML
本例中的 HTML 将与 Chris 的原始示例有所不同。我们不会将图片设置为背景图片,而是将其设置为一个元素。这样就可以移动图片,从而获得使用 translate 而不是动画化背景位置的酷炫平移效果。
<div class="slider-wrap">
<div class="slider" id="slider">
<div class="holder">
<div class="slide-wrapper">
<div class="slide"><img class="slide-image" src="//farm8.staticflickr.com/7347/8731666710_34d07e709e_z.jpg" /></div>
74
</div>
<div class="slide-wrapper">
<div class="slide"><img class="slide-image" src="//farm8.staticflickr.com/7384/8730654121_05bca33388_z.jpg" /></div>
64
</div>
<div class="slide-wrapper">
<div class="slide"><img class="slide-image" src="//farm8.staticflickr.com/7382/8732044638_9337082fc6_z.jpg" /></div>
82
</div>
</div>
</div>
</div>
CSS
在很大程度上,CSS 与 Chris 的 CSS 相同,因此我不会重复如何设置布局。但有一些关键的差异。
我们不仅要添加 overflow scroll,还要对幻灯片进行动画化。因此,我们将使用一个类来设置过渡,并在需要时用 JavaScript 添加它。
.animate { transition: transform 0.3s ease-out; }
IE 10 处理触控事件的方式与 Chrome 和 Safari 等移动 Webkit 浏览器不同。我们将通过 JavaScript 处理 Webkit 触控事件,但在 IE 10 中,我们可以只用 CSS 创建整个滑块(几乎)。
.ms-touch.slider {
overflow-x: scroll;
overflow-y: hidden;
-ms-overflow-style: none;
/* Hides the scrollbar. */
-ms-scroll-chaining: none;
/* Prevents Metro from swiping to the next tab or app. */
-ms-scroll-snap-type: mandatory;
/* Forces a snap scroll behavior on your images. */
-ms-scroll-snap-points-x: snapInterval(0%, 100%);
/* Defines the y and x intervals to snap to when scrolling. */
}
由于这些属性对大多数人来说可能很陌生(对我来说也是如此),因此我将逐一讲解每个属性及其功能。
-ms-scroll-chaining
Surface 平板电脑在您在页面上滑动时会切换浏览器标签页,这使得所有滑动事件对开发者来说毫无用处。幸运的是,通过将任何给定元素的 scroll chaining 设置为 none,可以很容易地覆盖这种行为。
-ms-scroll-snap-type
当设置为 mandatory 时,此属性会覆盖浏览器的默认滚动行为,并强制可滚动元素捕捉到某个间隔。
-ms-scroll-snap-points-x
此属性设置可滚动元素捕捉到的间隔。它接受两个数字:第一个数字是起始点;第二个数字是捕捉间隔。在本例中,每个幻灯片都是父元素的整个宽度,这意味着间隔应该是 100%(即元素会捕捉到 100%、200%、300% 等)。
-ms-overflow-style
此属性允许您在将其设置为 none 时隐藏滚动条。
JavaScript
我们在 JavaScript 中要做的第一件事是检测我们正在使用哪种触控设备。IE 10 使用指针事件,而 Webkit 则使用“touchstart”、“touchmove”和“touchend”。由于 IE 10 滑块(几乎)完全用 CSS 实现,因此我们需要检测它并向包装器添加一个类。
if (navigator.msMaxTouchPoints) {
$('#slider').addClass('ms-touch');
}
很简单。如果您现在测试滑块,它将是一个可用的可滑动幻灯片。但是我们还需要在图片上添加平移效果。
if (navigator.msMaxTouchPoints) {
$('#slider').addClass('ms-touch');
// Listed for the scroll event and move the image with translate.
$('#slider').on('scroll', function() {
$('.slide-image').css('transform','translate3d(-' + (100-$(this).scrollLeft()/6) + 'px,0,0)');
});
}
这就是 IE 10 的全部内容。
现在来看看 Webkit 的方法。所有这些都将包装在 `else` 语句中。首先,我们只需要定义几个变量。
else {
var slider = {
// The elements.
el: {
slider: $("#slider"),
holder: $(".holder"),
imgSlide: $(".slide-image")
},
// The stuff that makes the slider work.
slideWidth: $('#slider').width(), // Calculate the slider width.
// Define these as global variables so we can use them across the entire script.
touchstartx: undefined,
touchmovex: undefined,
movex: undefined,
index: 0,
longTouch: undefined,
// etc
然后我们需要初始化函数并定义事件。
// continued
init: function() {
this.bindUIEvents();
},
bindUIEvents: function() {
this.el.holder.on("touchstart", function(event) {
slider.start(event);
});
this.el.holder.on("touchmove", function(event) {
slider.move(event);
});
this.el.holder.on("touchend", function(event) {
slider.end(event);
});
},
现在来看看真正能让东西在您滑动滑块时发生变化的有趣部分。
Touchstart
在 iPhone(以及大多数其他触控滑块)上,如果您缓慢移动滑块,只有一点点,它会弹回原来的位置。但如果您快速移动,它会递增到下一张幻灯片。这种快速移动称为弹动。没有原生方法来测试弹动,所以我们必须使用一个小技巧。在 touchstart 上,我们初始化一个 setTimeout 函数,并在一定时间后设置一个变量。
this.longTouch = false;
setTimeout(function() {
// Since the root of setTimout is window we can’t reference this. That’s why this variable says window.slider in front of it.
window.slider.longTouch = true;
}, 250);
我们还需要获取触控的原始位置,以便我们的动画起作用。如果您以前从未做过,它有点奇怪。JavaScript 允许您定义多点触控事件,因此您可以传递触控事件一个表示您要监听的手指数量的数字。在本例中,我实际上只关心一个手指/拇指,因此在下面的代码示例中,这就是 [0] 的作用。
// Get the original touch position.
this.touchstartx = event.originalEvent.touches[0].pageX;
在我们开始移动滑块之前,我将删除 animate 类。我知道现在元素上没有 animate 类,但我们需要它用于后续的幻灯片。我们将在 touchend 上将它重新添加到元素中。
$('.animate').removeClass('animate');
Touchmove
touchmove 事件的行为类似于 JavaScript 中的 scroll 事件。也就是说,如果您在 scroll 上做了一些事情,那么在 scroll 发生时它会执行很多次。因此,我们将不断获取 touchmove 位置,直到手指/拇指移动。
// Continuously return touch position.
this.touchmovex = event.originalEvent.touches[0].pageX;
然后,我们将使用我们在上一次事件中获得的 touchstart 位置和 touchmove 位置进行快速计算,以确定如何平移幻灯片。
// Calculate distance to translate holder.
this.movex = this.index*this.slideWidth + (this.touchstartx - this.touchmovex);
然后,我们需要平移图片,就像 Chris 在原始示例中所做的那样。我们将使用他使用的相同神奇数字。
// Defines the speed the images should move at.
var panx = 100-this.movex/6;
现在,我们需要添加一些逻辑来处理边缘情况。如果您在第一张幻灯片或最后一张幻灯片上,如果您向错误的方向滚动(即没有内容的方向),则此逻辑将停止图片平移。这可能不是处理这种情况的最佳方法,但它目前对我有用。
if (this.movex < 600) { // Makes the holder stop moving when there is no more content.
this.el.holder.css('transform','translate3d(-' + this.movex + 'px,0,0)');
}
if (panx < 100) { // Corrects an edge-case problem where the background image moves without the container moving.
this.el.imgSlide.css('transform','translate3d(-' + panx + 'px,0,0)');
}
Touchend
在 touchend 事件中,我们需要弄清楚用户移动滑块的距离、速度以及此操作是否应该递增到下一张幻灯片。
首先,我们需要确定滑动的确切距离。我们将计算移动距离的绝对值,以查看用户是否进行了滑动操作。
// Calculate the distance swiped.
var absMove = Math.abs(this.index*this.slideWidth - this.movex);
现在,我们将确定滑块是否应该递增。本例中所有其他计算都基于索引变量,因此这是脚本背后的真正逻辑。它检查用户是否滑动了足够距离以递增滑块,或者移动是否是轻弹。如果满足其中任一条件,则滑动方向如何。
// Calculate the index. All other calculations are based on the index.
if (absMove > this.slideWidth/2 || this.longTouch === false) {
if (this.movex > this.index*this.slideWidth && this.index < 2) {
this.index++;
} else if (this.movex < this.index*this.slideWidth && this.index > 0) {
this.index--;
}
}
现在,我们添加动画类并设置新的平移位置。
// Move and animate the elements.
this.el.holder.addClass('animate').css('transform', 'translate3d(-' + this.index*this.slideWidth + 'px,0,0)');
this.el.imgSlide.addClass('animate').css('transform', 'translate3d(-' + 100-this.index*50 + 'px,0,0)');
结论
所以,是的,为 IE 10 欢呼吧。
但说真的,考虑到这是移动操作系统上的一个常见习语,创建可滑动的画廊有点麻烦。最终结果不会像原生滑动那样好。但会很接近。
完整的演示在这里
Check out this Pen!
但是你最好在你的触控设备上查看 这里。
我在我的 Nexus 7 平板电脑上尝试过,滑块根本不起作用。我知道我的手指很大,但这似乎出了问题……
我实际上没有在任何 Android 设备上测试过这个例子。我将尝试找到一个 Nexus 7,看看为什么它不起作用。
这是一张它在 Kindle Fire 上运行的照片:http://cl.ly/PdBK 它有点“卡住”,在每次滑动移动中都会一直卡住,但它确实改变了幻灯片并动画化了过渡。
我正在尝试预订我的 Nexus 7 平板电脑进行检查。但是它无法启动 当然。天哪,我讨厌它。但我会在 72 小时后检查,那时它有足够的电量启动。
它在 iOS 上的 Chrome 中也运行良好 - 但这不是一个很好的测试,因为它与 iOS 上的 Safari 相同(大部分情况下,我认为)。
我最好的猜测是因为我使用的是 3d 变换,它只在最新的 Android 浏览器中有效。2d 变换会得到更好的支持,但不会那么流畅。
https://caniuse.cn/transforms2d
https://caniuse.cn/transforms3d
在使用最新 Chrome 浏览器的 Nexus One 上,它无法按预期工作。如果我点击它,幻灯片会移动,我也可以拉动大约 15% 的下一张幻灯片,然后点击它才能获得该幻灯片。
在同一手机上使用 Firefox 运行得更接近,在这种情况下,只有三分之一的图像显示,然后当您按下它时,整个图像会显示出来,您可以漂亮地滑动它,松开手指,三分之二的图像会消失
同一手机上的 Dolphin 浏览器运行完美……
在我的 iPad3 上使用 Chome 运行完美 - 很棒的滑块!
希望你能为 Android 调试它
编辑 - Nexus 4 ...... 这周太长了。;-O
如何更改显示的图像数量?我有 10 张图像,但只显示 3 张。请帮忙!
喜欢它!做得太棒了 Kevin,在我的 iphone 上运行良好。
在移动版 Chrome(Samsung Galaxy S4)上无法运行。我只能够“滚动”几像素到左边,就到头了。
我在我的 GS4 的 Chrome 上也遇到了这个问题。
是的,我的 Galaxy S3 上的 Chrome 也出现了完全相同的问题。
我在 Samsung Galaxy SII 的 Chrome 上也是这样。
我也是。它在使用 4.1 的 Note Android 上的最新 Chrome 中根本无法运行。
我找到了这个用于这类事情的随机插件,它非常适合我们的需求并且超级流畅:http://uraniumjs.com/widgets/carousel.html
在 Google nexus 4 上几乎无法运行。当幻灯片确实滑动时,它看起来真的很好。
在 iphone 上运行得很好。必须在其他设备/浏览器上尝试一下 :-). 我对这个滑块的唯一问题是它没有水平锁定。我使用的大多数触控友好滑块都存在这个问题
很棒的文章!我在 Android 平板电脑上试过,它只在轻弹情况下运行 - 缓慢 -。另一方面,它在 iOS 上运行良好。
我认为 Android 上的多点触控事件只是速度慢:我曾在 一段时间前在 StackOverflow 上提出过这个问题,并由于性能问题而决定转为原生开发。
很酷的例子。
在 Galaxy S3(Android 4.1.2)上使用 Google Chrome,我最初的本能是滑动图像,但不起作用。点击图像后,滑动效果很棒。
你看到“停顿”或“卡住”的原因是因为你正在从非 3D/非加速 DIV 过渡到 3D/加速 DIV。这迫使浏览器分配一个新的位图,并将 DIV 重新渲染到其中,然后对它进行动画处理。
要消除这种停顿,在获取 touchstart 之前设置 transform: translate3D(0,0,0);。然后,复制将在正常页面渲染期间完成。瞧,很流畅。如果您担心浏览器会优化 0,0,0 版本,也可以使用 translate3D(0,0,1px)。
很好的建议。我会试试的。谢谢,Matt。
我分叉了它,并尝试了与之相关的许多事情,比如在 CSS 中添加 translate3d 内容(带有或不带有最后一个 1px),以及在事件之前在 JS 中添加它,以及其他事情,但没有成功(在我的 Nexus 7 上,我最终不得不拆开它并重新连接电池)。我想知道你是否可以分叉它,让 Matt(或任何其他人)让它工作。
jquery 怎么样?
无意垃圾邮件,只是想再次确认上述观察结果。我在 Android 4 上的 Firefox 和 Chrome 中进行了测试,它出现了上述问题。
@Kevin 如果您无法让它在 Android 上正常工作,请考虑在文章开头添加一个免责声明部分,说明它为什么在 Android 上无法正常工作。
Codepen 在我的 N4 上运行得不好
这个特定的那个..
在我的 Windows Phone 7.8 (HTC HD7) 上,它甚至没有显示第一张完整的图片。滚动根本不起作用……什么都没有……抱歉。猜想是这个糟糕的 IE 浏览器 :D
您使用的是哪个版本?IE 9 还是 IE 10?
WP 7.8 安装了 IE 9。指针事件直到 IE 10 才引入,而 IE 10 需要您的手机上安装 WP 8。
这在我的 Nexus 4(使用 Chrome)和我的 HTC X8 上的 Windows Phone 8 上都不起作用。
尝试了原生 Android 浏览器和 HTC One X 上的 Chrome,效果不好,右侧显示了另一张图片的提示,当我触摸时会出现大约 2 毫米。仅此而已。
三星 Galaxy S 2 的原生浏览器也是一样。
我将对其进行分叉,看看出了什么问题。
有趣的是,可以肯定的是。在 iphone4/ios 6.1.3 上有点卡顿,图像+1 显示在右边缘,
以及用两只手指缩放会触发滚动到堆栈的末尾。我们只能怪 6.1.3。:-)