Underliner 是一款基于 SVG 的超链接鼠标悬停下划线动画特效。Underliner 插件可以在鼠标 hover 或悬停在指定的超链接元素上面时,以动画的方式显示预定义的 SVG 元素。
<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" viewBox="0 0 100 100">
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#00bc9b" />
<stop offset="100%" stop-color="#5eaefd" />
</linearGradient>
<linearGradient id="gradient2" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#5eaefd" />
<stop offset="100%" stop-color="#00bc9b" />
</linearGradient>
</defs>
</svg>
<div class="group">
<ul class="c-nav underliner">
<li class="c-nav__item"><a href="#">News & Politics</a></li>
<li class="c-nav__item"><a href="#">Culture</a></li>
<li class="c-nav__item"><a href="#">Technology</a></li>
<li class="c-nav__item"><a href="#">Business</a></li>
<li class="c-nav__item"><a href="#">Human Interest</a></li>
</ul>
<ul class="c-nav c-nav-2 underliner-small">
<li class="c-nav__item"><a href="#">Works</a></li>
<li class="c-nav__item"><a href="#">Articles</a></li>
<li class="c-nav__item"><a href="#">About</a></li>
<li class="c-nav__item"><a href="#">Contact Me</a></li>
<li class="c-nav__item"><a href="#">My Books</a></li>
</ul>
</div>
ul li {
list-style: none;
}
svg {
display: block;
}
.group {
padding: 1rem;
margin-bottom: 2.5rem;
}
.c-nav:not(:last-child) {
margin: 1rem 0 2rem;
}
@media (min-width: 900px) {
.c-nav {
display: flex;
justify-content: center;
}
}
.c-nav__item {
margin-right: 1.5rem;
font-size: 1.5rem;
}
.c-nav__item a {
display: inline-block;
text-decoration: none;
color: #000;
margin-bottom: 4px;
transition: 0.4s;
}
.c-nav__item svg {
pointer-events: none;
transition: 0.5s;
}
.c-nav__item path {
transition: stroke-dasharray 0.5s, stroke-dashoffset 0.5s, opacity 0.5s;
}
.c-nav__item path:last-child {
opacity: 0.2;
}
.c-nav__item a:hover, .c-nav__item a:focus {
color: #00bc9b;
}
.c-nav__item a:hover + svg,
.c-nav__item a:focus + svg {
opacity: 1;
}
.c-nav__item a:hover + svg path,
.c-nav__item a:focus + svg path {
stroke-dashoffset: 0;
}
#path1 {
opacity: 0.25;
}
.c-form {
max-width: 500px;
margin: 0 auto;
}
.c-form legend {
margin-bottom: 1rem;
font-weight: bold;
}
.c-form label {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.c-form__item {
margin-bottom: 1rem;
}
.c-nav-2 .c-nav__item {
font-size: 1rem;
}
hr {
border: 0;
height: 1px;
background: lightgrey;
}
.ishadeed {
text-align: center;
}
.ishadeed a {
color: #3563D9;
}
class Underliner {
constructor(selector, color1, color2, thickness1, thickness2, strokeLinecap, rtl) {
this.links = document.querySelectorAll(selector)
this.fill = 'transparent';
this.color1 = color1;
this.color2 = color2;
this.thickness1 = thickness1;
this.thickness2 = thickness2;
this.strokeLinecap = strokeLinecap;
this.rtl = rtl;
this.init();
}
init() {
let self = this;
self.links.forEach(function (link) {
let linkWidth = parseInt(link.offsetWidth);
let svg = self.createSVG(linkWidth);
self.insertAfter(svg, link);
});
}
setPath(pathD, color, thickness, strokeLinecap) {
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("d", pathD);
path.setAttribute("fill", this.fill);
path.setAttribute("stroke", color);
path.setAttribute("stroke-width", thickness);
path.setAttribute("stroke-linecap", strokeLinecap);
path.setAttribute("stroke-dasharray", path.getTotalLength() + 10);
path.setAttribute("stroke-dashoffset", path.getTotalLength() + 10);
return path;
}
randomizePath(linkWidth) {
let moveYMin = 5;
let moveYMax = 12;
let curveXMin = 15;
let curveXMax = linkWidth; /* Width of the link */
let curveYMin = 7;
let curveYMax = linkWidth * 0.12; /* Making the quadratic propotional to the link width */
//let curveYMax = 20
let endYMin = 5;
let endYMax = 11;
let moveY = Math.floor(Math.random() * (moveYMax - moveYMin)) + moveYMin;
let curveX = Math.floor(Math.random() * (curveXMax - curveXMin)) + curveXMin;
let curveY = Math.floor(Math.random() * (curveYMax - curveYMin)) + curveYMin;
let endY = Math.floor(Math.random() * (endYMax - endYMin)) + endYMin;
return `M5 ${moveY} Q ${curveX} ${curveY} ${linkWidth - 7} ${endY}`
}
createSVG(linkWidth) {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width", linkWidth);
svg.setAttribute("height", "35");
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
const path2 = document.createElementNS("http://www.w3.org/2000/svg", "path");
let pathD = this.randomizePath(linkWidth);
let pathD2 = this.randomizePath(linkWidth);
if(this.rtl === true) {
pathD = this.reverseMe(pathD);
pathD2 = this.reverseMe(pathD2);
}
svg.appendChild(this.setPath(pathD, this.color1, this.thickness1, this.strokeLinecap));
svg.appendChild(this.setPath(pathD2, this.color2, this.thickness2, this.strokeLinecap));
svg.setAttribute("focusable", false);
return svg;
}
reverseMe(path) {
/* Regex functions borrwed from
https://github.com/krispo/svg-path-utils/blob/master/src/svg-path-utils.js */
let pathOperators = path.replace(/[\d,\-\s]+/g, '').split('');
let pathNums = path.replace(/[A-Za-z,]+/g, ' ').trim().replace(/\s\s+/g, ' ').split(' ');
return `${pathOperators[0]} ${pathNums[4]} ${pathNums[5]} ${pathOperators[1]} ${pathNums[2]} ${pathNums[3]} ${pathNums[0]} ${pathNums[1]}`;
}
// https://plainjs.com/javascript/manipulation/insert-an-element-after-or-before-another-32/
insertAfter(el, referenceNode) {
referenceNode.parentNode.insertBefore(el, referenceNode.nextSibling);
}
}
let test = new Underliner(".underliner a", "url(#gradient)", "url(#gradient2)", 7, 12, "round", false);
let test2 = new Underliner(".underliner-small a", "url(#gradient)", "url(#gradient2)", 3, 6, "round");
Codepen 网址:https://codepen.io/shadeed/pen/OeNbMO
演示地址 | 下载地址 |
专业提供WordPress主题安装、深度汉化、加速优化等各类网站建设服务,详询在线客服!