11

仅使用 HTML 和 CSS 实现的标签云效果

 3 years ago
source link: https://mp.weixin.qq.com/s/6608S_jwINLYnSI_NPxacA
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

标签云的效果在博客和网站上不难见到,它其实就是带有超链接的某些关键字,为了达到强调主题的作用。通常出现概率比较大或者受欢迎的标签文字显示比较大,相反的就显示的小。

zaqyUvV.png!mobile 来源于TagCrowd.com

我们就不去深入研究标签云带来的效率上的提升和可用性的细节,仅仅在外观上带来的美感和对全站或一整篇文章所起到的高度概括的作用就不容忽视了。

接下来,我们将讨论如何用HTML和CSS来创建标签云效果。注意,我们仅仅讨论如何实现这种UI效果而不去深究标签的权重或受欢迎程度是怎么算出来的。

HTML基础结构

前面说了,标签云就是一个连接列表。所以从语义化的视角,使用无序列表来表达每个标签是合理的,不用考虑按欢迎程度去排列,否则就毫无意义了。

在每个列表项里包含一个超链接标签,所以大体结构就是下面的样子:

<ul>
  <li><a href="/tag/word1">Word1</a></li>
  <li><a href="/tag/word2">Word2</a></li>
  <li><a href="/tag/word3">Word3</a></li>
  <!-- ... -->
</ul>

每个标签的权重要提前算好,然后把它加到 <a> 上或者 <li> 上,我们就暂且把它加到链接上。

权重大的标签对应显示的文字也大,代表了它的受欢迎程度大。

<ul>
  <li><a href="/tag/word1" data-weight="3">Word1</a></li>
  <li><a href="/tag/word2" data-weight="7">Word2</a></li>
  <li><a href="/tag/word3" data-weight="4">Word3</a></li>
  <!-- ... -->
</ul>

注意:这里的data-weight是通过data-count和data-total计算而来的,这里没办法直接通过两个属性计算表示,所以我们把目标聚焦在data-weight这样一个属性上。

这样基础的HTML结构代码就写好了,只要稍加一些属性就完美了。

  • class:有助于在添加样式的时候确定是哪个标签云

  • role:这个是一个导航组件,在屏幕阅读器上标识和辅助作用

  • aria-label:给辅助功能添加标题和描述

注意:如果列表位于 <nav> 标签内,就不需要添加 role="navigation" ,可以使用aria-labelledby代替导航标签来指向导航标题。

让我们来完善一下列表数据,添加一些和开发相关的名词作为标签,这样看起来更贴近实际一点。

<ul class="cloud" role="navigation" aria-label="Webdev tag cloud">
  <li><a data-weight="4" href="/tag/http">HTTP</a></li>
  <li><a data-weight="2" href="/tag/ember">Ember</a></li>
  <li><a data-weight="5" href="/tag/sass">Sass</a></li>
  <li><a data-weight="8" href="/tag/html">HTML</a></li>
  <li><a data-weight="6" href="/tag/flexbox">FlexBox</a></li>
  <li><a data-weight="4" href="/tag/api">API</a></li>
  <li><a data-weight="5" href="/tag/vuejs">VueJS</a></li>
  <li><a data-weight="6" href="/tag/grid">Grid</a></li>
  <li><a data-weight="3" href="/tag/rest">Rest</a></li>
  <li><a data-weight="9" href="/tag/javascript">JavaScript</a></li>
  <li><a data-weight="3" href="/tag/animation">Animation</a></li>
  <li><a data-weight="7" href="/tag/react">React</a></li>
  <li><a data-weight="8" href="/tag/css">CSS</a></li>
  <li><a data-weight="1" href="/tag/cache">Cache</a></li>
  <li><a data-weight="3" href="/tag/less">Less</a></li>
</ul>

下面是标签云现在看起来的效果:

NVbmaen.png!mobile

这是还没有添加任何样式的结果,然后给它添加一些样式

给标签云添加样式

以下是我们要通过添加样式解决的:

  • 让标签以行内元素显示

  • 让每个标签的font-size属性根据 data-weight 大小显示

  • 在标签的右边加上权重

  • 让标签的颜色随机显示

  • 给标签添加:hover和:focus动态样式

给ul添加样式

首先移除列表前面的小圆点和缩进

list-style: none;
padding-left: 0;

然后设置 ul 以Flexbox显示,并且水平垂直居中保证所有的标签元素在一行或者多行内显示

display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;

添加上行高相互之间保持一定的垂直距离,最终的ul元素的样式如下:

ul.cloud {
  list-style: none;
  padding-left: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  line-height: 2.5rem;
}

然而这个时候标签云看起来还差很多

f67nayf.png!mobile

根据权重调整标签的大小

让我们开始给标签一些小的变化

ul.cloud a {
  color: #a33;
  display: block;
  font-size: 1.5rem;
  padding: 0.125rem 0.25rem;
  text-decoration: none;
  position: relative;
}

通过上面的样式,所有的标签变成淡红色并且设置1.5rem大小

