老虎机选项卡

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 $200 免费积分!

我正在查看名为 Fluxiom 的 Web 服务的 功能页面。 我还没有使用过该产品(虽然它看起来非常不错,并且可能适合我们的一些客户)。 让我觉得很酷的是该页面上的选项卡。 当您单击不同的选项卡时,三列文本将以不同的速度向上飞起,并被新列替换。 它看起来有点像老虎机。 我没有深入研究他们是如何做到的,但正如我经常做的那样,我开始使用 jQuery 重新创建这种效果。

单击新选项卡后,三列会滑走,并以随机速度替换为新的列,就像老虎机一样。

查看演示   下载文件

我认为我做得还不错……虽然它肯定可以改进。 也有足够有趣的事情可以讨论,所以让我们开始吧。

HTML

这里只做一个简单的代码片段,这样您就可以看到所有内容。

<div id="slot-machine-tabs">

	<ul class="tabs">
		<li><a href="#one">Tab One</a></li>
		<li><a href="#two">Tab Two</a></li>
		<li><a href="#three">Tab Three</a></li>
	</ul>

	<div class="box-wrapper">

		<div id="one" class="content-box">
			<div class="col-one col">
				<img src="images/evangeline.jpg" alt="" />
			</div>
			<div class="col-two col">
				<h3>Kate</h3>
				<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
			</div>
			<div class="col-three col">
				<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
			</div>
		</div>

		<div id="two" class="content-box">
			<div class="col-one col">
				<img src="images/elizabeth.jpg" alt="" />
			</div>
			<div class="col-two col">
				<h3>Juliet</h3>
				<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
			</div>
			<div class="col-three col">
				<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
			</div>
		</div>

		<div id="three" class="content-box">
			<div class="col-one col">
				<img src="images/sonya.jpg" alt="" />
			</div>
			<div class="col-two col">
				<h3>Penelope</h3>
				<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
			</div>
			<div class="col-three col">
				<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
			</div>
		</div>

	</div> <!-- END Box Wrapper -->

</div> <!-- END Slot Machine Tabs -->

要点

  • 整个内容都被包裹在一个div中,它具有一个 ID 值。 如果我们将这个想法转换为一个插件,那么这个想法就是定位这个 ID 并进行操作。 但 ID 对 CSS 完全没有必要。 这意味着我们可以在一个页面上拥有多个老虎机选项卡实例。
  • “导航”(选项卡)位于内容框上方的顶部。 如果关闭样式,它看起来就像其他任何导航列表。 链接的href值指向内容框的 ID,因此链接将跳到页面下方对应的内容。 理想。
  • 当一行中有很多关闭的 </div> 时,我喜欢在后面添加注释来解释正在关闭的内容(例如 <!– END page wrap –>)

CSS

选项卡本身将像其他任何水平导航一样。 我们将列表项设置为内联,并将锚点块向左浮动。 简单边框和背景,以及“当前”的特殊状态(无边框并向下移动),我们就完成了。

