让文字沿着路径动起来 (SVG)

路径动画的效果还是挺有意思的,而 Web 中常用的方法就是 SVG。 先上一个效果图:

SVG

要在 SVG 里面实现文字路径动画还是比较简单的,SVG 里面就有天然的支持。

我们先搞个 SVG 路径

        <svg id="textPathDemo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="960" viewBox="0 0 581 120">
            <path stroke="#FF4444" d="M62.9 14.9c-25-7.74-56.6 4.8-60.4 24.3-3.73 19.6 21.6 35 39.6 37.6 42.8 6.2 72.9-53.4 116-58.9 65-18.2 191 101 215 28.8 5-16.7-7-49.1-34-44-34 11.5-31 46.5-14 69.3 9.38 12.6 24.2 20.6 39.8 22.9 91.4 9.05 102-98.9 176-86.7 18.8 3.81 33 17.3 36.7 34.6 2.01 10.2.124 21.1-5.18 30.1" fill="none" stroke-width="1" stroke-dasharray="5 5" id="text-path1"></path>
            <text>
                <textPath  xlink:href="#text-path1" class="text-content" textLength="160" startOffset="160">
                    SVG 文字路径动画 
                </textPath>
            </text>
        </svg>

这里的 path 就是用来定义路径的,这个路径我是网上找的,一般做的话可以用 AI 等 SVG 制作工具,几个关键属性介绍一下。

  • stroke-dasharray 表示用虚线描边。可选值为none, , inherit。
    • none 表示不用虚线描边
    • inherit 表示继承

    这个属性很有用,基本上 SVG 动画都需要用到,这是一个逗号或者空格分隔的数值列表,第一个值表示线段的长度,第二个值表示线段间空白的长度,例子中 stroke-dasharray="5 5"中,第一个 5 表示虚线中的线段的长度,而第二个 5 表示两个线段间的长度是 5px。所以出来的效果是每段虚线的间隔都是一样的。其实这个dasharray可以不只两个值,可以有很多个,会循环依次用到线段和空白之间。

  • stroke,stroke-width 分别表示描边长度和描边宽度,这里就是改颜色用的。

这里比较关键的是 textPath 这个节点,这里就是定义文字按照什么路径排列的。

其中主要的属性: - xlink:href : 这里表示要用哪条路径,注意我们在 path 那设置的 id 属性。 - textLength : 表示文字的长度,这里 160 是我瞎猜的。 - startOffset : 表示文字开始的偏移量,也就是文字开始点在路径中的位置。

现在出来的效果是这样的:

现在还没动,要让它动起来很简单,上面我们介绍了 startOffset 属性表示文字起始点,只要改变这个起始点就可以动了。

于是我们来加点 javascript:

    var txtEle = $(".text-content");
    var rafId = null;
    var isEnd = false;

    var animateFunc = function(start, step, end){
        var startOffset = start + step;
        if (startOffset >= end){
            startOffset = end
            isEnd = true;
        }
        // txtEle.attr("startOffset", startOffset); // not work
        txtEle[0].setAttribute("startOffset", startOffset);

        if(isEnd){
            cancelAnimationFrame(rafId);
            return;
        }

        requestAnimationFrame(function(){
            animateFunc(startOffset,step,end);
        })
    }

    var steps = Math.round( 5000/16.7 );  // 5000 means 5s
    var len = 830;

    var step = len / steps;

    animateFunc(-160, step, len);

代码比较简单,但是有几个地方我要说明一下。 首先是我把 jQuery 里面设置的 attr 的方法 txtEle.attr("startOffset", startOffset); 注释了,因为 jq 会把属性用小写写到元素里面,而这里的 startOffset 偏偏是大小写敏感的,所以要改用 dom 的 setAttribute 方法。 然后,这段代码里面有几个地方写的是 hard code,比如 var len = 830;,中的 830,animateFunc(-160, step, len); 中的 -160。

这里 830 表示的是文字要走的路径的长度,这 830 是我大概算出来的,因为我们想文字停在最后那里,而不是跑出去,计算方法是路径的长度减去文字长度,当然这个值不是很准确。

