[{"data":1,"prerenderedAt":743},["ShallowReactive",2],{"content-/transaction-animation-nuxt-smil":3,"all-pages-for-dir":741,"og-image-/transaction-animation-nuxt-smil":742},{"id":4,"title":5,"body":6,"category":722,"description":723,"extension":724,"meta":725,"navigation":495,"ogImage":726,"path":727,"project_name":728,"published":729,"publishedAt":730,"seo":731,"stem":732,"tags":733,"todo":739,"unpublished":729,"updatedAt":726,"__hash__":740},"pages/2026-05/2026-05-06/transaction-animation-nuxt-smil.md","22取引アニメーション：Nuxtコンポーネント登録ミスとSMIL花火アニメーション",{"type":7,"value":8,"toc":715},"minimark",[9,14,23,26,29,39,50,53,154,160,163,167,170,177,311,314,329,332,433,443,450,591,603,605,609,612,657,660,662,666,669,687,689,692,705,711],[10,11,13],"h2",{"id":12},"全取引svgが一切表示されない","全取引SVGが一切表示されない",[15,16,17,18,22],"p",{},"22取引分のアニメーション確認ページ（",[19,20,21],"code",{},"/dev/journal-push-animation-test","）を開いたら、全コンポーネントが空白だった。エラーメッセージも出ない。Vue の警告もない。ただ何もない。",[15,24,25],{},"原因はすぐ分かった。コンポーネントの命名ルールを完全に読み違えていた。",[15,27,28],{},"ファイルの置き場はこうなっている。",[30,31,36],"pre",{"className":32,"code":34,"language":35},[33],"language-text","app/components/dev/transactions/TransactionNo01Animated.vue\n","text",[19,37,34],{"__ignoreMap":38},"",[15,40,41,42,45,46,49],{},"Nuxt の自動登録は、ディレクトリ階層をコンポーネント名のプレフィックスとして付加する。結果、このコンポーネントは ",[19,43,44],{},"DevTransactionsTransactionNo01Animated"," として登録された。試作ページは ",[19,47,48],{},"\u003CTransactionNo01Animated>"," と書いていた。名前が噛み合っておらず、Nuxt はコンポーネントを静かに無視していた。",[15,51,52],{},"修正の方針は2つある。呼び出し側のタグ名をすべて書き直すか、登録側でプレフィックスを消すか。22個のコンポーネントがあるので、後者を選んだ。",[30,54,58],{"className":55,"code":56,"language":57,"meta":38,"style":38},"language-ts shiki shiki-themes vitesse-light vitesse-light","// nuxt.config.ts\ncomponents: [\n  { path: '~/components', pathPrefix: true },\n  { path: '~/components/dev/transactions', pathPrefix: false },\n],\n","ts",[19,59,60,69,83,121,148],{"__ignoreMap":38},[61,62,65],"span",{"class":63,"line":64},"line",1,[61,66,68],{"class":67},"sxvE3","// nuxt.config.ts\n",[61,70,72,76,80],{"class":63,"line":71},2,[61,73,75],{"class":74},"senZ8","components",[61,77,79],{"class":78},"shFtX",":",[61,81,82],{"class":78}," [\n",[61,84,86,89,93,96,100,104,106,109,112,114,118],{"class":63,"line":85},3,[61,87,88],{"class":78},"  { ",[61,90,92],{"class":91},"sz8Xr","path",[61,94,95],{"class":78},": ",[61,97,99],{"class":98},"sMJiu","'",[61,101,103],{"class":102},"sdGka","~/components",[61,105,99],{"class":98},[61,107,108],{"class":78},", ",[61,110,111],{"class":91},"pathPrefix",[61,113,95],{"class":78},[61,115,117],{"class":116},"sHkkW","true",[61,119,120],{"class":78}," },\n",[61,122,124,126,128,130,132,135,137,139,141,143,146],{"class":63,"line":123},4,[61,125,88],{"class":78},[61,127,92],{"class":91},[61,129,95],{"class":78},[61,131,99],{"class":98},[61,133,134],{"class":102},"~/components/dev/transactions",[61,136,99],{"class":98},[61,138,108],{"class":78},[61,140,111],{"class":91},[61,142,95],{"class":78},[61,144,145],{"class":116},"false",[61,147,120],{"class":78},[61,149,151],{"class":63,"line":150},5,[61,152,153],{"class":78},"],\n",[15,155,156,159],{},[19,157,158],{},"pathPrefix: false"," を付けると、ディレクトリ階層がコンポーネント名に入らない。再起動後、no01 の SVG が画面に現れた。仕訳をプッシュすると株式券が右から左へ移動し、取消ボタンもアクティブになった。",[161,162],"hr",{},[10,164,166],{"id":165},"css-transform-が-svg-に効かない","CSS transform が SVG に効かない",[15,168,169],{},"no08（買掛金発生）のアニメーション実装で詰まった。買掛債務が発生する瞬間に「パーン」と広がる花火を入れたかった。",[15,171,172,173,176],{},"最初は CSS の ",[19,174,175],{},"@keyframes"," で書いた。",[30,178,182],{"className":179,"code":180,"language":181,"meta":38,"style":38},"language-css shiki shiki-themes vitesse-light vitesse-light","@keyframes spark {\n  0%   { transform: scale(0) translate(0, 0); opacity: 1; }\n  100% { transform: scale(1) translate(30px, -20px); opacity: 0; }\n}\n","css",[19,183,184,199,255,306],{"__ignoreMap":38},[61,185,186,189,192,196],{"class":63,"line":64},[61,187,188],{"class":78},"@",[61,190,191],{"class":116},"keyframes",[61,193,195],{"class":194},"s4oTP"," spark",[61,197,198],{"class":78}," {\n",[61,200,201,204,207,210,212,215,218,222,225,228,230,232,235,238,241,244,246,249,252],{"class":63,"line":71},[61,202,203],{"class":74},"  0%",[61,205,206],{"class":78},"   {",[61,208,209],{"class":91}," transform",[61,211,79],{"class":78},[61,213,214],{"class":91}," scale",[61,216,217],{"class":78},"(",[61,219,221],{"class":220},"sM54T","0",[61,223,224],{"class":78},")",[61,226,227],{"class":91}," translate",[61,229,217],{"class":78},[61,231,221],{"class":220},[61,233,234],{"class":78},",",[61,236,237],{"class":220}," 0",[61,239,240],{"class":78},");",[61,242,243],{"class":91}," opacity",[61,245,79],{"class":78},[61,247,248],{"class":220}," 1",[61,250,251],{"class":78},";",[61,253,254],{"class":78}," }\n",[61,256,257,260,263,265,267,269,271,274,276,278,280,283,287,289,292,294,296,298,300,302,304],{"class":63,"line":85},[61,258,259],{"class":74},"  100%",[61,261,262],{"class":78}," {",[61,264,209],{"class":91},[61,266,79],{"class":78},[61,268,214],{"class":91},[61,270,217],{"class":78},[61,272,273],{"class":220},"1",[61,275,224],{"class":78},[61,277,227],{"class":91},[61,279,217],{"class":78},[61,281,282],{"class":220},"30",[61,284,286],{"class":285},"stQ0i","px",[61,288,234],{"class":78},[61,290,291],{"class":220}," -20",[61,293,286],{"class":285},[61,295,240],{"class":78},[61,297,243],{"class":91},[61,299,79],{"class":78},[61,301,237],{"class":220},[61,303,251],{"class":78},[61,305,254],{"class":78},[61,307,308],{"class":63,"line":123},[61,309,310],{"class":78},"}\n",[15,312,313],{},"ブラウザで確認すると、花火の点が一切動かない。スケールもゼロのまま固定されている。",[15,315,316,317,320,321,324,325,328],{},"調べて分かったのは、SVG 要素に対して CSS の ",[19,318,319],{},"transform"," が期待通りに動かないケースがあること。特に ",[19,322,323],{},"transform-box: fill-box"," を指定していても、ブラウザの実装差によって変換の基点が狂う。",[19,326,327],{},"transform: identity matrix"," に固定されているように見える状態になる。",[15,330,331],{},"CSS で組むのをやめて、SMIL に切り替えた。SVG が本来持っているアニメーション仕様だ。",[30,333,337],{"className":334,"code":335,"language":336,"meta":38,"style":38},"language-xml shiki shiki-themes vitesse-light vitesse-light","\u003Ccircle class=\"spark\" cx=\"200\" cy=\"150\" r=\"4\" fill=\"#ff6b35\" opacity=\"1\">\n  \u003CanimateTransform\n    attributeName=\"transform\"\n    type=\"translate\"\n    from=\"0,0\"\n    to=\"40,-30\"\n    dur=\"0.6s\"\n    fill=\"freeze\"\n  />\n  \u003Canimate\n    attributeName=\"opacity\"\n    from=\"1\"\n    to=\"0\"\n    dur=\"0.6s\"\n    fill=\"freeze\"\n  />\n\u003C/circle>\n","xml",[19,338,339,344,349,354,359,364,370,376,382,388,394,400,406,412,417,422,427],{"__ignoreMap":38},[61,340,341],{"class":63,"line":64},[61,342,343],{},"\u003Ccircle class=\"spark\" cx=\"200\" cy=\"150\" r=\"4\" fill=\"#ff6b35\" opacity=\"1\">\n",[61,345,346],{"class":63,"line":71},[61,347,348],{},"  \u003CanimateTransform\n",[61,350,351],{"class":63,"line":85},[61,352,353],{},"    attributeName=\"transform\"\n",[61,355,356],{"class":63,"line":123},[61,357,358],{},"    type=\"translate\"\n",[61,360,361],{"class":63,"line":150},[61,362,363],{},"    from=\"0,0\"\n",[61,365,367],{"class":63,"line":366},6,[61,368,369],{},"    to=\"40,-30\"\n",[61,371,373],{"class":63,"line":372},7,[61,374,375],{},"    dur=\"0.6s\"\n",[61,377,379],{"class":63,"line":378},8,[61,380,381],{},"    fill=\"freeze\"\n",[61,383,385],{"class":63,"line":384},9,[61,386,387],{},"  />\n",[61,389,391],{"class":63,"line":390},10,[61,392,393],{},"  \u003Canimate\n",[61,395,397],{"class":63,"line":396},11,[61,398,399],{},"    attributeName=\"opacity\"\n",[61,401,403],{"class":63,"line":402},12,[61,404,405],{},"    from=\"1\"\n",[61,407,409],{"class":63,"line":408},13,[61,410,411],{},"    to=\"0\"\n",[61,413,415],{"class":63,"line":414},14,[61,416,375],{},[61,418,420],{"class":63,"line":419},15,[61,421,381],{},[61,423,425],{"class":63,"line":424},16,[61,426,387],{},[61,428,430],{"class":63,"line":429},17,[61,431,432],{},"\u003C/circle>\n",[15,434,435,438,439,442],{},[19,436,437],{},"\u003CanimateTransform>"," と ",[19,440,441],{},"\u003Canimate>"," を組み合わせると、移動と透明化を同時に走らせられる。CSS と違って座標系が SVG 内部の単位に素直に従うため、計算した通りに動く。",[15,444,445,446,449],{},"Vue からアニメーションを起動するには ",[19,447,448],{},"beginElement()"," を使う。",[30,451,453],{"className":55,"code":452,"language":57,"meta":38,"style":38},"const svgRef = ref\u003CSVGSVGElement | null>(null)\n\nconst triggerFirework = () => {\n  const sparks = svgRef.value?.querySelectorAll('.spark animateTransform')\n  sparks?.forEach(anim => (anim as SVGAnimateTransformElement).beginElement())\n}\n",[19,454,455,491,497,514,550,587],{"__ignoreMap":38},[61,456,457,460,463,466,469,472,476,479,482,485,488],{"class":63,"line":64},[61,458,459],{"class":285},"const ",[61,461,462],{"class":194},"svgRef",[61,464,465],{"class":78}," =",[61,467,468],{"class":74}," ref",[61,470,471],{"class":78},"\u003C",[61,473,475],{"class":474},"sSkh3","SVGSVGElement",[61,477,478],{"class":78}," |",[61,480,481],{"class":285}," null",[61,483,484],{"class":78},">(",[61,486,487],{"class":285},"null",[61,489,490],{"class":78},")\n",[61,492,493],{"class":63,"line":71},[61,494,496],{"emptyLinePlaceholder":495},true,"\n",[61,498,499,501,504,506,509,512],{"class":63,"line":85},[61,500,459],{"class":285},[61,502,503],{"class":74},"triggerFirework",[61,505,465],{"class":78},[61,507,508],{"class":78}," ()",[61,510,511],{"class":78}," =>",[61,513,198],{"class":78},[61,515,516,519,522,524,527,530,533,536,539,541,543,546,548],{"class":63,"line":123},[61,517,518],{"class":285},"  const ",[61,520,521],{"class":194},"sparks",[61,523,465],{"class":78},[61,525,526],{"class":194}," svgRef",[61,528,529],{"class":78},".",[61,531,532],{"class":194},"value",[61,534,535],{"class":78},"?.",[61,537,538],{"class":74},"querySelectorAll",[61,540,217],{"class":78},[61,542,99],{"class":98},[61,544,545],{"class":102},".spark animateTransform",[61,547,99],{"class":98},[61,549,490],{"class":78},[61,551,552,555,557,560,562,565,567,570,572,575,578,581,584],{"class":63,"line":150},[61,553,554],{"class":194},"  sparks",[61,556,535],{"class":78},[61,558,559],{"class":74},"forEach",[61,561,217],{"class":78},[61,563,564],{"class":194},"anim",[61,566,511],{"class":78},[61,568,569],{"class":78}," (",[61,571,564],{"class":194},[61,573,574],{"class":116}," as",[61,576,577],{"class":474}," SVGAnimateTransformElement",[61,579,580],{"class":78},").",[61,582,583],{"class":74},"beginElement",[61,585,586],{"class":78},"())\n",[61,588,589],{"class":63,"line":366},[61,590,310],{"class":78},[15,592,593,595,596,599,600,602],{},[19,594,462],{}," から ",[19,597,598],{},"querySelector"," で SMIL 要素を直接拾い、",[19,601,448],{}," を呼ぶ。仕訳プッシュのタイミングでこれを実行すると、花火が弾けた。",[161,604],{},[10,606,608],{"id":607},"no09-の座標ミスを修正","no09 の座標ミスを修正",[15,610,611],{},"no09（売掛金発生）は no01（設立出資）の鏡像パターンで作った。お金袋と商品を左右対称に配置するつもりだったが、座標を手計算した際にミスが入った。",[613,614,615,631],"table",{},[616,617,618],"thead",{},[619,620,621,625,628],"tr",{},[622,623,624],"th",{},"要素",[622,626,627],{},"誤った before 位置",[622,629,630],{},"正しい before 位置",[632,633,634,646],"tbody",{},[619,635,636,640,643],{},[637,638,639],"td",{},"お金袋",[637,641,642],{},"x ≈ 690",[637,644,645],{},"x ≈ 824",[619,647,648,651,654],{},[637,649,650],{},"リンゴ",[637,652,653],{},"x ≈ 464",[637,655,656],{},"x ≈ 643",[15,658,659],{},"no01 の値を参照して上書きした。ブラウザで並べると、左右の動線がきれいに揃った。",[161,661],{},[10,663,665],{"id":664},"no06-の-svg-をファイルとして書き出す","no06 の SVG をファイルとして書き出す",[15,667,668],{},"no06 は Illustrator 側で手直しが必要になったため、Vue コンポーネントにインラインで書かれた SVG を単体ファイルとして抽出した。",[15,670,671,674,675,678,679,682,683,686],{},[19,672,673],{},"TransactionNo06Animated.vue"," の ",[19,676,677],{},"\u003Csvg>"," ブロックをそのまま切り出して ",[19,680,681],{},"memo/2026-05-05/transaction-no06-simple.svg"," に保存した。インライン SVG をコンポーネント外に出すだけなので、手順は単純だが、Illustrator が読めるように ",[19,684,685],{},"xmlns"," 属性が正しく入っているか確認した。",[161,688],{},[10,690,691],{"id":691},"今日わかったこと",[15,693,694,695,697,698,701,702,704],{},"CSS ",[19,696,319],{}," は SVG 要素に対してブラウザが思った通りに動かさないことがある。同じ ",[19,699,700],{},"translate"," でも、",[19,703,437],{}," を使えば座標系が素直で安定する。",[15,706,707,708,710],{},"Nuxt のコンポーネント自動登録は、ディレクトリ構造がそのまま名前に乗る。深い階層に置いたコンポーネントを短い名前で使いたいなら ",[19,709,158],{}," を設定する。警告なしに無視されるので、表示されない原因として最初に疑うべき場所だった。",[712,713,714],"style",{},"html pre.shiki code .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}html pre.shiki code .senZ8, html code.shiki .senZ8{--shiki-default:#59873A;--shiki-dark:#59873A}html pre.shiki code .shFtX, html code.shiki .shFtX{--shiki-default:#999999;--shiki-dark:#999999}html pre.shiki code .sz8Xr, html code.shiki .sz8Xr{--shiki-default:#998418;--shiki-dark:#998418}html pre.shiki code .sMJiu, html code.shiki .sMJiu{--shiki-default:#B5695977;--shiki-dark:#B5695977}html pre.shiki code .sdGka, html code.shiki .sdGka{--shiki-default:#B56959;--shiki-dark:#B56959}html pre.shiki code .sHkkW, html code.shiki .sHkkW{--shiki-default:#1E754F;--shiki-dark:#1E754F}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4oTP, html code.shiki .s4oTP{--shiki-default:#B07D48;--shiki-dark:#B07D48}html pre.shiki code .sM54T, html code.shiki .sM54T{--shiki-default:#2F798A;--shiki-dark:#2F798A}html pre.shiki code .stQ0i, html code.shiki .stQ0i{--shiki-default:#AB5959;--shiki-dark:#AB5959}html pre.shiki code .sSkh3, html code.shiki .sSkh3{--shiki-default:#2E8F82;--shiki-dark:#2E8F82}",{"title":38,"searchDepth":71,"depth":71,"links":716},[717,718,719,720,721],{"id":12,"depth":71,"text":13},{"id":165,"depth":71,"text":166},{"id":607,"depth":71,"text":608},{"id":664,"depth":71,"text":665},{"id":691,"depth":71,"text":691},"dev","pathPrefix: false で22取引SVGをすべて表示できるようにし、買掛金発生シーンの花火アニメーションをCSS transformからSMILに切り替えた試行錯誤の記録。","md",{},null,"/transaction-animation-nuxt-smil","eurekapu-nuxt4",false,"2026-05-06T00:00:00.000Z",{"title":5,"description":723},"2026-05/2026-05-06/transaction-animation-nuxt-smil",[734,735,736,737,738],"Vue","SMIL","SVGアニメーション","Nuxt","財務3表","memo","tf1QhpdnM1MksDV0xRcYW1TejmZcQ2jZFma6digCnfc",[],"https://log.eurekapu.com/og/blog/transaction-animation-nuxt-smil.png?v=2026-05-06T00%3A00%3A00.000Z&title=22%E5%8F%96%E5%BC%95%E3%82%A2%E3%83%8B%E3%83%A1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%EF%BC%9ANuxt%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E7%99%BB%E9%8C%B2%E3%83%9F%E3%82%B9%E3%81%A8SMIL%E8%8A%B1%E7%81%AB%E3%82%A2%E3%83%8B%E3%83%A1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3&author=Kei%20Komatsu&sig=0f7544f494fd748e",1782528833621]