[{"data":1,"prerenderedAt":954},["ShallowReactive",2],{"article-mandala-art":3},{"id":4,"title":5,"body":6,"date":946,"description":947,"extension":948,"meta":949,"navigation":167,"path":950,"seo":951,"stem":952,"__hash__":953},"articles\u002Farticles\u002Fmandala-art.md","SVG で描くマンダラ — stroke-dashoffset アニメーション",{"type":7,"value":8,"toc":936},"minimark",[9,13,17,20,23,27,35,40,52,68,79,205,208,218,400,403,485,487,490,493,504,625,641,643,646,649,848,859,861,865,932],[10,11,12],"h2",{"id":12},"作品",[14,15,16],"p",{},"ページを開いた瞬間、中心から外に向かってパスが順番に描かれていきます。\n描き終わると、六角形のペアが互いに逆方向へゆっくり回転し続けます。",[18,19],"drawing-mandala",{},[21,22],"hr",{},[10,24,26],{"id":25},"核心技術-stroke-dashoffset","核心技術 — stroke-dashoffset",[14,28,29,30,34],{},"SVG のパスには「破線のオフセット」を制御する ",[31,32,33],"code",{},"stroke-dashoffset"," プロパティがあります。\nこれを使って「未描画 → 描画済み」を表現するのが、このアニメーションの核心です。",[36,37,39],"h3",{"id":38},"pathlength-で長さを正規化","pathLength で長さを正規化",[14,41,42,43,46,47,51],{},"すべてのパス（円・多角形・線）の長さはバラバラです。\n",[31,44,45],{},"pathLength=\"100\""," を指定すると、ブラウザがそのパスの総長さを ",[48,49,50],"strong",{},"100"," として扱ってくれます。",[53,54,59],"pre",{"className":55,"code":56,"language":57,"meta":58,"style":58},"language-svg shiki shiki-themes github-light github-dark","\u003Ccircle pathLength=\"100\" cx=\"200\" cy=\"200\" r=\"172\" \u002F>\n","svg","",[31,60,61],{"__ignoreMap":58},[62,63,66],"span",{"class":64,"line":65},"line",1,[62,67,56],{},[14,69,70,71,74,75,78],{},"これで ",[31,72,73],{},"stroke-dasharray: 100; stroke-dashoffset: 100"," が「全部隠れた状態」、\n",[31,76,77],{},"stroke-dashoffset: 0"," が「全部描かれた状態」と統一できます。",[53,80,84],{"className":81,"code":82,"language":83,"meta":58,"style":58},"language-css shiki shiki-themes github-light github-dark",".draw {\n  stroke-dasharray: 100;\n  stroke-dashoffset: 100;            \u002F* 初期：全部隠す *\u002F\n  animation: draw 1s ease forwards;  \u002F* → 0 まで動かして描く *\u002F\n}\n\n@keyframes draw {\n  to { stroke-dashoffset: 0; }\n}\n","css",[31,85,86,96,111,128,156,162,169,181,200],{"__ignoreMap":58},[62,87,88,92],{"class":64,"line":65},[62,89,91],{"class":90},"sScJk",".draw",[62,93,95],{"class":94},"sVt8B"," {\n",[62,97,99,103,106,108],{"class":64,"line":98},2,[62,100,102],{"class":101},"sj4cs","  stroke-dasharray",[62,104,105],{"class":94},": ",[62,107,50],{"class":101},[62,109,110],{"class":94},";\n",[62,112,114,117,119,121,124],{"class":64,"line":113},3,[62,115,116],{"class":101},"  stroke-dashoffset",[62,118,105],{"class":94},[62,120,50],{"class":101},[62,122,123],{"class":94},";            ",[62,125,127],{"class":126},"sJ8bj","\u002F* 初期：全部隠す *\u002F\n",[62,129,131,134,137,140,144,147,150,153],{"class":64,"line":130},4,[62,132,133],{"class":101},"  animation",[62,135,136],{"class":94},": draw ",[62,138,139],{"class":101},"1",[62,141,143],{"class":142},"szBVR","s",[62,145,146],{"class":101}," ease",[62,148,149],{"class":101}," forwards",[62,151,152],{"class":94},";  ",[62,154,155],{"class":126},"\u002F* → 0 まで動かして描く *\u002F\n",[62,157,159],{"class":64,"line":158},5,[62,160,161],{"class":94},"}\n",[62,163,165],{"class":64,"line":164},6,[62,166,168],{"emptyLinePlaceholder":167},true,"\n",[62,170,172,175,179],{"class":64,"line":171},7,[62,173,174],{"class":142},"@keyframes",[62,176,178],{"class":177},"s4XuR"," draw",[62,180,95],{"class":94},[62,182,184,187,190,192,194,197],{"class":64,"line":183},8,[62,185,186],{"class":90},"  to",[62,188,189],{"class":94}," { ",[62,191,33],{"class":101},[62,193,105],{"class":94},[62,195,196],{"class":101},"0",[62,198,199],{"class":94},"; }\n",[62,201,203],{"class":64,"line":202},9,[62,204,161],{"class":94},[36,206,207],{"id":207},"遅延で順番を制御",[14,209,210,213,214,217],{},[31,211,212],{},"animation-delay"," に CSS カスタムプロパティ（",[31,215,216],{},"--d","）を使い、要素ごとに描画タイミングをずらします。",[53,219,223],{"className":220,"code":221,"language":222,"meta":58,"style":58},"language-vue shiki shiki-themes github-light github-dark","\u003C!-- 中心から外に向かって遅延を増やす -->\n\u003Ccircle class=\"draw\" style=\"--d: 0s;   --t: 0.6s\" r=\"24\" \u002F>   \u003C!-- 中心リング -->\n\u003Ccircle class=\"draw\" style=\"--d: 0.8s; --t: 1.0s\" r=\"100\" \u002F>  \u003C!-- 中間リング -->\n\u003Ccircle class=\"draw\" style=\"--d: 2.5s; --t: 1.2s\" r=\"172\" \u002F>  \u003C!-- 外側リング -->\n","vue",[31,224,225,230,294,348],{"__ignoreMap":58},[62,226,227],{"class":64,"line":65},[62,228,229],{"class":126},"\u003C!-- 中心から外に向かって遅延を増やす -->\n",[62,231,232,235,239,242,245,249,252,254,257,259,261,263,265,268,271,273,276,278,280,283,285,288,291],{"class":64,"line":98},[62,233,234],{"class":94},"\u003C",[62,236,238],{"class":237},"s9eBZ","circle",[62,240,241],{"class":90}," class",[62,243,244],{"class":94},"=",[62,246,248],{"class":247},"sZZnC","\"draw\"",[62,250,251],{"class":90}," style",[62,253,244],{"class":94},[62,255,256],{"class":247},"\"",[62,258,216],{"class":177},[62,260,105],{"class":94},[62,262,196],{"class":101},[62,264,143],{"class":142},[62,266,267],{"class":94},";   ",[62,269,270],{"class":177},"--t",[62,272,105],{"class":94},[62,274,275],{"class":101},"0.6",[62,277,143],{"class":142},[62,279,256],{"class":247},[62,281,282],{"class":90}," r",[62,284,244],{"class":94},[62,286,287],{"class":247},"\"24\"",[62,289,290],{"class":94}," \u002F>   ",[62,292,293],{"class":126},"\u003C!-- 中心リング -->\n",[62,295,296,298,300,302,304,306,308,310,312,314,316,319,321,324,326,328,331,333,335,337,339,342,345],{"class":64,"line":113},[62,297,234],{"class":94},[62,299,238],{"class":237},[62,301,241],{"class":90},[62,303,244],{"class":94},[62,305,248],{"class":247},[62,307,251],{"class":90},[62,309,244],{"class":94},[62,311,256],{"class":247},[62,313,216],{"class":177},[62,315,105],{"class":94},[62,317,318],{"class":101},"0.8",[62,320,143],{"class":142},[62,322,323],{"class":94},"; ",[62,325,270],{"class":177},[62,327,105],{"class":94},[62,329,330],{"class":101},"1.0",[62,332,143],{"class":142},[62,334,256],{"class":247},[62,336,282],{"class":90},[62,338,244],{"class":94},[62,340,341],{"class":247},"\"100\"",[62,343,344],{"class":94}," \u002F>  ",[62,346,347],{"class":126},"\u003C!-- 中間リング -->\n",[62,349,350,352,354,356,358,360,362,364,366,368,370,373,375,377,379,381,384,386,388,390,392,395,397],{"class":64,"line":130},[62,351,234],{"class":94},[62,353,238],{"class":237},[62,355,241],{"class":90},[62,357,244],{"class":94},[62,359,248],{"class":247},[62,361,251],{"class":90},[62,363,244],{"class":94},[62,365,256],{"class":247},[62,367,216],{"class":177},[62,369,105],{"class":94},[62,371,372],{"class":101},"2.5",[62,374,143],{"class":142},[62,376,323],{"class":94},[62,378,270],{"class":177},[62,380,105],{"class":94},[62,382,383],{"class":101},"1.2",[62,385,143],{"class":142},[62,387,256],{"class":247},[62,389,282],{"class":90},[62,391,244],{"class":94},[62,393,394],{"class":247},"\"172\"",[62,396,344],{"class":94},[62,398,399],{"class":126},"\u003C!-- 外側リング -->\n",[14,401,402],{},"12本の放射線も個別に遅延をずらして、「扇形に広がる」演出をしています。",[53,404,408],{"className":405,"code":406,"language":407,"meta":58,"style":58},"language-ts shiki shiki-themes github-light github-dark","const radialLines = Array.from({ length: 12 }, (_, i) => ({\n  delay: 0.4 + i * 0.04,  \u002F\u002F 0.04秒ずつずらす\n}))\n","ts",[31,409,410,454,480],{"__ignoreMap":58},[62,411,412,415,418,421,424,427,430,433,436,439,442,445,448,451],{"class":64,"line":65},[62,413,414],{"class":142},"const",[62,416,417],{"class":101}," radialLines",[62,419,420],{"class":142}," =",[62,422,423],{"class":94}," Array.",[62,425,426],{"class":90},"from",[62,428,429],{"class":94},"({ length: ",[62,431,432],{"class":101},"12",[62,434,435],{"class":94}," }, (",[62,437,438],{"class":177},"_",[62,440,441],{"class":94},", ",[62,443,444],{"class":177},"i",[62,446,447],{"class":94},") ",[62,449,450],{"class":142},"=>",[62,452,453],{"class":94}," ({\n",[62,455,456,459,462,465,468,471,474,477],{"class":64,"line":98},[62,457,458],{"class":94},"  delay: ",[62,460,461],{"class":101},"0.4",[62,463,464],{"class":142}," +",[62,466,467],{"class":94}," i ",[62,469,470],{"class":142},"*",[62,472,473],{"class":101}," 0.04",[62,475,476],{"class":94},",  ",[62,478,479],{"class":126},"\u002F\u002F 0.04秒ずつずらす\n",[62,481,482],{"class":64,"line":113},[62,483,484],{"class":94},"}))\n",[21,486],{},[10,488,489],{"id":489},"六角形の逆回転",[14,491,492],{},"描き終わった後、2つの六角形（30° ずらして重ねた Star of David 状のペア）が逆方向に回り続けます。",[14,494,495,496,499,500,503],{},"各六角形を ",[31,497,498],{},"\u003Cg>"," で包み、",[31,501,502],{},"transform-origin"," を SVG 中心（200px 200px）に設定します。",[53,505,507],{"className":81,"code":506,"language":83,"meta":58,"style":58},".spin-cw  { animation: spin-cw  18s linear infinite; }\n.spin-ccw { animation: spin-ccw 18s linear infinite; }\n\n@keyframes spin-cw  { to { transform: rotate(360deg);  } }\n@keyframes spin-ccw { to { transform: rotate(-360deg); } }\n",[31,508,509,536,558,562,596],{"__ignoreMap":58},[62,510,511,514,517,520,523,526,528,531,534],{"class":64,"line":65},[62,512,513],{"class":90},".spin-cw",[62,515,516],{"class":94},"  { ",[62,518,519],{"class":101},"animation",[62,521,522],{"class":94},": spin-cw  ",[62,524,525],{"class":101},"18",[62,527,143],{"class":142},[62,529,530],{"class":101}," linear",[62,532,533],{"class":101}," infinite",[62,535,199],{"class":94},[62,537,538,541,543,545,548,550,552,554,556],{"class":64,"line":98},[62,539,540],{"class":90},".spin-ccw",[62,542,189],{"class":94},[62,544,519],{"class":101},[62,546,547],{"class":94},": spin-ccw ",[62,549,525],{"class":101},[62,551,143],{"class":142},[62,553,530],{"class":101},[62,555,533],{"class":101},[62,557,199],{"class":94},[62,559,560],{"class":64,"line":113},[62,561,168],{"emptyLinePlaceholder":167},[62,563,564,566,569,571,574,576,579,581,584,587,590,593],{"class":64,"line":130},[62,565,174],{"class":142},[62,567,568],{"class":177}," spin-cw",[62,570,516],{"class":94},[62,572,573],{"class":90},"to",[62,575,189],{"class":94},[62,577,578],{"class":101},"transform",[62,580,105],{"class":94},[62,582,583],{"class":101},"rotate",[62,585,586],{"class":94},"(",[62,588,589],{"class":101},"360",[62,591,592],{"class":142},"deg",[62,594,595],{"class":94},");  } }\n",[62,597,598,600,603,605,607,609,611,613,615,617,620,622],{"class":64,"line":158},[62,599,174],{"class":142},[62,601,602],{"class":177}," spin-ccw",[62,604,189],{"class":94},[62,606,573],{"class":90},[62,608,189],{"class":94},[62,610,578],{"class":101},[62,612,105],{"class":94},[62,614,583],{"class":101},[62,616,586],{"class":94},[62,618,619],{"class":101},"-360",[62,621,592],{"class":142},[62,623,624],{"class":94},"); } }\n",[14,626,627,628,631,632,634,635,637,638,640],{},"ポイントは ",[31,629,630],{},"animation-delay: 4.2s"," で描画アニメーション終了まで回転を待つことです。\n",[31,633,498],{}," の ",[31,636,212],{}," と子要素の描画 ",[31,639,212],{}," は独立しているため、互いに干渉しません。",[21,642],{},[10,644,645],{"id":645},"座標の計算",[14,647,648],{},"マンダラは「円の上に等間隔で点を打つ」繰り返しです。\nJavaScript の三角関数で座標を求めます。",[53,650,652],{"className":405,"code":651,"language":407,"meta":58,"style":58},"const pt = (r: number, deg: number) => ({\n  x: CX + r * Math.cos((deg * Math.PI) \u002F 180),\n  y: CY + r * Math.sin((deg * Math.PI) \u002F 180),\n})\n\n\u002F\u002F 正六角形（頂点6個、60°間隔）\nconst hexPoints = Array.from({ length: 6 }, (_, i) =>\n  pt(100, -90 + i * 60)  \u002F\u002F -90° = 真上から開始\n)\n",[31,653,654,689,731,766,771,775,780,811,843],{"__ignoreMap":58},[62,655,656,658,661,663,666,669,672,675,677,679,681,683,685,687],{"class":64,"line":65},[62,657,414],{"class":142},[62,659,660],{"class":90}," pt",[62,662,420],{"class":142},[62,664,665],{"class":94}," (",[62,667,668],{"class":177},"r",[62,670,671],{"class":142},":",[62,673,674],{"class":101}," number",[62,676,441],{"class":94},[62,678,592],{"class":177},[62,680,671],{"class":142},[62,682,674],{"class":101},[62,684,447],{"class":94},[62,686,450],{"class":142},[62,688,453],{"class":94},[62,690,691,694,697,699,702,704,707,710,713,715,717,720,722,725,728],{"class":64,"line":98},[62,692,693],{"class":94},"  x: ",[62,695,696],{"class":101},"CX",[62,698,464],{"class":142},[62,700,701],{"class":94}," r ",[62,703,470],{"class":142},[62,705,706],{"class":94}," Math.",[62,708,709],{"class":90},"cos",[62,711,712],{"class":94},"((deg ",[62,714,470],{"class":142},[62,716,706],{"class":94},[62,718,719],{"class":101},"PI",[62,721,447],{"class":94},[62,723,724],{"class":142},"\u002F",[62,726,727],{"class":101}," 180",[62,729,730],{"class":94},"),\n",[62,732,733,736,739,741,743,745,747,750,752,754,756,758,760,762,764],{"class":64,"line":113},[62,734,735],{"class":94},"  y: ",[62,737,738],{"class":101},"CY",[62,740,464],{"class":142},[62,742,701],{"class":94},[62,744,470],{"class":142},[62,746,706],{"class":94},[62,748,749],{"class":90},"sin",[62,751,712],{"class":94},[62,753,470],{"class":142},[62,755,706],{"class":94},[62,757,719],{"class":101},[62,759,447],{"class":94},[62,761,724],{"class":142},[62,763,727],{"class":101},[62,765,730],{"class":94},[62,767,768],{"class":64,"line":130},[62,769,770],{"class":94},"})\n",[62,772,773],{"class":64,"line":158},[62,774,168],{"emptyLinePlaceholder":167},[62,776,777],{"class":64,"line":164},[62,778,779],{"class":126},"\u002F\u002F 正六角形（頂点6個、60°間隔）\n",[62,781,782,784,787,789,791,793,795,798,800,802,804,806,808],{"class":64,"line":171},[62,783,414],{"class":142},[62,785,786],{"class":101}," hexPoints",[62,788,420],{"class":142},[62,790,423],{"class":94},[62,792,426],{"class":90},[62,794,429],{"class":94},[62,796,797],{"class":101},"6",[62,799,435],{"class":94},[62,801,438],{"class":177},[62,803,441],{"class":94},[62,805,444],{"class":177},[62,807,447],{"class":94},[62,809,810],{"class":142},"=>\n",[62,812,813,816,818,820,822,825,828,830,832,834,837,840],{"class":64,"line":183},[62,814,815],{"class":90},"  pt",[62,817,586],{"class":94},[62,819,50],{"class":101},[62,821,441],{"class":94},[62,823,824],{"class":142},"-",[62,826,827],{"class":101},"90",[62,829,464],{"class":142},[62,831,467],{"class":94},[62,833,470],{"class":142},[62,835,836],{"class":101}," 60",[62,838,839],{"class":94},")  ",[62,841,842],{"class":126},"\u002F\u002F -90° = 真上から開始\n",[62,844,845],{"class":64,"line":202},[62,846,847],{"class":94},")\n",[849,850,852],"callout",{"type":851},"tip",[14,853,854,855,858],{},"SVG の Y 軸は下向きが正です。",[31,856,857],{},"-90°"," を開始角にすると頂点が真上に来て、幾何学的に自然な形になります。",[21,860],{},[10,862,864],{"id":863},"css-前作との比較","CSS 前作との比較",[866,867,868,884],"table",{},[869,870,871],"thead",{},[872,873,874,878,881],"tr",{},[875,876,877],"th",{},"手法",[875,879,880],{},"本記事 (SVG stroke)",[875,882,883],{},"前作 CSS (gradient)",[885,886,887,899,910,921],"tbody",{},[872,888,889,893,896],{},[890,891,892],"td",{},"動き",[890,894,895],{},"描画アニメーション",[890,897,898],{},"脈動・軌道回転",[872,900,901,904,907],{},[890,902,903],{},"形状の精度",[890,905,906],{},"数学的に正確",[890,908,909],{},"近似的",[872,911,912,915,918],{},[890,913,914],{},"インタラクティブ性",[890,916,917],{},"パスごとに制御可能",[890,919,920],{},"要素単位",[872,922,923,926,929],{},[890,924,925],{},"コード量",[890,927,928],{},"多め",[890,930,931],{},"少なめ",[933,934,935],"style",{},"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 .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":58,"searchDepth":98,"depth":98,"links":937},[938,939,943,944,945],{"id":12,"depth":98,"text":12},{"id":25,"depth":98,"text":26,"children":940},[941,942],{"id":38,"depth":113,"text":39},{"id":207,"depth":113,"text":207},{"id":489,"depth":98,"text":489},{"id":645,"depth":98,"text":645},{"id":863,"depth":98,"text":864},"2026-05-01T16:00:00+09:00","SVG の stroke-dashoffset を使ってパスが自己描画するマンダラアニメーションを作る手法を解説します。","md",{},"\u002Farticles\u002Fmandala-art",{"title":5,"description":947},"articles\u002Fmandala-art","h_epan9PlIxLQuYmrIDOuqdPa9R5olIkcbIcE0WWh2g",1777568742087]