我曾经认为实现滑动手势非常困难,但最近我发现自己需要这样做,并发现现实并没有我想象的那么糟糕。
本文将逐步引导您完成实现过程,并使用我所能想到的最少代码量。所以,让我们直接开始吧!
HTML 结构
我们首先使用一个 .container
,其中包含许多图像
<div class='container'>
<img src='img1.jpg' alt='image description'/>
...
</div>
基本样式
我们使用 display: flex
确保图像彼此并排,且之间没有空格。align-items: center
将它们垂直居中对齐。我们使图像和容器都占用容器父元素(在本例中为 body
)的 width
。
.container {
display: flex;
align-items: center;
width: 100%;
img {
min-width: 100%; /* needed so Firefox doesn't make img shrink to fit */
width: 100%; /* can't take this out either as it breaks Chrome */
}
}
.container
及其子图像具有相同的 width
,这使得这些图像溢出到右侧(如红色轮廓所示),从而创建了一个水平滚动条,但这正是我们想要的。

鉴于并非所有图像都具有相同的尺寸和纵横比,因此某些图像的上方和下方会有一些空白区域。因此,我们将通过为 .container
设置一个明确的 height
来修剪这些空白区域,该高度应该适用于这些图像的平均纵横比,并将 overflow-y
设置为 hidden
。
.container {
/* same as before */
overflow-y: hidden;
height: 50vw;
max-height: 100vh;
}
结果如下所示,所有图像都被修剪到相同的 height
,并且不再有空隙。

.container
上的 overflow-y
修剪图像后的结果(参见 在线演示)。好的,但现在 .container
本身有一个水平滚动条。嗯,对于无 JavaScript 的情况来说,这实际上是一件好事。
否则,我们创建一个 CSS 变量 --n
来表示图像的数量,并使用它使 .container
足够宽,以容纳所有其图像子元素,这些子元素仍与父元素(在本例中为 body
)具有相同的宽度。
.container {
--n: 1;
width: 100%;
width: calc(var(--n)*100%);
img {
min-width: 100%;
width: 100%;
width: calc(100%/var(--n));
}
}
请注意,我们将之前的 width
声明保留为回退。在从 JavaScript 获取 .container
及其包含的子图像数量后设置 --n
之前,calc()
值不会有任何变化。
const _C = document.querySelector('.container'),
N = _C.children.length;
_C.style.setProperty('--n', N)
现在,我们的 .container
已扩展以适应所有图像。
切换图像
接下来,我们通过在容器的父元素(在本例中为 body
)上设置 overflow-x: hidden
来去除水平滚动条,并创建另一个 CSS 变量来保存当前选定图像的索引(--i
)。我们使用它通过平移来正确地定位 .container
相对于视口的位置(请记住,translate()
函数中的 %
值相对于我们设置此 transform
的元素的尺寸)。
body { overflow-x: hidden }
.container {
/* same styles as before */
transform: translate(calc(var(--i, 0)/var(--n)*-100%));
}
将 --i
更改为另一个大于或等于零但小于 --n
的整数值,会将另一张图像带入视野,如下面的交互式演示所示(其中 --i
的值由范围输入控制)。
查看 thebabydino 在 CodePen 上的 Pen(@thebabydino)。
好的,但我们不想使用滑块来执行此操作。
基本思想是我们将在 "touchstart"
(或 "mousedown"
)事件和 "touchend"
(或 "mouseup"
)事件之间检测运动方向,然后相应地更新 --i
以移动容器,以便所需方向上的下一张图像(如果有)移动到视口。
function lock(e) {};
function move(e) {};
_C.addEventListener('mousedown', lock, false);
_C.addEventListener('touchstart', lock, false);
_C.addEventListener('mouseup', move, false);
_C.addEventListener('touchend', move, false);
请注意,如果我们在图像上设置 pointer-events: none
,则此方法仅适用于鼠标。
.container {
/* same styles as before */
img {
/* same styles as before */
pointer-events: none;
}
}
此外,Edge 需要从 about:flags 中启用触摸事件,因为此选项默认情况下是关闭的。

