SVGアニメーションを作るときのポイント

自分の新しいロゴを作ってみた。
このロゴはアニメーションを想定して作っている。

SVGの中身はこんなふうになっている。

<?xml version="1.0" encoding="UTF-8"?>
<svg id="layer1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 583 583">
<defs>
<style>
#layer1{
    --logo-color:#333;

    /* linear */
    --ease-linear:cubic-bezier(0.0, 0.0, 1.0, 1.0);
    /* Sine(最も弱い) */
    --ease-in-sine:cubic-bezier(0.47, 0, 0.745, 0.715);
    --ease-out-sine:cubic-bezier(0.39, 0.575, 0.565, 1);
    --ease-in-out-sine:cubic-bezier(0.445, 0.05, 0.55, 0.95);
    /* Quad(弱め。Sineより強く、Cubicより弱い) */
    --ease-in-quad:cubic-bezier(0.55, 0.085, 0.68, 0.53);
    --ease-out-quad:cubic-bezier(0.25, 0.46, 0.45, 0.94);
    --ease-in-out-quad:cubic-bezier(0.455, 0.03, 0.515, 0.955);
    /* Cubic(Quadより強く、Quartより弱い) */
    --ease-in-cubic:cubic-bezier(0.55, 0.055, 0.675, 0.19);
    --ease-out-cubic:cubic-bezier(0.215, 0.61, 0.355, 1);
    --ease-in-out-cubic:cubic-bezier(0.645, 0.045, 0.355, 1);
    /* Quart(Cubicより強く、Quintより弱い) */
    --ease-in-quart:cubic-bezier(0.895, 0.03, 0.685, 0.22);
    --ease-out-quart:cubic-bezier(0.165, 0.84, 0.44, 1);
    --ease-in-out-quart:cubic-bezier(0.77, 0, 0.175, 1);
    /* Quint(Quartより強く、Expoより弱い) */
    --ease-in-quint:cubic-bezier(0.755, 0.05, 0.855, 0.06);
    --ease-out-quint:cubic-bezier(0.23, 1, 0.32, 1);
    --ease-in-out-quint:cubic-bezier(0.86, 0, 0.07, 1);
    /* Expo(最も強い) */
    --ease-in-expo:cubic-bezier(0.95, 0.05, 0.795, 0.035);
    --ease-out-expo:cubic-bezier(0.19, 1, 0.22, 1);
    --ease-in-out-expo:cubic-bezier(1, 0, 0, 1);
    /* Circ(Expoのような強さを持つが、加速や減速の時間がよりゆるやか。) */
    --ease-in-circ:cubic-bezier(0.6, 0.04, 0.98, 0.335);
    --ease-out-circ:cubic-bezier(0.075, 0.82, 0.165, 1);
    --ease-in-out-circ:cubic-bezier(0.785, 0.135, 0.15, 0.86);
    /* Back(少し行き過ぎてから戻ってくるような動き) */
    --ease-in-back:cubic-bezier(0.6, -0.28, 0.735, 0.045);
    --ease-out-back:cubic-bezier(0.175, 0.885, 0.32, 1.275);
    --ease-in-out-back:cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.cls-1{fill:none;}