.tabs { list-style: none; overflow: hidden; padding-left: 1px; }
.tabs li { display: inline; }
.tabs li a { display: block; float: left; padding: 4px 8px; color: black; border: 1px solid #ccc; background: #eee; margin: 0 0 0 -1px; }
.tabs li a.current { background: white; border-bottom: 0; position: relative; top: 2px; z-index: 2; }

我们使用的一个有点不语义的东西是#box-wrapperdiv,但无论如何它没有那么糟糕,它在很多方面都帮助了我们。 首先,它是相对定位容器,它限制了内部绝对定位的范围。 然后,我们可以将每个内容框绝对定位在彼此之上。

我最喜欢的一部分是围绕#box-wrapperdiv 的 box-shadow CSS。 它不仅有助于选项卡区域稍微突出显示,而且还处理选项卡本身的阴影。 框包装器位于非当前选项卡的顶部(从字面上看,z-index 方面)。 这正是我们追求的错觉/视觉隐喻:当前选项卡位于顶部并连接,非当前选项卡位于“后面”(阴影投射在它们上面)。

.box-wrapper { -moz-box-shadow: 0 0 20px black; -webkit-box-shadow: 0 0 20px black; padding: 20px; background: white; border: 1px solid #ccc; margin: -1px 0 0 0; height: 210px; position: relative; }
.content-box { overflow: hidden; position: absolute; top: 20px; left: 20px; width: 658px; height: 230px; }

请注意,我们还使用负顶部边距将框向上拉动一个像素。 这确保了非当前选项卡的下边框不会形成 2 像素的线,而是一条一致的 1 像素的线。

这里另一个重要的赋能概念是#box-wrapper具有隐藏的溢出和设置的高度。 所有非当前内容框中的列通过将它们的顶部值推到 350px(大于框高度的值)来隐藏。 由于隐藏了溢出,这将它们完全推出了视图。 JavaScript 稍后将通过在需要时拉起新列并将旧列推开来完成这项工作。

.col { width: 30%; float: left; position: relative; top: 350px; }
.col-one, .col-two { margin-right: 3%; }

请注意,我们只对前两列应用了右侧边距。 另一种方法是对所有列应用边距,但使用.col:last-child { margin-right: 0; }将其移除。 如果您打算使用可变数量的列,这可能是最好的方法。 请注意 IE 对 伪选择器 的支持不足。

jQuery JavaScript

它还没有被插件化,但它可能/应该被插件化。 在进行插件化之前,有一些事情我想要修复/使其不那么冗余,这些内容将在后面介绍。 您可以在这里查看 完整的带注释的 JavaScript 文件

我不会做一个完整的代码片段,但我会介绍一些有趣的行。

第一个选项卡和第一个内容框立即被声明为当前。 当前内容列被移动到 0 的顶部位置(因此它们是可见的),而不是 CSS 中的默认隐藏值。

$(".tabs li:first-child a, .content-box:first").addClass("current");
$(".box-wrapper .current .col").css("top", 0);

我们使用委托函数来处理选项卡上的单击事件,因为这非常有效(并且可以处理动态添加的选项卡,如果出现这种情况)

$("#slot-machine-tabs").delegate(".tabs a", "click", function() {
   // stuff
}

当发生单击时,只有在单击的选项卡 *不是* 当前选项卡 *并且* 页面上没有其他动画正在进行时,才会执行操作。 在这个有限的演示中,唯一可能的动画是列。 在更“真实”的环境中,此测试的范围可能应该缩小到老虎机选项卡特定的 ID 内。 例如$("#slot-machine-tabs *:animated)

$el = $(this);
		
 if ( (!$el.hasClass("current")) && ($(":animated").length == 0 ) ) {
    // stuff
}

我认为,如果列每次不以相同的速率更改,效果会更吸引人。 我为每个动画设置了伪随机速度,但基本值为半秒。 离开列和进入列的速度匹配,因此没有重叠。

speedOne = Math.floor(Math.random()*1000) + 500;
speedTwo = Math.floor(Math.random()*1000) + 500;
speedThree = Math.floor(Math.random()*1000) + 500;

请注意,在演示中,所有新的列始终从底部向上滑动。 但当它们离开时,它们向上滑动到顶部。 这是因为在向上滑动并消失动画完成后,我们会立即将它们向下移动到默认的低隐藏位置。 在执行此操作之前,我们需要确保所有动画都已完成。 我在做这件事时遇到了一些困难。 通常,要在动画完成后触发某件事,这没什么大不了,因为您可以在动画上有一个回调函数,它只在动画完成后触发。 但我们在这里运行了六个不同的动画,并且由于它们都花费了随机的时间长度,我们不知道哪个动画会最后结束。

到目前为止,我的解决方案是在每个完成的动画的回调上调用同一个函数。 例如

$(".box-wrapper .current .col-one").animate({
	"top": 0
}, speedOne, function() {
	ifReadyThenReset();
});

ifReadyThenReset() 函数只会在被调用三次时才会执行它的任务(重置列顶部位置)

var columnReadyCounter = 0;

function ifReadyThenReset() {
	
	columnReadyCounter++;
	
	if (columnReadyCounter == 3) {
		$(".col").not(".current .col").css("top", 350);
		columnReadyCounter = 0;
	}

};

问题

  • 它不是一个插件。
  • 参与选项卡更改事件的每一列都是独立动画的。 这意味着同时对六个事物进行动画处理,并且所有这些事物都是“硬编码”的。 这使得添加或删除列比应该的要麻烦。
  • 三次调用回调的做法对我来说似乎很笨拙,并且硬编码的三次调用与上述问题相同。

演示和下载

查看演示   下载文件

与往常一样,您可以随意使用它,包括在公司项目中使用它来给您的老板留下深刻印象,并以此为例说明为什么您应该加薪。