animateFunc(-160, step, len); 中的 -160 表示的是文字起始点,因为我们想有个进场效果,而不是所有文字一开始就排在起始点,所以要负出去,而 160 是我大概算的文字长度,这里也不准确。

写完这段 js,发现自己兜了个大圈。之前在 一个比想象中更骚气的圆-svg实现 一文中介绍过 SVG 的 animate 标签,如果只是单纯的动,这里大可以用 animate 来做。

添加 animatetextPath 中:

        <textPath  xlink:href="#text-path1" class="text-content" textLength="160" startOffset="-160">
            SVG 文字路径动画 
             <animate attributeName="startOffset" from="-160" to ="830" begin="0s" dur="5s" repeatCount="1"/>
        </textPath>

这里 attributeName表示动画属性是 startOffset,begin 表示开始的延时,dur 表示时间整个动画的时间,frome 和 to 表示初始值和最终值,repeatCount 表示重复次数,这里是 1 次,如果用 indefinite 表示无限次。只是加这个进去,是没有停在最后的样式的,做完一次,字就不见了。这里 from 和 to 也可以用 0% 和 100% ,这样就没有进场效果。这个节点虽然好用,但是还是比较适合无限循环运动的场景。

这里要想精细的控制,还是要靠 js。

但是,作为一个要弄懂这是什么,从哪里来,到哪里去的程序员,面对代码中很多半猜半算的值,是不能视而不见的,而且路径什么的现在都是写死的,弊端略大,于是我们可以借助一个强大的库,snap.svg.js。

Snap.svg

Snap.svg 是一个强大的 SVG 操作库,提供了丰富的接口,唯一的问题是,你需要了解熟悉 SVG 的基础知识。

引入 Snap.svg 之后,我们的 html 代码可以变得比较简单:

    <svg id="textPathDemo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="960" viewBox="0 0 581 120">

    </svg>
    <script src="http://cdn.bootcss.com/snap.svg/0.4.1/snap.svg-min.js"></script>

就留一个 SVG 元素就好。

然后我们就开始利用 Snap.svg 帮我们干活。

    window.onload = function () {
        var path = "M62.9 14.9c-25-7.74-56.6 4.8-60.4 24.3-3.73 19.6 21.6 35 39.6 37.6 42.8 6.2 72.9-53.4 116-58.9 65-18.2 191 101 215 28.8 5-16.7-7-49.1-34-44-34 11.5-31 46.5-14 69.3 9.38 12.6 24.2 20.6 39.8 22.9 91.4 9.05 102-98.9 176-86.7 18.8 3.81 33 17.3 36.7 34.6 2.01 10.2.124 21.1-5.18 30.1";

        var s = Snap('#textPathDemo');

        path = s.path(path).attr({ 'fill': 'none', 'stroke': '1',"stroke":"#FF4444","stroke-dasharray":"5 5"});

        var pathLength  = Snap.path.getTotalLength(path);

        console.log(pathLength);

        var txt = s.text(0,0,'SVG 文字路径动画');

        var txtLength = txt.node.clientWidth;

        var text = txt
                .attr({ 'textpath': path, 'fill':'#003399' })
                .textPath.attr({ 'startOffset': -txtLength })
                .animate({ 'startOffset': pathLength-txtLength }, 5000, mina.easeinout );
    };