2iu6zq3.png!mobile

然而我们的要求是字体大小根据 data-weight 而来的,怎么实现呢?

web标准有一种方式是CSS可以通过 attr() 方法读取HTML的data属性值,所以我们可以通过以下方式读取 data-weight

attr([attribute-name] [attribute-unit]? [, default-value]?)

不幸的是,目前任何浏览器均不支持该表示法和功能。相反,attr()将仅返回一个字符串,并且只能在content属性中使用。

如果web标准支持这种做法,那好办,我们可以直接读取属性权重的数据,将它们保存到CSS变量中,并直接对其进行操作,如下所示:

ul.cloud a {
  --size: attr(data-weight number, 2); 
  font-size: calc(var(--size) * 1rem);
}

但是这样不行,我们只能通过CSS规则,属性选择器:data-attribute

ul.cloud a[data-weight="1"] { --size: 1; }
ul.cloud a[data-weight="2"] { --size: 2; }
ul.cloud a[data-weight="3"] { --size: 3; }
ul.cloud a[data-weight="4"] { --size: 4; }
ul.cloud a[data-weight="5"] { --size: 5; }
ul.cloud a[data-weight="6"] { --size: 6; }
ul.cloud a[data-weight="7"] { --size: 7; }
ul.cloud a[data-weight="8"] { --size: 8; }
ul.cloud a[data-weight="9"] { --size: 9; }

ul.cloud a {
  --size: 4;
  font-size: calc(var(--size) * 0.25rem + 0.5rem);
  /* ... */
}

为了避免将字体大小直接设置成权重导致太大,因此通过一定的方法计算,结果显示如下:

uaEB3yB.png!mobile

现在看来已经小有成就。

为标签添加权重显示

我们常见的标签云效果在标签的旁边都有权重显示,权重已经有了,我们要将它展示在伪元素 ::after 的content中

ul.cloud[data-show-value] a::after {
  content: " (" attr(data-weight) ")";
  font-size: 1rem;
}

然后还需要给 ul 元素增加 [data-show-value] 属性选择器,将它的值设为true或者false来控制标签后面的数字权重是否显示

注意: data-show-value 属性选择器的值为布尔值,即使你设置为false,它也会显示,如果不让它显示,就移除该属性

这是显示数字权重的效果

reyqEnf.png!mobile

下面的案例中将不显示标签旁边的数字

为标签云添加颜色

所有的标签一个颜色看起来很沉闷,我们将尝试用两种不同的方法来为它添加不同的颜色。

使用随机生成的颜色

在CSS中没有随机数这么一说(虽然可以模拟出来)。我们将要做的是根据标签的序列号来给它定义不同的颜色,方法如下:

ul.cloud li:nth-child(2n+1) a { --color: #181; }
ul.cloud li:nth-child(3n+1) a { --color: #33a; }
ul.cloud li:nth-child(4n+1) a { --color: #c38; }

通过这种方式我们给它添加了绿色,蓝色和紫色代替了之前的淡红色,虽然也不是完全随机(有一定的规律在),但是用户很难发现。

aAzMFnY.png!mobile

使用同一种颜色的不同透明度值

我们通过增加标签对比度来达到强调受欢迎程度的效果,在浅色背景下,使用深色更加明显。

HSL格式的颜色值可以实现,但是我们采用最快的方式,直接对标签设置透明度,透明度的值根据权重值计算而来:

看下变化后的效果

B7z2qqv.png!mobile

定义它的轮廓

当我们触摸标签的时候 outline 样式会比较重要,尤其对于残障人士在可访问性方面。

我们将给 outline 添加和标签字体颜色一致的边框

ul.cloud a:focus {
  outline: 1px dashed;
} 
euQFNnb.png!mobile

在这里我们是通过鼠标点击事件去模拟的

添加动态效果

为了增加交互性,我们为它添加一个简单的动画:当用户将鼠标移动或者悬停在一个标签上时,标签的背景色变换并且有一个水平展开的效果。

因为它是取决于状态的动画,所以我们将使用transition而不是animation动画。

实现方式是通过 a 标签的伪元素::before的宽度值变化,在focus和hover状态下从0变为100%来达到这样一个微交互。

CSS代码和伪元素的状态行为:

ul.cloud a::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: 0;
  height: 100%;
  background: var(--color);
  transform: translate(-50%, 0);
  opacity: 0.15;
  transition: width 0.25s;
}

ul.cloud a:focus::before,
ul.cloud a:hover::before {
  width: 100%;
}
INfq2mz.gif!mobile

对于动画效果:用户不能根据自己的喜好来开启或者关闭。如果真是一个需求的话,我们还是要尊重用户的喜好移除动画的。

可以通过媒体查询特性 prefers-reduced-motion 来达到

(https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion

@media (prefers-reduced-motion) {
  ul.cloud * {
    transition: none !important;
  }
}

好了,以上就是实现全过程,如果有哪里不懂,可以留言一起交流。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK