[{"data":1,"prerenderedAt":479},["ShallowReactive",2],{"content-/2026-04-30-gestalt-page-mp4-and-svg-cleanup":3,"all-pages-for-dir":477,"og-image-/2026-04-30-gestalt-page-mp4-and-svg-cleanup":478},{"id":4,"title":5,"body":6,"category":459,"description":460,"extension":461,"meta":462,"navigation":200,"path":463,"project_name":464,"published":465,"publishedAt":466,"seo":467,"stem":468,"tags":469,"todo":475,"updatedAt":475,"__hash__":476},"pages/2026-04/2026-04-30/gestalt-page-mp4-and-svg-cleanup.md","ゲシュタルトの法則ページに自作MP4を埋め込み、SVGも縦組みに修正",{"type":7,"value":8,"toc":451},"minimark",[9,13,18,30,34,41,47,54,58,66,98,288,295,298,313,324,331,381,384,391,394,433,444,447],[10,11,12],"p",{},"朝、昨日の積み残しから手を付けた。ゲシュタルトの法則を扱う章で、近接・類似・連続性などの6法則を解説するスライドが、いまは静止画のスクリーンショットだけで止まっていた。旧プロジェクト（WordPress版）には自作のMP4が10本あって、それぞれ「列幅を1にする」「行を挟む」といったExcel操作のビフォー・アフターを録画してある。今日のミッションはまずこれを新サイトに引っ張ってくることだった。",[14,15,17],"h2",{"id":16},"r2への載せ替え","R2への載せ替え",[10,19,20,21,25,26,29],{},"旧サイトのMP4はWordPressの URL から配信されていた。ローカルにファイルが残っているか心配したが、URL を直接叩いたら10本すべて生きていた。合計4.3MB。",[22,23,24],"code",{},"public/images/excel/gestalt/"," に一旦ダウンロードして中身を確認したあと、R2 (",[22,27,28],{},"assets.info-accounting.com/images/excel/gestalt/",") にまとめてアップロードした。WordPressの動画資産はこれで新サイト側に独立した形になる。",[14,31,33],{"id":32},"scrollarticle-に動画グリッドを足す","ScrollArticle に動画グリッドを足す",[10,35,36,37,40],{},"ScrollArticle.vue に動画用のスタイルを追加し、Chapter 3 を統合記事パターンに書き換えた。最初の構成は「6法則を冒頭で2行×3列のグリッドで紹介」「各法則を ",[22,38,39],{},"gestalt-row"," という2カラム（左SVG＋右動画）で深掘り」というもの。実機で開いた瞬間、ユーザーから方針変更の指示が飛んできた。",[42,43,44],"blockquote",{},[10,45,46],{},"2カラムになっているレイアウトはやめてください。普通に原理を縦に並べてもらって構わないです。",[10,48,49,50,53],{},"縦組みに切り替えた。",[22,51,52],{},".gestalt-row"," のスタイルを削って、SVG と動画を上下に積むだけのシンプルな並びにした。動画の最大幅を抑えて、横スクロールが出ないようにも調整している。",[14,55,57],{"id":56},"svgの左半分をpythonで一括削除","SVGの左半分をPythonで一括削除",[10,59,60,61,65],{},"もう1つの指示が地味に重い作業だった。各法則のSVGは「左に原理イラスト（紫の背景）＋右にExcelでの活用例」という構成になっていて、新しい縦組みでは左半分が動画と意味的に被ってしまう。",[62,63,64],"strong",{},"4つのSVGから紫の原理エリアだけを切り落としたい","。",[10,67,68,69,72,73,75,76,79,80,83,84,87,88,91,92,83,95,97],{},"最初は ",[22,70,71],{},"/tmp"," で書こうとしたが、Git Bash の ",[22,74,71],{}," と Windows Python のパス認識がずれていてスクリプトが動かなかった。プロジェクト内に ",[22,77,78],{},"scripts/edit-gestalt-svg.py"," を置いて、",[22,81,82],{},"viewBox"," と ",[22,85,86],{},"\u003Cg>"," の transform を読み取り、左半分のグループを ",[22,89,90],{},"lxml"," で削除して書き戻すスクリプトに書き直した。",[22,93,94],{},"width",[22,96,82],{}," を新しいサイズに合わせて再計算する処理も入れて、4ファイルを一気に処理した。",[99,100,105],"pre",{"className":101,"code":102,"language":103,"meta":104,"style":104},"language-python shiki shiki-themes vitesse-light vitesse-light","# 左半分のグループを ID で特定して削除\nfor left_g in tree.xpath('//svg:g[@id=\"left-principle\"]', namespaces=NS):\n    left_g.getparent().remove(left_g)\n\n# viewBox と width を右半分の幅に合わせて縮める\nsvg.set(\"viewBox\", f\"0 0 {new_w} {h}\")\nsvg.set(\"width\", str(new_w))\n","python","",[22,106,107,116,170,195,202,208,258],{"__ignoreMap":104},[108,109,112],"span",{"class":110,"line":111},"line",1,[108,113,115],{"class":114},"sxvE3","# 左半分のグループを ID で特定して削除\n",[108,117,119,123,127,130,133,137,140,143,147,151,153,156,160,163,167],{"class":110,"line":118},2,[108,120,122],{"class":121},"sHkkW","for",[108,124,126],{"class":125},"sG7-3"," left_g ",[108,128,129],{"class":121},"in",[108,131,132],{"class":125}," tree",[108,134,136],{"class":135},"shFtX",".",[108,138,139],{"class":125},"xpath",[108,141,142],{"class":135},"(",[108,144,146],{"class":145},"sMJiu","'",[108,148,150],{"class":149},"sdGka","//svg:g[@id=\"left-principle\"]",[108,152,146],{"class":145},[108,154,155],{"class":135},",",[108,157,159],{"class":158},"s4oTP"," namespaces",[108,161,162],{"class":135},"=",[108,164,166],{"class":165},"snbK4","NS",[108,168,169],{"class":135},"):\n",[108,171,173,176,178,181,184,187,189,192],{"class":110,"line":172},3,[108,174,175],{"class":125},"    left_g",[108,177,136],{"class":135},[108,179,180],{"class":125},"getparent",[108,182,183],{"class":135},"().",[108,185,186],{"class":125},"remove",[108,188,142],{"class":135},[108,190,191],{"class":125},"left_g",[108,193,194],{"class":135},")\n",[108,196,198],{"class":110,"line":197},4,[108,199,201],{"emptyLinePlaceholder":200},true,"\n",[108,203,205],{"class":110,"line":204},5,[108,206,207],{"class":114},"# viewBox と width を右半分の幅に合わせて縮める\n",[108,209,211,214,216,219,221,224,226,228,230,234,237,240,243,246,249,252,254,256],{"class":110,"line":210},6,[108,212,213],{"class":125},"svg",[108,215,136],{"class":135},[108,217,218],{"class":125},"set",[108,220,142],{"class":135},[108,222,223],{"class":145},"\"",[108,225,82],{"class":149},[108,227,223],{"class":145},[108,229,155],{"class":135},[108,231,233],{"class":232},"stQ0i"," f",[108,235,236],{"class":149},"\"0 0 ",[108,238,239],{"class":165},"{",[108,241,242],{"class":125},"new_w",[108,244,245],{"class":165},"}",[108,247,248],{"class":165}," {",[108,250,251],{"class":125},"h",[108,253,245],{"class":165},[108,255,223],{"class":149},[108,257,194],{"class":135},[108,259,261,263,265,267,269,271,273,275,277,281,283,285],{"class":110,"line":260},7,[108,262,213],{"class":125},[108,264,136],{"class":135},[108,266,218],{"class":125},[108,268,142],{"class":135},[108,270,223],{"class":145},[108,272,94],{"class":149},[108,274,223],{"class":145},[108,276,155],{"class":135},[108,278,280],{"class":279},"sz8Xr"," str",[108,282,142],{"class":135},[108,284,242],{"class":125},[108,286,287],{"class":135},"))\n",[10,289,290,291,294],{},"加工済みのSVGを再びR2にアップロードし、データファイルの URL を ",[22,292,293],{},"?v=2"," 付きで差し替えた。リロードしたら、紫の原理エリアが消えて Excel の図だけが大きく残った。動画とSVGがそれぞれ別の役割を担う構図になり、視線が左右に揺れなくて済むようになった。",[14,296,297],{"id":297},"拡大モーダルをプラグイン側で広げる",[10,299,300,301,304,305,308,309,312],{},"仕上げに、ユーザーから「他のページと同じく、SVG をクリックで拡大できるようにしておいて」と言われた。既存の ",[22,302,303],{},"image-modal.client.ts"," プラグインを開いてみると、対象クラスが ",[22,306,307],{},"step-img"," ",[22,310,311],{},"scroll-img"," などに限定されていた。",[10,314,315,316,319,320,323],{},"最初は素直にデータファイル側で ",[22,317,318],{},"class=\"step-img\""," を6つのSVGに付けて回ろうとした。手が止まる。",[62,321,322],{},"6個ならまだしも、これからゲシュタルト以外の章でも同じ要望が出るたびに class を散らすのは負債が増えるだけ","だ。",[10,325,326,327,330],{},"方針を切り替えて、プラグイン側のセレクタを広げた。ScrollArticle 配下の ",[22,328,329],{},"img"," タグはすべてモーダル対象にする、という条件に変更したことで、データファイル側の class 指定は不要になった。リロードして6つのSVGすべてをクリックしたら、全画面オーバーレイが立ち上がった。",[99,332,336],{"className":333,"code":334,"language":335,"meta":104,"style":104},"language-ts shiki shiki-themes vitesse-light vitesse-light","// 個別class指定をやめて、ScrollArticle配下のimgを一括対象に\nconst targets = document.querySelectorAll(\n  '.scroll-article img, .step-img, .scroll-img'\n)\n","ts",[22,337,338,343,366,377],{"__ignoreMap":104},[108,339,340],{"class":110,"line":111},[108,341,342],{"class":114},"// 個別class指定をやめて、ScrollArticle配下のimgを一括対象に\n",[108,344,345,348,351,354,357,359,363],{"class":110,"line":118},[108,346,347],{"class":232},"const ",[108,349,350],{"class":158},"targets",[108,352,353],{"class":135}," =",[108,355,356],{"class":158}," document",[108,358,136],{"class":135},[108,360,362],{"class":361},"senZ8","querySelectorAll",[108,364,365],{"class":135},"(\n",[108,367,368,371,374],{"class":110,"line":172},[108,369,370],{"class":145},"  '",[108,372,373],{"class":149},".scroll-article img, .step-img, .scroll-img",[108,375,376],{"class":145},"'\n",[108,378,379],{"class":110,"line":197},[108,380,194],{"class":135},[14,382,383],{"id":383},"動画の自動再生はビューポート内のみ",[10,385,386,387,390],{},"動作確認中にひとつ気になる挙動があった。ページを開いた直後、画面外の動画は再生されていなかった。一瞬「IntersectionObserver の設定ミスか？」と疑ったが、Chromeのリソース節約挙動で、ビューポート外の ",[22,388,389],{},"\u003Cvideo autoplay>"," は自動でpauseされる仕様だった。スクロールして画面に入ると再生が始まる。これは正常動作なので触らないことにした。",[14,392,393],{"id":393},"振り返り",[395,396,399,409,415,421,427],"ul",{"className":397},[398],"contains-task-list",[400,401,404,408],"li",{"className":402},[403],"task-list-item",[405,406],"input",{"checked":200,"disabled":200,"type":407},"checkbox"," 旧WordPressから MP4 10本を回収して R2 に載せ替え",[400,410,412,414],{"className":411},[403],[405,413],{"checked":200,"disabled":200,"type":407}," ScrollArticle の 2カラム → 縦組みに作り直し",[400,416,418,420],{"className":417},[403],[405,419],{"checked":200,"disabled":200,"type":407}," SVG 4本の左半分（紫の原理エリア）を Python スクリプトで削除",[400,422,424,426],{"className":423},[403],[405,425],{"checked":200,"disabled":200,"type":407}," 拡大モーダルをプラグイン側で広げ、データファイルの class 指定を不要に",[400,428,430,432],{"className":429},[403],[405,431],{"checked":200,"disabled":200,"type":407}," ビューポート外動画の停止が Chrome の仕様と確認",[10,434,435,436,439,440,443],{},"最初に「2カラムでいきます」と組んでみせて、ユーザーの「いや縦で」を受けてから素直に作り直す、というやり取りが今日は2、3回続いた。SVGの左半分削除も、最初は ",[22,437,438],{},"\u003Crect>"," のwidthを縮めるだけで済まそうとして、Pythonでグループごと取り去る方針に切り替えたタイミングで筋が通った。",[62,441,442],{},"先にレイアウトを実装して見せる → 違和感を拾う → 削る方向に倒す","の順番が、今日は終始効いていた一日だった。",[10,445,446],{},"明日以降は、Excel風のテーブルレンダリング（A列スペーサー、F列スペーサー、参照値の緑色化など）をゲシュタルト章のSVGの後段に組み込む流れになりそうだ。これは別記事に分ける。",[448,449,450],"style",{},"html pre.shiki code .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}html pre.shiki code .sHkkW, html code.shiki .sHkkW{--shiki-default:#1E754F;--shiki-dark:#1E754F}html pre.shiki code .sG7-3, html code.shiki .sG7-3{--shiki-default:#393A34;--shiki-dark:#393A34}html pre.shiki code .shFtX, html code.shiki .shFtX{--shiki-default:#999999;--shiki-dark:#999999}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 .s4oTP, html code.shiki .s4oTP{--shiki-default:#B07D48;--shiki-dark:#B07D48}html pre.shiki code .snbK4, html code.shiki .snbK4{--shiki-default:#A65E2B;--shiki-dark:#A65E2B}html pre.shiki code .stQ0i, html code.shiki .stQ0i{--shiki-default:#AB5959;--shiki-dark:#AB5959}html pre.shiki code .sz8Xr, html code.shiki .sz8Xr{--shiki-default:#998418;--shiki-dark:#998418}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 .senZ8, html code.shiki .senZ8{--shiki-default:#59873A;--shiki-dark:#59873A}",{"title":104,"searchDepth":118,"depth":118,"links":452},[453,454,455,456,457,458],{"id":16,"depth":118,"text":17},{"id":32,"depth":118,"text":33},{"id":56,"depth":118,"text":57},{"id":297,"depth":118,"text":297},{"id":383,"depth":118,"text":383},{"id":393,"depth":118,"text":393},"dev","旧WordPressから自作MP4を10本回収してR2へ載せ替え、ScrollArticleの2カラムを縦組みに作り直し、SVGの左半分をPythonで一括削除した一日の記録。","md",{},"/2026-04-30-gestalt-page-mp4-and-svg-cleanup","eurekapu-nuxt4",false,"2026-04-30T00:00:00.000Z",{"title":5,"description":460},"2026-04/2026-04-30/gestalt-page-mp4-and-svg-cleanup",[470,471,472,473,474],"ScrollArticle","MP4動画","SVG","Cloudflare R2","レイアウト調整",null,"lASN0AZl68xqte5po1kyXIZdgWrzrXklqwZIViV9TSU",[],"https://log.eurekapu.com/favicon.svg",1777617050495]