〽️サイトをおめでたくしよう。 〽️構造を整理する。 〽️画像を貼り付けてクリップ。 〽️複製してみる。 〽️for文と配列で自動化。 〽️全体をアニメーション。 〽️好きな画像に差し替え。
こんにちは、「ふ」です。
前回の記事では、SVGで「扇子」の構造を作ってアニメーションさせました。今回はそこに、自由に画像を貼り付ける方法について紹介していきます。
コードは少し複雑なものとなりますが、1度作ってしまえば自由に画像を差し替えられることができます。
実装すれば、サイトが一気におめでたいものとなるので(笑)、ぜひ試してみてください。
SVGで扇子のアニメーション。
2つのキーフレームで実現。
当記事は前回実装した素材に手を加えていく形で、話を進めていきます。
「扇子アニメーションの基本形」については⬆︎を参考にしてください。
前回作成した骨と扇面、要の1セットをベースに作成していきます。
そのまま書き出したSVGコードがこちら⬇︎。
<svg viewBox="0 0 841.9 508.8"> <style type="text/css"> .st4{fill:#FFEDA2;stroke:#231815;stroke-width:1.7398;stroke-miterlimit:10;} .st5{「「4fill:none;」」stroke:#231815;stroke-width:1.4702;stroke-miterlimit:10;} .st6{fill:#231815;} </style> 「「3<!-- 骨 -->」」 <path class="st4" d="M423,485.4h-4.1c-6.2,0-11.2-5-11.2-11.2V26.8c0-6.2,5-11.2,11.2-11.2h4.1c6.2,0,11.2,5,11.2,11.2v447.4 C434.2,480.4,429.2,485.4,423,485.4z"/> 「「3<!-- 扇面左 -->」」 <polygon class="st5" points="420.9,15.6 531,30.1 457.9,302.8 420.9,298 "/> 「「3<!-- 扇面右 -->」」 <polygon class="st5" points="310.9,30.1 420.9,15.6 420.9,298 384,302.8 "/> 「「3<!-- 要 -->」」 <circle class="st6" cx="420.9" cy="440.9" r="7.3"/> </svg>
扇面には画像を貼るので、fill(塗り)は「none」としています。
ここがちょっと大変。
要素をこの⬆︎ような階層構造にします。青で示されているのが実際のオブジェクトで、骨のオブジェクトにはid「hone」を付けています。
紫の部分はグループに付けたidまたはclass名です。
<g id = 「「4"back"」」> bb<path id = 「「1"hone"」」 class="st4" d=..../> </g> <g id = 「「4"men"」」> bb<g id = 「「4"master"」」> bbbb<g class = 「「4"oritatami"」」> bbbb<polygon class="st5" points=..../> bbbb</g> bbbb<g class = 「「4"oritatami"」」> bbbb<polygon class="st5" points=..../> bbbb</g> bb</g> </g> <circle class="st6" cx="420.9" cy="440.9" r="7.3"/>
実装するアニメーション自体は前回と同じくシンプルなものなのですが、今回はtransformプロパティを要素に何重にも掛けていきます。
指定値同士の汚染を避けるため、特に扇面の部分は深い階層を作って内包していく必要があります。
■ omedeto_.svg
サンプルとして使う画像、「omedeto_svg」です。
2つの「.oritatami」クラスの中に、1枚ずつ貼り付けましょう。
<g class = "oritatami"> 「「1<image href = "omedeto_.svg" width = "100%"/>」」 <polygon class="st5" points=..../> </g> <g class = "oritatami"> 「「1<image href = "omedeto_.svg" width = "100%"/>」」 <polygon class="st5" points=..../> </g>
現在の状態です。
画像がそのまま貼り付けられているので、扇面の形で切り抜きましょう。
左右の扇面のデータを複製して、クリップパスを生成します。
「#master」グループの直下に配置してください。<image>タグ内で参照し、クリップします。
<g id = "master"> <g class = "oritatami"> <image href = "omedeto_.svg" width = "100%" 「「1style = "clip-path:url(#r);"」」/> <polygon class="st5" points=..../>「「4// ◀︎ copy」」 </g> <g class = "oritatami"> <image href = "omedeto_.svg" width = "100%" 「「1style = "clip-path:url(#l);」」"/> <polygon class="st5" points=..../>「「4// ◀︎ copy」」 </g> <clipPath 「「1id = "r"」」> <polygon points=..../>「「4// ◀︎ paste」」 </clipPath> <clipPath 「「1id = "l"」」> <polygon points=..../>「「4// ◀︎ paste」」 </clipPath> </g>
扇面の形に切り取ることができました。
SVG領域に画像を配置する際の、縦横比のとり方について。
⬇︎の記事にて詳しく紹介しています。
SVGに画像をぴったりと配置〜
preserveAspectRatio。
縦横比が違っても安心。
ここでtransform-originを指定しておきましょう。
「「3<!-- 要のオブジェクト -->」」 <circle class="st6" cx="「「5420.9」」" cy="「「5440.9」」" r="7.3"/>
transformさせる要素の基準点はどのみち「要」であるオブジェクトの中心座標です。
svg * { transform-origin: 「「5420.9px 440.9px」」; }
各要素を逐一class名などでセレクトするのもめんどーなのでこう⬆︎しました。
「svg *」とすると、svg要素内の全ての子要素に適用されます。
JavaScriptを使って複製・角度を付けていきます。
JavaScriptで行う作業の大きな流れとしては、
・#hone(骨オブジェクト)を複製して親である#backに追加
・#masterグループ(扇子面/画像/クリップパス)を複製して親の#menに追加
複製したcloneはそれぞれ角度を調整した上で、親要素に格納します。
はじめに、複製する要素とその親要素を取得しましょう。
■ JavaScript
const men = document.getElementById("men"); const master = document.getElementById("master"); const back = document.getElementById("back"); const hone = document.getElementById("hone");
それでは試しに、扇子面を複製、30°回転させて親要素に追加してみます。
「「3//masterグループを複製」」「「1 ..①」」 let clone = master.cloneNode(true); 「「3//子要素、孫要素を取得」」「「1 ..②」」 let child = clone.children; let mago0 = child[0].children; let mago1 = child[1].children; 「「3clipPathのidと参照を差し替え」」「「1 ..③」」 child[2].id = "r_1"; child[3].id = "l_1"; mago0[0].style.clipPath = "url(#r_1)"; mago1[0].style.clipPath = "url(#l_2)";
① masterグループを複製します。引数の「true」は子要素も含めて複製する、という指定です。
② 子要素と、
さらに孫要素を取得します。
取得したものは「HTMLCollection」という型になり、配列のように添字[n]で呼び出すことができます。
③ クリップパスの参照先が重複しないように、id名を変更しています。
伴って、imageの参照先も変更しました。
「「3clipPathを回転」」「「1 ..④」」 child[2].style.transform = "rotate(30deg)"; child[3].style.transform = "rotate(30deg)"; 「「3imageを逆回転」」「「1 ..⑤」」 mago0[0].style.transform = "rotate(-30deg)" ; mago1[0].style.transform = "rotate(-30deg)" ; 「「3扇面全体を回転」」「「1 ..⑥」」 clone.style.transform = "rotate(30deg)"; 「「3回転したものを親要素に追加」」「「1 ..⑦」」 men.appendChild(clone);
④ 画像の切り取り位置をずらすため、<clipPath>を回転させます。
⑤ ④の処理を行なった時点では「切り取り位置」のみが回転している状態です。
このままでは、⑥で扇面全体を回転させるときに「ズレた状態」のまま回転してしまうので、<image>要素を逆回転させて元に戻しておきましょう。
⑥ つじつまが合ったところで、扇子面全体を回転。
⑦ 扇子面を親である#menに追加します。
ここまで処理をした状態⬆︎です。
「骨」オブジェクトも複製・回転させましょう。
「「3//骨を複製→回転→親要素に追加」」「「1 ..⑧」」 let honeclone = hone.cloneNode(true); honeclone.style.transform = "rotate(30deg)"; back.appendChild(honeclone);
骨グループは単純な構成になっているので、clone作業も簡単に済みます。
骨オブジェクトが追加されました。
1つcloneを作るだけで結構な手間が掛かりました。
あと3つ、cloneを追加しなければいけません。ここは繰り返し処理を使いましょう。
let kakudo = ["30deg","60deg","-30deg","-60deg"];
cloneに与える回転角度については、scriptのグローバル部分に配列を置きました。
このようにしておけば、
・正回転 rotate(kakudo[i]) ・逆回転 rotate(kakudo[(i+2)%4])
と指定することで、<image>要素の逆回転にも対応できます。
前のセクションで作った複製処理を、for文に落とし込みましょう。
const men = document.getElementById("men"); const master = document.getElementById("master"); const back = document.getElementById("back"); const hone = document.getElementById("hone"); 「「3//回転角度用の配列」」「「1 ..⑪」」 let kakudo = ["30deg","60deg","-30deg","-60deg"]; 「「3//for文に落とし込む」」「「1 ..⑫」」 for(i=0;i<kakudo.length;i++) { 「「3//扇面の処理」」 let clone = master.cloneNode(true); let child = clone.children; let mago0 = child[0].children; let mago1 = child[1].children; child[2].id = "r_" + String(i); child[3].id = "l_" + String(i); mago0[0].style.clipPath = "url(#" +child[2].id + ")"; mago1[0].style.clipPath = "url(#" +child[3].id + ")"; child[2].style.transform = "rotate(" + kakudo[i] + ")"; child[3].style.transform = "rotate(" + kakudo[i] + ")"; mago0[0].style.transform = "rotate(" + kakudo[(i+2)%4] + ")" ; mago1[0].style.transform = "rotate(" + kakudo[(i+2)%4] + ")" ; clone.style.transform = "rotate(" + kakudo[i] + ")"; men.appendChild(clone); 「「3//骨の処理」」 let honeclone = hone.cloneNode(true); honeclone.style.transform = "rotate(" + kakudo[i] + ")"; back.appendChild(honeclone); }
文字列の連結はめんどくさい💧ですが、がんばりましょう。
複製の自動化に成功しました。
が、何か違和感が.........................
appendChildで要素を追加すると、親要素内の1番後ろにどんどん追加されていきます。
骨の部分を見ると分かりますが、片側から順に重なっていません。
上に重なってしまっている要素を、親要素内の先頭に移し替えましょう。そうすると表示上は再背面となります。
「irekae」関数を定義します。
「「3//irekae関数を定義」」「「1 ..⑬」」 function irekae(parent) { parent.insertBefore(parent.children[3],parent.children[0]); parent.insertBefore(parent.children[4],parent.children[0]); } 「「3//扇面と骨に適用」」「「1 ..⑭」」 irekae(men); irekae(back);
関数の実行はすべての複製が終わったあとに行うため、scriptの1番後ろに記述してください。
扇面についても今の静止状態では分かりませんが、重ね順がちぐはぐです。このままアニメーションさせるとうまくいきません。
irekae関数に1度通しておきましょう。
オブジェクトが順番通りに重なりました。
前後の親骨をここで追加します。
#honeオブジェクトのデータを2つ複製。1つは再背面に、もう1つは最前面の「要」のすぐ後ろに配置します。
<path class="st4 「「5oya」」" 「「1style = "transform:rotate(-75deg);"」」 d=..../> <g id = "back"> bb<path id = "hone" class="st4" d=..../>「「4// ◀︎ copy」」 bb 〜 略 〜 </g> <g id = "men"> 〜 略 〜 </g> <path class="st4 「「5oya」」" 「「1style = "transform:rotate(75deg);"」」 d=..../> <circle class="st6" cx="420.9" cy="440.9" r="7.3"/>
#honeを複製したものは、id名の指定はキャンセルしてclass名「oya」を追加しています。
また親骨の回転は、タグ内で指定しました。
これで全ての部品が出揃いました。
前回のようにCSSを使って、「扇子全体の開閉」「扇子面1セットの折りたたみ」の2つのアニメーションを指定します。
「「3/* 開閉アニメーション */」」 #hone,#men #master,.oya{ animation:zentai 4s ease-out alternate infinite; } @keyframes zentai { from { transform:rotate(0deg); } } 「「3/* 折りたたみアニメーション */」」 .oritatami { animation:oritatami 4s ease-out alternate infinite; } @keyframes oritatami { from { transform:scaleX(0); } }
これでついに完成です。お疲れ様でした!
最後のセクションで、画像の差し替え方法を紹介します。
■kagami.svg
それではこちら⬆︎の画像に差し替えてみましょう。
と言っても、#oritatamiグループの中にある<image>要素の参照先を変更するだけです。
<g class = "oritatami"> <image href = "「「1kagami.svg」」" width = "100%" style = "clip-path:url(#r)"/> <polygon class="st5" points= ..../> </g> <g class = "oritatami"> <image href = "「「1kagami.svg」」" width = "100%" style = "clip-path:url(#l)"/> <polygon class="st5" points= ..../> </g>
はい、できました ♬
<image bbhref = "画像のurl" bbx = "x座標" bby = "y座標" bbwidth = "幅" />
貼り付ける画像の微調整は、<image>タグ内で指定することができます。前半に紹介したpreserveAspectRatio属性の記事も参考にしてください。
また、元の扇面オブジェクトの塗りは「none」になっているので、状況に合わせて着色したり、画像との重ね順を入れ替えたりしてください。
最後までお読みくださり、ありがとうございました。
今回は過去に紹介してきた色んな実装を施し、「集大成」的なチュートリアルになったかもしれません。
長くて複雑なチュートリアルとなってしまいました(←すいません)が、特別な動画編集ソフトも使わず、無料のコードエディタとブラウザだけでここまでのことができます。今後もSVGを含めたコーディングを学習して、楽しいwebサイトを作っていきましょう!
ではまた〜 ♪
SVGアニメーション、 回転の中心を指定する。
2021.03.09
座標を取得して%変換。
swift、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。