代码不是很长,简单解释一下。

  1. 一开始定义了一段 path,这里就是我们要用来做路径动画用的。
  2. 然后创建一个 snap 对象:var s = Snap('#textPathDemo');
  3. 把 path 放到我们的 snap 中,并设置了一堆之前我们在 html 里面设置的属性 path = s.path(path).attr({ 'fill': 'none', 'stroke': '1',"stroke":"#FF4444","stroke-dasharray":"5 5"});
  4. 高潮来了,var pathLength = Snap.path.getTotalLength(path); 算出路径的长度,不用猜了。
  5. 创建文字,var txt = s.text(0,0,'SVG 文字路径动画');
  6. 算出文字长度,var txtLength = txt.node.clientWidth; ,这里说一下,我在 Snap 的文档中没找到怎么算文字长度的,这个是打印上面那个 txt 对象后发现的,如果有更好的方法,麻烦告知哈。
  7. 给文字设置一堆东西 var text = txt.attr({ 'textpath': path, 'fill':'#003399' }) ,主要设置 textpath
  8. 设置 textpath 中的属性:.textPath.attr({ 'startOffset': -txtLength }) 这里是支持链式调用的,设置起始点为 -txtLength 是为了有文字进场效果。
  9. 设置动画,给爷动一个。.animate({ 'startOffset': pathLength-txtLength }, 5000, mina.easeinout ); 这里设置了要做动画的属性为 startOffset,最后的位置为 pathLength-txtLength 即路径长度减去文字长度,动画时间 5000 毫秒,以及动画函数 main.easeinout 这个动画函数是库提供的,类比 css3 中的 easeinout 的效果,比之前我们用匀速要生动一些。

效果:

简简单单,再也不用猜长度值了,文字改版的时候也不用手动计算文字长度。 产品经理跟我说文字是不会变的,我知道这是最大的谎言。

结语

由于我对 SVG 也不是太熟悉,有些地方可能有所疏漏,有什么疑问欢迎留言,有什么写错的地方,欢迎指出哈。

SVG Demo 源码地址:https://github.com/bob-chen/canvas-demo/blob/master/basic/path-animation-svg.html

Snap.svg Demo 源码地址:https://github.com/bob-chen/canvas-demo/blob/master/basic/path-animation-snap.html

如果页面使用了 base 标签,可能会引起路径找不到的情况,可以参考 tips - 解决 base 标签造成 SVG 效果失效

参考

http://snapsvg.io/

http://javascript.ruanyifeng.com/htmlapi/svg.html#toc3

http://www.htmleaf.com/ziliaoku/qianduanjiaocheng/201506262114.html

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏葡萄城控件技术团队

CSS变量(自定义属性)实践指南

Sass和Less这样的预处理器,让我们的CSS代码保持良好的结构和可维护性。像变量、混合(mixins)、循环控制等特性,增强了动态编写CSS的能力,从而减少...

1101
来自专栏技术博客

Knockout.Js官网学习(visible绑定)

让visible绑定到DOM元素上,使得该元素的hidden或visible取决于绑定的值。

1111
来自专栏阿炬.NET

按键精灵-常用脚本命令汇集

3689
来自专栏hightopo

原 基于 HTML5 Canvas 的 3

1525
来自专栏Nian糕的私人厨房

CSS 消除 inline-block 元素间的间隙

从上图的运行结果可以看到,添加 display: inline-block; 属性后,水平呈现的元素间产生了空隙,出现这一现象的本质是,HTML 中存在的空白符...

1194
来自专栏代码世界

jQuery

jQuery jQuery介绍 jQuery 是一个轻量级的、兼容多浏览器的JavaScript 库; jQuery 使用户能够更方便地处理HTML Docum...

5165
来自专栏知道一点点

bootstrap快速入门笔记(六)-代码

一,内联代码<code>:For example, <code>&lt;section&gt;</code> should be wrapped as inli...

862
来自专栏Android先生

Android开发人员初识JavaScript

JavaScript是一种脚本语言;网页,以及基于H5的手机app等都靠JavaScript来驱动;更简单的来说,JavaScript就像是一种运行在浏览器中的...

1172
来自专栏极乐技术社区

微信小程序入门《二》实例:条件渲染、数据遍历、网络请求、获取本地图片

实例内容 条件渲染 数据遍历 网络请求 获取本地图片 实例一: 条件渲染 如果motto为Hello World,则输出你好世界,否则原样输出。 这里是分支条件...

3099
来自专栏极乐技术社区

wxss学习系列《一》定位(position),布局(Layout)

定位(position) 2017的微信公开课pro如期进行了,小程序将于2017年1月9日对个人开放,公司项目的demo版做了个大概,过程中花的时间最多的还是...

33310

扫码关注云+社区

领取腾讯云代金券