人人网FED博客

专注于前端技术

SVG导航下划线光标跟随效果

之前看到一篇博文,介绍导航下划线光标跟随的效果,是用的CSS的hover结合CSS3的选择器做的,总感觉效果不太自然,所以我就在想能不能用SVG来做这个效果,试了一下,还是可以的,不过要借助一点JS。先来看下一下按正常思路用JS应当怎么实现。

1. 正常思路的实现

我的思路是这样的,在导航的下样画一条线,然后当鼠标hover的时候看它是在哪个item上面,然后改变下划线的位置,并给这个位置加一个transition的动画。html结构:

动画的CSS:

因为不同导航宽度不一样,所以除了transform之外再加个width的动画。

然后监听导航的mouseover事件,在里面改变下划线的transform和width属性,激发transition动画:

效果如下图所示(录屏的效果稍微差了一点,实际上是挺好的):

一个demo:underline-move-js.html.(手机可通过点击触发mouseover事件)

感觉这个的效果已经很好了,而且代码也不复杂,主要是postition计算那里稍微复杂一点,如果没有用jq的话。

用SVG的动画可以怎么实现呢?

2. SVG动画效果

SVG有一个animate标签,可以指定需要做动画的属性,还可以通过begin属性指定动画开始的时机,例如某个元素hover的时候才开始动画。如果用CSS的hover配合选择器话,它有一个缺点就是只能选相邻或者子元素,这个限制就导致我们只能把下划线放在导航栏元素里面,如做为它的border-bottom,或者紧跟在所有元素的后面。而svg没有这个限制,svg动画的触发可以是页面任意svg元素的任意事件

所以我们借助这个特性来做动画,先用svg的rect画一条线,这条线具有宽度和位置的属性,导航栏元素li触发mouseenter的时候就相应地改变这个rect的x坐标(注意mouseenter和mouseover的区别,mouseenter是不会冒泡的,这里要用mouseenter,避免li的子元素冒泡上去)。由于触发只能是svg的元素才有效,所以需要在每个li里面写一个svg给盖住:

这里面每个svg元素都是用的一个透明的矩形rect铺满,每个rect都有一个id:svgitem1、svgitem2等。

然后再用一个独立的svg的rect画下划线:

把这个svg绝对定位到导航栏nav第一个元素的下面:

然后给这个svg添加动画元素:

其中attributeName指定要做动画的属性,这里为x坐标,还有from/to属性,表示从哪个值变到哪个值,这里没有from,就会使用当前的值,这里先指定固定的to,我们先假定导航是等宽的,每个为80px,所以to的值递增。dur表示动画的时间,这里为0.2s。fill=”freeze”表示动画结束后停留在最后一帧。begin为动画的触发时机,每个animate元素的begin对应每个li里面svg元素,当li的svg触发mouseenter的时候就会开始相应的animate动画。而动画的效果是改变to属性指定的值,这样就实现了x坐标随着光标移动的动画。

缺点是需要写很多animate元素,但是一般我们用模板渲染,所以这个情况应该会好些。效果如下图所示:

一个完整的demo:underline-move-svg.html.

上面我们假定了导航是等宽的,但实际上导航往往是不等宽的,那怎么办呢?不等宽的情况基本只能借助JS计算元素宽度,然后动记地改变animate元素里面to的值,同时添加一个下划线宽度width的动画,如下示例:

然后写一点JS改变一个这两个to的值:

如果使用Vue/React等框架,可以在mounted的时候,动态地改变to绑定的值,这个代码也是挺简单的。

效果如下:

一个完整的Demo:underline-move-svg-2.html.

这个效果也很好,但是相对来说还是第1点正常思路的实现比较简单一点。有个问题就是重复进入同一个元素会重复动画,它的from还是上一次的值。

但是不管怎么样,当你发现用html/css不太好做动画时,可以往SVG动画的方向思考,SVG做动画还是很灵活的。上面那个例子其实不是很典型,再介绍另一个使用SVG做动画的例子。

3. SVG路径动画

SVG还可以做路径的动画,可以用钢笔工具勾勒一条路径,然后让目标元素沿着这个路径运动。如下效果示例:

代码如下所示:

这里主要使用了animateMotion标签,通过它的mpath指定一个动画的路径,就可以了。

这个路径可以用PS的钢笔工具画一个形状,然后导出成SVG(PS CC版本支持)或者是用在线的一些svg编辑工具也是可以。而运动的目标元素(如上面的手)可以使用图标字体里的SVG,这里比较麻烦的地方是自己勾勒的路径需要根据图标字体SVG的viewbox做调整,比例才对得上。

例如如果图标字体的viewbox是1024 * 1024的,那么你的PS画布大小也得是1024 * 1024的,如下笔者画的孤形路径:

(据说现在的前端很多不会PS,因为公司切图基本用zeplin).

导出来的SVG文件是这样的:

复制里面的path路径:

M-514,665c0,0,1378.463-1138.762,2891,0

作为上面动画的路径。

我们可以进以进一步完善这个动画,如果现在要求动画不是立马开始,如刷新页面后隔个0.5s再开始,并且要求那只手一开始的时候没有出现,动画开始才出现,主要是为了给个时间先处理页面其它元素。

可以把动画的begin改成0.5s,hand元素一开始设置成不可见:

上面代码主要通过在g元素里面添加一个set标签:

也就是说动画的begin可以是另外一个动画的begin或者end。如果要在某个动画结束后开始,则可以把begin改成arcmove.end即可。

 

相关阅读:

  1. 怎样做一个圆环放大的动画

 

【号外】《高效前端》已经上市 ,京东和亚马逊、早读课等均有售

 

目录: 基础技术, 页面优化

Tags:

1 回复

  1. 前几天刚看到腾讯的CSS实现下划线跟随效果,今天又读到SVG实现。前端界现在真是百花齐放,百家争鸣啊。

发表评论

电子邮件地址不会被公开。 必填项已用*标注