在填充 lock()
和 move()
函数之前,我们将触摸和点击情况统一。
function unify(e) { return e.changedTouches ? e.changedTouches[0] : e };
在 "touchstart"
(或 "mousedown"
)上锁定意味着获取并将 x 坐标存储到初始坐标变量 x0
中。
let x0 = null;
function lock(e) { x0 = unify(e).clientX };
为了查看如何移动 .container
(或者是否需要移动,因为当我们到达末尾时不想进一步移动),我们检查是否已执行 lock()
操作,如果已执行,则读取当前 x 坐标,计算它与 x0
之间的差值,并根据其符号和当前索引确定要执行的操作。
let i = 0;
function move(e) {
if(x0 || x0 === 0) {
let dx = unify(e).clientX - x0, s = Math.sign(dx);
if((i > 0 || s < 0) && (i < N - 1 || s > 0))
_C.style.setProperty('--i', i -= s);
x0 = null
}
};
向左/向右拖动的结果如下所示。

以上是预期结果,也是我们在 Chrome 中进行少量拖动以及 Firefox 中获得的结果。但是,当我们向左或向右拖动时,Edge 会向前或向后导航,这与 Chrome 在拖动更多时也会发生。

为了覆盖这一点,我们需要添加一个 "touchmove"
事件监听器。
_C.addEventListener('touchmove', e => {e.preventDefault()}, false)
好的,我们现在有了 在所有浏览器中都能正常工作的内容,但它看起来还不是我们真正想要的…… 还没有!
流畅的运动
实现我们想要的效果最简单的方法是添加一个 transition
。
.container {
/* same styles as before */
transition: transform .5s ease-out;
}
就是这样,一个非常基本的滑动效果,大约 25 行 JavaScript 和 25 行 CSS 代码。
遗憾的是,Edge 中存在一个 bug,导致任何对依赖于 CSS 变量的 calc()
平移的 transition
都失败。唉,我想我们现在必须忘记 Edge 了。
完善整个过程
在所有炫酷的滑动效果中,我们目前所拥有的效果还不太够,所以让我们看看可以进行哪些改进。
拖动时的更好视觉提示
首先,在我们拖动期间没有任何事情发生,所有操作都遵循 "touchend"
(或 "mouseup"
)事件。因此,在我们拖动时,我们无法了解接下来会发生什么。在所需方向上是否有下一张图像可以切换?或者我们是否已到达终点,并且不会发生任何事情?
为了解决这个问题,我们通过添加一个最初为 0px
的 CSS 变量 --tx
来稍微调整平移量。
transform: translate(calc(var(--i, 0)/var(--n)*-100% + var(--tx, 0px)))
我们使用另外两个事件监听器:一个用于 "touchmove"
,另一个用于 "mousemove"
。请注意,我们已经使用 "touchmove"
监听器阻止了 Chrome 中的向前和向后导航。
function drag(e) { e.preventDefault() };
_C.addEventListener('mousemove', drag, false);
_C.addEventListener('touchmove', drag, false);
现在让我们填充 drag()
函数!如果我们已执行 lock()
操作,则读取当前 x 坐标,计算此坐标与初始坐标 x0
之间的差值 dx
,并将 --tx
设置为此值(这是一个像素值)。
function drag(e) {
e.preventDefault();
if(x0 || x0 === 0)
_C.style.setProperty('--tx', `${Math.round(unify(e).clientX - x0)}px`)
};
我们还需要确保在最后将 --tx
重置为 0px
,并在拖动期间删除 transition
。为了使这更容易,我们将 transition
声明移动到一个 .smooth
类上。
.smooth { transition: transform .5s ease-out; }
在 lock()
函数中,我们从 .container
中删除此类(我们将在最后在 "touchend"
和 "mouseup"
上再次添加它),并设置一个 locked
布尔变量,以便我们不必继续执行 x0 || x0 === 0
检查。然后我们使用 locked
变量进行检查。
let locked = false;
function lock(e) {
x0 = unify(e).clientX;
_C.classList.toggle('smooth', !(locked = true))
};
function drag(e) {
e.preventDefault();
if(locked) { /* same as before */ }
};
function move(e) {
if(locked) {
let dx = unify(e).clientX - x0, s = Math.sign(dx);
if((i > 0 || s < 0) && (i < N - 1 || s > 0))
_C.style.setProperty('--i', i -= s);
_C.style.setProperty('--tx', '0px');
_C.classList.toggle('smooth', !(locked = false));
x0 = null
}
};
结果如下所示。在我们仍在拖动时,我们现在可以看到接下来会发生什么的视觉指示。
transition-duration
修复此时,无论在拖动后图像的width
还剩多少需要平移,我们始终使用相同的transition-duration
。我们可以通过引入一个因子f
以相当直接的方式解决这个问题,我们也将其设置为 CSS 变量,以帮助我们计算实际的动画持续时间。
.smooth { transition: transform calc(var(--f, 1)*.5s) ease-out; }
在 JavaScript 中,我们获取图像的width
(在"resize"
事件中更新),并计算我们水平拖动了该宽度的几分之几。
let w;
function size() { w = window.innerWidth };
function move(e) {
if(locked) {
let dx = unify(e).clientX - x0, s = Math.sign(dx),
f = +(s*dx/w).toFixed(2);
if((i > 0 || s < 0) && (i < N - 1 || s > 0)) {
_C.style.setProperty('--i', i -= s);
f = 1 - f
}
_C.style.setProperty('--tx', '0px');
_C.style.setProperty('--f', f);
_C.classList.toggle('smooth', !(locked = false));
x0 = null
}
};
size();
addEventListener('resize', size, false);
现在,这给了我们一个更好的结果。
如果拖动不足则返回
假设我们不想在只稍微拖动了一点点(低于某个阈值)的情况下就切换到下一张图像。因为现在,拖动过程中的1px
差异意味着我们将切换到下一张图像,这感觉有点不自然。
为了解决这个问题,我们将阈值设置为例如图像width
的20%
。
function move(e) {
if(locked) {
let dx = unify(e).clientX - x0, s = Math.sign(dx),
f = +(s*dx/w).toFixed(2);
if((i > 0 || s < 0) && (i < N - 1 || s > 0) && f > .2) {
/* same as before */
}
/* same as before */
}
};
结果可以在下面看到。
也许添加弹跳?
我不确定这是否是一个好主意,但我还是忍不住想尝试一下:更改时间函数,以便引入弹跳。在 cubic-bezier.com 上稍微拖动了一下控制点后,我想出了一个看起来很有希望的结果
![Animated gif. Shows the graphical representation of the cubic Bézier curve, with start point at (0, 0), end point at (1, 1) and control points at (1, 1.59) and (.61, .74), the progression on the [0, 1] interval being a function of time in the [0, 1] interval. Also illustrates how the transition function given by this cubic Bézier curve looks when applied on a translation compared to a plain ease-out.](https://i0.wp.com/css-tricks.com/wp-content/uploads/2018/03/bounce_css.gif?ssl=1)
ease-out
相比。transition: transform calc(var(--f)*.5s) cubic-bezier(1, 1.59, .61, .74);
那么 JavaScript 方法怎么样?
我们可以通过采用 JavaScript 方法进行转换来更好地控制更自然和更复杂的弹跳。这也能让我们支持 Edge 浏览器。
我们首先删除transition
以及--tx
和--f
CSS 变量。这将我们的transform
还原到最初的样子。
transform: translate(calc(var(--i, 0)/var(--n)*-100%));
上面的代码也意味着--i
不一定再是整数了。虽然在我们有一张图像完全进入视野时它仍然是整数,但在我们拖动或触发"touchend"
或"mouseup"
事件后的运动过程中则不再是整数了。

--i
为0
。当第二张图像完全进入视野时,--i
为1
。当我们处于第一张和第二张图像之间时,--i
为.5
。当我们看到第一张图像的四分之一和第二张图像的四分之三时,--i
为.75
。然后我们更新 JavaScript 代码,以替换我们更新这些 CSS 变量的部分。首先,我们处理lock()
函数,其中我们放弃了切换.smooth
类,以及drag()
函数,其中我们用更新--i
替换了更新我们已删除的--tx
变量,如前所述,--i
不再需要是整数。
function lock(e) {
x0 = unify(e).clientX;
locked = true
};
function drag(e) {
e.preventDefault();
if(locked) {
let dx = unify(e).clientX - x0,
f = +(dx/w).toFixed(2);
_C.style.setProperty('--i', i - f)
}
};
在我们更新move()
函数之前,我们引入两个新变量ini
和fin
。它们分别表示我们在动画开始时设置的--i
的初始值,以及我们在动画结束时设置的相同变量的最终值。我们还创建了一个动画函数ani()
。
let ini, fin;
function ani() {};
function move(e) {
if(locked) {
let dx = unify(e).clientX - x0,
s = Math.sign(dx),
f = +(s*dx/w).toFixed(2);
ini = i - s*f;
if((i > 0 || s < 0) && (i < N - 1 || s > 0) && f > .2) {
i -= s;
f = 1 - f
}
fin = i;
ani();
x0 = null;
locked = false;
}
};
这与我们之前的代码没有什么不同。变化在于我们不再在这个函数中设置任何 CSS 变量,而是设置了ini
和fin
JavaScript 变量并调用动画ani()
函数。
ini
是在"touchend"
/"mouseup"
事件触发的动画开始时设置的--i
的初始值。它由这两个事件之一触发时我们当前的位置给出。
fin
是在相同动画结束时设置的--i
的最终值。它始终是一个整数值,因为我们总是以一张图像完全进入视野结束,因此fin
和--i
是该图像的索引。如果我们拖动了足够距离(f > .2
),并且在所需方向上有下一张图像((i > 0 || s < 0) && (i < N - 1 || s > 0)
),那么它就是所需方向的下一张图像。在这种情况下,我们还会更新存储当前图像索引(i
)和到它的相对距离(f
)的 JavaScript 变量。否则,它就是同一张图像,因此i
和f
不需要更新。
现在,让我们继续讨论ani()
函数。我们从一个简化的线性版本开始,它省略了方向变化。
const NF = 30;
let rID = null;
function stopAni() {
cancelAnimationFrame(rID);
rID = null
};
function ani(cf = 0) {
_C.style.setProperty('--i', ini + (fin - ini)*cf/NF);
if(cf === NF) {
stopAni();
return
}
rID = requestAnimationFrame(ani.bind(this, ++cf))
};
这里的主要思想是,初始值ini
和最终值fin
之间的转换发生在总帧数NF
内。每次我们调用ani()
函数时,我们都将进度计算为当前帧索引cf
与总帧数NF
的比率。它始终是一个介于0
和1
之间的数字(或者你可以将其视为百分比,从0%
到100%
)。然后,我们使用此进度值来获取--i
的当前值,并将其设置在我们的容器_C
的样式属性中。如果我们到达了最终状态(当前帧索引cf
等于总帧数NF
),则退出动画循环。否则,我们只需递增当前帧索引cf
并再次调用ani()
。
此时,我们有一个带有线性 JavaScript 转换的工作演示。
但是,它存在我们在 CSS 案例中最初遇到的问题:无论距离如何,我们都必须在释放("touchend"
/"mouseup"
)时平滑地平移我们的元素,并且持续时间始终相同,因为我们始终在相同的帧数NF
内进行动画。
让我们修复它!
为此,我们引入另一个变量anf
,在其中存储我们使用的实际帧数,并在调用动画函数ani()
之前在move()
函数中计算其值。
function move(e) {
if(locked) {
let dx = unify(e).clientX - x0,
s = Math.sign(dx),
f = +(s*dx/w).toFixed(2);
/* same as before */
anf = Math.round(f*NF);
ani();
/* same as before */
}
};
我们还需要在动画函数ani()
中用anf
替换NF
。
function ani(cf = 0) {
_C.style.setProperty('--i', ini + (fin - ini)*cf/anf);
if(cf === anf) { /* same as before */ }
/* same as before */
};
这样,我们就解决了时间问题!
linear
JavaScript 转换的版本(在线演示)。好吧,但是线性时间函数并不是那么令人兴奋。
我们可以尝试 CSS 时间函数(例如ease-in
、ease-out
或ease-in-out
)的 JavaScript 等效项,并查看它们的比较结果。我已经在之前链接的文章中详细解释了如何获得这些函数,因此我不会再详细介绍,而只是将包含所有这些函数的对象放入代码中。
const TFN = {
'linear': function(k) { return k },
'ease-in': function(k, e = 1.675) {
return Math.pow(k, e)
},
'ease-out': function(k, e = 1.675) {
return 1 - Math.pow(1 - k, e)
},
'ease-in-out': function(k) {
return .5*(Math.sin((k - .5)*Math.PI) + 1)
}
};
k
值是进度,它是当前帧索引cf
与转换发生的实际帧数anf
的比率。这意味着如果我们想例如使用ease-out
选项,我们需要稍微修改ani()
函数。
function ani(cf = 0) {
_C.style.setProperty('--i', ini + (fin - ini)*TFN['ease-out'](cf/anf));
/* same as before */
};
ease-out
JavaScript 转换的版本(在线演示)。我们还可以通过使用 CSS 无法提供的弹跳时间函数类型来使事情变得更有趣。例如,类似于下面演示中说明的函数(点击触发转换)。
查看 thebabydino 在 CodePen 上创建的Pen(@thebabydino)。
此函数的图形将与 easings.net 中的easeOutBounce
时间函数的图形有些相似。

获得这种时间函数的过程类似于获得 CSS ease-in-out
的 JavaScript 版本的过程(同样,在之前链接的文章中描述了如何使用 JavaScript 模拟 CSS 时间函数)。
我们从[0, 90°]
区间(或弧度制的[0, π/2]
)上的余弦函数开始,没有弹跳,[0, 270°]
([0, 3·π/2]
)进行1
次弹跳,[0, 450°]
([0, 5·π/2]
)进行2
次弹跳,依此类推……一般来说,对于n
次弹跳,区间为[0, (n + ½)·180°]
([0, (n + ½)·π]
)。
查看 thebabydino 在 CodePen 上创建的Pen(@thebabydino)。
此cos(k)
函数的输入在[0, 450°]
区间内,而其输出在[-1, 1]
区间内。但我们想要的是一个定义域为[0, 1]
区间且值域也为[0, 1]
区间的函数。
我们可以通过只取绝对值|cos(k)|
来将值域限制在[0, 1]
区间内。
查看 thebabydino 在 CodePen 上创建的Pen(@thebabydino)。
虽然我们得到了我们想要的余域区间,但我们希望该函数在0
处的值为0
,在区间另一端的值为1
。目前,情况正好相反,但如果我们将函数改为1 - |cos(k)|
,我们可以解决这个问题。
查看thebabydino在CodePen上创建的Pen(@thebabydino)。
现在我们可以继续将定义域从[0, (n + ½)·180°]
区间限制到[0, 1]
区间。为了做到这一点,我们将函数改为1 - |cos(k·(n + ½)·180°)|
。
查看thebabydino在CodePen上创建的Pen(@thebabydino)。
这给了我们期望的定义域和余域,但我们仍然有一些问题。
首先,我们所有的反弹高度都相同,但我们希望随着k
从0
增加到1
,反弹高度逐渐降低。在这种情况下,我们的解决方案是用1 - k
(或1 - k
的幂,以实现非线性幅度衰减)乘以余弦。下面的交互式演示展示了对于不同的指数a
,幅度是如何变化的,以及这对我们目前的函数有何影响。
查看thebabydino在CodePen上创建的Pen(@thebabydino)。
其次,所有反弹花费的时间都相同,即使它们的幅度不断减小。这里的第一个想法是在余弦函数内部使用k
的幂而不是k
本身。这导致了一些奇怪的结果,因为余弦不再以相等的时间间隔达到0
,这意味着我们不再总是得到f(1) = 1
,而这是我们实际使用的定时函数始终需要的。然而,对于像a = 2.75
、n = 3
和b = 1.5
这样的值,我们得到了一个看起来令人满意的结果,所以我们将保留它,即使它可以进行微调以获得更好的控制。

如果我们想要一些反弹效果,这是我们在JavaScript中尝试的函数。
const TFN = {
/* the other function we had before */
'bounce-out': function(k, n = 3, a = 2.75, b = 1.5) {
return 1 - Math.pow(1 - k, a)*Math.abs(Math.cos(Math.pow(k, b)*(n + .5)*Math.PI))
}
};
嗯,在实践中似乎有点太极端了。
也许我们可以使n
依赖于从释放那一刻起我们仍然需要执行的平移量。我们将其转换为一个变量,然后在调用动画函数ani()
之前,在move()
函数中设置它。
const TFN = {
/* the other function we had before */
'bounce-out': function(k, a = 2.75, b = 1.5) {
return 1 - Math.pow(1 - k, a)*Math.abs(Math.cos(Math.pow(k, b)*(n + .5)*Math.PI))
}
};
var n;
function move(e) {
if(locked) {
let dx = unify(e).clientX - x0,
s = Math.sign(dx),
f = +(s*dx/w).toFixed(2);
/* same as before */
n = 2 + Math.round(f)
ani();
/* same as before */
}
};
这给了我们最终的结果。
肯定还有改进的空间,但我对什么构成良好的动画没有感觉,所以我就此结束。就这样,它现在可以在跨浏览器环境中正常工作(没有使用CSS过渡版本的任何Edge问题),并且非常灵活。
是否值得使用指针事件而不是触摸事件?
可能吧。但我距离理解指针事件还有很长的路要走。
它们本质上是带有额外属性的鼠标事件(因为它们扩展了鼠标事件)。这使得滑块可以对触摸、触笔甚至鼠标本身使用相同的(或不同的,因为你可以区分)代码来实现相同的功能。
这是一篇关于如何使用指针事件的优秀文章。https://developers.google.com/web/fundamentals/design-and-ux/input/touch/#add_event_listeners
可能还需要将你的
mouseup
监听器添加到window
中。如果在点击和拖动的过程中,你的鼠标离开了浏览器窗口,你释放鼠标按钮,容器可能会错过该事件。即使mouseup
发生在窗口外部,附加到window
的监听器也会被调用。哎呀……我一直以为
addEventListener
与window.addEventListener
相同……这太酷了,但你到底是怎么记住每个单字母变量的作用的?我在阅读过程中就迷失了方向了 :(
命名总是很困难。一种约定对于某些人来说可能比其他人更容易,但幸运的是,结果并不依赖于一种约定优于另一种约定。 :)
我不需要记住它们,我只需要从上下文中理解它们。这就是我使用单字母的原因。如果我使用更长的变量名,我将难以理解它们,并将它们映射到它们定义的概念。我不知道如何解释这一点,但我确实不是用文字、句子思考的。
如果我看到一个变量
a
设置为度数或弧度值,我就会立即在脑海中绘制一个角度的几何图形。如果我看到一个变量angleForBlaBla
,我就会感到恐慌,因为字母太多,我无法理解它在说什么,其中到底有什么是相关的。我不知道,这只是我大脑的工作方式。这就是为什么我阅读和写作有困难的原因。这就是为什么我在我的文章中使用了如此多的视觉演示。我完全不知道如何用文字解释某些事情。或者为什么如果用文字而不是视觉方式来解释,我就不理解。
哦,天哪,这是一个疯狂的理由。我想这是我们永远无法达成一致的事情,完全基于我们大脑的工作方式。我并不是说这是错的,一点也不,我理解起来很困难,正是因为你发现它很容易。我不能轻易推断上下文。
很酷,但为什么要这样做?已经存在可以为你做到这一点的库。对于如此简单的操作,这需要花费太多的编码时间。
因为这里提到的非常合理的观点(#1):https://www.leaseweb.com/labs/2013/07/10-very-good-reasons-to-stop-using-javascript/
你有没有看过实际演示中的代码量?认真的问题。
我可能写了一篇关于它的非常长的文章,但实际上编写滑动部分的代码时间非常短,比我使用库实现它花费的时间少得多。我花了不到半小时的时间完成了最初的滑动部分的JS代码,其中一半时间花在了优化和调整上。
使用库意味着将这半小时的时间仅仅花在寻找和选择库上。我至少需要花一天的时间才能理解如何使用该库。我不了解其他人,但对于我来说,这是使用库的最大障碍——我很难理解如何使用它们,如果我设法理解如何使用它们的话。即使它们有很好的文档。所以即使使用库意味着我只需要编写两行JS代码,它仍然需要花费更多的时间。
可能是一个愚蠢的问题,但你如何在开始时设置变量
--i
?这行代码:
transform: translate(calc(var(--i)/var(--n)*-100%));
这是一个很好的问题。
我没有设置。从CSS中没有,甚至最初从JS中也没有。我只是在切换到另一张图片时,才从JS中设置它。如果一开始根本没有设置,效果与将其设置为
0
相同。因为它没有区别,所以我采取了懒惰的方法,根本没有设置它。不错的方法。动画函数总是有点让人望而生畏,也许是因为我没有数学背景 ;-) 使用更易于理解的变量名会有所帮助。
我之前构建了一个类似的轮播,它没有使用CSS变量。相反,轮播中的每个项目都会自行动画(使用
TranslateX(-100%)
)。优点是,你无需计算容器的宽度、每个图像的宽度以及轮播中项目的数量。如果你好奇(当然,任何反馈都受欢迎)https://github.com/reinoute/progressive-carousel
很棒的文章,我喜欢你逐步实现的过程。写这篇文章一定花费了相当长的时间!
我也喜欢你如何平静地添加这些交互式图表,然后它们最终的代码比你的滑块还要多。
无论如何,我唯一不明白的是,为什么在可以使用JS计算所有值的情况下,你还要使用calc()?这样就不会有Edge的过渡问题了吗?
没错,那样就不会有这个问题了。我想我只是不喜欢在JS中阅读和计算值。总的来说,我不喜欢数值计算。
但是的,这绝对是一个有效的选项,尤其是在实践中。
关于图形/辅助演示……它们在后台比滑动演示更复杂,而且确实花费了更长时间。有时我在处理辅助交互式内容时处于能力边缘。
感谢分享如此精彩的文章,非常有信息量。
这是一个很棒的教程!非常感谢!!
但请尽量记住,我们不再使用C语言了。在2018年,我们可以为变量使用长名称 :)
这将更容易让新手阅读和理解;)
这个教程太棒了!!!一步一步,非常清晰。我很容易就能理解!谢谢
很棒的文章!首先是CSS(技巧),然后是JS增强,就像我的http://radogado.github.io/native-slider/
此致。
你好!很棒的例子。我今天使用了另一个,但它没有按我想要的方式工作(自动高度)。我的将使用最高卡片的高度。我可以使用你的,并以某种方式将JS文件剥离到仅包含自动高度设置吗?
嗨 Adrian,谢谢。自动高度只是一个选项,我担心所有 JS 都是必需的。此工具是https://github.com/radogado/natuive的一个子集
祝你好运!