Quantcast
Channel: w3cplus
Viewing all articles
Browse latest Browse all 1557

CSS秘密花园: 环形文本

$
0
0

CSS Secrets》是@Lea Verou最新著作,这本书讲解了有关于CSS中一些小秘密。是一本CSSer值得一读的一本书,经过一段时间的阅读,我、@南北@彦子一起将在W3cplus发布一系列相关的读后感,与大家一起分享。

CSS Secrets

尽管这不是一个常见的文本效果,有时候有一些比较短的文本需要遵循环形路径显示。这种时候,CSS就弃我们而去了。没有任何CSS属性或功能可以完成这个效果,我们唯一想到的CSS解决方案都非常麻烦,所以我们也就只是想想而已。真的没有什么办法可以实现这样的样式吗?除了使用图像,除了不影响我们文本的整体美观?

环形文本

juliancheal.co.uk上使用环形文本作为左边的按钮(知道我指的是哪里吗?);注意那里的环形文本是避免打破按钮的唯一办法,按钮形状的中间位置是由孔和螺纹组成的

解决方案

有一些脚本可以完成这个效果。它们通过将每个字母包裹在单独的<span>元素中,然后旋转到合适的角度来把它们一个一个组合成圆。这个方案不仅非常麻烦,而且还给页面的DOM元素添加了很多不必要的臃肿的标记。

尽管目前没有办法通过纯CSS来完成这个效果,但是我们可以通过一点内联SVG来很简单地完成。SVG本身就支持文本以任何路径显示,环形只不过是路径的一个特殊情况。我们来试一下!

SVG中文本按路径显示的基本方案是通过一个<textPath>元素来包裹我们的文本,把它放到一个<text>元素中。<textPath>元素通过id引用我们的路径中定义的<path>元素。在内联SVG中的文本还继承了我们的字体样式(除了line-height,因为这是SVG中默认的样式),所以我们不需要担心这个问题,就像我们引入一个外部的SVG图像一样。

可惜,<textPath>只可以存在于<path>元素中,这就是我们为什么不可以使用可读性更好的<circle>元素作为我们的路径圆的原因。

假设我们要把“circular reasoning works because”这句话做成环形文本,占据一个圆圈的整个圆周,如图所示。

环形文本

我们需要在HTML中添加一个内联SVG,并定义一个圆形路径:

<div class="circular">
    <svg viewBox="0 0 100 100">
        <path d="M 0,50 a 50,50 0 1,1 0,1 z" id="circle" />
    </svg>
</div>

注意我们是通过viewBox,而不是widthheight来定义它的单位。这可以让我们设置坐标系统和长宽比,而不是一个固定的大小。这不仅是因为它更紧凑,还因为它帮我们节省了几行CSS,因为我们不再需要为<svg>元素应用值为100%widthheight值——它会自动调整为其容器的大小。

如果你不清楚path的语法,别担心。大家都是一样的,甚至是那些已经了解了SVG path语法的人,还是会忘记语法。如果你好奇,下面这三个命令已经非常好地概括了下面这些语法:

  • M 0,50: 移动到点 (0,50)
  • a 50,50 0 1,1 0,1: 从你当前点的位置,画一条圆弧,到一个新的点,新点的位置距原点往右0,往下1。圆弧的半径为50,水平和垂直方向都是。除了两种可能的角度,选择两条可能的弧形里边最大的,选择两个点中在右边的,而不是左边那个。
  • z: 通过直线段闭合路径。

为什么SVG路径的语法这么隐蔽?它当初被设计出来的时候,大家觉得没有人会手动编写SVG,所以SVG工作小组尽可能地用了最紧凑的语法,来减少文件大小。

目前,我们的路径只是一个黑色的圆。

环形文本

我们需要通过<text><textPath>元素添加文本,并通过xlink:href属性把它链到我们的圆上,如下:

<div class="circular">
    <svg viewBox="0 0 100 100">
        <path d="M 0,50 a 50,50 0 1,1 0,1 z" id="circle" />
        <text>
            <textPath xlink:href="#circle">
            circular reasoning works because
            </textPath>
        </text>
    </svg>
</div>

环形文本

如上图所示,尽管我们还需要做很多东西,来让它更像样更有可读性,我们已经取得了我们想要的效果,这是CSS很多年内都无法完成的!

下一步是移除我们的圆形路径上的黑色填充。我们并不希望圆形以任何方式显示出来;我们只是想要它作为我们文本的一个引导。有很多方式可以完成,比如把它加入到一个<defs>中(这是专门为此而设计的)。但是,这里我们希望能够尽量较少SVG标签,所以我们直接通过CSS应用一个fill: none

.circular path { fill: none; }

现在黑色的圆已经消失了

环形文本

我们可以仔细研究其它问题了。下一步最大的问题是,我们大多数的文本是在SVG元素之外的,还被裁剪了。为了解决这个问题,我们需要让我们的容器元素更小一些,然后为SVG元素应用overflow: visible,这样它就不会裁剪它的视窗之外的任何内容了:

.circular {
    width: 30em;
    height: 30em;
}
.circular svg {
    display: block;
    overflow: visible;
}

环形文本

你可以在上图中看到效果。注意到我们已经差不多完成了,但是有一些文本还是被裁剪了。原因是SVG元素是根据它的尺寸浮动的,而不是溢出。因此,事实上,溢出盒子边界的<svg>元素不会让整个SVG元素往下浮动。我们需要手动添加一个外边距来完成:

.circular {
    width: 30em;
    height: 30em;
    margin: 3em auto 0;
}
.circular svg {
    display: block;
    overflow: visible;
}

就是它了!我们的示例现在看起来和下图非常相像

环形文本

文本也是可访问的。如果我们的圆形文本只有一个实例(如,一个网站的logo),那我们到这里就完成了。但是,如果我们关于这种类型的文本有不止一个实例,我们不希望每次都重复这些SVG标签,可以写一个简短的脚本来自动生成所需的SVG元素,如类似这样的标签:

<div class="circular">
    circular reasoning works because
</div>

该代码可以通过一个“circular”类遍历所有元素,移除它们的文本并把它存储在一个变量中,并添加必要的SVG元素:

$$('.circular').forEach(function(el) {
    var NS = "http://www.w3.org/2000/svg";
    var xlinkNS = "http://www.w3.org/1999/xlink";
    var svg = document.createElementNS(NS, "svg");
    var circle = document.createElementNS(NS, "path");
    var text = document.createElementNS(NS, "text");
    var textPath = document.createElementNS(NS, "textPath");
    svg.setAttribute("viewBox", "0 0 100 100");
    circle.setAttribute("d", "M0,50 a50,50 0 1,1 0,1z");
    circle.setAttribute("id", "circle");
    textPath.textContent = el.textContent;
    textPath.setAttributeNS(xlinkNS, "xlink:href", "#circle");
    text.appendChild(textPath);
    svg.appendChild(circle);
    svg.appendChild(text);
    el.textContent = '';
    el.appendChild(svg);
});

扩展阅读

如需转载,烦请注明出处:http://www.w3cplus.com/css3/css-secrets/circular-text.html


Viewing all articles
Browse latest Browse all 1557

Trending Articles