在阅读 antfu 大佬的 Animated SVG Logo 时,发现了一个很有意思的动画效果,最终实现的效果如下。
其中原理可以参考下面两篇文章:
下面以内马尔签名为例,介绍一下如何实现这个效果。
签名文件处理
谷歌上搜索内马尔签名,找到 原始 SVG 文件。
源文件为 fill 填充,而只有 stroke 才能实现此动画效果,所以需要使用 Adobe Illustration 或 Figma 或 Motiff 处理。
先将填充取消,并进行描边处理,最后使用钢笔工具在填充中间增加路径。
使用 AI 软件处理后,得到下面文件,再导出为 SVG 格式。由于技术不过关,少了很多细节处理,但是不影响效果。
实现动画
得到 stroke 文件后,就可以使用 stroke-dasharray
和 stroke-dashoffset
属性实现动画效果。
由于这里的有四条路径,因此需要分别设置四个动画。
这里先获得四条路径的总长度。
js
const path1 = document.getElementById('path1')
const path2 = document.getElementById('path2')
const path3 = document.getElementById('path3')
const path4 = document.getElementById('path4')
console.log(path1.getTotalLength())
console.log(path2.getTotalLength())
console.log(path3.getTotalLength())
console.log(path4.getTotalLength())
获取完长度后,因为需要依次出现每个路径,所以需要设置动画延迟时间,这样就能保证每个路径依次出现,最终就能得到上述的效果了。
css
:root {
--dash-array-1: 1722px;
--dash-array-2: 633px;
--dash-array-3: 436px;
--dash-array-4: 1386px;
}
@media (prefers-reduced-motion) {
path {
animation: none !important;
stroke-dasharray: unset !important;
}
}
@keyframes draw {
0% {
stroke-dashoffset: var(--dash-array);
opacity: 0;
}
100% {
stroke-dashoffset: 0;
opacity: 1;
}
}
path:nth-child(1) {
--dash-array: var(--dash-array-1);
animation: draw 1s ease forwards 0s;
}
path:nth-child(2) {
--dash-array: var(--dash-array-2);
animation: draw 0.7s ease forwards 1s;
}
path:nth-child(3) {
--dash-array: var(--dash-array-3);
animation: draw 0.5s ease forwards 2s;
}
path:nth-child(4) {
--dash-array: var(--dash-array-4);
animation: draw 1s ease forwards 3s;
}
path {
stroke-dashoffset: 1px;
stroke-dasharray: var(--dash-array);
transform-origin: center;
stroke: #303030;
opacity: 0;
}
适配 vuepress
这里使用的是 vuepress-theme-hope
主题,参考 替换主题组件 中通过导航栏组件别名替换了默认的导航栏组件。
js
alias: {
'@theme-hope/modules/navbar/components/NavbarBrand': path.resolve(
__dirname,
'./components/NavLogo.vue'
)
}
适配深色主题
vuepress
并未提供是否为深色主题的接口,需要监听 data-theme
属性变化,来判断是否为深色主题。
js
import { onUnmounted, onMounted, ref } from 'vue'
const isDarkMode = ref(false)
onMounted(() => {
const html = document.documentElement
console.log(html, '===')
isDarkMode.value = html.dataset.theme === 'dark'
// watch theme change
const observer = new MutationObserver(() => {
isDarkMode.value = html.dataset.theme === 'dark'
console.log(isDarkMode.value, '---')
})
observer.observe(html, {
attributeFilter: ['data-theme'],
attributes: true
})
onUnmounted(() => {
observer.disconnect()
})
})
console.log(isDarkMode.value, '---')