列表元素有限的旋转
List rotation with limited elements
我有 div container
里面有列表(卡片)。当我悬停它时,卡片开始移动 (translateX animation
)。 container
的width
是300px
,元素数在container:3
,每个元素width:100px
.
因此您可以在容器中同时看到 3 个元素 overflow:hidden
。我想做的是,当没有元素显示 translateX 动画 -100px = 100px 空白 space 第三个元素后,它从列表中的 1 个元素开始紧跟在最后,没有空白 space.
现在,我不知道如何在没有重复等的情况下完成
这是我目前拥有的:
Fiddle(悬停卡片以查看翻译动画)
更新 1:
以代码和数据(卡片数量、容器大小)为例,我将尝试更好地解释我想要的内容:我的目标是构建卡片列表,按下按钮后,列表将开始移动(如示例使用 translateX 动画)一段时间(例如 translateX: 12491px, animation-duration: 15s;)然后停止。但问题是列表中的卡片数量在 3-40 张卡片的范围内(每张卡片的宽度和高度为 100 像素)。因此,当我设置 translateX: 12491px 时,它将超出范围并且列表中的最后一张卡片将显示为空白space。我希望第一张和最后一张卡片以某种方式绑定,最后一张卡片立即出现在列表中的第一张卡片等等。也许我正在以错误的方式寻找解决方案,但我想你理解了主要思想。
更新 2:
我发现 cs:go 使用了我想在 html\css\js 上写的动画。这是视频:youtube.com
html:
<div class="container">
<div class="cards">
<div class="card">
1
</div>
<div class="card">
2
</div>
<div class="card">
3
</div>
</div>
</div>
css:
.container
{
width:300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card
{
float:left;
height: 100px;
width: 100px;
background-color:blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
}
.cards:hover
{
transform: translateX(-100px);
transition-duration: 3s;
animation-duration: 3s;
animation-fill-mode: forwards;
}
更新二:
我写了一个 jquery 插件,它可以按照你想要的方式运行:
你可以添加任意数量的卡片,现在 "translateX" 是随机的(脚本会随机选择最后一张卡片)
更新:
我知道,我使用了重复项,但现在我的代码适用于三张卡片:
- 我添加了三张 "fake" 卡片
- 每张 "real" 卡片都有自己的动画
- 一旦循环结束,"fake" 卡片将与真实卡片重叠(如您所问,"when there is no element to show")
检查片段:
.container {
width: 300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card {
float: left;
height: 100px;
width: 100px;
background-color: blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
}
.cards {
width: 600px;
}
.container:hover .card1{
animation: 1600ms slide1 infinite linear;
}
.container:hover .card2{
animation: 1600ms slide2 infinite linear;
}
.container:hover .card3{
animation: 1600ms slide3 infinite linear;
}
.fakecard{z-index:-1000;}
.container:hover .fakecard{
animation: 1600ms fakeslide infinite linear;
}
@keyframes slide1 {
0% { transform: translateX(0px); }
33% { transform: translateX(-100px); }
33.1% { transform: translateX(+200px); }
100% { transform: translateX(0px); }
}
@keyframes slide2 {
0% { transform: translateX(0px); }
66% { transform: translateX(-200px); }
66.1% { transform: translateX(100px); }
100% { transform: translateX(0px); }
}
@keyframes slide3 {
0% { transform: translateX(0px); }
99% { transform: translateX(-300px); }
99.1% { transform: translateX(+300px); }
100% { transform: translateX(0px); }
}
@keyframes fakeslide {
0% { transform: translateX(0px); }
99% { transform: translateX(-300px); }
99.1% { transform: translateX(+300px); }
100% { transform: translateX(0px); }
}
<div class="container">
<div class="cards">
<div class="card card1">
1
</div>
<div class="card card2">
2
</div>
<div class="card card3">
3
</div>
<div class="card fakecard">
1 (fake)
</div>
<div class="card fakecard">
2 (fake)
</div>
<div class="card fakecard">
3 (fake)
</div>
</div>
</div>
上一个回答:
这是您要实现的目标吗?
我不认为你可以不重复...
如果不是,你能更好地解释一下你在这里想要实现的目标吗?
[已删除代码片段]
如果您不想修改 dom 元素,您可以利用 flex-item's order
property;
要做到这一点,您仍然需要一点 JS 来在动画结束后添加这个 属性;
我还更改为动画而不是过渡,因此它会在动画结束时自动重置变换 属性。
$('.cards').mouseenter(function() {
setTimeout(function() {
$('.card').first().css("order", "2");
}, 3000);
});
$('.cards').mouseleave(function() {
$('.card').first().css("order", "-1");
});
.container {
width: 300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card {
float: left;
/* height: 100px;
width: 100px;*/
background-color: blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
flex: 0 0 25%;
}
.cards:hover {
animation: trans 3s;
}
/**/
.cards {
width: 400px;
height: 100%;
display: flex;
transition: transform 3s;
}
@keyframes trans {
0% {
transform: translateX(0)
}
100% {
transform: translateX(-100px)
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="container">
<div class="cards">
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
</div>
</div>
但是如果你可以使用 JS,我建议你直接操作 DOM 元素的顺序,获取 .cards
的第一个子元素并将其附加到列表的末尾每个动画结束;
试试这个:
var anim;
$('.cards').mouseenter(function(){
anim = setInterval(function(){
$('.cards').append($('.card').first())
},3000)
});
$('.cards').mouseleave(function(){
clearInterval(anim)
});
.container{
width:300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card{
float:left;
/* height: 100px;
width: 100px;*/
background-color:blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
/**/
flex:0 0 25%;
}
.cards:hover{
animation: trans 3s infinite;
}
/**/
.cards{
width:400px;
height:100%;
display:flex;
}
@keyframes trans {
0% {
transform: translateX(0)
}
100% {
transform: translateX(-100px)
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="container">
<div class="cards">
<div class="card">
1
</div>
<div class="card">
2
</div>
<div class="card">
3
</div>
</div>
</div>
如果您希望一张卡片同时出现在卡片列表的开头和结尾,您需要对元素进行深层复制/克隆;
Here 与您提到的效果相同,对您的 CSS 进行了一些调整,jQuery.
提供了帮助
CSS
更改 translateX
动画的选择器,以在直接父级 悬停 时应用于每个 .card
框,而不是 .cards
(这是 .card
的直接父代)。这是因为您希望 cards 移动到左侧,而不是 window运动。
即
.cards:hover .card {
transform: translateX(-100px);
transition-duration: 1.5s;
animation-duration: 1.5s;
animation-fill-mode: forwards;
}
jQuery
var $container = $('.container');
var cardWidth = 100;
$container.on('mouseenter', function (e) {
e.preventDefault();
var $card0Clone = $('.card').eq(0).clone(); // clone of the first .card element
$('.cards').append($card0Clone);
updateWidth();
});
$container.on('mouseleave', function (e) {
e.preventDefault();
var $cards = $('.card');
$cards.eq(0).remove(); // remove the last .card element
});
function updateWidth() {
$('.cards').width(($('.card').length) * cardWidth); // no of cards in the queue times the width of each card would result in a container fit enough for all of them
}
代码说明
当您移动鼠标指针时,将创建第一张卡片的克隆,并附加到卡片集合的末尾。此外,当您将鼠标移出悬停区域时,原始 .card
( 之前克隆的 )将从队列的头部移除 - 因此,产生循环效应。
真正的技巧是 updateWidth
函数。每次鼠标进入 .container
时, .card
的直接父级(即 .cards
div)的宽度都会更新,因此 .cards
div 的宽度足以容纳所有 .card
,因此,确保每张 卡片 相互推挤并保持在一行中翻译动画正在制作中。
这里我使用了JQuery,你可以使用两个变量来配置你的动画
var translateX = 1000; //adjust the whole distance to translate
var stepSpeed = 100; //adjust the speed of each step transition in milliseconds
设置变量后,在卡片的点击事件上执行以下操作:-
- 根据translateX得到需要的步数
- 循环步数
- 在每个循环中(每一步)将卡片向左移动 1 步,然后将第一张卡片放在卡片的末尾以形成相连的循环,然后 return 将卡片放回初始位置位置
代码如下:
var stepsNumber = translateX/100;
for(var i=0; i< stepsNumber; i++)
{
$('.cards').animate({'left' : -100}, stepSpeed,function(){
$('.cards div:last').after($('.cards div:first'));
$('.cards').css({'left' : '0px'});
});
}
start from 1 elements in the list immediately after last, with no
blank space
这超出了 CSS,您将需要 Javascript。因为,您用 Javascript 而不是 jQuery 标记了问题,所以我的回答仅限于纯 Javascript。看妈妈,没有 JQuery ;)
I have no idea how it could be done without duplicates
这是一个 DIY(自己动手)的想法..
- 主要技巧是显示至少一项少于您拥有的总数。如果你有 3 张卡片,则只显示 2。如果你有 4 张卡片,则只显示 3。为什么,因为你需要在卡片离开视野时重新定位并在最后将其包裹起来。如果您展示的卡片数量与您拥有的卡片数量完全相同,那么您将无法打破半张卡片并将其包裹起来,您会看到一些空白 space 直到第一张卡片消失。你明白了吗?
- 不要使用
translate
,否则您在编写脚本时最终会使事情变得复杂。保持简单。
- 不要为您的卡片使用包装纸。为什么?因为,我们将重新定位已经消失的卡片。当我们这样做时,下一张卡片将占据它的位置并立即消失,使您的事情变得更加困难。
- 为简单起见,根据容器的
absolute
定位排列卡片。首先,让所有卡片堆叠在 top:0; and left: 0;
。
- 接下来连线Javascript根据每张牌的
width
定位left
属性并线性排列
- 使用
requestAnimationFrame
控制动画。
- 跟踪最左边的卡片及其
left
位置。当它离开视野时( 即 0 减去宽度 ),appendChild
将这张卡片放到它的容器中。这会将卡片移动到卡片的末尾。此外,根据列表中的最后一张卡片将 left
属性 更改为它。
- 仅此而已。
下面是一个演示。为了方便您进行实验,我使用了一个设置对象来保留您可以轻松调整和查看的可配置属性。仔细看代码,你会发现它简单易懂。您可以将 iterations
设置设置为 0
以使动画无限。
另外请注意,您不需要复制或伪造卡片。尝试演示并添加任意数量的卡片。
片段中的内联代码注释,将进一步帮助您理解每一行代码并与上述步骤相关。
片段:
var list = document.querySelector('.cardList'), // cache the container
cards = document.querySelectorAll('.card'), // cache the list of cards
start = document.getElementById('start'), // buttons
stop = document.getElementById('stop'),
reset = document.getElementById('reset'),
raf, init = 0, counter = 0, lastCard, currentIteration = 0, // general purpose variables
settings = { // settings object to help make things configurable
'width': 100, 'height': 100, 'speed': 2,
'iterations': 2, 'count': cards.length
}
;
start.addEventListener('click', startClick); // wire up click event on buttons
stop.addEventListener('click', stopClick);
reset.addEventListener('click', resetClick);
initialize(); // initialize to arrange the cards at start
function initialize() {
// loop thru all cards and set the left property as per width and index position
[].forEach.call(cards, function(elem, idx) {
elem.style.left = (settings.width * idx) + 'px';
});
init = -(settings.width); // initialize the view cutoff
lastCard = cards[settings.count - 1]; // identify the last card
counter = 0; currentIteration = 0; // reset some counters
settings.speed = +(document.getElementById('speed').value);
settings.iterations = +(document.getElementById('iter').value);
}
function startClick() {
initialize(); raf = window.requestAnimationFrame(keyframes); // start animating
}
function stopClick() { window.cancelAnimationFrame(raf); } // stop animating
function resetClick() { // stop animating and re-initialize cards to start again
window.cancelAnimationFrame(raf);
document.getElementById('speed').value = '2';
document.getElementById('iter').value = '2';
initialize();
}
// actual animation function
function keyframes() {
var currentCard, currentLeft = 0, newLeft = 0;
// iterate all cards and decrease the left property based on speed
[].forEach.call(cards, function(elem, idx) {
elem.style.left = (parseInt(elem.style.left) - settings.speed) + 'px';
});
currentCard = cards[counter]; // identify left-most card
currentLeft = parseInt(currentCard.style.left); // get its left position
if (currentLeft <= init) { // check if it has gone out of view
// calculate position of last card
newLeft = parseInt(lastCard.style.left) + settings.width;
list.appendChild(currentCard); // move the card to end of list
currentCard.style.left = newLeft + 'px'; // change left position based on last card
lastCard = currentCard; // set this as the last card for next iteration
counter = (counter + 1) % settings.count; // set the next card index
if ((settings.iterations > 0) && (counter >= (settings.count - 1))) {
currentIteration++; // check settings for repeat iterations
}
}
if (currentIteration >= settings.iterations) { return; } // when to stop
raf = window.requestAnimationFrame(keyframes); // request another animation frame
};
* { box-sizing: border-box; padding: 0; margin: 0; }
.cardList {
position: relative; height: 100px; width: 300px;
margin: 10px; border: 2px solid #33e;
overflow: hidden; white-space: nowrap;
}
.card {
position: absolute; left: 0; top: 0; text-align: center;
height: 100px; width: 100px; line-height: 100px;
background-color: #99e;
font-family: monospace; font-size: 2em; color: #444;
border-left: 1px solid #33e; border-right: 1px solid #33e;
}
div.controls, button { margin: 10px; padding: 8px; font-family: monospace; }
div.controls input { width: 48px; padding: 2px; text-align: center; font-family: monospace; }
<div class="controls">
<label>Speed <input id="speed" type="number" min="1" max="8" value="2" />x</label>
|
<label>Iterations <input id="iter" type="number" min="0" max="8" value="2" /></label>
</div>
<div class="cardList">
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
<div class="card">4</div>
</div>
<button id="start">Start</button>
<button id="stop">Stop</button>
<button id="reset">Reset</button>
Fiddle: http://jsfiddle.net/abhitalks/1hkw1v0w/
注意:我在演示中遗漏了一些东西。特别是,虽然卡片的宽度和高度是设置对象的一部分,但目前它是固定的。您也可以轻松地使用设置对象来配置卡片的尺寸。
编辑:
(根据 Op 的评论)
如果您想更好地控制滚动距离、持续时间和计时功能(缓动),那么您可以使用库自己实现这些功能。 Robert Penner's Easing Functions and a jQuery plugin from GSGD 就是几个这样好的库。尽管您可以使用纯 Javascript 实现所有这些,但如果您使用像 jQuery.
这样的库会更容易
这里要注意的是,为了有效地做到这一点,您必须复制卡片。您可以通过多次克隆整个列表来轻松做到这一点。
尽管您没有用 jQuery 标记这个问题,这里有一个小演示(使用 jQuery 快速完成),您可以在其中配置速度和距离。
代码段 2:
var $cardList = $('.cardList').first(),
$cards = $('.card'),
$speed = $('input[name=speed]'),
width = 100,
randomize = true,
distance = 20 * width
;
for (var i = 0; i < 50; i++) {
$cards.clone().appendTo($cardList);
}
function spin() {
var newMargin = 0, newDistance = distance,
speed = +($speed.filter(':checked').val());
if (randomize) {
newDistance = Math.floor(Math.random() * $cards.length * 5);
newDistance += $cards.length * 5;
newDistance *= width;
}
newMargin = -(newDistance);
$cards.first().animate({
marginLeft: newMargin
}, speed);
}
$('#spin').click(function() {
$cards.first().css('margin-left', 0);
spin();
return false;
});
* { box-sizing: border-box; padding: 0; margin: 0; }
.cardList {
height: 100px; width: 302px; position: relative;
margin: 10px; border: 1px solid #33e;
overflow: hidden; white-space: nowrap;
}
.card {
display: inline-block; text-align: center;
height: 100px; width: 100px; line-height: 100px;
background-color: #99e;
font-family: monospace; font-size: 2em; color: #444;
border-left: 1px solid #33e; border-right: 1px solid #33e;
}
.cardList::before, .cardList::after {
content: ''; display: block; z-index: 100;
width: 0px; height: 0px; transform: translateX(-50%);
border-left: 8px solid transparent;
border-right: 8px solid transparent;
}
.cardList::before {
position: absolute; top: 0px; left: 50%;
border-top: 12px solid #33e;
}
.cardList::after {
position: absolute; bottom: 0px; left: 50%;
border-bottom: 12px solid #33e;
}
div.controls, button { margin: 10px; padding: 8px; font-family: monospace; }
div.controls input { width: 48px; padding: 2px; text-align: center; font-family: monospace; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="controls">
<label>Speed: </label>
|
<label><input name="speed" type="radio" value='6000' />Slow</label>
<label><input name="speed" type="radio" value='5000' checked />Medium</label>
<label><input name="speed" type="radio" value='3000' />Fast</label>
</div>
<div class="cardList"><!--
--><div class="card">1</div><!--
--><div class="card">2</div><!--
--><div class="card">3</div><!--
--><div class="card">4</div><!--
--></div>
<button id="spin">Spin</button>
Fiddle 2: http://jsfiddle.net/abhitalks/c50upco5/
这是一个简单的技巧,可以操纵 Dom 来创建您想要的效果
Javascript:
document.querySelector('.cards').addEventListener('mousedown', function(e) {
if (e.clientX < (this.offsetWidth >> 1)) {
this.appendChild(this.removeChild(this.firstElementChild));
} else {
this.insertBefore(this.lastElementChild, this.firstElementChild);
}});
然后在您 css 中使用 nth-of-type
选择器根据需要定位元素。
这是你的 fiddle
如果您正在使用鼠标悬停,您可能需要在再次触发之前等待 transitionend 事件。
我有 div container
里面有列表(卡片)。当我悬停它时,卡片开始移动 (translateX animation
)。 container
的width
是300px
,元素数在container:3
,每个元素width:100px
.
因此您可以在容器中同时看到 3 个元素 overflow:hidden
。我想做的是,当没有元素显示 translateX 动画 -100px = 100px 空白 space 第三个元素后,它从列表中的 1 个元素开始紧跟在最后,没有空白 space.
现在,我不知道如何在没有重复等的情况下完成
这是我目前拥有的: Fiddle(悬停卡片以查看翻译动画)
更新 1: 以代码和数据(卡片数量、容器大小)为例,我将尝试更好地解释我想要的内容:我的目标是构建卡片列表,按下按钮后,列表将开始移动(如示例使用 translateX 动画)一段时间(例如 translateX: 12491px, animation-duration: 15s;)然后停止。但问题是列表中的卡片数量在 3-40 张卡片的范围内(每张卡片的宽度和高度为 100 像素)。因此,当我设置 translateX: 12491px 时,它将超出范围并且列表中的最后一张卡片将显示为空白space。我希望第一张和最后一张卡片以某种方式绑定,最后一张卡片立即出现在列表中的第一张卡片等等。也许我正在以错误的方式寻找解决方案,但我想你理解了主要思想。
更新 2: 我发现 cs:go 使用了我想在 html\css\js 上写的动画。这是视频:youtube.com
html:
<div class="container">
<div class="cards">
<div class="card">
1
</div>
<div class="card">
2
</div>
<div class="card">
3
</div>
</div>
</div>
css:
.container
{
width:300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card
{
float:left;
height: 100px;
width: 100px;
background-color:blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
}
.cards:hover
{
transform: translateX(-100px);
transition-duration: 3s;
animation-duration: 3s;
animation-fill-mode: forwards;
}
更新二:
我写了一个 jquery 插件,它可以按照你想要的方式运行:
你可以添加任意数量的卡片,现在 "translateX" 是随机的(脚本会随机选择最后一张卡片)
更新:
我知道,我使用了重复项,但现在我的代码适用于三张卡片:
- 我添加了三张 "fake" 卡片
- 每张 "real" 卡片都有自己的动画
- 一旦循环结束,"fake" 卡片将与真实卡片重叠(如您所问,"when there is no element to show")
检查片段:
.container {
width: 300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card {
float: left;
height: 100px;
width: 100px;
background-color: blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
}
.cards {
width: 600px;
}
.container:hover .card1{
animation: 1600ms slide1 infinite linear;
}
.container:hover .card2{
animation: 1600ms slide2 infinite linear;
}
.container:hover .card3{
animation: 1600ms slide3 infinite linear;
}
.fakecard{z-index:-1000;}
.container:hover .fakecard{
animation: 1600ms fakeslide infinite linear;
}
@keyframes slide1 {
0% { transform: translateX(0px); }
33% { transform: translateX(-100px); }
33.1% { transform: translateX(+200px); }
100% { transform: translateX(0px); }
}
@keyframes slide2 {
0% { transform: translateX(0px); }
66% { transform: translateX(-200px); }
66.1% { transform: translateX(100px); }
100% { transform: translateX(0px); }
}
@keyframes slide3 {
0% { transform: translateX(0px); }
99% { transform: translateX(-300px); }
99.1% { transform: translateX(+300px); }
100% { transform: translateX(0px); }
}
@keyframes fakeslide {
0% { transform: translateX(0px); }
99% { transform: translateX(-300px); }
99.1% { transform: translateX(+300px); }
100% { transform: translateX(0px); }
}
<div class="container">
<div class="cards">
<div class="card card1">
1
</div>
<div class="card card2">
2
</div>
<div class="card card3">
3
</div>
<div class="card fakecard">
1 (fake)
</div>
<div class="card fakecard">
2 (fake)
</div>
<div class="card fakecard">
3 (fake)
</div>
</div>
</div>
上一个回答:
这是您要实现的目标吗?
我不认为你可以不重复...
如果不是,你能更好地解释一下你在这里想要实现的目标吗?
[已删除代码片段]
如果您不想修改 dom 元素,您可以利用 flex-item's order
property;
要做到这一点,您仍然需要一点 JS 来在动画结束后添加这个 属性;
我还更改为动画而不是过渡,因此它会在动画结束时自动重置变换 属性。
$('.cards').mouseenter(function() {
setTimeout(function() {
$('.card').first().css("order", "2");
}, 3000);
});
$('.cards').mouseleave(function() {
$('.card').first().css("order", "-1");
});
.container {
width: 300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card {
float: left;
/* height: 100px;
width: 100px;*/
background-color: blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
flex: 0 0 25%;
}
.cards:hover {
animation: trans 3s;
}
/**/
.cards {
width: 400px;
height: 100%;
display: flex;
transition: transform 3s;
}
@keyframes trans {
0% {
transform: translateX(0)
}
100% {
transform: translateX(-100px)
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="container">
<div class="cards">
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
</div>
</div>
但是如果你可以使用 JS,我建议你直接操作 DOM 元素的顺序,获取 .cards
的第一个子元素并将其附加到列表的末尾每个动画结束;
试试这个:
var anim;
$('.cards').mouseenter(function(){
anim = setInterval(function(){
$('.cards').append($('.card').first())
},3000)
});
$('.cards').mouseleave(function(){
clearInterval(anim)
});
.container{
width:300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card{
float:left;
/* height: 100px;
width: 100px;*/
background-color:blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
/**/
flex:0 0 25%;
}
.cards:hover{
animation: trans 3s infinite;
}
/**/
.cards{
width:400px;
height:100%;
display:flex;
}
@keyframes trans {
0% {
transform: translateX(0)
}
100% {
transform: translateX(-100px)
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="container">
<div class="cards">
<div class="card">
1
</div>
<div class="card">
2
</div>
<div class="card">
3
</div>
</div>
</div>
如果您希望一张卡片同时出现在卡片列表的开头和结尾,您需要对元素进行深层复制/克隆;
Here 与您提到的效果相同,对您的 CSS 进行了一些调整,jQuery.
提供了帮助CSS
更改 translateX
动画的选择器,以在直接父级 悬停 时应用于每个 .card
框,而不是 .cards
(这是 .card
的直接父代)。这是因为您希望 cards 移动到左侧,而不是 window运动。
即
.cards:hover .card {
transform: translateX(-100px);
transition-duration: 1.5s;
animation-duration: 1.5s;
animation-fill-mode: forwards;
}
jQuery
var $container = $('.container');
var cardWidth = 100;
$container.on('mouseenter', function (e) {
e.preventDefault();
var $card0Clone = $('.card').eq(0).clone(); // clone of the first .card element
$('.cards').append($card0Clone);
updateWidth();
});
$container.on('mouseleave', function (e) {
e.preventDefault();
var $cards = $('.card');
$cards.eq(0).remove(); // remove the last .card element
});
function updateWidth() {
$('.cards').width(($('.card').length) * cardWidth); // no of cards in the queue times the width of each card would result in a container fit enough for all of them
}
代码说明
当您移动鼠标指针时,将创建第一张卡片的克隆,并附加到卡片集合的末尾。此外,当您将鼠标移出悬停区域时,原始 .card
( 之前克隆的 )将从队列的头部移除 - 因此,产生循环效应。
真正的技巧是 updateWidth
函数。每次鼠标进入 .container
时, .card
的直接父级(即 .cards
div)的宽度都会更新,因此 .cards
div 的宽度足以容纳所有 .card
,因此,确保每张 卡片 相互推挤并保持在一行中翻译动画正在制作中。
这里我使用了JQuery,你可以使用两个变量来配置你的动画
var translateX = 1000; //adjust the whole distance to translate
var stepSpeed = 100; //adjust the speed of each step transition in milliseconds
设置变量后,在卡片的点击事件上执行以下操作:-
- 根据translateX得到需要的步数
- 循环步数
- 在每个循环中(每一步)将卡片向左移动 1 步,然后将第一张卡片放在卡片的末尾以形成相连的循环,然后 return 将卡片放回初始位置位置
代码如下:
var stepsNumber = translateX/100;
for(var i=0; i< stepsNumber; i++)
{
$('.cards').animate({'left' : -100}, stepSpeed,function(){
$('.cards div:last').after($('.cards div:first'));
$('.cards').css({'left' : '0px'});
});
}
start from 1 elements in the list immediately after last, with no blank space
这超出了 CSS,您将需要 Javascript。因为,您用 Javascript 而不是 jQuery 标记了问题,所以我的回答仅限于纯 Javascript。看妈妈,没有 JQuery ;)
I have no idea how it could be done without duplicates
这是一个 DIY(自己动手)的想法..
- 主要技巧是显示至少一项少于您拥有的总数。如果你有 3 张卡片,则只显示 2。如果你有 4 张卡片,则只显示 3。为什么,因为你需要在卡片离开视野时重新定位并在最后将其包裹起来。如果您展示的卡片数量与您拥有的卡片数量完全相同,那么您将无法打破半张卡片并将其包裹起来,您会看到一些空白 space 直到第一张卡片消失。你明白了吗?
- 不要使用
translate
,否则您在编写脚本时最终会使事情变得复杂。保持简单。 - 不要为您的卡片使用包装纸。为什么?因为,我们将重新定位已经消失的卡片。当我们这样做时,下一张卡片将占据它的位置并立即消失,使您的事情变得更加困难。
- 为简单起见,根据容器的
absolute
定位排列卡片。首先,让所有卡片堆叠在top:0; and left: 0;
。 - 接下来连线Javascript根据每张牌的
width
定位left
属性并线性排列 - 使用
requestAnimationFrame
控制动画。 - 跟踪最左边的卡片及其
left
位置。当它离开视野时( 即 0 减去宽度 ),appendChild
将这张卡片放到它的容器中。这会将卡片移动到卡片的末尾。此外,根据列表中的最后一张卡片将left
属性 更改为它。 - 仅此而已。
下面是一个演示。为了方便您进行实验,我使用了一个设置对象来保留您可以轻松调整和查看的可配置属性。仔细看代码,你会发现它简单易懂。您可以将 iterations
设置设置为 0
以使动画无限。
另外请注意,您不需要复制或伪造卡片。尝试演示并添加任意数量的卡片。
片段中的内联代码注释,将进一步帮助您理解每一行代码并与上述步骤相关。
片段:
var list = document.querySelector('.cardList'), // cache the container
cards = document.querySelectorAll('.card'), // cache the list of cards
start = document.getElementById('start'), // buttons
stop = document.getElementById('stop'),
reset = document.getElementById('reset'),
raf, init = 0, counter = 0, lastCard, currentIteration = 0, // general purpose variables
settings = { // settings object to help make things configurable
'width': 100, 'height': 100, 'speed': 2,
'iterations': 2, 'count': cards.length
}
;
start.addEventListener('click', startClick); // wire up click event on buttons
stop.addEventListener('click', stopClick);
reset.addEventListener('click', resetClick);
initialize(); // initialize to arrange the cards at start
function initialize() {
// loop thru all cards and set the left property as per width and index position
[].forEach.call(cards, function(elem, idx) {
elem.style.left = (settings.width * idx) + 'px';
});
init = -(settings.width); // initialize the view cutoff
lastCard = cards[settings.count - 1]; // identify the last card
counter = 0; currentIteration = 0; // reset some counters
settings.speed = +(document.getElementById('speed').value);
settings.iterations = +(document.getElementById('iter').value);
}
function startClick() {
initialize(); raf = window.requestAnimationFrame(keyframes); // start animating
}
function stopClick() { window.cancelAnimationFrame(raf); } // stop animating
function resetClick() { // stop animating and re-initialize cards to start again
window.cancelAnimationFrame(raf);
document.getElementById('speed').value = '2';
document.getElementById('iter').value = '2';
initialize();
}
// actual animation function
function keyframes() {
var currentCard, currentLeft = 0, newLeft = 0;
// iterate all cards and decrease the left property based on speed
[].forEach.call(cards, function(elem, idx) {
elem.style.left = (parseInt(elem.style.left) - settings.speed) + 'px';
});
currentCard = cards[counter]; // identify left-most card
currentLeft = parseInt(currentCard.style.left); // get its left position
if (currentLeft <= init) { // check if it has gone out of view
// calculate position of last card
newLeft = parseInt(lastCard.style.left) + settings.width;
list.appendChild(currentCard); // move the card to end of list
currentCard.style.left = newLeft + 'px'; // change left position based on last card
lastCard = currentCard; // set this as the last card for next iteration
counter = (counter + 1) % settings.count; // set the next card index
if ((settings.iterations > 0) && (counter >= (settings.count - 1))) {
currentIteration++; // check settings for repeat iterations
}
}
if (currentIteration >= settings.iterations) { return; } // when to stop
raf = window.requestAnimationFrame(keyframes); // request another animation frame
};
* { box-sizing: border-box; padding: 0; margin: 0; }
.cardList {
position: relative; height: 100px; width: 300px;
margin: 10px; border: 2px solid #33e;
overflow: hidden; white-space: nowrap;
}
.card {
position: absolute; left: 0; top: 0; text-align: center;
height: 100px; width: 100px; line-height: 100px;
background-color: #99e;
font-family: monospace; font-size: 2em; color: #444;
border-left: 1px solid #33e; border-right: 1px solid #33e;
}
div.controls, button { margin: 10px; padding: 8px; font-family: monospace; }
div.controls input { width: 48px; padding: 2px; text-align: center; font-family: monospace; }
<div class="controls">
<label>Speed <input id="speed" type="number" min="1" max="8" value="2" />x</label>
|
<label>Iterations <input id="iter" type="number" min="0" max="8" value="2" /></label>
</div>
<div class="cardList">
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
<div class="card">4</div>
</div>
<button id="start">Start</button>
<button id="stop">Stop</button>
<button id="reset">Reset</button>
Fiddle: http://jsfiddle.net/abhitalks/1hkw1v0w/
注意:我在演示中遗漏了一些东西。特别是,虽然卡片的宽度和高度是设置对象的一部分,但目前它是固定的。您也可以轻松地使用设置对象来配置卡片的尺寸。
编辑:
(根据 Op 的评论)
如果您想更好地控制滚动距离、持续时间和计时功能(缓动),那么您可以使用库自己实现这些功能。 Robert Penner's Easing Functions and a jQuery plugin from GSGD 就是几个这样好的库。尽管您可以使用纯 Javascript 实现所有这些,但如果您使用像 jQuery.
这样的库会更容易这里要注意的是,为了有效地做到这一点,您必须复制卡片。您可以通过多次克隆整个列表来轻松做到这一点。
尽管您没有用 jQuery 标记这个问题,这里有一个小演示(使用 jQuery 快速完成),您可以在其中配置速度和距离。
代码段 2:
var $cardList = $('.cardList').first(),
$cards = $('.card'),
$speed = $('input[name=speed]'),
width = 100,
randomize = true,
distance = 20 * width
;
for (var i = 0; i < 50; i++) {
$cards.clone().appendTo($cardList);
}
function spin() {
var newMargin = 0, newDistance = distance,
speed = +($speed.filter(':checked').val());
if (randomize) {
newDistance = Math.floor(Math.random() * $cards.length * 5);
newDistance += $cards.length * 5;
newDistance *= width;
}
newMargin = -(newDistance);
$cards.first().animate({
marginLeft: newMargin
}, speed);
}
$('#spin').click(function() {
$cards.first().css('margin-left', 0);
spin();
return false;
});
* { box-sizing: border-box; padding: 0; margin: 0; }
.cardList {
height: 100px; width: 302px; position: relative;
margin: 10px; border: 1px solid #33e;
overflow: hidden; white-space: nowrap;
}
.card {
display: inline-block; text-align: center;
height: 100px; width: 100px; line-height: 100px;
background-color: #99e;
font-family: monospace; font-size: 2em; color: #444;
border-left: 1px solid #33e; border-right: 1px solid #33e;
}
.cardList::before, .cardList::after {
content: ''; display: block; z-index: 100;
width: 0px; height: 0px; transform: translateX(-50%);
border-left: 8px solid transparent;
border-right: 8px solid transparent;
}
.cardList::before {
position: absolute; top: 0px; left: 50%;
border-top: 12px solid #33e;
}
.cardList::after {
position: absolute; bottom: 0px; left: 50%;
border-bottom: 12px solid #33e;
}
div.controls, button { margin: 10px; padding: 8px; font-family: monospace; }
div.controls input { width: 48px; padding: 2px; text-align: center; font-family: monospace; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="controls">
<label>Speed: </label>
|
<label><input name="speed" type="radio" value='6000' />Slow</label>
<label><input name="speed" type="radio" value='5000' checked />Medium</label>
<label><input name="speed" type="radio" value='3000' />Fast</label>
</div>
<div class="cardList"><!--
--><div class="card">1</div><!--
--><div class="card">2</div><!--
--><div class="card">3</div><!--
--><div class="card">4</div><!--
--></div>
<button id="spin">Spin</button>
Fiddle 2: http://jsfiddle.net/abhitalks/c50upco5/
这是一个简单的技巧,可以操纵 Dom 来创建您想要的效果
Javascript:
document.querySelector('.cards').addEventListener('mousedown', function(e) {
if (e.clientX < (this.offsetWidth >> 1)) {
this.appendChild(this.removeChild(this.firstElementChild));
} else {
this.insertBefore(this.lastElementChild, this.firstElementChild);
}});
然后在您 css 中使用 nth-of-type
选择器根据需要定位元素。
这是你的 fiddle
如果您正在使用鼠标悬停,您可能需要在再次触发之前等待 transitionend 事件。