.cls-2{clip-path:url(#clippath);}
.cls-3{clip-path:url(#clippath-1);}
.cls-4{clip-path:url(#clippath-3);}
.cls-5{clip-path:url(#clippath-2);}
.cls-6{stroke-linecap:square;}
.cls-6,.cls-7{
    fill:transparent;
    stroke:var(--logo-color);
    stroke-miterlimit:10;
    stroke-width:30px;
}
#huyu-dot1,#huyu-dot2{
    fill:var(--logo-color);
}
@media (prefers-reduced-motion: no-preference) {
    .cls-6,.cls-7{
        stroke-dasharray: 583;
        stroke-dashoffset: 583;
        animation: line 1.5s var(--ease-out-expo) forwards;
    }
    #huyu-line1,#huyu-line2{
        animation-delay: 0.2s;
    }
    #huyu-line3{
        animation-delay: 0.4s;
    }
    #huyu-dot1,#huyu-dot2{
        transform: scale(0);
        animation: dot 0.8s var(--ease-out-back) forwards;
    }
    #huyu-dot1{
        animation-delay: 0.4s;
        transform-origin: 74.96% 65.69%; /* (座標/viewBoxサイズ*100)% */
    }
    #huyu-dot2{
        animation-delay: 0.6s;
        transform-origin: 74.96% 87.99%; /* (座標/viewBoxサイズ*100)% */
    }
}
@keyframes line {
    to {
        stroke-dashoffset: 0;
    }
}
@keyframes dot {
    to {
        transform: scale(1);
    }
}
</style>
<clipPath id="clippath"><rect id="huyu-clippath" class="cls-1" x="291" width="292" height="583"/></clipPath>
<clipPath id="clippath-1"><polygon id="huyu-line3-clippath" class="cls-1" points="583 0 291 291.5 291 583 583 583 583 0"/></clipPath>
<clipPath id="clippath-2"><rect id="ki-bottom-clippath" class="cls-1" y="270" width="230" height="313"/></clipPath>
<clipPath id="clippath-3"><rect id="ki-top-clippath" class="cls-1" width="230" height="167"/></clipPath>
</defs>
<g id="huyu">
    <g class="cls-2">
        <circle id="huyu-dot2" cx="437" cy="513" r="30"/>
        <circle id="huyu-dot1" cx="437" cy="383" r="30"/>
        <g id="huyu-line3-mask">
            <g class="cls-3">
                <line id="huyu-line3" class="cls-6" x1="291.25" x2="582.38" y2="291.12"/>
            </g>
        </g>
        <line id="huyu-line2" class="cls-6" x1="582.75" y1="-.25" x2="291" y2="291.5"/>
        <line id="huyu-line1" class="cls-6" x1="436.75" y1="-.25" x2="145" y2="291.5"/>
    </g>
</g>
<g id="ki">
    <g id="ki-bottom">
        <g class="cls-5">
            <line id="ki-line3-bottom" class="cls-7" x1="215" y1="583" x2="215" y2="0"/>
            <line id="ki-line2-bottom" class="cls-7" x1="15" y1="583" x2="15" y2="0"/>
        </g>
    </g>
    <g id="ki-top">
        <g class="cls-4">
            <line id="ki-line3-top" class="cls-7" x1="215" y1="583" x2="215" y2="0"/>
            <line id="ki-line2-top" class="cls-7" x1="15" y1="583" x2="15" y2="0"/>
        </g>
    </g>
    <line id="ki-line1" class="cls-7" x1="115" x2="115" y2="583"/>
</g>
</svg>

解説

ストロークのアニメーション

.cls-6,.cls-7{
    stroke-dasharray: 583;
    stroke-dashoffset: 583;
    animation: line 1.5s var(--ease-out-expo) forwards;
}
@keyframes line {
    to {
        stroke-dashoffset: 0;
    }
}

stroke-dasharrayでストロークの長さと同じ長さの破線を作り、stroke-dashoffsetで破線の基準位置をずらしてあげる。

とりあえずstroke-dasharray: 1000;とか適当な数字で紹介している記事もあるが、アニメーションのイージングや細かいタイミングの調整が難しくなるので、ここはちゃんと調整するべき。

その場で拡大するアニメーション

#huyu-dot1,#huyu-dot2{
    transform: scale(0);
    animation: dot 0.8s var(--ease-out-back) forwards;
}
#huyu-dot1{
    animation-delay: 0.4s;
    transform-origin: 74.96% 65.69%; /* (座標/viewBoxサイズ*100)% */
}
#huyu-dot2{
    animation-delay: 0.6s;
    transform-origin: 74.96% 87.99%; /* (座標/viewBoxサイズ*100)% */
}

transform: scale();で拡大する場合、viewBoxの中心を基準に拡大してしまう。
rotateの場合は中心座標を指定できるがscaleだとできないので、transform-originを使っている。

「視差効果を減らす」への対応

prefers-reduced-motionメディアクエリの中にアニメーション関連の指示をまとめておくと、「視差効果を減らす」などOSのアクセシビリティ機能に対応できる。

reduceが「視差効果を減らす」が有効な状態。
no-preferenceが「視差効果を減らす」の設定が行われていない状態。

imgか、svgか、objectか、インラインか

実際にサイトに読み込むときにいつもimgタグを使っていたのだが、imgの場合svgファイルが読み込まれた最初の1回目のみアニメーションが動き、その後の読み込みではキャッシュクリアしない限りアニメーションは動作しなかった。

インラインにしても良かったのだけど結構コードが長くなるし、svguseタグとかもsymbolタグとか準備が面倒だったので、今回はobjectで行くことにした。

<a href="<?php echo home_url(); ?>">
    <object type="image/svg+xml" data="<?php echo get_template_directory_uri(); ?>/images/logo_animation.svg" width="60" height="60" aria-label="<?php bloginfo('name'); ?>"></object>
</a>

この場合objectにマウスカーソルが吸われてa要素がクリックできない。
object要素にpointer-events: none;をかけて対処した。

参考

関連記事

コメント

この記事へのコメントはありません。

TOP