[{"data":1,"prerenderedAt":21327},["ShallowReactive",2],{"tag-js":3},{"count":4,"content":5},30,[6,1436,5534,9202,14080,15434,16228,16358,17986,19025],{"id":7,"title":8,"body":9,"category":1422,"createdAt":1424,"description":1425,"extension":1426,"index":1427,"meta":1428,"navigation":340,"path":1429,"publish":340,"seo":1430,"series":1427,"seriesTitle":1427,"stem":1431,"tag":1432,"thumbnail":1434,"updatedAt":1424,"__hash__":1435},"articles\u002Farticles\u002Fmultiple-select-vue.md","multiple属性での複数選択セレクトボックスで選択した値をemitする方法。",{"type":10,"value":11,"toc":1416},"minimark",[12,16,20,23,41,176,197,200,576,583,590,594,597,605,717,728,733,736,740,744,747,1226,1232,1238,1247,1314,1331,1388,1397,1400,1403,1406,1409,1412],[13,14,15],"p",{},"こんにちはjunです。vue.jsを用いてフォームUIを作成していたところmultipule属性を付与したセレクトボックスで選択した値を$emitする方法についてメモ＆共有しようと思います。",[17,18,19],"h2",{"id":19},"vueを用いてselectの値を受け取る",[13,21,22],{},"セレクトボックスは基本的には以下の様に、プルダウン式のメニューから一つの値を選ぶ要素です。",[24,25,27,28,27,33,27,37],"select",{"name":26},"sample","\n ",[29,30,32],"option",{"value":31},"1","選択肢１",[29,34,36],{"value":35},"2","選択肢2",[29,38,40],{"value":39},"3","選択肢3",[42,43,48],"pre",{"className":44,"code":45,"language":46,"meta":47,"style":47},"language-html shiki shiki-themes material-theme-ocean","\u003Cselect name=\"sample\">\n \u003Coption value=\"1\">選択肢１\u003C\u002Foption>\n \u003Coption value=\"2\">選択肢2\u003C\u002Foption>\n \u003Coption value=\"3\">選択肢3\u003C\u002Foption>\n\u003C\u002Fselect>\n","html","",[49,50,51,81,113,140,167],"code",{"__ignoreMap":47},[52,53,56,60,63,67,70,73,76,78],"span",{"class":54,"line":55},"line",1,[52,57,59],{"class":58},"sAklC","\u003C",[52,61,24],{"class":62},"s-wAU",[52,64,66],{"class":65},"sJ14y"," name",[52,68,69],{"class":58},"=",[52,71,72],{"class":58},"\"",[52,74,26],{"class":75},"sfyAc",[52,77,72],{"class":58},[52,79,80],{"class":58},">\n",[52,82,84,87,89,92,94,96,98,100,103,106,109,111],{"class":54,"line":83},2,[52,85,86],{"class":58}," \u003C",[52,88,29],{"class":62},[52,90,91],{"class":65}," value",[52,93,69],{"class":58},[52,95,72],{"class":58},[52,97,31],{"class":75},[52,99,72],{"class":58},[52,101,102],{"class":58},">",[52,104,32],{"class":105},"s0W1g",[52,107,108],{"class":58},"\u003C\u002F",[52,110,29],{"class":62},[52,112,80],{"class":58},[52,114,116,118,120,122,124,126,128,130,132,134,136,138],{"class":54,"line":115},3,[52,117,86],{"class":58},[52,119,29],{"class":62},[52,121,91],{"class":65},[52,123,69],{"class":58},[52,125,72],{"class":58},[52,127,35],{"class":75},[52,129,72],{"class":58},[52,131,102],{"class":58},[52,133,36],{"class":105},[52,135,108],{"class":58},[52,137,29],{"class":62},[52,139,80],{"class":58},[52,141,143,145,147,149,151,153,155,157,159,161,163,165],{"class":54,"line":142},4,[52,144,86],{"class":58},[52,146,29],{"class":62},[52,148,91],{"class":65},[52,150,69],{"class":58},[52,152,72],{"class":58},[52,154,39],{"class":75},[52,156,72],{"class":58},[52,158,102],{"class":58},[52,160,40],{"class":105},[52,162,108],{"class":58},[52,164,29],{"class":62},[52,166,80],{"class":58},[52,168,170,172,174],{"class":54,"line":169},5,[52,171,108],{"class":58},[52,173,24],{"class":62},[52,175,80],{"class":58},[13,177,178,179,182,183,185,186,188,189,192,193,196],{},"基本的にinput系の値を取得するときは",[49,180,181],{},"v-model","を使用するのが定石ですが、",[49,184,181],{},"では解決できないコンポーネントの状況もあるでしょう。私も",[49,187,181],{},"という糖衣構文ではなく",[49,190,191],{},"v-bind=\"~~~\"",", ",[49,194,195],{},"@input=\"$emit(~~~~)\"","という形で入力された値を親コンポーネントに返す様にしていました。",[13,198,199],{},"一択のセレクトボックスであれば以下の様に記述できます。",[42,201,205],{"className":202,"code":203,"language":204,"meta":47,"style":47},"language-vue shiki shiki-themes material-theme-ocean","\u003Ctemplate>\n \u003Cselect name=\"postName\" @input=\"$emit('newInput', $event.target.value)\">\n   \u003Coption v-for=\"val in selectBox\"\n           :value=\"val.value\" \n           :selected=\"inputValue === val.value\">\n           {{val.text}}\n   \u003C\u002Foption>\n \u003C\u002Fselect>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default{\n name:'childSelect',\n props:['inputed'],  \u002F\u002F親コンポーネントから初期値のvalueを受け取るためのprops\n data(){\n   return{\n     selector:[\n          {text:'選択肢1',value:'1'},\n          {text:'選択肢2',value:'2'},\n          {text:'選択肢3',value:'3'}\n     ],\n   }\n },\n \n}\n\u003C\u002Fscript>\n","vue",[49,206,207,216,247,267,284,300,306,316,326,335,342,352,365,385,413,425,433,444,478,507,537,545,551,557,562,567],{"__ignoreMap":47},[52,208,209,211,214],{"class":54,"line":55},[52,210,59],{"class":58},[52,212,213],{"class":62},"template",[52,215,80],{"class":58},[52,217,218,220,222,224,226,228,231,233,236,238,240,243,245],{"class":54,"line":83},[52,219,86],{"class":58},[52,221,24],{"class":62},[52,223,66],{"class":65},[52,225,69],{"class":58},[52,227,72],{"class":58},[52,229,230],{"class":75},"postName",[52,232,72],{"class":58},[52,234,235],{"class":65}," @input",[52,237,69],{"class":58},[52,239,72],{"class":58},[52,241,242],{"class":75},"$emit('newInput', $event.target.value)",[52,244,72],{"class":58},[52,246,80],{"class":58},[52,248,249,252,254,257,259,261,264],{"class":54,"line":115},[52,250,251],{"class":58},"   \u003C",[52,253,29],{"class":62},[52,255,256],{"class":65}," v-for",[52,258,69],{"class":58},[52,260,72],{"class":58},[52,262,263],{"class":75},"val in selectBox",[52,265,266],{"class":58},"\"\n",[52,268,269,272,274,276,279,281],{"class":54,"line":142},[52,270,271],{"class":65},"           :value",[52,273,69],{"class":58},[52,275,72],{"class":58},[52,277,278],{"class":75},"val.value",[52,280,72],{"class":58},[52,282,283],{"class":58}," \n",[52,285,286,289,291,293,296,298],{"class":54,"line":169},[52,287,288],{"class":65},"           :selected",[52,290,69],{"class":58},[52,292,72],{"class":58},[52,294,295],{"class":75},"inputValue === val.value",[52,297,72],{"class":58},[52,299,80],{"class":58},[52,301,303],{"class":54,"line":302},6,[52,304,305],{"class":105},"           {{val.text}}\n",[52,307,309,312,314],{"class":54,"line":308},7,[52,310,311],{"class":58},"   \u003C\u002F",[52,313,29],{"class":62},[52,315,80],{"class":58},[52,317,319,322,324],{"class":54,"line":318},8,[52,320,321],{"class":58}," \u003C\u002F",[52,323,24],{"class":62},[52,325,80],{"class":58},[52,327,329,331,333],{"class":54,"line":328},9,[52,330,108],{"class":58},[52,332,213],{"class":62},[52,334,80],{"class":58},[52,336,338],{"class":54,"line":337},10,[52,339,341],{"emptyLinePlaceholder":340},true,"\n",[52,343,345,347,350],{"class":54,"line":344},11,[52,346,59],{"class":58},[52,348,349],{"class":62},"script",[52,351,80],{"class":58},[52,353,355,358,362],{"class":54,"line":354},12,[52,356,357],{"class":65},"export",[52,359,361],{"class":360},"s6cf3"," default",[52,363,364],{"class":58},"{\n",[52,366,368,371,374,377,380,382],{"class":54,"line":367},13,[52,369,66],{"class":370},"s5Dmg",[52,372,373],{"class":58},":",[52,375,376],{"class":58},"'",[52,378,379],{"class":75},"childSelect",[52,381,376],{"class":58},[52,383,384],{"class":58},",\n",[52,386,388,391,393,396,398,401,403,406,409],{"class":54,"line":387},14,[52,389,390],{"class":370}," props",[52,392,373],{"class":58},[52,394,395],{"class":62},"[",[52,397,376],{"class":58},[52,399,400],{"class":75},"inputed",[52,402,376],{"class":58},[52,404,405],{"class":62},"]",[52,407,408],{"class":58},",",[52,410,412],{"class":411},"sC9rS","  \u002F\u002F親コンポーネントから初期値のvalueを受け取るためのprops\n",[52,414,416,420,423],{"class":54,"line":415},15,[52,417,419],{"class":418},"sdLwU"," data",[52,421,422],{"class":62},"()",[52,424,364],{"class":58},[52,426,428,431],{"class":54,"line":427},16,[52,429,430],{"class":360},"   return",[52,432,364],{"class":58},[52,434,436,439,441],{"class":54,"line":435},17,[52,437,438],{"class":62},"     selector",[52,440,373],{"class":58},[52,442,443],{"class":62},"[\n",[52,445,447,450,453,455,457,460,462,464,467,469,471,473,475],{"class":54,"line":446},18,[52,448,449],{"class":58},"          {",[52,451,452],{"class":62},"text",[52,454,373],{"class":58},[52,456,376],{"class":58},[52,458,459],{"class":75},"選択肢1",[52,461,376],{"class":58},[52,463,408],{"class":58},[52,465,466],{"class":62},"value",[52,468,373],{"class":58},[52,470,376],{"class":58},[52,472,31],{"class":75},[52,474,376],{"class":58},[52,476,477],{"class":58},"},\n",[52,479,481,483,485,487,489,491,493,495,497,499,501,503,505],{"class":54,"line":480},19,[52,482,449],{"class":58},[52,484,452],{"class":62},[52,486,373],{"class":58},[52,488,376],{"class":58},[52,490,36],{"class":75},[52,492,376],{"class":58},[52,494,408],{"class":58},[52,496,466],{"class":62},[52,498,373],{"class":58},[52,500,376],{"class":58},[52,502,35],{"class":75},[52,504,376],{"class":58},[52,506,477],{"class":58},[52,508,510,512,514,516,518,520,522,524,526,528,530,532,534],{"class":54,"line":509},20,[52,511,449],{"class":58},[52,513,452],{"class":62},[52,515,373],{"class":58},[52,517,376],{"class":58},[52,519,40],{"class":75},[52,521,376],{"class":58},[52,523,408],{"class":58},[52,525,466],{"class":62},[52,527,373],{"class":58},[52,529,376],{"class":58},[52,531,39],{"class":75},[52,533,376],{"class":58},[52,535,536],{"class":58},"}\n",[52,538,540,543],{"class":54,"line":539},21,[52,541,542],{"class":62},"     ]",[52,544,384],{"class":58},[52,546,548],{"class":54,"line":547},22,[52,549,550],{"class":58},"   }\n",[52,552,554],{"class":54,"line":553},23,[52,555,556],{"class":58}," },\n",[52,558,560],{"class":54,"line":559},24,[52,561,283],{"class":62},[52,563,565],{"class":54,"line":564},25,[52,566,536],{"class":58},[52,568,570,572,574],{"class":54,"line":569},26,[52,571,108],{"class":58},[52,573,349],{"class":62},[52,575,80],{"class":58},[13,577,578,579,582],{},"この様にすることで親コンポーネントでは",[49,580,581],{},"newInput","を用いてこのセレクトボックスからの値を受け取ることができます。",[13,584,585,586,589],{},"もし編集画面など、セレクトボックスに予め選択済みの設定を行う場合はpropsにvalueの値を入れておけば、一致する選択肢に",[49,587,588],{},"selected","が付与されます。",[17,591,593],{"id":592},"multipuleで複数選択を可能にする","Multipuleで複数選択を可能にする",[13,595,596],{},"チェックボックスにしては値が多く、リストの方が見やすいのでセレクトボックスから複数選択ができる様にするためには、multipleという属性を付与するだけで実装できます。",[24,598,27,599,27,601,27,603],{"name":26,"multiple":340},[29,600,32],{"value":31},[29,602,36],{"value":35},[29,604,40],{"value":39},[42,606,608],{"className":44,"code":607,"language":46,"meta":47,"style":47},"\u003Cselect name=\"sample\" multiple>\n \u003Coption value=\"1\">選択肢１\u003C\u002Foption>\n \u003Coption value=\"2\">選択肢2\u003C\u002Foption>\n \u003Coption value=\"3\">選択肢3\u003C\u002Foption>\n\u003C\u002Fselect>\n",[49,609,610,631,657,683,709],{"__ignoreMap":47},[52,611,612,614,616,618,620,622,624,626,629],{"class":54,"line":55},[52,613,59],{"class":58},[52,615,24],{"class":62},[52,617,66],{"class":65},[52,619,69],{"class":58},[52,621,72],{"class":58},[52,623,26],{"class":75},[52,625,72],{"class":58},[52,627,628],{"class":65}," multiple",[52,630,80],{"class":58},[52,632,633,635,637,639,641,643,645,647,649,651,653,655],{"class":54,"line":83},[52,634,86],{"class":58},[52,636,29],{"class":62},[52,638,91],{"class":65},[52,640,69],{"class":58},[52,642,72],{"class":58},[52,644,31],{"class":75},[52,646,72],{"class":58},[52,648,102],{"class":58},[52,650,32],{"class":105},[52,652,108],{"class":58},[52,654,29],{"class":62},[52,656,80],{"class":58},[52,658,659,661,663,665,667,669,671,673,675,677,679,681],{"class":54,"line":115},[52,660,86],{"class":58},[52,662,29],{"class":62},[52,664,91],{"class":65},[52,666,69],{"class":58},[52,668,72],{"class":58},[52,670,35],{"class":75},[52,672,72],{"class":58},[52,674,102],{"class":58},[52,676,36],{"class":105},[52,678,108],{"class":58},[52,680,29],{"class":62},[52,682,80],{"class":58},[52,684,685,687,689,691,693,695,697,699,701,703,705,707],{"class":54,"line":142},[52,686,86],{"class":58},[52,688,29],{"class":62},[52,690,91],{"class":65},[52,692,69],{"class":58},[52,694,72],{"class":58},[52,696,39],{"class":75},[52,698,72],{"class":58},[52,700,102],{"class":58},[52,702,40],{"class":105},[52,704,108],{"class":58},[52,706,29],{"class":62},[52,708,80],{"class":58},[52,710,711,713,715],{"class":54,"line":169},[52,712,108],{"class":58},[52,714,24],{"class":62},[52,716,80],{"class":58},[13,718,719,720,723,724,727],{},"実際に",[49,721,722],{},"$event.target.value","を",[49,725,726],{},"console.log()","で出力して見てみると、選択肢１をクリックしてから他の選択肢も含めましたが帰って来た結果は全て「１」でした。",[729,730],"image-render",{":src":731,":width":732},"'_mix\u002Fvue-select03-768x197.png'","'100%'",[13,734,735],{},"格納する親コンポーネントのdataもこの様に１から変化ありませんでした。",[729,737],{":src":738,":width":739},"'_mix\u002Fvue-select04.png'","'400px'",[17,741,743],{"id":742},"multipleの場合はeventtargetoptions","multipleの場合は$event.target.options",[13,745,746],{},"選択された値は全て配列で帰ってくる様に調整をします。inputされた時、以下の様に値を取得する様に変更します。",[42,748,750],{"className":202,"code":749,"language":204,"meta":47,"style":47},"\u003Ctemplate>\n \u003Cselect name=\"postName\" @input=\"returnValeu($event)\" multiple>\n   \u003Coption v-for=\"val in selectBox\"\n           :value=\"val.value\" \n           :selected=\"inputValue === val.value\">\n           {{val.text}}\n   \u003C\u002Foption>\n \u003C\u002Fselect>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default{\n name:'childSelect',\n props:['inputed'],  \u002F\u002F親コンポーネントから初期値のvalueを受け取るためのprops\n methods:{\n  returnValeu($event){\n    let opt = Object.values($event.target.options).filter(ele=>{\n                  return ele.selected;\n              })\n    let values = opt.map(ele=>ele.value);\n    this.$emit('newInput', values);\n  }\n }\n data(){\n   return{\n     selector:[\n          {text:'選択肢1',value:'1'},\n          {text:'選択肢2',value:'2'},\n          {text:'選択肢3',value:'3'}\n     ],\n   }\n },\n \n}\n\u003C\u002Fscript>\n",[49,751,752,760,791,807,821,835,839,847,855,863,867,875,883,897,917,925,941,993,1008,1016,1048,1072,1077,1082,1090,1096,1104,1133,1162,1191,1197,1202,1207,1212,1217],{"__ignoreMap":47},[52,753,754,756,758],{"class":54,"line":55},[52,755,59],{"class":58},[52,757,213],{"class":62},[52,759,80],{"class":58},[52,761,762,764,766,768,770,772,774,776,778,780,782,785,787,789],{"class":54,"line":83},[52,763,86],{"class":58},[52,765,24],{"class":62},[52,767,66],{"class":65},[52,769,69],{"class":58},[52,771,72],{"class":58},[52,773,230],{"class":75},[52,775,72],{"class":58},[52,777,235],{"class":65},[52,779,69],{"class":58},[52,781,72],{"class":58},[52,783,784],{"class":75},"returnValeu($event)",[52,786,72],{"class":58},[52,788,628],{"class":65},[52,790,80],{"class":58},[52,792,793,795,797,799,801,803,805],{"class":54,"line":115},[52,794,251],{"class":58},[52,796,29],{"class":62},[52,798,256],{"class":65},[52,800,69],{"class":58},[52,802,72],{"class":58},[52,804,263],{"class":75},[52,806,266],{"class":58},[52,808,809,811,813,815,817,819],{"class":54,"line":142},[52,810,271],{"class":65},[52,812,69],{"class":58},[52,814,72],{"class":58},[52,816,278],{"class":75},[52,818,72],{"class":58},[52,820,283],{"class":58},[52,822,823,825,827,829,831,833],{"class":54,"line":169},[52,824,288],{"class":65},[52,826,69],{"class":58},[52,828,72],{"class":58},[52,830,295],{"class":75},[52,832,72],{"class":58},[52,834,80],{"class":58},[52,836,837],{"class":54,"line":302},[52,838,305],{"class":105},[52,840,841,843,845],{"class":54,"line":308},[52,842,311],{"class":58},[52,844,29],{"class":62},[52,846,80],{"class":58},[52,848,849,851,853],{"class":54,"line":318},[52,850,321],{"class":58},[52,852,24],{"class":62},[52,854,80],{"class":58},[52,856,857,859,861],{"class":54,"line":328},[52,858,108],{"class":58},[52,860,213],{"class":62},[52,862,80],{"class":58},[52,864,865],{"class":54,"line":337},[52,866,341],{"emptyLinePlaceholder":340},[52,868,869,871,873],{"class":54,"line":344},[52,870,59],{"class":58},[52,872,349],{"class":62},[52,874,80],{"class":58},[52,876,877,879,881],{"class":54,"line":354},[52,878,357],{"class":65},[52,880,361],{"class":360},[52,882,364],{"class":58},[52,884,885,887,889,891,893,895],{"class":54,"line":367},[52,886,66],{"class":370},[52,888,373],{"class":58},[52,890,376],{"class":58},[52,892,379],{"class":75},[52,894,376],{"class":58},[52,896,384],{"class":58},[52,898,899,901,903,905,907,909,911,913,915],{"class":54,"line":387},[52,900,390],{"class":370},[52,902,373],{"class":58},[52,904,395],{"class":62},[52,906,376],{"class":58},[52,908,400],{"class":75},[52,910,376],{"class":58},[52,912,405],{"class":62},[52,914,408],{"class":58},[52,916,412],{"class":411},[52,918,919,922],{"class":54,"line":415},[52,920,921],{"class":370}," methods",[52,923,924],{"class":58},":{\n",[52,926,927,930,933,936,939],{"class":54,"line":427},[52,928,929],{"class":418},"  returnValeu",[52,931,932],{"class":62},"(",[52,934,935],{"class":105},"$event",[52,937,938],{"class":62},")",[52,940,364],{"class":58},[52,942,943,946,949,952,955,958,961,963,965,967,970,972,975,977,979,982,984,988,991],{"class":54,"line":435},[52,944,945],{"class":65},"    let",[52,947,948],{"class":105}," opt",[52,950,951],{"class":58}," =",[52,953,954],{"class":105}," Object",[52,956,957],{"class":58},".",[52,959,960],{"class":418},"values",[52,962,932],{"class":62},[52,964,935],{"class":105},[52,966,957],{"class":58},[52,968,969],{"class":105},"target",[52,971,957],{"class":58},[52,973,974],{"class":105},"options",[52,976,938],{"class":62},[52,978,957],{"class":58},[52,980,981],{"class":418},"filter",[52,983,932],{"class":62},[52,985,987],{"class":986},"s7ZW3","ele",[52,989,990],{"class":65},"=>",[52,992,364],{"class":58},[52,994,995,998,1001,1003,1005],{"class":54,"line":446},[52,996,997],{"class":360},"                  return",[52,999,1000],{"class":105}," ele",[52,1002,957],{"class":58},[52,1004,588],{"class":105},[52,1006,1007],{"class":58},";\n",[52,1009,1010,1013],{"class":54,"line":480},[52,1011,1012],{"class":58},"              }",[52,1014,1015],{"class":62},")\n",[52,1017,1018,1020,1023,1025,1027,1029,1032,1034,1036,1038,1040,1042,1044,1046],{"class":54,"line":509},[52,1019,945],{"class":65},[52,1021,1022],{"class":105}," values",[52,1024,951],{"class":58},[52,1026,948],{"class":105},[52,1028,957],{"class":58},[52,1030,1031],{"class":418},"map",[52,1033,932],{"class":62},[52,1035,987],{"class":986},[52,1037,990],{"class":65},[52,1039,987],{"class":105},[52,1041,957],{"class":58},[52,1043,466],{"class":105},[52,1045,938],{"class":62},[52,1047,1007],{"class":58},[52,1049,1050,1053,1056,1058,1060,1062,1064,1066,1068,1070],{"class":54,"line":539},[52,1051,1052],{"class":58},"    this.",[52,1054,1055],{"class":418},"$emit",[52,1057,932],{"class":62},[52,1059,376],{"class":58},[52,1061,581],{"class":75},[52,1063,376],{"class":58},[52,1065,408],{"class":58},[52,1067,1022],{"class":105},[52,1069,938],{"class":62},[52,1071,1007],{"class":58},[52,1073,1074],{"class":54,"line":547},[52,1075,1076],{"class":58},"  }\n",[52,1078,1079],{"class":54,"line":553},[52,1080,1081],{"class":58}," }\n",[52,1083,1084,1086,1088],{"class":54,"line":559},[52,1085,419],{"class":418},[52,1087,422],{"class":62},[52,1089,364],{"class":58},[52,1091,1092,1094],{"class":54,"line":564},[52,1093,430],{"class":360},[52,1095,364],{"class":58},[52,1097,1098,1100,1102],{"class":54,"line":569},[52,1099,438],{"class":62},[52,1101,373],{"class":58},[52,1103,443],{"class":62},[52,1105,1107,1109,1111,1113,1115,1117,1119,1121,1123,1125,1127,1129,1131],{"class":54,"line":1106},27,[52,1108,449],{"class":58},[52,1110,452],{"class":62},[52,1112,373],{"class":58},[52,1114,376],{"class":58},[52,1116,459],{"class":75},[52,1118,376],{"class":58},[52,1120,408],{"class":58},[52,1122,466],{"class":62},[52,1124,373],{"class":58},[52,1126,376],{"class":58},[52,1128,31],{"class":75},[52,1130,376],{"class":58},[52,1132,477],{"class":58},[52,1134,1136,1138,1140,1142,1144,1146,1148,1150,1152,1154,1156,1158,1160],{"class":54,"line":1135},28,[52,1137,449],{"class":58},[52,1139,452],{"class":62},[52,1141,373],{"class":58},[52,1143,376],{"class":58},[52,1145,36],{"class":75},[52,1147,376],{"class":58},[52,1149,408],{"class":58},[52,1151,466],{"class":62},[52,1153,373],{"class":58},[52,1155,376],{"class":58},[52,1157,35],{"class":75},[52,1159,376],{"class":58},[52,1161,477],{"class":58},[52,1163,1165,1167,1169,1171,1173,1175,1177,1179,1181,1183,1185,1187,1189],{"class":54,"line":1164},29,[52,1166,449],{"class":58},[52,1168,452],{"class":62},[52,1170,373],{"class":58},[52,1172,376],{"class":58},[52,1174,40],{"class":75},[52,1176,376],{"class":58},[52,1178,408],{"class":58},[52,1180,466],{"class":62},[52,1182,373],{"class":58},[52,1184,376],{"class":58},[52,1186,39],{"class":75},[52,1188,376],{"class":58},[52,1190,536],{"class":58},[52,1192,1193,1195],{"class":54,"line":4},[52,1194,542],{"class":62},[52,1196,384],{"class":58},[52,1198,1200],{"class":54,"line":1199},31,[52,1201,550],{"class":58},[52,1203,1205],{"class":54,"line":1204},32,[52,1206,556],{"class":58},[52,1208,1210],{"class":54,"line":1209},33,[52,1211,283],{"class":62},[52,1213,1215],{"class":54,"line":1214},34,[52,1216,536],{"class":58},[52,1218,1220,1222,1224],{"class":54,"line":1219},35,[52,1221,108],{"class":58},[52,1223,349],{"class":62},[52,1225,80],{"class":58},[13,1227,1228,1229,1231],{},"処理が少し長めなのでmethodに切り分けました。",[49,1230,784],{},"の中身で、イベント（選択）が発生したターゲットの要素を捉えるまでは同じですが、その中にvalueではなくてoptionsというプロパティがあります。",[13,1233,1234,1237],{},[49,1235,1236],{},"$event.target.options","は配列ですがHTMLCollectionなのでObject.valuesをもちいて、filter()が使用できる様にしています。",[13,1239,1240,1242,1243,1246],{},[49,1241,1236],{},"には選択肢の",[49,1244,1245],{},"\u003Coption>\u003C\u002Foption>","部分に関する情報が入っており、選択されたか（selected）とその値（value）というプロパティがあります。",[42,1248,1252],{"className":1249,"code":1250,"language":1251,"meta":47,"style":47},"language-javascript shiki shiki-themes material-theme-ocean","let opt = Object.values($event.target.options).filter(ele=>{\n　　return ele.selected;\n})\n","javascript",[49,1253,1254,1294,1307],{"__ignoreMap":47},[52,1255,1256,1259,1262,1264,1266,1268,1270,1273,1275,1277,1279,1282,1284,1286,1288,1290,1292],{"class":54,"line":55},[52,1257,1258],{"class":65},"let",[52,1260,1261],{"class":105}," opt ",[52,1263,69],{"class":58},[52,1265,954],{"class":105},[52,1267,957],{"class":58},[52,1269,960],{"class":418},[52,1271,1272],{"class":105},"($event",[52,1274,957],{"class":58},[52,1276,969],{"class":105},[52,1278,957],{"class":58},[52,1280,1281],{"class":105},"options)",[52,1283,957],{"class":58},[52,1285,981],{"class":418},[52,1287,932],{"class":105},[52,1289,987],{"class":986},[52,1291,990],{"class":65},[52,1293,364],{"class":58},[52,1295,1296,1299,1301,1303,1305],{"class":54,"line":83},[52,1297,1298],{"class":360},"　　return",[52,1300,1000],{"class":105},[52,1302,957],{"class":58},[52,1304,588],{"class":105},[52,1306,1007],{"class":58},[52,1308,1309,1312],{"class":54,"line":115},[52,1310,1311],{"class":58},"}",[52,1313,1015],{"class":105},[13,1315,1316,1317,1319,1320,1323,1324,1327,1328,1330],{},"ここで",[49,1318,588],{},"プロパティが",[49,1321,1322],{},"true","か",[49,1325,1326],{},"false","で選択されたかがわかるので、",[49,1329,1322],{},"の要素のみ取得して新しい配列を手に入れます。その配列に対して",[42,1332,1334],{"className":1249,"code":1333,"language":1251,"meta":47,"style":47},"let values = opt.map(ele=>ele.value);\nthis.$emit('newInput', values);\n",[49,1335,1336,1366],{"__ignoreMap":47},[52,1337,1338,1340,1343,1345,1347,1349,1351,1353,1355,1357,1359,1361,1364],{"class":54,"line":55},[52,1339,1258],{"class":65},[52,1341,1342],{"class":105}," values ",[52,1344,69],{"class":58},[52,1346,948],{"class":105},[52,1348,957],{"class":58},[52,1350,1031],{"class":418},[52,1352,932],{"class":105},[52,1354,987],{"class":986},[52,1356,990],{"class":65},[52,1358,987],{"class":105},[52,1360,957],{"class":58},[52,1362,1363],{"class":105},"value)",[52,1365,1007],{"class":58},[52,1367,1368,1371,1373,1375,1377,1379,1381,1383,1386],{"class":54,"line":83},[52,1369,1370],{"class":58},"this.",[52,1372,1055],{"class":418},[52,1374,932],{"class":105},[52,1376,376],{"class":58},[52,1378,581],{"class":75},[52,1380,376],{"class":58},[52,1382,408],{"class":58},[52,1384,1385],{"class":105}," values)",[52,1387,1007],{"class":58},[13,1389,1390,1393,1394,1396],{},[49,1391,1392],{},"map()","を用いて",[49,1395,466],{},"プロパティのみを抽出したものを新しい配列として返し、それを親コンポーネントに渡します。これで選択したものを配列として渡すことが可能になります。",[17,1398,1399],{"id":1399},"値を出力してみる",[13,1401,1402],{},"先ほどの様に選択肢１から順に選択して値の変化を見てみましょう。",[729,1404],{":src":1405,":width":739},"'_mix\u002Fvue-select06-768x203-2.png'",[729,1407],{":src":1408,":width":739},"'_mix\u002Fvue-select05-1.png'",[13,1410,1411],{},"取得された値が順番順番に配列で取得できてますね。親コンポーネントにもちゃんと入っています。これでmultipule属性を持ったセレクトボックスの値を$emitすることができます。",[1413,1414,1415],"style",{},"html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}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 pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}",{"title":47,"searchDepth":115,"depth":115,"links":1417},[1418,1419,1420,1421],{"id":19,"depth":83,"text":19},{"id":592,"depth":83,"text":593},{"id":742,"depth":83,"text":743},{"id":1399,"depth":83,"text":1399},[1423],"devstack","2026-04-15","multiple属性での複数選択セレクトボックスで選択した値をemitする方法","md",null,{},"\u002Farticles\u002Fmultiple-select-vue",{"title":8,"description":1425},"articles\u002Fmultiple-select-vue",[1433,204],"js","_mix\u002Fvue-select06-768x203.png","VP5RFAbgJFA3otr_esQFmbFyOCNxilN184p7o6oAQZo",{"id":1437,"title":1438,"body":1439,"category":5525,"createdAt":5526,"description":1438,"extension":1426,"index":1427,"meta":5527,"navigation":340,"path":5528,"publish":340,"seo":5529,"series":1427,"seriesTitle":1427,"stem":5530,"tag":5531,"thumbnail":5532,"updatedAt":5526,"__hash__":5533},"articles\u002Farticles\u002Fjs-canvas-wave.md","画像下部が波でうねるアニメーションをcanvasで実装する",{"type":10,"value":1440,"toc":5502},[1441,1444,1447,1457,1460,1463,1479,1482,1485,1488,1491,1494,1497,1514,1517,1522,1525,1528,1531,1542,1545,1814,1817,1820,1823,2115,2118,2178,2181,2377,2388,2401,2404,2408,2413,2421,2482,2485,2488,2514,2517,2584,2590,2597,2603,2606,2609,2615,3298,3301,3312,3315,3319,3430,3436,3440,3446,3762,3765,3785,3788,3791,3795,3863,3875,3881,3883,3886,3889,3892,4092,4095,4098,4112,4115,4129,4132,4135,4138,4176,4185,4195,4198,4200,4203,4213,4322,4332,4342,4382,4385,4388,5401,5404,5407,5476,5493,5496,5499],[13,1442,1443],{},"こんにちはjunです。今日は以下のようなcanvasを用いたアニメーションを作っていきます。\nレスポンシブをとりあえず画像の一部をうねらせるだけであれば70行ほどのjsコードですみます。",[729,1445],{":src":1446,":width":732},"'_mix\u002Fex01-768x299.png'",[13,1448,1449,1456],{},[1450,1451,1455],"a",{"href":1452,"rel":1453},"https:\u002F\u002Fapps.jun-app.com\u002Fwave\u002F",[1454],"nofollow","こちら","にて実際に動かしています。",[17,1458,1459],{"id":1459},"必要な知識",[13,1461,1462],{},"細かい実装の説明にうつる前に今回用いる必要な知識と原理について確認します。以下の知識にある程度知見がある人はすっ飛ばしてください。",[1464,1465,1466,1470,1473,1476],"ul",{},[1467,1468,1469],"li",{},"canvas要素",[1467,1471,1472],{},"jsでcanvasを読み込む方法",[1467,1474,1475],{},"jsでcanvasに画像を設定する方法",[1467,1477,1478],{},"三角関数（超基礎）",[17,1480,1481],{"id":1481},"canvasでのアニメーションの原理",[13,1483,1484],{},"実装サイトでも見ていただいと思いますが、きちんとアニメーションをしておりまた、動画を流しているわけではありません。このアニメーションはcanvas要素というものをjsで操作することで実装ができます。",[13,1486,1487],{},"画像を波打たせる方法は普通のimgタグやdivでは難しいです。柔軟に簡単に実装するためにcanvasを用います。",[13,1489,1490],{},"このcanvasでのアニメーションは実は見えないスピードで「画像を消しては、再描画、消して、再描画…」というのを行っています。また描画する画像は下部を透明な波線で消してから描画しています。また波が連なり、流れるように見せるために三角関数を用いて波型に消す箇所を計算しています。",[13,1492,1493],{},"ちょっとわかりにくいにので図にしてみます。",[729,1495],{":src":1496,":width":732},"'_mix\u002Fwave01.jpeg'",[1464,1498,1499,1502,1505,1508,1511],{},[1467,1500,1501],{},"canvasでは上図のオレンジ点で示した様な描画地点を座標で指定します。（１）",[1467,1503,1504],{},"描画地点は数式を用いて指定できるので、波の部分は三角関数で座標を指定します。（２）",[1467,1506,1507],{},"そして画像の端にたどり着いたら元の描画いちに戻る様にします。（３、４）",[1467,1509,1510],{},"囲まれた部分の色などを指定できるので、透明化を行います。（５）",[1467,1512,1513],{},"そしてすぐに画像を元に戻して、１〜５を再度行う。",[13,1515,1516],{},"また１〜６を繰り返すたびにsni(θ)のθを増やしていけば毎回異なる波がうねる様に見えます。この様にして波のアニメーションを実装します。",[1518,1519,1521],"h3",{"id":1520},"canvasって何","canvasって何？",[13,1523,1524],{},"canvasというのはHTML5で扱われる要素の一つであり、2次元図形・グラフィック・アニメーションをjavaScriptを用いて描画することができます。cssでは解決できない図形やアニメーションを実装することができます。数十年前だとflashが担っていたことをHTMLで行うような感じです。",[17,1526,1527],{"id":1527},"canvasに画像を表示させる",[13,1529,1530],{},"ではまずはうねらせる画像をcanvas要素に表示させるところまで行います。今回は同階層に",[1464,1532,1533,1536,1539],{},[1467,1534,1535],{},"index.html",[1467,1537,1538],{},"app.js",[1467,1540,1541],{},"sample.jpg",[13,1543,1544],{},"を用意しておきます。sample.jpgは縦横比１：２にトリミングをしておきます。では作っていきましょう。まずは以下のように適当にHTMLを作っておきます。",[42,1546,1548],{"className":44,"code":1547,"language":46,"meta":47,"style":47},"\u003C!DOCTYPE html>\n\u003Chtml>\n    \u003Chead>\n        \u003Cmeta charset=\"utf-8\">\n        \u003Ctitle>waving image\u003C\u002Ftitle>\n        \u003Cstyle>\n            *{\n                margin:0;\n                padding:0;\n            }\n            main{\n                background:rgb(240, 255, 255);;\n            }\n        \u003C\u002Fstyle>\n    \u003C\u002Fhead>\n    \u003Cbody>\n        \u003Cmain>\n            \u003Ccanvas id=\"canvas\">\u003C\u002Fcanvas>\n        \u003C\u002Fmain>\n        \u003Cscript src=\".\u002Fapp.js\">\u003C\u002Fscript>\n    \u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[49,1549,1550,1563,1571,1581,1603,1621,1629,1636,1650,1661,1666,1673,1700,1704,1713,1722,1731,1740,1766,1774,1798,1806],{"__ignoreMap":47},[52,1551,1552,1555,1558,1561],{"class":54,"line":55},[52,1553,1554],{"class":58},"\u003C!",[52,1556,1557],{"class":62},"DOCTYPE",[52,1559,1560],{"class":65}," html",[52,1562,80],{"class":58},[52,1564,1565,1567,1569],{"class":54,"line":83},[52,1566,59],{"class":58},[52,1568,46],{"class":62},[52,1570,80],{"class":58},[52,1572,1573,1576,1579],{"class":54,"line":115},[52,1574,1575],{"class":58},"    \u003C",[52,1577,1578],{"class":62},"head",[52,1580,80],{"class":58},[52,1582,1583,1586,1589,1592,1594,1596,1599,1601],{"class":54,"line":142},[52,1584,1585],{"class":58},"        \u003C",[52,1587,1588],{"class":62},"meta",[52,1590,1591],{"class":65}," charset",[52,1593,69],{"class":58},[52,1595,72],{"class":58},[52,1597,1598],{"class":75},"utf-8",[52,1600,72],{"class":58},[52,1602,80],{"class":58},[52,1604,1605,1607,1610,1612,1615,1617,1619],{"class":54,"line":169},[52,1606,1585],{"class":58},[52,1608,1609],{"class":62},"title",[52,1611,102],{"class":58},[52,1613,1614],{"class":105},"waving image",[52,1616,108],{"class":58},[52,1618,1609],{"class":62},[52,1620,80],{"class":58},[52,1622,1623,1625,1627],{"class":54,"line":302},[52,1624,1585],{"class":58},[52,1626,1413],{"class":62},[52,1628,80],{"class":58},[52,1630,1631,1634],{"class":54,"line":308},[52,1632,1633],{"class":370},"            *",[52,1635,364],{"class":58},[52,1637,1638,1642,1644,1648],{"class":54,"line":318},[52,1639,1641],{"class":1640},"s6YsC","                margin",[52,1643,373],{"class":58},[52,1645,1647],{"class":1646},"sx098","0",[52,1649,1007],{"class":58},[52,1651,1652,1655,1657,1659],{"class":54,"line":328},[52,1653,1654],{"class":1640},"                padding",[52,1656,373],{"class":58},[52,1658,1647],{"class":1646},[52,1660,1007],{"class":58},[52,1662,1663],{"class":54,"line":337},[52,1664,1665],{"class":58},"            }\n",[52,1667,1668,1671],{"class":54,"line":344},[52,1669,1670],{"class":370},"            main",[52,1672,364],{"class":58},[52,1674,1675,1678,1680,1683,1685,1688,1690,1693,1695,1697],{"class":54,"line":354},[52,1676,1677],{"class":1640},"                background",[52,1679,373],{"class":58},[52,1681,1682],{"class":418},"rgb",[52,1684,932],{"class":58},[52,1686,1687],{"class":1646},"240",[52,1689,408],{"class":58},[52,1691,1692],{"class":1646}," 255",[52,1694,408],{"class":58},[52,1696,1692],{"class":1646},[52,1698,1699],{"class":58},");;\n",[52,1701,1702],{"class":54,"line":367},[52,1703,1665],{"class":58},[52,1705,1706,1709,1711],{"class":54,"line":387},[52,1707,1708],{"class":58},"        \u003C\u002F",[52,1710,1413],{"class":62},[52,1712,80],{"class":58},[52,1714,1715,1718,1720],{"class":54,"line":415},[52,1716,1717],{"class":58},"    \u003C\u002F",[52,1719,1578],{"class":62},[52,1721,80],{"class":58},[52,1723,1724,1726,1729],{"class":54,"line":427},[52,1725,1575],{"class":58},[52,1727,1728],{"class":62},"body",[52,1730,80],{"class":58},[52,1732,1733,1735,1738],{"class":54,"line":435},[52,1734,1585],{"class":58},[52,1736,1737],{"class":62},"main",[52,1739,80],{"class":58},[52,1741,1742,1745,1748,1751,1753,1755,1757,1759,1762,1764],{"class":54,"line":446},[52,1743,1744],{"class":58},"            \u003C",[52,1746,1747],{"class":62},"canvas",[52,1749,1750],{"class":65}," id",[52,1752,69],{"class":58},[52,1754,72],{"class":58},[52,1756,1747],{"class":75},[52,1758,72],{"class":58},[52,1760,1761],{"class":58},">\u003C\u002F",[52,1763,1747],{"class":62},[52,1765,80],{"class":58},[52,1767,1768,1770,1772],{"class":54,"line":480},[52,1769,1708],{"class":58},[52,1771,1737],{"class":62},[52,1773,80],{"class":58},[52,1775,1776,1778,1780,1783,1785,1787,1790,1792,1794,1796],{"class":54,"line":509},[52,1777,1585],{"class":58},[52,1779,349],{"class":62},[52,1781,1782],{"class":65}," src",[52,1784,69],{"class":58},[52,1786,72],{"class":58},[52,1788,1789],{"class":75},".\u002Fapp.js",[52,1791,72],{"class":58},[52,1793,1761],{"class":58},[52,1795,349],{"class":62},[52,1797,80],{"class":58},[52,1799,1800,1802,1804],{"class":54,"line":539},[52,1801,1717],{"class":58},[52,1803,1728],{"class":62},[52,1805,80],{"class":58},[52,1807,1808,1810,1812],{"class":54,"line":547},[52,1809,108],{"class":58},[52,1811,46],{"class":62},[52,1813,80],{"class":58},[13,1815,1816],{},"描画がされるcanvasを用意して、javascriptで拾えるようにidをつけておきましょう。あとは描画を実行するjsファイルをcanvasより後に書いておきます。",[1518,1818,1819],{"id":1819},"javascriptで描画対象のcanvasを設定",[13,1821,1822],{},"app.jsに以下のようにコードを書きます。",[42,1824,1826],{"className":1249,"code":1825,"language":1251,"meta":47,"style":47},"function initAnimation(){\n    var canvas = document.getElementById('canvas');\n    var ctx = canvas.getContext('2d');\n\n    var imagePath = ('.\u002Fsample.jpg');\n    var image = new Image();\n    image.src = imagePath;\n\n    canvas.width = Number(window.innerWidth);\n    canvas.height = Number(canvas.width\u002F2);\n    image.onload = function(){\n          ctx.drawImage(image,0,0,image.width,image.height,0,0,canvas.width,canvas.height);\n    }\n}\n",[49,1827,1828,1839,1869,1898,1902,1925,1944,1960,1964,1993,2023,2039,2106,2111],{"__ignoreMap":47},[52,1829,1830,1833,1836],{"class":54,"line":55},[52,1831,1832],{"class":65},"function",[52,1834,1835],{"class":418}," initAnimation",[52,1837,1838],{"class":58},"(){\n",[52,1840,1841,1844,1847,1849,1852,1854,1857,1859,1861,1863,1865,1867],{"class":54,"line":83},[52,1842,1843],{"class":65},"    var",[52,1845,1846],{"class":105}," canvas",[52,1848,951],{"class":58},[52,1850,1851],{"class":105}," document",[52,1853,957],{"class":58},[52,1855,1856],{"class":418},"getElementById",[52,1858,932],{"class":62},[52,1860,376],{"class":58},[52,1862,1747],{"class":75},[52,1864,376],{"class":58},[52,1866,938],{"class":62},[52,1868,1007],{"class":58},[52,1870,1871,1873,1876,1878,1880,1882,1885,1887,1889,1892,1894,1896],{"class":54,"line":115},[52,1872,1843],{"class":65},[52,1874,1875],{"class":105}," ctx",[52,1877,951],{"class":58},[52,1879,1846],{"class":105},[52,1881,957],{"class":58},[52,1883,1884],{"class":418},"getContext",[52,1886,932],{"class":62},[52,1888,376],{"class":58},[52,1890,1891],{"class":75},"2d",[52,1893,376],{"class":58},[52,1895,938],{"class":62},[52,1897,1007],{"class":58},[52,1899,1900],{"class":54,"line":142},[52,1901,341],{"emptyLinePlaceholder":340},[52,1903,1904,1906,1909,1911,1914,1916,1919,1921,1923],{"class":54,"line":169},[52,1905,1843],{"class":65},[52,1907,1908],{"class":105}," imagePath",[52,1910,951],{"class":58},[52,1912,1913],{"class":62}," (",[52,1915,376],{"class":58},[52,1917,1918],{"class":75},".\u002Fsample.jpg",[52,1920,376],{"class":58},[52,1922,938],{"class":62},[52,1924,1007],{"class":58},[52,1926,1927,1929,1932,1934,1937,1940,1942],{"class":54,"line":302},[52,1928,1843],{"class":65},[52,1930,1931],{"class":105}," image",[52,1933,951],{"class":58},[52,1935,1936],{"class":58}," new",[52,1938,1939],{"class":418}," Image",[52,1941,422],{"class":62},[52,1943,1007],{"class":58},[52,1945,1946,1949,1951,1954,1956,1958],{"class":54,"line":308},[52,1947,1948],{"class":105},"    image",[52,1950,957],{"class":58},[52,1952,1953],{"class":105},"src",[52,1955,951],{"class":58},[52,1957,1908],{"class":105},[52,1959,1007],{"class":58},[52,1961,1962],{"class":54,"line":318},[52,1963,341],{"emptyLinePlaceholder":340},[52,1965,1966,1969,1971,1974,1976,1979,1981,1984,1986,1989,1991],{"class":54,"line":328},[52,1967,1968],{"class":105},"    canvas",[52,1970,957],{"class":58},[52,1972,1973],{"class":105},"width",[52,1975,951],{"class":58},[52,1977,1978],{"class":418}," Number",[52,1980,932],{"class":62},[52,1982,1983],{"class":105},"window",[52,1985,957],{"class":58},[52,1987,1988],{"class":105},"innerWidth",[52,1990,938],{"class":62},[52,1992,1007],{"class":58},[52,1994,1995,1997,1999,2002,2004,2006,2008,2010,2012,2014,2017,2019,2021],{"class":54,"line":337},[52,1996,1968],{"class":105},[52,1998,957],{"class":58},[52,2000,2001],{"class":105},"height",[52,2003,951],{"class":58},[52,2005,1978],{"class":418},[52,2007,932],{"class":62},[52,2009,1747],{"class":105},[52,2011,957],{"class":58},[52,2013,1973],{"class":105},[52,2015,2016],{"class":58},"\u002F",[52,2018,35],{"class":1646},[52,2020,938],{"class":62},[52,2022,1007],{"class":58},[52,2024,2025,2027,2029,2032,2034,2037],{"class":54,"line":344},[52,2026,1948],{"class":105},[52,2028,957],{"class":58},[52,2030,2031],{"class":418},"onload",[52,2033,951],{"class":58},[52,2035,2036],{"class":65}," function",[52,2038,1838],{"class":58},[52,2040,2041,2044,2046,2049,2051,2054,2056,2058,2060,2062,2064,2066,2068,2070,2072,2074,2076,2078,2080,2082,2084,2086,2088,2090,2092,2094,2096,2098,2100,2102,2104],{"class":54,"line":354},[52,2042,2043],{"class":105},"          ctx",[52,2045,957],{"class":58},[52,2047,2048],{"class":418},"drawImage",[52,2050,932],{"class":62},[52,2052,2053],{"class":105},"image",[52,2055,408],{"class":58},[52,2057,1647],{"class":1646},[52,2059,408],{"class":58},[52,2061,1647],{"class":1646},[52,2063,408],{"class":58},[52,2065,2053],{"class":105},[52,2067,957],{"class":58},[52,2069,1973],{"class":105},[52,2071,408],{"class":58},[52,2073,2053],{"class":105},[52,2075,957],{"class":58},[52,2077,2001],{"class":105},[52,2079,408],{"class":58},[52,2081,1647],{"class":1646},[52,2083,408],{"class":58},[52,2085,1647],{"class":1646},[52,2087,408],{"class":58},[52,2089,1747],{"class":105},[52,2091,957],{"class":58},[52,2093,1973],{"class":105},[52,2095,408],{"class":58},[52,2097,1747],{"class":105},[52,2099,957],{"class":58},[52,2101,2001],{"class":105},[52,2103,938],{"class":62},[52,2105,1007],{"class":58},[52,2107,2108],{"class":54,"line":367},[52,2109,2110],{"class":58},"    }\n",[52,2112,2113],{"class":54,"line":387},[52,2114,536],{"class":58},[13,2116,2117],{},"この箇所ではHTMLからcanvas要素を指定して、jsを用いてcanvasの操作を行える様にするおまじないです。以降はこのctx(描画コンテキストインスタンス)に様々な指定を行います。",[42,2119,2121],{"className":1249,"code":2120,"language":1251,"meta":47,"style":47},"var canvas = document.getElementById('canvas');\nvar ctx = canvas.getContext('2d');\n",[49,2122,2123,2151],{"__ignoreMap":47},[52,2124,2125,2128,2131,2133,2135,2137,2139,2141,2143,2145,2147,2149],{"class":54,"line":55},[52,2126,2127],{"class":65},"var",[52,2129,2130],{"class":105}," canvas ",[52,2132,69],{"class":58},[52,2134,1851],{"class":105},[52,2136,957],{"class":58},[52,2138,1856],{"class":418},[52,2140,932],{"class":105},[52,2142,376],{"class":58},[52,2144,1747],{"class":75},[52,2146,376],{"class":58},[52,2148,938],{"class":105},[52,2150,1007],{"class":58},[52,2152,2153,2155,2158,2160,2162,2164,2166,2168,2170,2172,2174,2176],{"class":54,"line":83},[52,2154,2127],{"class":65},[52,2156,2157],{"class":105}," ctx ",[52,2159,69],{"class":58},[52,2161,1846],{"class":105},[52,2163,957],{"class":58},[52,2165,1884],{"class":418},[52,2167,932],{"class":105},[52,2169,376],{"class":58},[52,2171,1891],{"class":75},[52,2173,376],{"class":58},[52,2175,938],{"class":105},[52,2177,1007],{"class":58},[13,2179,2180],{},"そして",[42,2182,2184],{"className":1249,"code":2183,"language":1251,"meta":47,"style":47},"var imagePath = ('.\u002Fsample.jpg');\nvar image = new Image();\nimage.src = imagePath;\n\ncanvas.width = Number(window.innerWidth);\ncanvas.height = Number(canvas.width\u002F2);\nimage.onload = function(){\n        ctx.drawImage(image,0,0,image.width,image.height,0,0,canvas.width,canvas.height);\n}\n",[49,2185,2186,2207,2224,2239,2243,2266,2294,2308,2373],{"__ignoreMap":47},[52,2187,2188,2190,2193,2195,2197,2199,2201,2203,2205],{"class":54,"line":55},[52,2189,2127],{"class":65},[52,2191,2192],{"class":105}," imagePath ",[52,2194,69],{"class":58},[52,2196,1913],{"class":105},[52,2198,376],{"class":58},[52,2200,1918],{"class":75},[52,2202,376],{"class":58},[52,2204,938],{"class":105},[52,2206,1007],{"class":58},[52,2208,2209,2211,2214,2216,2218,2220,2222],{"class":54,"line":83},[52,2210,2127],{"class":65},[52,2212,2213],{"class":105}," image ",[52,2215,69],{"class":58},[52,2217,1936],{"class":58},[52,2219,1939],{"class":418},[52,2221,422],{"class":105},[52,2223,1007],{"class":58},[52,2225,2226,2228,2230,2233,2235,2237],{"class":54,"line":115},[52,2227,2053],{"class":105},[52,2229,957],{"class":58},[52,2231,2232],{"class":105},"src ",[52,2234,69],{"class":58},[52,2236,1908],{"class":105},[52,2238,1007],{"class":58},[52,2240,2241],{"class":54,"line":142},[52,2242,341],{"emptyLinePlaceholder":340},[52,2244,2245,2247,2249,2252,2254,2256,2259,2261,2264],{"class":54,"line":169},[52,2246,1747],{"class":105},[52,2248,957],{"class":58},[52,2250,2251],{"class":105},"width ",[52,2253,69],{"class":58},[52,2255,1978],{"class":418},[52,2257,2258],{"class":105},"(window",[52,2260,957],{"class":58},[52,2262,2263],{"class":105},"innerWidth)",[52,2265,1007],{"class":58},[52,2267,2268,2270,2272,2275,2277,2279,2282,2284,2286,2288,2290,2292],{"class":54,"line":302},[52,2269,1747],{"class":105},[52,2271,957],{"class":58},[52,2273,2274],{"class":105},"height ",[52,2276,69],{"class":58},[52,2278,1978],{"class":418},[52,2280,2281],{"class":105},"(canvas",[52,2283,957],{"class":58},[52,2285,1973],{"class":105},[52,2287,2016],{"class":58},[52,2289,35],{"class":1646},[52,2291,938],{"class":105},[52,2293,1007],{"class":58},[52,2295,2296,2298,2300,2302,2304,2306],{"class":54,"line":308},[52,2297,2053],{"class":105},[52,2299,957],{"class":58},[52,2301,2031],{"class":418},[52,2303,951],{"class":58},[52,2305,2036],{"class":65},[52,2307,1838],{"class":58},[52,2309,2310,2313,2315,2317,2319,2321,2323,2325,2327,2329,2331,2333,2335,2337,2339,2341,2343,2345,2347,2349,2351,2353,2355,2357,2359,2361,2363,2365,2367,2369,2371],{"class":54,"line":318},[52,2311,2312],{"class":105},"        ctx",[52,2314,957],{"class":58},[52,2316,2048],{"class":418},[52,2318,932],{"class":62},[52,2320,2053],{"class":105},[52,2322,408],{"class":58},[52,2324,1647],{"class":1646},[52,2326,408],{"class":58},[52,2328,1647],{"class":1646},[52,2330,408],{"class":58},[52,2332,2053],{"class":105},[52,2334,957],{"class":58},[52,2336,1973],{"class":105},[52,2338,408],{"class":58},[52,2340,2053],{"class":105},[52,2342,957],{"class":58},[52,2344,2001],{"class":105},[52,2346,408],{"class":58},[52,2348,1647],{"class":1646},[52,2350,408],{"class":58},[52,2352,1647],{"class":1646},[52,2354,408],{"class":58},[52,2356,1747],{"class":105},[52,2358,957],{"class":58},[52,2360,1973],{"class":105},[52,2362,408],{"class":58},[52,2364,1747],{"class":105},[52,2366,957],{"class":58},[52,2368,2001],{"class":105},[52,2370,938],{"class":62},[52,2372,1007],{"class":58},[52,2374,2375],{"class":54,"line":328},[52,2376,536],{"class":58},[13,2378,2379,2380,2383,2384,2387],{},"jsのImageオブジェクトを用いてsrcプロパティに映す画像のパスを入れます。",[49,2381,2382],{},"canvas.width","、",[49,2385,2386],{},"canvas.height","でcanvasの大きさを指定します。画像が縦横１：２なので canvasもその比率に沿う様にしました。",[13,2389,2390,2393,2394,2397,2398,2400],{},[49,2391,2392],{},"image.onload"," を用いてsrcで指定した画像の読み込みが終わったら、\n",[49,2395,2396],{},"drawImage()"," メソッドを用いてcanvasに画像を描画する様にします。\n",[49,2399,2392],{}," を使わないと画像が読み込まれる前に描画しようとするので、映されません。",[13,2402,2403],{},"とりあえずここまでくると、canvas要素しかないHTMLにもかかわらず、以下の様に画像が描画されているはずです。",[729,2405],{":src":2406,":width":2407,":center":1322},"'_mix\u002Fwave02.png'","'500px'",[2409,2410,2412],"h4",{"id":2411},"drawimageの使い方","drawImage()の使い方",[13,2414,2415,2416],{},"drawImage()はcanvasに範囲を指定して画像を描画します。canvas全体に画像を描画するならば必ず、最後の引数まで入力したほうがいいです。",[1450,2417,2420],{"href":2418,"rel":2419},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FCanvasRenderingContext2D\u002FdrawImage",[1454],"（MDNの解説（英語））",[42,2422,2424],{"className":1249,"code":2423,"language":1251,"meta":47,"style":47},"void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);\n",[49,2425,2426],{"__ignoreMap":47},[52,2427,2428,2431,2433,2435,2437,2440,2442,2445,2447,2450,2452,2455,2457,2460,2462,2465,2467,2470,2472,2475,2477,2480],{"class":54,"line":55},[52,2429,2430],{"class":58},"void",[52,2432,1875],{"class":105},[52,2434,957],{"class":58},[52,2436,2048],{"class":418},[52,2438,2439],{"class":105},"(image",[52,2441,408],{"class":58},[52,2443,2444],{"class":105}," sx",[52,2446,408],{"class":58},[52,2448,2449],{"class":105}," sy",[52,2451,408],{"class":58},[52,2453,2454],{"class":105}," sWidth",[52,2456,408],{"class":58},[52,2458,2459],{"class":105}," sHeight",[52,2461,408],{"class":58},[52,2463,2464],{"class":105}," dx",[52,2466,408],{"class":58},[52,2468,2469],{"class":105}," dy",[52,2471,408],{"class":58},[52,2473,2474],{"class":105}," dWidth",[52,2476,408],{"class":58},[52,2478,2479],{"class":105}," dHeight)",[52,2481,1007],{"class":58},[13,2483,2484],{},"それぞれの引数を説明すると",[13,2486,2487],{},"Imageインスタンス。つまり描画する対象の画像",[2489,2490,2491,2494,2497,2500,2503,2506,2508,2511],"ol",{},[1467,2492,2493],{},"画像の切り抜き開始地点のX座標",[1467,2495,2496],{},"画像の切り抜き開始地点のY座標",[1467,2498,2499],{},"第２引数で指定したX座標から切り抜くX方向の距離（幅）",[1467,2501,2502],{},"第３引数で指定したY座標から切り抜くY方向の距離（高さ）",[1467,2504,2505],{},"canvasに描画する開始地点のX座標",[1467,2507,2505],{},[1467,2509,2510],{},"第６引数で指定したX座標から描画するX方向の距離（幅）",[1467,2512,2513],{},"第７引数で指定したY座標から描画するY方向の距離（高さ）",[13,2515,2516],{},"という感じです！私の今回の例でいくと",[42,2518,2520],{"className":1249,"code":2519,"language":1251,"meta":47,"style":47},"ctx.drawImage(image,0,0,image.width,image.height,0,0,canvas.width,canvas.height);\n",[49,2521,2522],{"__ignoreMap":47},[52,2523,2524,2527,2529,2531,2533,2535,2537,2539,2541,2543,2545,2547,2549,2551,2553,2555,2557,2559,2561,2563,2565,2567,2569,2571,2573,2575,2577,2579,2582],{"class":54,"line":55},[52,2525,2526],{"class":105},"ctx",[52,2528,957],{"class":58},[52,2530,2048],{"class":418},[52,2532,2439],{"class":105},[52,2534,408],{"class":58},[52,2536,1647],{"class":1646},[52,2538,408],{"class":58},[52,2540,1647],{"class":1646},[52,2542,408],{"class":58},[52,2544,2053],{"class":105},[52,2546,957],{"class":58},[52,2548,1973],{"class":105},[52,2550,408],{"class":58},[52,2552,2053],{"class":105},[52,2554,957],{"class":58},[52,2556,2001],{"class":105},[52,2558,408],{"class":58},[52,2560,1647],{"class":1646},[52,2562,408],{"class":58},[52,2564,1647],{"class":1646},[52,2566,408],{"class":58},[52,2568,1747],{"class":105},[52,2570,957],{"class":58},[52,2572,1973],{"class":105},[52,2574,408],{"class":58},[52,2576,1747],{"class":105},[52,2578,957],{"class":58},[52,2580,2581],{"class":105},"height)",[52,2583,1007],{"class":58},[13,2585,2586,2589],{},[49,2587,2588],{},"0,0,image.width,image.height",", で画像の（０,０）座標地点から画像の幅と高さ分、画像を切り取るという意味です。つまり画像全体を読み取っているのと同じです。",[13,2591,2592,2593,2596],{},"もし",[49,2594,2595],{},"0,0,image.width\u002F2,image.height\u002F2",", としたら元画像の1\u002F4だけの部分切り取られた画像が映し出されます。",[13,2598,2599,2602],{},[49,2600,2601],{},"0,0,canvas.width,canvas.height"," ここもcanvasの（０,０）座標地点からcanvasの幅と高さ分、画像を貼り付けるという意味です。つまりcanvas全体に画像を貼り付けるのと同じです。",[13,2604,2605],{},"ここは実際にコードを描いて比率をいじって見てください。そうすればここの意味がわかる様になります。",[17,2607,2608],{"id":2608},"波線に画像を切り抜く",[13,2610,2611,2612,2614],{},"それでは次にこの画像を波線に切り抜きます。切り抜きのイメージとしては上で説明した図の様に、一辺が波線の四角形を描いて、透明に塗り潰します。",[49,2613,2392],{},"以降に以下のコードを加えます。",[42,2616,2618],{"className":1249,"code":2617,"language":1251,"meta":47,"style":47},"image.onload = function(){\n    initDraw();\n}\n    \nvar canvasEndX = canvas.width;\nvar canvasEndY = canvas.height;\nvar waveStartPoint = canvasEndY-150;\n\nvar amplitude = 30;\nvar period = 1000;\nvar degree = 0;\n\nfunction initDraw(){\n    imageSet(image,canvasEndX,canvasEndY);\n    waveDrawing(waveStartPoint,canvasEndX,canvasEndY,degree,amplitude,period);\n}\n\nfunction imageSet(imageObj,canvasEndX,canvasEndY){\n    var imgWidth = imageObj.width;\n    var imgHeight = imageObj.height;\n    ctx.drawImage(image,0,0,imgWidth,imgHeight,0,0,canvasEndX,canvasEndY);\n}\n\nfunction waveDrawing(waveStartPoint,canvasEndX,canvasEndY,deg,am,tp){\n    var waveStartY = waveStartPoint;\n    ctx.globalCompositeOperation = \"destination-out\";\n    ctx.beginPath();\n    ctx.moveTo(0, waveStartY);\n\n    for (var x=0; x \u003C= canvasEndX; x+= 1) {\n        var y = -am*Math.sin((Math.PI\u002Ftp)*(deg+x));\n        ctx.lineTo(x, y+waveStartY);\n    }\n\n    ctx.lineTo(canvasEndX,canvasEndY);\n    ctx.lineTo(0,canvasEndY);\n    ctx.closePath();\n\n    　ctx.fillStyle = \"rgba(255,255,255,1)\"; \u002F\u002Fopacity 1\n    ctx.fill();\n}\n",[49,2619,2620,2634,2643,2647,2652,2669,2686,2706,2710,2724,2738,2752,2756,2765,2788,2825,2829,2833,2856,2874,2891,2942,2946,2950,2986,3000,3021,3034,3055,3059,3101,3160,3186,3190,3194,3214,3235,3249,3254,3279,3293],{"__ignoreMap":47},[52,2621,2622,2624,2626,2628,2630,2632],{"class":54,"line":55},[52,2623,2053],{"class":105},[52,2625,957],{"class":58},[52,2627,2031],{"class":418},[52,2629,951],{"class":58},[52,2631,2036],{"class":65},[52,2633,1838],{"class":58},[52,2635,2636,2639,2641],{"class":54,"line":83},[52,2637,2638],{"class":418},"    initDraw",[52,2640,422],{"class":62},[52,2642,1007],{"class":58},[52,2644,2645],{"class":54,"line":115},[52,2646,536],{"class":58},[52,2648,2649],{"class":54,"line":142},[52,2650,2651],{"class":105},"    \n",[52,2653,2654,2656,2659,2661,2663,2665,2667],{"class":54,"line":169},[52,2655,2127],{"class":65},[52,2657,2658],{"class":105}," canvasEndX ",[52,2660,69],{"class":58},[52,2662,1846],{"class":105},[52,2664,957],{"class":58},[52,2666,1973],{"class":105},[52,2668,1007],{"class":58},[52,2670,2671,2673,2676,2678,2680,2682,2684],{"class":54,"line":302},[52,2672,2127],{"class":65},[52,2674,2675],{"class":105}," canvasEndY ",[52,2677,69],{"class":58},[52,2679,1846],{"class":105},[52,2681,957],{"class":58},[52,2683,2001],{"class":105},[52,2685,1007],{"class":58},[52,2687,2688,2690,2693,2695,2698,2701,2704],{"class":54,"line":308},[52,2689,2127],{"class":65},[52,2691,2692],{"class":105}," waveStartPoint ",[52,2694,69],{"class":58},[52,2696,2697],{"class":105}," canvasEndY",[52,2699,2700],{"class":58},"-",[52,2702,2703],{"class":1646},"150",[52,2705,1007],{"class":58},[52,2707,2708],{"class":54,"line":318},[52,2709,341],{"emptyLinePlaceholder":340},[52,2711,2712,2714,2717,2719,2722],{"class":54,"line":328},[52,2713,2127],{"class":65},[52,2715,2716],{"class":105}," amplitude ",[52,2718,69],{"class":58},[52,2720,2721],{"class":1646}," 30",[52,2723,1007],{"class":58},[52,2725,2726,2728,2731,2733,2736],{"class":54,"line":337},[52,2727,2127],{"class":65},[52,2729,2730],{"class":105}," period ",[52,2732,69],{"class":58},[52,2734,2735],{"class":1646}," 1000",[52,2737,1007],{"class":58},[52,2739,2740,2742,2745,2747,2750],{"class":54,"line":344},[52,2741,2127],{"class":65},[52,2743,2744],{"class":105}," degree ",[52,2746,69],{"class":58},[52,2748,2749],{"class":1646}," 0",[52,2751,1007],{"class":58},[52,2753,2754],{"class":54,"line":354},[52,2755,341],{"emptyLinePlaceholder":340},[52,2757,2758,2760,2763],{"class":54,"line":367},[52,2759,1832],{"class":65},[52,2761,2762],{"class":418}," initDraw",[52,2764,1838],{"class":58},[52,2766,2767,2770,2772,2774,2776,2779,2781,2784,2786],{"class":54,"line":387},[52,2768,2769],{"class":418},"    imageSet",[52,2771,932],{"class":62},[52,2773,2053],{"class":105},[52,2775,408],{"class":58},[52,2777,2778],{"class":105},"canvasEndX",[52,2780,408],{"class":58},[52,2782,2783],{"class":105},"canvasEndY",[52,2785,938],{"class":62},[52,2787,1007],{"class":58},[52,2789,2790,2793,2795,2798,2800,2802,2804,2806,2808,2811,2813,2816,2818,2821,2823],{"class":54,"line":415},[52,2791,2792],{"class":418},"    waveDrawing",[52,2794,932],{"class":62},[52,2796,2797],{"class":105},"waveStartPoint",[52,2799,408],{"class":58},[52,2801,2778],{"class":105},[52,2803,408],{"class":58},[52,2805,2783],{"class":105},[52,2807,408],{"class":58},[52,2809,2810],{"class":105},"degree",[52,2812,408],{"class":58},[52,2814,2815],{"class":105},"amplitude",[52,2817,408],{"class":58},[52,2819,2820],{"class":105},"period",[52,2822,938],{"class":62},[52,2824,1007],{"class":58},[52,2826,2827],{"class":54,"line":427},[52,2828,536],{"class":58},[52,2830,2831],{"class":54,"line":435},[52,2832,341],{"emptyLinePlaceholder":340},[52,2834,2835,2837,2840,2842,2845,2847,2849,2851,2853],{"class":54,"line":446},[52,2836,1832],{"class":65},[52,2838,2839],{"class":418}," imageSet",[52,2841,932],{"class":58},[52,2843,2844],{"class":986},"imageObj",[52,2846,408],{"class":58},[52,2848,2778],{"class":986},[52,2850,408],{"class":58},[52,2852,2783],{"class":986},[52,2854,2855],{"class":58},"){\n",[52,2857,2858,2860,2863,2865,2868,2870,2872],{"class":54,"line":480},[52,2859,1843],{"class":65},[52,2861,2862],{"class":105}," imgWidth",[52,2864,951],{"class":58},[52,2866,2867],{"class":105}," imageObj",[52,2869,957],{"class":58},[52,2871,1973],{"class":105},[52,2873,1007],{"class":58},[52,2875,2876,2878,2881,2883,2885,2887,2889],{"class":54,"line":509},[52,2877,1843],{"class":65},[52,2879,2880],{"class":105}," imgHeight",[52,2882,951],{"class":58},[52,2884,2867],{"class":105},[52,2886,957],{"class":58},[52,2888,2001],{"class":105},[52,2890,1007],{"class":58},[52,2892,2893,2896,2898,2900,2902,2904,2906,2908,2910,2912,2914,2917,2919,2922,2924,2926,2928,2930,2932,2934,2936,2938,2940],{"class":54,"line":539},[52,2894,2895],{"class":105},"    ctx",[52,2897,957],{"class":58},[52,2899,2048],{"class":418},[52,2901,932],{"class":62},[52,2903,2053],{"class":105},[52,2905,408],{"class":58},[52,2907,1647],{"class":1646},[52,2909,408],{"class":58},[52,2911,1647],{"class":1646},[52,2913,408],{"class":58},[52,2915,2916],{"class":105},"imgWidth",[52,2918,408],{"class":58},[52,2920,2921],{"class":105},"imgHeight",[52,2923,408],{"class":58},[52,2925,1647],{"class":1646},[52,2927,408],{"class":58},[52,2929,1647],{"class":1646},[52,2931,408],{"class":58},[52,2933,2778],{"class":105},[52,2935,408],{"class":58},[52,2937,2783],{"class":105},[52,2939,938],{"class":62},[52,2941,1007],{"class":58},[52,2943,2944],{"class":54,"line":547},[52,2945,536],{"class":58},[52,2947,2948],{"class":54,"line":553},[52,2949,341],{"emptyLinePlaceholder":340},[52,2951,2952,2954,2957,2959,2961,2963,2965,2967,2969,2971,2974,2976,2979,2981,2984],{"class":54,"line":559},[52,2953,1832],{"class":65},[52,2955,2956],{"class":418}," waveDrawing",[52,2958,932],{"class":58},[52,2960,2797],{"class":986},[52,2962,408],{"class":58},[52,2964,2778],{"class":986},[52,2966,408],{"class":58},[52,2968,2783],{"class":986},[52,2970,408],{"class":58},[52,2972,2973],{"class":986},"deg",[52,2975,408],{"class":58},[52,2977,2978],{"class":986},"am",[52,2980,408],{"class":58},[52,2982,2983],{"class":986},"tp",[52,2985,2855],{"class":58},[52,2987,2988,2990,2993,2995,2998],{"class":54,"line":564},[52,2989,1843],{"class":65},[52,2991,2992],{"class":105}," waveStartY",[52,2994,951],{"class":58},[52,2996,2997],{"class":105}," waveStartPoint",[52,2999,1007],{"class":58},[52,3001,3002,3004,3006,3009,3011,3014,3017,3019],{"class":54,"line":569},[52,3003,2895],{"class":105},[52,3005,957],{"class":58},[52,3007,3008],{"class":105},"globalCompositeOperation",[52,3010,951],{"class":58},[52,3012,3013],{"class":58}," \"",[52,3015,3016],{"class":75},"destination-out",[52,3018,72],{"class":58},[52,3020,1007],{"class":58},[52,3022,3023,3025,3027,3030,3032],{"class":54,"line":1106},[52,3024,2895],{"class":105},[52,3026,957],{"class":58},[52,3028,3029],{"class":418},"beginPath",[52,3031,422],{"class":62},[52,3033,1007],{"class":58},[52,3035,3036,3038,3040,3043,3045,3047,3049,3051,3053],{"class":54,"line":1135},[52,3037,2895],{"class":105},[52,3039,957],{"class":58},[52,3041,3042],{"class":418},"moveTo",[52,3044,932],{"class":62},[52,3046,1647],{"class":1646},[52,3048,408],{"class":58},[52,3050,2992],{"class":105},[52,3052,938],{"class":62},[52,3054,1007],{"class":58},[52,3056,3057],{"class":54,"line":1164},[52,3058,341],{"emptyLinePlaceholder":340},[52,3060,3061,3064,3066,3068,3071,3073,3075,3078,3080,3083,3086,3088,3090,3093,3096,3099],{"class":54,"line":4},[52,3062,3063],{"class":360},"    for",[52,3065,1913],{"class":62},[52,3067,2127],{"class":65},[52,3069,3070],{"class":105}," x",[52,3072,69],{"class":58},[52,3074,1647],{"class":1646},[52,3076,3077],{"class":58},";",[52,3079,3070],{"class":105},[52,3081,3082],{"class":58}," \u003C=",[52,3084,3085],{"class":105}," canvasEndX",[52,3087,3077],{"class":58},[52,3089,3070],{"class":105},[52,3091,3092],{"class":58},"+=",[52,3094,3095],{"class":1646}," 1",[52,3097,3098],{"class":62},") ",[52,3100,364],{"class":58},[52,3102,3103,3106,3109,3111,3114,3116,3119,3122,3124,3127,3130,3132,3134,3137,3139,3141,3143,3145,3147,3149,3152,3155,3158],{"class":54,"line":1199},[52,3104,3105],{"class":65},"        var",[52,3107,3108],{"class":105}," y",[52,3110,951],{"class":58},[52,3112,3113],{"class":58}," -",[52,3115,2978],{"class":105},[52,3117,3118],{"class":58},"*",[52,3120,3121],{"class":105},"Math",[52,3123,957],{"class":58},[52,3125,3126],{"class":418},"sin",[52,3128,3129],{"class":62},"((",[52,3131,3121],{"class":105},[52,3133,957],{"class":58},[52,3135,3136],{"class":105},"PI",[52,3138,2016],{"class":58},[52,3140,2983],{"class":105},[52,3142,938],{"class":62},[52,3144,3118],{"class":58},[52,3146,932],{"class":62},[52,3148,2973],{"class":105},[52,3150,3151],{"class":58},"+",[52,3153,3154],{"class":105},"x",[52,3156,3157],{"class":62},"))",[52,3159,1007],{"class":58},[52,3161,3162,3164,3166,3169,3171,3173,3175,3177,3179,3182,3184],{"class":54,"line":1204},[52,3163,2312],{"class":105},[52,3165,957],{"class":58},[52,3167,3168],{"class":418},"lineTo",[52,3170,932],{"class":62},[52,3172,3154],{"class":105},[52,3174,408],{"class":58},[52,3176,3108],{"class":105},[52,3178,3151],{"class":58},[52,3180,3181],{"class":105},"waveStartY",[52,3183,938],{"class":62},[52,3185,1007],{"class":58},[52,3187,3188],{"class":54,"line":1209},[52,3189,2110],{"class":58},[52,3191,3192],{"class":54,"line":1214},[52,3193,341],{"emptyLinePlaceholder":340},[52,3195,3196,3198,3200,3202,3204,3206,3208,3210,3212],{"class":54,"line":1219},[52,3197,2895],{"class":105},[52,3199,957],{"class":58},[52,3201,3168],{"class":418},[52,3203,932],{"class":62},[52,3205,2778],{"class":105},[52,3207,408],{"class":58},[52,3209,2783],{"class":105},[52,3211,938],{"class":62},[52,3213,1007],{"class":58},[52,3215,3217,3219,3221,3223,3225,3227,3229,3231,3233],{"class":54,"line":3216},36,[52,3218,2895],{"class":105},[52,3220,957],{"class":58},[52,3222,3168],{"class":418},[52,3224,932],{"class":62},[52,3226,1647],{"class":1646},[52,3228,408],{"class":58},[52,3230,2783],{"class":105},[52,3232,938],{"class":62},[52,3234,1007],{"class":58},[52,3236,3238,3240,3242,3245,3247],{"class":54,"line":3237},37,[52,3239,2895],{"class":105},[52,3241,957],{"class":58},[52,3243,3244],{"class":418},"closePath",[52,3246,422],{"class":62},[52,3248,1007],{"class":58},[52,3250,3252],{"class":54,"line":3251},38,[52,3253,341],{"emptyLinePlaceholder":340},[52,3255,3257,3260,3262,3265,3267,3269,3272,3274,3276],{"class":54,"line":3256},39,[52,3258,3259],{"class":105},"    　ctx",[52,3261,957],{"class":58},[52,3263,3264],{"class":105},"fillStyle",[52,3266,951],{"class":58},[52,3268,3013],{"class":58},[52,3270,3271],{"class":75},"rgba(255,255,255,1)",[52,3273,72],{"class":58},[52,3275,3077],{"class":58},[52,3277,3278],{"class":411}," \u002F\u002Fopacity 1\n",[52,3280,3282,3284,3286,3289,3291],{"class":54,"line":3281},40,[52,3283,2895],{"class":105},[52,3285,957],{"class":58},[52,3287,3288],{"class":418},"fill",[52,3290,422],{"class":62},[52,3292,1007],{"class":58},[52,3294,3296],{"class":54,"line":3295},41,[52,3297,536],{"class":58},[13,3299,3300],{},"ここでは３つの関数を作成します。",[1464,3302,3303,3306,3309],{},[1467,3304,3305],{},"initDraw()：初期の波線くりぬき画像を描画する。",[1467,3307,3308],{},"imageSet()：画像をキャンバスに描画する。またすでに画像がある場合はそれをクリアする。",[1467,3310,3311],{},"waveDrawing()：画像を波線にくり抜く。",[13,3313,3314],{},"それぞれを解説していきます。",[1518,3316,3318],{"id":3317},"imageset-で画像を描画","imageSet() で画像を描画",[42,3320,3322],{"className":1249,"code":3321,"language":1251,"meta":47,"style":47},"function imageSet(imageObj,canvasEndX,canvasEndY){\n     var imgWidth = imageObj.width;\n     var imgHeight = imageObj.height;\n     ctx.drawImage(image,0,0,imgWidth,imgHeight,0,0,canvasEndX,canvasEndY);\n}\n",[49,3323,3324,3344,3361,3377,3426],{"__ignoreMap":47},[52,3325,3326,3328,3330,3332,3334,3336,3338,3340,3342],{"class":54,"line":55},[52,3327,1832],{"class":65},[52,3329,2839],{"class":418},[52,3331,932],{"class":58},[52,3333,2844],{"class":986},[52,3335,408],{"class":58},[52,3337,2778],{"class":986},[52,3339,408],{"class":58},[52,3341,2783],{"class":986},[52,3343,2855],{"class":58},[52,3345,3346,3349,3351,3353,3355,3357,3359],{"class":54,"line":83},[52,3347,3348],{"class":65},"     var",[52,3350,2862],{"class":105},[52,3352,951],{"class":58},[52,3354,2867],{"class":105},[52,3356,957],{"class":58},[52,3358,1973],{"class":105},[52,3360,1007],{"class":58},[52,3362,3363,3365,3367,3369,3371,3373,3375],{"class":54,"line":115},[52,3364,3348],{"class":65},[52,3366,2880],{"class":105},[52,3368,951],{"class":58},[52,3370,2867],{"class":105},[52,3372,957],{"class":58},[52,3374,2001],{"class":105},[52,3376,1007],{"class":58},[52,3378,3379,3382,3384,3386,3388,3390,3392,3394,3396,3398,3400,3402,3404,3406,3408,3410,3412,3414,3416,3418,3420,3422,3424],{"class":54,"line":142},[52,3380,3381],{"class":105},"     ctx",[52,3383,957],{"class":58},[52,3385,2048],{"class":418},[52,3387,932],{"class":62},[52,3389,2053],{"class":105},[52,3391,408],{"class":58},[52,3393,1647],{"class":1646},[52,3395,408],{"class":58},[52,3397,1647],{"class":1646},[52,3399,408],{"class":58},[52,3401,2916],{"class":105},[52,3403,408],{"class":58},[52,3405,2921],{"class":105},[52,3407,408],{"class":58},[52,3409,1647],{"class":1646},[52,3411,408],{"class":58},[52,3413,1647],{"class":1646},[52,3415,408],{"class":58},[52,3417,2778],{"class":105},[52,3419,408],{"class":58},[52,3421,2783],{"class":105},[52,3423,938],{"class":62},[52,3425,1007],{"class":58},[52,3427,3428],{"class":54,"line":169},[52,3429,536],{"class":58},[13,3431,3432,3433,3435],{},"この関数は単に画像を描画するだけです。引数に画像オブジェクトとキャンバスの幅・高さ情報をとり、先ほども説明した",[49,3434,2396],{},"を用いてキャンバスに画像を描画します。",[1518,3437,3439],{"id":3438},"wavedrawingで波線くりぬきをする","waveDrawing()で波線くりぬきをする",[13,3441,3442,3445],{},[49,3443,3444],{},"waveDrawing()","という関数で波線のくりぬき処理をかいていきます。",[42,3447,3449],{"className":1249,"code":3448,"language":1251,"meta":47,"style":47},"function waveDrawing(waveStartPoint,canvasEndX,canvasEndY,deg,am,tp){\n      var waveStartY = waveStartPoint;\n      ctx.globalCompositeOperation = \"destination-out\";\n      ctx.beginPath();\n      ctx.moveTo(0, waveStartY);\n\n      for (var x=0; x \u003C= canvasEndX; x+= 1) {\n           var y = -am*Math.sin((Math.PI\u002Ftp)*(deg+x));;\n           ctx.lineTo(x, y+waveStartY);\n      }\n\n      ctx.lineTo(canvasEndX,canvasEndY);\n      ctx.lineTo(0,canvasEndY);\n      ctx.closePath();\n\n      ctx.fillStyle = \"rgba(255,255,255,1)\"; \u002F\u002Fopacity 1\n      ctx.fill();\n}\n",[49,3450,3451,3483,3496,3515,3527,3547,3551,3586,3636,3661,3666,3670,3690,3710,3722,3726,3746,3758],{"__ignoreMap":47},[52,3452,3453,3455,3457,3459,3461,3463,3465,3467,3469,3471,3473,3475,3477,3479,3481],{"class":54,"line":55},[52,3454,1832],{"class":65},[52,3456,2956],{"class":418},[52,3458,932],{"class":58},[52,3460,2797],{"class":986},[52,3462,408],{"class":58},[52,3464,2778],{"class":986},[52,3466,408],{"class":58},[52,3468,2783],{"class":986},[52,3470,408],{"class":58},[52,3472,2973],{"class":986},[52,3474,408],{"class":58},[52,3476,2978],{"class":986},[52,3478,408],{"class":58},[52,3480,2983],{"class":986},[52,3482,2855],{"class":58},[52,3484,3485,3488,3490,3492,3494],{"class":54,"line":83},[52,3486,3487],{"class":65},"      var",[52,3489,2992],{"class":105},[52,3491,951],{"class":58},[52,3493,2997],{"class":105},[52,3495,1007],{"class":58},[52,3497,3498,3501,3503,3505,3507,3509,3511,3513],{"class":54,"line":115},[52,3499,3500],{"class":105},"      ctx",[52,3502,957],{"class":58},[52,3504,3008],{"class":105},[52,3506,951],{"class":58},[52,3508,3013],{"class":58},[52,3510,3016],{"class":75},[52,3512,72],{"class":58},[52,3514,1007],{"class":58},[52,3516,3517,3519,3521,3523,3525],{"class":54,"line":142},[52,3518,3500],{"class":105},[52,3520,957],{"class":58},[52,3522,3029],{"class":418},[52,3524,422],{"class":62},[52,3526,1007],{"class":58},[52,3528,3529,3531,3533,3535,3537,3539,3541,3543,3545],{"class":54,"line":169},[52,3530,3500],{"class":105},[52,3532,957],{"class":58},[52,3534,3042],{"class":418},[52,3536,932],{"class":62},[52,3538,1647],{"class":1646},[52,3540,408],{"class":58},[52,3542,2992],{"class":105},[52,3544,938],{"class":62},[52,3546,1007],{"class":58},[52,3548,3549],{"class":54,"line":302},[52,3550,341],{"emptyLinePlaceholder":340},[52,3552,3553,3556,3558,3560,3562,3564,3566,3568,3570,3572,3574,3576,3578,3580,3582,3584],{"class":54,"line":308},[52,3554,3555],{"class":360},"      for",[52,3557,1913],{"class":62},[52,3559,2127],{"class":65},[52,3561,3070],{"class":105},[52,3563,69],{"class":58},[52,3565,1647],{"class":1646},[52,3567,3077],{"class":58},[52,3569,3070],{"class":105},[52,3571,3082],{"class":58},[52,3573,3085],{"class":105},[52,3575,3077],{"class":58},[52,3577,3070],{"class":105},[52,3579,3092],{"class":58},[52,3581,3095],{"class":1646},[52,3583,3098],{"class":62},[52,3585,364],{"class":58},[52,3587,3588,3591,3593,3595,3597,3599,3601,3603,3605,3607,3609,3611,3613,3615,3617,3619,3621,3623,3625,3627,3629,3631,3633],{"class":54,"line":318},[52,3589,3590],{"class":65},"           var",[52,3592,3108],{"class":105},[52,3594,951],{"class":58},[52,3596,3113],{"class":58},[52,3598,2978],{"class":105},[52,3600,3118],{"class":58},[52,3602,3121],{"class":105},[52,3604,957],{"class":58},[52,3606,3126],{"class":418},[52,3608,3129],{"class":62},[52,3610,3121],{"class":105},[52,3612,957],{"class":58},[52,3614,3136],{"class":105},[52,3616,2016],{"class":58},[52,3618,2983],{"class":105},[52,3620,938],{"class":62},[52,3622,3118],{"class":58},[52,3624,932],{"class":62},[52,3626,2973],{"class":105},[52,3628,3151],{"class":58},[52,3630,3154],{"class":105},[52,3632,3157],{"class":62},[52,3634,3635],{"class":58},";;\n",[52,3637,3638,3641,3643,3645,3647,3649,3651,3653,3655,3657,3659],{"class":54,"line":328},[52,3639,3640],{"class":105},"           ctx",[52,3642,957],{"class":58},[52,3644,3168],{"class":418},[52,3646,932],{"class":62},[52,3648,3154],{"class":105},[52,3650,408],{"class":58},[52,3652,3108],{"class":105},[52,3654,3151],{"class":58},[52,3656,3181],{"class":105},[52,3658,938],{"class":62},[52,3660,1007],{"class":58},[52,3662,3663],{"class":54,"line":337},[52,3664,3665],{"class":58},"      }\n",[52,3667,3668],{"class":54,"line":344},[52,3669,341],{"emptyLinePlaceholder":340},[52,3671,3672,3674,3676,3678,3680,3682,3684,3686,3688],{"class":54,"line":354},[52,3673,3500],{"class":105},[52,3675,957],{"class":58},[52,3677,3168],{"class":418},[52,3679,932],{"class":62},[52,3681,2778],{"class":105},[52,3683,408],{"class":58},[52,3685,2783],{"class":105},[52,3687,938],{"class":62},[52,3689,1007],{"class":58},[52,3691,3692,3694,3696,3698,3700,3702,3704,3706,3708],{"class":54,"line":367},[52,3693,3500],{"class":105},[52,3695,957],{"class":58},[52,3697,3168],{"class":418},[52,3699,932],{"class":62},[52,3701,1647],{"class":1646},[52,3703,408],{"class":58},[52,3705,2783],{"class":105},[52,3707,938],{"class":62},[52,3709,1007],{"class":58},[52,3711,3712,3714,3716,3718,3720],{"class":54,"line":387},[52,3713,3500],{"class":105},[52,3715,957],{"class":58},[52,3717,3244],{"class":418},[52,3719,422],{"class":62},[52,3721,1007],{"class":58},[52,3723,3724],{"class":54,"line":415},[52,3725,341],{"emptyLinePlaceholder":340},[52,3727,3728,3730,3732,3734,3736,3738,3740,3742,3744],{"class":54,"line":427},[52,3729,3500],{"class":105},[52,3731,957],{"class":58},[52,3733,3264],{"class":105},[52,3735,951],{"class":58},[52,3737,3013],{"class":58},[52,3739,3271],{"class":75},[52,3741,72],{"class":58},[52,3743,3077],{"class":58},[52,3745,3278],{"class":411},[52,3747,3748,3750,3752,3754,3756],{"class":54,"line":435},[52,3749,3500],{"class":105},[52,3751,957],{"class":58},[52,3753,3288],{"class":418},[52,3755,422],{"class":62},[52,3757,1007],{"class":58},[52,3759,3760],{"class":54,"line":446},[52,3761,536],{"class":58},[13,3763,3764],{},"各パラメーターは",[1464,3766,3767,3770,3773,3776,3779,3782],{},[1467,3768,3769],{},"waveStartPoint：波を書き始めるY軸の開始位置",[1467,3771,3772],{},"canvasEndX：canvasの右端のX座標（最大のcanvasX座標）",[1467,3774,3775],{},"canvasEndY：canvasの下端のY座標（最大のcanvasXY座標）",[1467,3777,3778],{},"deg：角度の初期値",[1467,3780,3781],{},"am：振幅（波の最大の高さを変化させる）",[1467,3783,3784],{},"tp：周期（１波の幅を変化させる）",[13,3786,3787],{},"最後の３つは高校の三角関数を思い出してください。それほど難しく考えず、amを大きくすれば波が大きくなり、tpの場合は大きいほどなだらかな波になります。",[13,3789,3790],{},"またctxは上部で定義したcanvasインスタンスです。今回はグローバルにしてます。",[2409,3792,3794],{"id":3793},"描画位置を定義し重ね合わせの設定をする","描画位置を定義し、重ね合わせの設定をする",[42,3796,3798],{"className":1249,"code":3797,"language":1251,"meta":47,"style":47},"var waveStartY = waveStartPoint;\nctx.globalCompositeOperation = \"destination-out\";\nctx.beginPath();\nctx.moveTo(0, waveStartY);\n",[49,3799,3800,3813,3832,3844],{"__ignoreMap":47},[52,3801,3802,3804,3807,3809,3811],{"class":54,"line":55},[52,3803,2127],{"class":65},[52,3805,3806],{"class":105}," waveStartY ",[52,3808,69],{"class":58},[52,3810,2997],{"class":105},[52,3812,1007],{"class":58},[52,3814,3815,3817,3819,3822,3824,3826,3828,3830],{"class":54,"line":83},[52,3816,2526],{"class":105},[52,3818,957],{"class":58},[52,3820,3821],{"class":105},"globalCompositeOperation ",[52,3823,69],{"class":58},[52,3825,3013],{"class":58},[52,3827,3016],{"class":75},[52,3829,72],{"class":58},[52,3831,1007],{"class":58},[52,3833,3834,3836,3838,3840,3842],{"class":54,"line":115},[52,3835,2526],{"class":105},[52,3837,957],{"class":58},[52,3839,3029],{"class":418},[52,3841,422],{"class":105},[52,3843,1007],{"class":58},[52,3845,3846,3848,3850,3852,3854,3856,3858,3861],{"class":54,"line":142},[52,3847,2526],{"class":105},[52,3849,957],{"class":58},[52,3851,3042],{"class":418},[52,3853,932],{"class":105},[52,3855,1647],{"class":1646},[52,3857,408],{"class":58},[52,3859,3860],{"class":105}," waveStartY)",[52,3862,1007],{"class":58},[13,3864,3865,3866,3869,3870],{},"画像を波線に透過させる場合にこの設定 ",[49,3867,3868],{},"ctx.globalCompositeOperation = \"destination-out\"; ","が重要です。",[1450,3871,3874],{"href":3872,"rel":3873},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fja\u002Fdocs\u002FWeb\u002FAPI\u002FCanvasRenderingContext2D\u002FglobalCompositeOperation",[1454],"MDNの解説",[13,3876,3877,3878,3880],{},"この",[49,3879,3008],{},"はcanvasにおける図形どうしが重ね合わさった際にどう描画するのかを定義します。冒頭で出したこの図を見てみてください。",[729,3882],{":src":1496,":width":732},[13,3884,3885],{},"透明のくりぬきは画像の上に、赤線で範囲を指定してその中を透明色に塗りつぶすということをしています。その時に「すでに描画された画像」と「透明色に塗り潰された図形」が重ね合わさっています。",[13,3887,3888],{},"その時にを設定していると、後で描画した図形と重なり合わない部分だけが残る様に描画されます。つまり上図の５の様に一部分だけ透明になります。",[2409,3890,3891],{"id":3891},"くりぬき範囲を指定する",[42,3893,3895],{"className":1249,"code":3894,"language":1251,"meta":47,"style":47}," ctx.beginPath();\n ctx.moveTo(0, waveStartY);\n for (var x=0; x \u003C= canvasEndX; x+= 1) {\n     var y = -am*Math.sin((Math.PI\u002Ftp)*(deg+x));;\n     ctx.lineTo(x, y+waveStartY);\n }\n\n ctx.lineTo(canvasEndX,canvasEndY);\n ctx.lineTo(0,canvasEndY);\n ctx.closePath();\n",[49,3896,3897,3909,3927,3964,4012,4036,4040,4044,4062,4080],{"__ignoreMap":47},[52,3898,3899,3901,3903,3905,3907],{"class":54,"line":55},[52,3900,1875],{"class":105},[52,3902,957],{"class":58},[52,3904,3029],{"class":418},[52,3906,422],{"class":105},[52,3908,1007],{"class":58},[52,3910,3911,3913,3915,3917,3919,3921,3923,3925],{"class":54,"line":83},[52,3912,1875],{"class":105},[52,3914,957],{"class":58},[52,3916,3042],{"class":418},[52,3918,932],{"class":105},[52,3920,1647],{"class":1646},[52,3922,408],{"class":58},[52,3924,3860],{"class":105},[52,3926,1007],{"class":58},[52,3928,3929,3932,3934,3936,3938,3940,3942,3944,3947,3950,3952,3954,3956,3958,3960,3962],{"class":54,"line":115},[52,3930,3931],{"class":360}," for",[52,3933,1913],{"class":105},[52,3935,2127],{"class":65},[52,3937,3070],{"class":105},[52,3939,69],{"class":58},[52,3941,1647],{"class":1646},[52,3943,3077],{"class":58},[52,3945,3946],{"class":105}," x ",[52,3948,3949],{"class":58},"\u003C=",[52,3951,3085],{"class":105},[52,3953,3077],{"class":58},[52,3955,3070],{"class":105},[52,3957,3092],{"class":58},[52,3959,3095],{"class":1646},[52,3961,3098],{"class":105},[52,3963,364],{"class":58},[52,3965,3966,3968,3970,3972,3974,3976,3978,3980,3982,3984,3986,3988,3990,3992,3994,3996,3998,4000,4002,4004,4006,4008,4010],{"class":54,"line":142},[52,3967,3348],{"class":65},[52,3969,3108],{"class":105},[52,3971,951],{"class":58},[52,3973,3113],{"class":58},[52,3975,2978],{"class":105},[52,3977,3118],{"class":58},[52,3979,3121],{"class":105},[52,3981,957],{"class":58},[52,3983,3126],{"class":418},[52,3985,3129],{"class":62},[52,3987,3121],{"class":105},[52,3989,957],{"class":58},[52,3991,3136],{"class":105},[52,3993,2016],{"class":58},[52,3995,2983],{"class":105},[52,3997,938],{"class":62},[52,3999,3118],{"class":58},[52,4001,932],{"class":62},[52,4003,2973],{"class":105},[52,4005,3151],{"class":58},[52,4007,3154],{"class":105},[52,4009,3157],{"class":62},[52,4011,3635],{"class":58},[52,4013,4014,4016,4018,4020,4022,4024,4026,4028,4030,4032,4034],{"class":54,"line":169},[52,4015,3381],{"class":105},[52,4017,957],{"class":58},[52,4019,3168],{"class":418},[52,4021,932],{"class":62},[52,4023,3154],{"class":105},[52,4025,408],{"class":58},[52,4027,3108],{"class":105},[52,4029,3151],{"class":58},[52,4031,3181],{"class":105},[52,4033,938],{"class":62},[52,4035,1007],{"class":58},[52,4037,4038],{"class":54,"line":302},[52,4039,1081],{"class":58},[52,4041,4042],{"class":54,"line":308},[52,4043,341],{"emptyLinePlaceholder":340},[52,4045,4046,4048,4050,4052,4055,4057,4060],{"class":54,"line":318},[52,4047,1875],{"class":105},[52,4049,957],{"class":58},[52,4051,3168],{"class":418},[52,4053,4054],{"class":105},"(canvasEndX",[52,4056,408],{"class":58},[52,4058,4059],{"class":105},"canvasEndY)",[52,4061,1007],{"class":58},[52,4063,4064,4066,4068,4070,4072,4074,4076,4078],{"class":54,"line":328},[52,4065,1875],{"class":105},[52,4067,957],{"class":58},[52,4069,3168],{"class":418},[52,4071,932],{"class":105},[52,4073,1647],{"class":1646},[52,4075,408],{"class":58},[52,4077,4059],{"class":105},[52,4079,1007],{"class":58},[52,4081,4082,4084,4086,4088,4090],{"class":54,"line":337},[52,4083,1875],{"class":105},[52,4085,957],{"class":58},[52,4087,3244],{"class":418},[52,4089,422],{"class":105},[52,4091,1007],{"class":58},[13,4093,4094],{},"次にくりぬきの範囲を指定します。上図でいうと１〜４を指します。フォトショップやイラストレーターを使っている人なら「パスで選択範囲を指定」という意味がわかると思います。それをここでjsを用いて行っています。",[13,4096,4097],{},"念のために解説すると、パスというのは図形を構成する点（座標）みたいなものです。そして図形はその点を結ぶことで描画できます。四角形であれば点（頂点）は４つあって、それを一筆書きすると四角ができますよね。その一筆書きの順路と位置をこのコードで定義しています。",[2489,4099,4100,4103,4106,4109],{},[1467,4101,4102],{},"ctx.beginPath()でパスの指定を開始します。",[1467,4104,4105],{},"ctx.moveTo(X,Y)で指定した座標にパスを移動させます。",[1467,4107,4108],{},"ctx.lineTo(nextX,nextY)で次の座標を指定しパスを移動させつつ線をひきます。",[1467,4110,4111],{},"ctx.closePath()でパスの指定を終了します。",[13,4113,4114],{},"今回はこのパスの指定を",[2489,4116,4117,4120,4123,4126],{},[1467,4118,4119],{},"画像の左端（X=０）、指定した波の開始地点（waveStartY）より（0, waveStartY）からパスを開始。",[1467,4121,4122],{},"画像の右端（X＝canvasEndX）までfor文を用いて、さらに三角関数の式にx座標を入れて、波線を描く様にパスを指定していく。",[1467,4124,4125],{},"画像の右端までついたら、画像の右下端、左下端を通って、開始地点に戻る。",[1467,4127,4128],{},"パスを閉じる。",[13,4130,4131],{},"この様にしています。",[2409,4133,4134],{"id":4134},"選択範囲を透明色で塗りつぶす",[13,4136,4137],{},"上記の方法で選択範囲を指定すれば、あとは塗りつぶすだけです。",[42,4139,4141],{"className":1249,"code":4140,"language":1251,"meta":47,"style":47},"ctx.fillStyle = \"rgba(255,255,255,1)\"; \u002F\u002Fopacity 1\nctx.fill();\n",[49,4142,4143,4164],{"__ignoreMap":47},[52,4144,4145,4147,4149,4152,4154,4156,4158,4160,4162],{"class":54,"line":55},[52,4146,2526],{"class":105},[52,4148,957],{"class":58},[52,4150,4151],{"class":105},"fillStyle ",[52,4153,69],{"class":58},[52,4155,3013],{"class":58},[52,4157,3271],{"class":75},[52,4159,72],{"class":58},[52,4161,3077],{"class":58},[52,4163,3278],{"class":411},[52,4165,4166,4168,4170,4172,4174],{"class":54,"line":83},[52,4167,2526],{"class":105},[52,4169,957],{"class":58},[52,4171,3288],{"class":418},[52,4173,422],{"class":105},[52,4175,1007],{"class":58},[13,4177,4178,4180,4181,4184],{},[49,4179,3264],{},"で塗り潰しの色を設定できます。ここは実際、",[49,4182,4183],{},"globalCompositeOperation = \"destination-out\";","を設定していれば何色でも大丈夫です。ですが念のため透明色を設定。",[13,4186,2180,4187,4190,4191,4194],{},[49,4188,4189],{},"fill()","で指定したスタイル、パスの範囲で塗り潰しを行います。g",[49,4192,4193],{},"lobalCompositeOperation = \"destination-out\";","が設定されているので塗り潰された部分と画像の重なり合う部分以外が残り、重なり部分は透明になります。",[13,4196,4197],{},"ここまで来れば以下の様に波線にくり抜かれた画像が得られます。",[729,4199],{":src":1446,":width":732},[17,4201,4202],{"id":4202},"ループでアニメーションを実装",[13,4204,4205,4206,4209,4210,4212],{},"定義した",[49,4207,4208],{},"imageSet()","と",[49,4211,3444],{},"を用いて以下のループを設定します。",[42,4214,4216],{"className":1249,"code":4215,"language":1251,"meta":47,"style":47},"function loop(){\n      setInterval(function(){\n      imageSet(image,canvasEndX,canvasEndY);\n      waveDrawing(waveStartPoint,canvasEndX,canvasEndY,degree,amplitude,period);\n      degree += 12; \u002F\u002F12はなんとなく\n    },30)\n}\n",[49,4217,4218,4227,4238,4259,4292,4308,4318],{"__ignoreMap":47},[52,4219,4220,4222,4225],{"class":54,"line":55},[52,4221,1832],{"class":65},[52,4223,4224],{"class":418}," loop",[52,4226,1838],{"class":58},[52,4228,4229,4232,4234,4236],{"class":54,"line":83},[52,4230,4231],{"class":418},"      setInterval",[52,4233,932],{"class":62},[52,4235,1832],{"class":65},[52,4237,1838],{"class":58},[52,4239,4240,4243,4245,4247,4249,4251,4253,4255,4257],{"class":54,"line":115},[52,4241,4242],{"class":418},"      imageSet",[52,4244,932],{"class":62},[52,4246,2053],{"class":105},[52,4248,408],{"class":58},[52,4250,2778],{"class":105},[52,4252,408],{"class":58},[52,4254,2783],{"class":105},[52,4256,938],{"class":62},[52,4258,1007],{"class":58},[52,4260,4261,4264,4266,4268,4270,4272,4274,4276,4278,4280,4282,4284,4286,4288,4290],{"class":54,"line":142},[52,4262,4263],{"class":418},"      waveDrawing",[52,4265,932],{"class":62},[52,4267,2797],{"class":105},[52,4269,408],{"class":58},[52,4271,2778],{"class":105},[52,4273,408],{"class":58},[52,4275,2783],{"class":105},[52,4277,408],{"class":58},[52,4279,2810],{"class":105},[52,4281,408],{"class":58},[52,4283,2815],{"class":105},[52,4285,408],{"class":58},[52,4287,2820],{"class":105},[52,4289,938],{"class":62},[52,4291,1007],{"class":58},[52,4293,4294,4297,4300,4303,4305],{"class":54,"line":169},[52,4295,4296],{"class":105},"      degree",[52,4298,4299],{"class":58}," +=",[52,4301,4302],{"class":1646}," 12",[52,4304,3077],{"class":58},[52,4306,4307],{"class":411}," \u002F\u002F12はなんとなく\n",[52,4309,4310,4313,4316],{"class":54,"line":302},[52,4311,4312],{"class":58},"    },",[52,4314,4315],{"class":1646},"30",[52,4317,1015],{"class":62},[52,4319,4320],{"class":54,"line":308},[52,4321,536],{"class":58},[13,4323,4324,4325,4328,4329,4331],{},"ここで一番大切なのは",[49,4326,4327],{},"deggre +=4","の様に",[49,4330,3444],{},"で用いる初期角度を足していくことです。こうすることで波がウネウネします。degreeの加算が多いほど波が早くなります。50以上にすると荒波になります笑",[13,4333,4334,4335,4338,4339,4341],{},"そしてこの",[49,4336,4337],{},"loop()","の関数を",[49,4340,2392],{},"で呼び出して発火させます。",[42,4343,4345],{"className":1249,"code":4344,"language":1251,"meta":47,"style":47},"image.onload = function(){\n    initDraw();\n    loop();\n}\n",[49,4346,4347,4361,4369,4378],{"__ignoreMap":47},[52,4348,4349,4351,4353,4355,4357,4359],{"class":54,"line":55},[52,4350,2053],{"class":105},[52,4352,957],{"class":58},[52,4354,2031],{"class":418},[52,4356,951],{"class":58},[52,4358,2036],{"class":65},[52,4360,1838],{"class":58},[52,4362,4363,4365,4367],{"class":54,"line":83},[52,4364,2638],{"class":418},[52,4366,422],{"class":62},[52,4368,1007],{"class":58},[52,4370,4371,4374,4376],{"class":54,"line":115},[52,4372,4373],{"class":418},"    loop",[52,4375,422],{"class":62},[52,4377,1007],{"class":58},[52,4379,4380],{"class":54,"line":142},[52,4381,536],{"class":58},[17,4383,4384],{"id":4384},"コード全体",[13,4386,4387],{},"上記をまとめたコードがこちらです。",[42,4389,4391],{"className":1249,"code":4390,"language":1251,"meta":47,"style":47},"window.onload = init();\nfunction init(){\n    initAnimation();\n\n    function initAnimation(){\n        var canvas = document.getElementById('canvas');\n        var ctx = canvas.getContext('2d');\n\n        var imagePath = ('.\u002Fsample.jpg');\n        var image = new Image();\n        image.src = imagePath;\n\n        \u002F\u002Fset canvas width and height\n        canvas.width = Number(window.innerWidth);\n        canvas.height = Number(canvas.width\u002F2);\n        image.onload = function(){\n                initDraw();\n                loop();\n            }\n            \n        var canvasEndX = canvas.width;\n        var canvasEndY = canvas.height;\n        var waveStartPoint = canvasEndY-150;\n\n        var amplitude = 30;\n        var period = 600;\n        var degree = 0;\n\n        function initDraw(){\n            imageSet(image,canvasEndX,canvasEndY);\n            waveDrawing(waveStartPoint,canvasEndX,canvasEndY,degree,amplitude,period);\n        }\n\n        function loop(){\n            setInterval(function(){\n                imageSet(image,canvasEndX,canvasEndY);\n                waveDrawing(waveStartPoint,canvasEndX,canvasEndY,degree,amplitude,period);\n                degree += 12;\n            },30)\n        }\n\n        function imageSet(imageObj,canvasEndX,canvasEndY){\n            var imgWidth = imageObj.width;\n            var imgHeight = imageObj.height;\n\n            ctx.globalCompositeOperation = \"destination-over\";\n            ctx.drawImage(image,0,0,imgWidth,imgHeight,0,0,canvasEndX,canvasEndY);\n        }\n\n        function waveDrawing(waveStartPoint,canvasEndX,canvasEndY,deg,am,tp){\n            var waveStartY = waveStartPoint;\n            ctx.globalCompositeOperation = \"destination-out\";\n            ctx.beginPath();\n            ctx.moveTo(0, waveStartY);\n\n            for (var x=0; x \u003C= canvasEndX; x+= 1) {\n                var y = -am*Math.sin((Math.PI\u002Ftp)*(deg+x));\n                ctx.lineTo(x, y+waveStartY);\n            }\n\n            ctx.lineTo(canvasEndX,canvasEndY);\n            ctx.lineTo(0,canvasEndY);\n            ctx.closePath();\n\n            ctx.fillStyle = \"rgba(255,255,255,1)\"; \u002F\u002Fopacity 1\n            ctx.fill();\n        }\n\n    }\n}\n",[49,4392,4393,4411,4419,4428,4432,4441,4467,4493,4497,4517,4533,4548,4552,4557,4582,4610,4624,4633,4642,4646,4651,4667,4683,4699,4703,4716,4730,4743,4747,4756,4777,4810,4815,4819,4827,4838,4859,4892,4903,4912,4916,4920,4941,4959,4976,4981,5002,5051,5056,5061,5094,5107,5126,5139,5160,5165,5201,5251,5277,5282,5287,5308,5329,5342,5347,5368,5381,5386,5391,5396],{"__ignoreMap":47},[52,4394,4395,4397,4399,4402,4404,4407,4409],{"class":54,"line":55},[52,4396,1983],{"class":105},[52,4398,957],{"class":58},[52,4400,4401],{"class":105},"onload ",[52,4403,69],{"class":58},[52,4405,4406],{"class":418}," init",[52,4408,422],{"class":105},[52,4410,1007],{"class":58},[52,4412,4413,4415,4417],{"class":54,"line":83},[52,4414,1832],{"class":65},[52,4416,4406],{"class":418},[52,4418,1838],{"class":58},[52,4420,4421,4424,4426],{"class":54,"line":115},[52,4422,4423],{"class":418},"    initAnimation",[52,4425,422],{"class":62},[52,4427,1007],{"class":58},[52,4429,4430],{"class":54,"line":142},[52,4431,341],{"emptyLinePlaceholder":340},[52,4433,4434,4437,4439],{"class":54,"line":169},[52,4435,4436],{"class":65},"    function",[52,4438,1835],{"class":418},[52,4440,1838],{"class":58},[52,4442,4443,4445,4447,4449,4451,4453,4455,4457,4459,4461,4463,4465],{"class":54,"line":302},[52,4444,3105],{"class":65},[52,4446,1846],{"class":105},[52,4448,951],{"class":58},[52,4450,1851],{"class":105},[52,4452,957],{"class":58},[52,4454,1856],{"class":418},[52,4456,932],{"class":62},[52,4458,376],{"class":58},[52,4460,1747],{"class":75},[52,4462,376],{"class":58},[52,4464,938],{"class":62},[52,4466,1007],{"class":58},[52,4468,4469,4471,4473,4475,4477,4479,4481,4483,4485,4487,4489,4491],{"class":54,"line":308},[52,4470,3105],{"class":65},[52,4472,1875],{"class":105},[52,4474,951],{"class":58},[52,4476,1846],{"class":105},[52,4478,957],{"class":58},[52,4480,1884],{"class":418},[52,4482,932],{"class":62},[52,4484,376],{"class":58},[52,4486,1891],{"class":75},[52,4488,376],{"class":58},[52,4490,938],{"class":62},[52,4492,1007],{"class":58},[52,4494,4495],{"class":54,"line":318},[52,4496,341],{"emptyLinePlaceholder":340},[52,4498,4499,4501,4503,4505,4507,4509,4511,4513,4515],{"class":54,"line":328},[52,4500,3105],{"class":65},[52,4502,1908],{"class":105},[52,4504,951],{"class":58},[52,4506,1913],{"class":62},[52,4508,376],{"class":58},[52,4510,1918],{"class":75},[52,4512,376],{"class":58},[52,4514,938],{"class":62},[52,4516,1007],{"class":58},[52,4518,4519,4521,4523,4525,4527,4529,4531],{"class":54,"line":337},[52,4520,3105],{"class":65},[52,4522,1931],{"class":105},[52,4524,951],{"class":58},[52,4526,1936],{"class":58},[52,4528,1939],{"class":418},[52,4530,422],{"class":62},[52,4532,1007],{"class":58},[52,4534,4535,4538,4540,4542,4544,4546],{"class":54,"line":344},[52,4536,4537],{"class":105},"        image",[52,4539,957],{"class":58},[52,4541,1953],{"class":105},[52,4543,951],{"class":58},[52,4545,1908],{"class":105},[52,4547,1007],{"class":58},[52,4549,4550],{"class":54,"line":354},[52,4551,341],{"emptyLinePlaceholder":340},[52,4553,4554],{"class":54,"line":367},[52,4555,4556],{"class":411},"        \u002F\u002Fset canvas width and height\n",[52,4558,4559,4562,4564,4566,4568,4570,4572,4574,4576,4578,4580],{"class":54,"line":387},[52,4560,4561],{"class":105},"        canvas",[52,4563,957],{"class":58},[52,4565,1973],{"class":105},[52,4567,951],{"class":58},[52,4569,1978],{"class":418},[52,4571,932],{"class":62},[52,4573,1983],{"class":105},[52,4575,957],{"class":58},[52,4577,1988],{"class":105},[52,4579,938],{"class":62},[52,4581,1007],{"class":58},[52,4583,4584,4586,4588,4590,4592,4594,4596,4598,4600,4602,4604,4606,4608],{"class":54,"line":415},[52,4585,4561],{"class":105},[52,4587,957],{"class":58},[52,4589,2001],{"class":105},[52,4591,951],{"class":58},[52,4593,1978],{"class":418},[52,4595,932],{"class":62},[52,4597,1747],{"class":105},[52,4599,957],{"class":58},[52,4601,1973],{"class":105},[52,4603,2016],{"class":58},[52,4605,35],{"class":1646},[52,4607,938],{"class":62},[52,4609,1007],{"class":58},[52,4611,4612,4614,4616,4618,4620,4622],{"class":54,"line":427},[52,4613,4537],{"class":105},[52,4615,957],{"class":58},[52,4617,2031],{"class":418},[52,4619,951],{"class":58},[52,4621,2036],{"class":65},[52,4623,1838],{"class":58},[52,4625,4626,4629,4631],{"class":54,"line":435},[52,4627,4628],{"class":418},"                initDraw",[52,4630,422],{"class":62},[52,4632,1007],{"class":58},[52,4634,4635,4638,4640],{"class":54,"line":446},[52,4636,4637],{"class":418},"                loop",[52,4639,422],{"class":62},[52,4641,1007],{"class":58},[52,4643,4644],{"class":54,"line":480},[52,4645,1665],{"class":58},[52,4647,4648],{"class":54,"line":509},[52,4649,4650],{"class":62},"            \n",[52,4652,4653,4655,4657,4659,4661,4663,4665],{"class":54,"line":539},[52,4654,3105],{"class":65},[52,4656,3085],{"class":105},[52,4658,951],{"class":58},[52,4660,1846],{"class":105},[52,4662,957],{"class":58},[52,4664,1973],{"class":105},[52,4666,1007],{"class":58},[52,4668,4669,4671,4673,4675,4677,4679,4681],{"class":54,"line":547},[52,4670,3105],{"class":65},[52,4672,2697],{"class":105},[52,4674,951],{"class":58},[52,4676,1846],{"class":105},[52,4678,957],{"class":58},[52,4680,2001],{"class":105},[52,4682,1007],{"class":58},[52,4684,4685,4687,4689,4691,4693,4695,4697],{"class":54,"line":553},[52,4686,3105],{"class":65},[52,4688,2997],{"class":105},[52,4690,951],{"class":58},[52,4692,2697],{"class":105},[52,4694,2700],{"class":58},[52,4696,2703],{"class":1646},[52,4698,1007],{"class":58},[52,4700,4701],{"class":54,"line":559},[52,4702,341],{"emptyLinePlaceholder":340},[52,4704,4705,4707,4710,4712,4714],{"class":54,"line":564},[52,4706,3105],{"class":65},[52,4708,4709],{"class":105}," amplitude",[52,4711,951],{"class":58},[52,4713,2721],{"class":1646},[52,4715,1007],{"class":58},[52,4717,4718,4720,4723,4725,4728],{"class":54,"line":569},[52,4719,3105],{"class":65},[52,4721,4722],{"class":105}," period",[52,4724,951],{"class":58},[52,4726,4727],{"class":1646}," 600",[52,4729,1007],{"class":58},[52,4731,4732,4734,4737,4739,4741],{"class":54,"line":1106},[52,4733,3105],{"class":65},[52,4735,4736],{"class":105}," degree",[52,4738,951],{"class":58},[52,4740,2749],{"class":1646},[52,4742,1007],{"class":58},[52,4744,4745],{"class":54,"line":1135},[52,4746,341],{"emptyLinePlaceholder":340},[52,4748,4749,4752,4754],{"class":54,"line":1164},[52,4750,4751],{"class":65},"        function",[52,4753,2762],{"class":418},[52,4755,1838],{"class":58},[52,4757,4758,4761,4763,4765,4767,4769,4771,4773,4775],{"class":54,"line":4},[52,4759,4760],{"class":418},"            imageSet",[52,4762,932],{"class":62},[52,4764,2053],{"class":105},[52,4766,408],{"class":58},[52,4768,2778],{"class":105},[52,4770,408],{"class":58},[52,4772,2783],{"class":105},[52,4774,938],{"class":62},[52,4776,1007],{"class":58},[52,4778,4779,4782,4784,4786,4788,4790,4792,4794,4796,4798,4800,4802,4804,4806,4808],{"class":54,"line":1199},[52,4780,4781],{"class":418},"            waveDrawing",[52,4783,932],{"class":62},[52,4785,2797],{"class":105},[52,4787,408],{"class":58},[52,4789,2778],{"class":105},[52,4791,408],{"class":58},[52,4793,2783],{"class":105},[52,4795,408],{"class":58},[52,4797,2810],{"class":105},[52,4799,408],{"class":58},[52,4801,2815],{"class":105},[52,4803,408],{"class":58},[52,4805,2820],{"class":105},[52,4807,938],{"class":62},[52,4809,1007],{"class":58},[52,4811,4812],{"class":54,"line":1204},[52,4813,4814],{"class":58},"        }\n",[52,4816,4817],{"class":54,"line":1209},[52,4818,341],{"emptyLinePlaceholder":340},[52,4820,4821,4823,4825],{"class":54,"line":1214},[52,4822,4751],{"class":65},[52,4824,4224],{"class":418},[52,4826,1838],{"class":58},[52,4828,4829,4832,4834,4836],{"class":54,"line":1219},[52,4830,4831],{"class":418},"            setInterval",[52,4833,932],{"class":62},[52,4835,1832],{"class":65},[52,4837,1838],{"class":58},[52,4839,4840,4843,4845,4847,4849,4851,4853,4855,4857],{"class":54,"line":3216},[52,4841,4842],{"class":418},"                imageSet",[52,4844,932],{"class":62},[52,4846,2053],{"class":105},[52,4848,408],{"class":58},[52,4850,2778],{"class":105},[52,4852,408],{"class":58},[52,4854,2783],{"class":105},[52,4856,938],{"class":62},[52,4858,1007],{"class":58},[52,4860,4861,4864,4866,4868,4870,4872,4874,4876,4878,4880,4882,4884,4886,4888,4890],{"class":54,"line":3237},[52,4862,4863],{"class":418},"                waveDrawing",[52,4865,932],{"class":62},[52,4867,2797],{"class":105},[52,4869,408],{"class":58},[52,4871,2778],{"class":105},[52,4873,408],{"class":58},[52,4875,2783],{"class":105},[52,4877,408],{"class":58},[52,4879,2810],{"class":105},[52,4881,408],{"class":58},[52,4883,2815],{"class":105},[52,4885,408],{"class":58},[52,4887,2820],{"class":105},[52,4889,938],{"class":62},[52,4891,1007],{"class":58},[52,4893,4894,4897,4899,4901],{"class":54,"line":3251},[52,4895,4896],{"class":105},"                degree",[52,4898,4299],{"class":58},[52,4900,4302],{"class":1646},[52,4902,1007],{"class":58},[52,4904,4905,4908,4910],{"class":54,"line":3256},[52,4906,4907],{"class":58},"            },",[52,4909,4315],{"class":1646},[52,4911,1015],{"class":62},[52,4913,4914],{"class":54,"line":3281},[52,4915,4814],{"class":58},[52,4917,4918],{"class":54,"line":3295},[52,4919,341],{"emptyLinePlaceholder":340},[52,4921,4923,4925,4927,4929,4931,4933,4935,4937,4939],{"class":54,"line":4922},42,[52,4924,4751],{"class":65},[52,4926,2839],{"class":418},[52,4928,932],{"class":58},[52,4930,2844],{"class":986},[52,4932,408],{"class":58},[52,4934,2778],{"class":986},[52,4936,408],{"class":58},[52,4938,2783],{"class":986},[52,4940,2855],{"class":58},[52,4942,4944,4947,4949,4951,4953,4955,4957],{"class":54,"line":4943},43,[52,4945,4946],{"class":65},"            var",[52,4948,2862],{"class":105},[52,4950,951],{"class":58},[52,4952,2867],{"class":105},[52,4954,957],{"class":58},[52,4956,1973],{"class":105},[52,4958,1007],{"class":58},[52,4960,4962,4964,4966,4968,4970,4972,4974],{"class":54,"line":4961},44,[52,4963,4946],{"class":65},[52,4965,2880],{"class":105},[52,4967,951],{"class":58},[52,4969,2867],{"class":105},[52,4971,957],{"class":58},[52,4973,2001],{"class":105},[52,4975,1007],{"class":58},[52,4977,4979],{"class":54,"line":4978},45,[52,4980,341],{"emptyLinePlaceholder":340},[52,4982,4984,4987,4989,4991,4993,4995,4998,5000],{"class":54,"line":4983},46,[52,4985,4986],{"class":105},"            ctx",[52,4988,957],{"class":58},[52,4990,3008],{"class":105},[52,4992,951],{"class":58},[52,4994,3013],{"class":58},[52,4996,4997],{"class":75},"destination-over",[52,4999,72],{"class":58},[52,5001,1007],{"class":58},[52,5003,5005,5007,5009,5011,5013,5015,5017,5019,5021,5023,5025,5027,5029,5031,5033,5035,5037,5039,5041,5043,5045,5047,5049],{"class":54,"line":5004},47,[52,5006,4986],{"class":105},[52,5008,957],{"class":58},[52,5010,2048],{"class":418},[52,5012,932],{"class":62},[52,5014,2053],{"class":105},[52,5016,408],{"class":58},[52,5018,1647],{"class":1646},[52,5020,408],{"class":58},[52,5022,1647],{"class":1646},[52,5024,408],{"class":58},[52,5026,2916],{"class":105},[52,5028,408],{"class":58},[52,5030,2921],{"class":105},[52,5032,408],{"class":58},[52,5034,1647],{"class":1646},[52,5036,408],{"class":58},[52,5038,1647],{"class":1646},[52,5040,408],{"class":58},[52,5042,2778],{"class":105},[52,5044,408],{"class":58},[52,5046,2783],{"class":105},[52,5048,938],{"class":62},[52,5050,1007],{"class":58},[52,5052,5054],{"class":54,"line":5053},48,[52,5055,4814],{"class":58},[52,5057,5059],{"class":54,"line":5058},49,[52,5060,341],{"emptyLinePlaceholder":340},[52,5062,5064,5066,5068,5070,5072,5074,5076,5078,5080,5082,5084,5086,5088,5090,5092],{"class":54,"line":5063},50,[52,5065,4751],{"class":65},[52,5067,2956],{"class":418},[52,5069,932],{"class":58},[52,5071,2797],{"class":986},[52,5073,408],{"class":58},[52,5075,2778],{"class":986},[52,5077,408],{"class":58},[52,5079,2783],{"class":986},[52,5081,408],{"class":58},[52,5083,2973],{"class":986},[52,5085,408],{"class":58},[52,5087,2978],{"class":986},[52,5089,408],{"class":58},[52,5091,2983],{"class":986},[52,5093,2855],{"class":58},[52,5095,5097,5099,5101,5103,5105],{"class":54,"line":5096},51,[52,5098,4946],{"class":65},[52,5100,2992],{"class":105},[52,5102,951],{"class":58},[52,5104,2997],{"class":105},[52,5106,1007],{"class":58},[52,5108,5110,5112,5114,5116,5118,5120,5122,5124],{"class":54,"line":5109},52,[52,5111,4986],{"class":105},[52,5113,957],{"class":58},[52,5115,3008],{"class":105},[52,5117,951],{"class":58},[52,5119,3013],{"class":58},[52,5121,3016],{"class":75},[52,5123,72],{"class":58},[52,5125,1007],{"class":58},[52,5127,5129,5131,5133,5135,5137],{"class":54,"line":5128},53,[52,5130,4986],{"class":105},[52,5132,957],{"class":58},[52,5134,3029],{"class":418},[52,5136,422],{"class":62},[52,5138,1007],{"class":58},[52,5140,5142,5144,5146,5148,5150,5152,5154,5156,5158],{"class":54,"line":5141},54,[52,5143,4986],{"class":105},[52,5145,957],{"class":58},[52,5147,3042],{"class":418},[52,5149,932],{"class":62},[52,5151,1647],{"class":1646},[52,5153,408],{"class":58},[52,5155,2992],{"class":105},[52,5157,938],{"class":62},[52,5159,1007],{"class":58},[52,5161,5163],{"class":54,"line":5162},55,[52,5164,341],{"emptyLinePlaceholder":340},[52,5166,5168,5171,5173,5175,5177,5179,5181,5183,5185,5187,5189,5191,5193,5195,5197,5199],{"class":54,"line":5167},56,[52,5169,5170],{"class":360},"            for",[52,5172,1913],{"class":62},[52,5174,2127],{"class":65},[52,5176,3070],{"class":105},[52,5178,69],{"class":58},[52,5180,1647],{"class":1646},[52,5182,3077],{"class":58},[52,5184,3070],{"class":105},[52,5186,3082],{"class":58},[52,5188,3085],{"class":105},[52,5190,3077],{"class":58},[52,5192,3070],{"class":105},[52,5194,3092],{"class":58},[52,5196,3095],{"class":1646},[52,5198,3098],{"class":62},[52,5200,364],{"class":58},[52,5202,5204,5207,5209,5211,5213,5215,5217,5219,5221,5223,5225,5227,5229,5231,5233,5235,5237,5239,5241,5243,5245,5247,5249],{"class":54,"line":5203},57,[52,5205,5206],{"class":65},"                var",[52,5208,3108],{"class":105},[52,5210,951],{"class":58},[52,5212,3113],{"class":58},[52,5214,2978],{"class":105},[52,5216,3118],{"class":58},[52,5218,3121],{"class":105},[52,5220,957],{"class":58},[52,5222,3126],{"class":418},[52,5224,3129],{"class":62},[52,5226,3121],{"class":105},[52,5228,957],{"class":58},[52,5230,3136],{"class":105},[52,5232,2016],{"class":58},[52,5234,2983],{"class":105},[52,5236,938],{"class":62},[52,5238,3118],{"class":58},[52,5240,932],{"class":62},[52,5242,2973],{"class":105},[52,5244,3151],{"class":58},[52,5246,3154],{"class":105},[52,5248,3157],{"class":62},[52,5250,1007],{"class":58},[52,5252,5254,5257,5259,5261,5263,5265,5267,5269,5271,5273,5275],{"class":54,"line":5253},58,[52,5255,5256],{"class":105},"                ctx",[52,5258,957],{"class":58},[52,5260,3168],{"class":418},[52,5262,932],{"class":62},[52,5264,3154],{"class":105},[52,5266,408],{"class":58},[52,5268,3108],{"class":105},[52,5270,3151],{"class":58},[52,5272,3181],{"class":105},[52,5274,938],{"class":62},[52,5276,1007],{"class":58},[52,5278,5280],{"class":54,"line":5279},59,[52,5281,1665],{"class":58},[52,5283,5285],{"class":54,"line":5284},60,[52,5286,341],{"emptyLinePlaceholder":340},[52,5288,5290,5292,5294,5296,5298,5300,5302,5304,5306],{"class":54,"line":5289},61,[52,5291,4986],{"class":105},[52,5293,957],{"class":58},[52,5295,3168],{"class":418},[52,5297,932],{"class":62},[52,5299,2778],{"class":105},[52,5301,408],{"class":58},[52,5303,2783],{"class":105},[52,5305,938],{"class":62},[52,5307,1007],{"class":58},[52,5309,5311,5313,5315,5317,5319,5321,5323,5325,5327],{"class":54,"line":5310},62,[52,5312,4986],{"class":105},[52,5314,957],{"class":58},[52,5316,3168],{"class":418},[52,5318,932],{"class":62},[52,5320,1647],{"class":1646},[52,5322,408],{"class":58},[52,5324,2783],{"class":105},[52,5326,938],{"class":62},[52,5328,1007],{"class":58},[52,5330,5332,5334,5336,5338,5340],{"class":54,"line":5331},63,[52,5333,4986],{"class":105},[52,5335,957],{"class":58},[52,5337,3244],{"class":418},[52,5339,422],{"class":62},[52,5341,1007],{"class":58},[52,5343,5345],{"class":54,"line":5344},64,[52,5346,341],{"emptyLinePlaceholder":340},[52,5348,5350,5352,5354,5356,5358,5360,5362,5364,5366],{"class":54,"line":5349},65,[52,5351,4986],{"class":105},[52,5353,957],{"class":58},[52,5355,3264],{"class":105},[52,5357,951],{"class":58},[52,5359,3013],{"class":58},[52,5361,3271],{"class":75},[52,5363,72],{"class":58},[52,5365,3077],{"class":58},[52,5367,3278],{"class":411},[52,5369,5371,5373,5375,5377,5379],{"class":54,"line":5370},66,[52,5372,4986],{"class":105},[52,5374,957],{"class":58},[52,5376,3288],{"class":418},[52,5378,422],{"class":62},[52,5380,1007],{"class":58},[52,5382,5384],{"class":54,"line":5383},67,[52,5385,4814],{"class":58},[52,5387,5389],{"class":54,"line":5388},68,[52,5390,341],{"emptyLinePlaceholder":340},[52,5392,5394],{"class":54,"line":5393},69,[52,5395,2110],{"class":58},[52,5397,5399],{"class":54,"line":5398},70,[52,5400,536],{"class":58},[17,5402,5403],{"id":5403},"波の様子を変えたい場合",[13,5405,5406],{},"このコードの場合は",[42,5408,5410],{"className":1249,"code":5409,"language":1251,"meta":47,"style":47},"var amplitude = 30;\nvar period = 600;\n\nfunction loop(){\n      ...\n      degree += 12;\n      },30)\n}\n",[49,5411,5412,5424,5436,5440,5448,5453,5463,5472],{"__ignoreMap":47},[52,5413,5414,5416,5418,5420,5422],{"class":54,"line":55},[52,5415,2127],{"class":65},[52,5417,2716],{"class":105},[52,5419,69],{"class":58},[52,5421,2721],{"class":1646},[52,5423,1007],{"class":58},[52,5425,5426,5428,5430,5432,5434],{"class":54,"line":83},[52,5427,2127],{"class":65},[52,5429,2730],{"class":105},[52,5431,69],{"class":58},[52,5433,4727],{"class":1646},[52,5435,1007],{"class":58},[52,5437,5438],{"class":54,"line":115},[52,5439,341],{"emptyLinePlaceholder":340},[52,5441,5442,5444,5446],{"class":54,"line":142},[52,5443,1832],{"class":65},[52,5445,4224],{"class":418},[52,5447,1838],{"class":58},[52,5449,5450],{"class":54,"line":169},[52,5451,5452],{"class":58},"      ...\n",[52,5454,5455,5457,5459,5461],{"class":54,"line":302},[52,5456,4296],{"class":105},[52,5458,4299],{"class":58},[52,5460,4302],{"class":1646},[52,5462,1007],{"class":58},[52,5464,5465,5468,5470],{"class":54,"line":308},[52,5466,5467],{"class":58},"      },",[52,5469,4315],{"class":1646},[52,5471,1015],{"class":105},[52,5473,5474],{"class":54,"line":318},[52,5475,536],{"class":105},[13,5477,5478,5481,5482,5485,5486,5489,5490,5492],{},[49,5479,5480],{},"amplitude（振幅）","で波の最大の高さ、",[49,5483,5484],{},"period（周期）","で１波の周期、",[49,5487,5488],{},"degree +=","で波の速さを変化させることができます。他にも",[49,5491,3444],{},"で定義した三角関数の式を変えることで単純なサイン波でなく、複雑な波を描画できます。",[17,5494,5495],{"id":5495},"最後に",[13,5497,5498],{},"以上がcanvasを用いて画像をウネウネさせる方法です。canvasではこの様に複雑なアニメーションを用いた描画が可能です。jsで記述するのでFlashのActionScriptとかやっていた人は馴染みがあるかもしれません。",[1413,5500,5501],{},"html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .s6YsC, html code.shiki .s6YsC{--shiki-default:#B2CCD6}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}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 pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}",{"title":47,"searchDepth":115,"depth":115,"links":5503},[5504,5505,5508,5513,5521,5522,5523,5524],{"id":1459,"depth":83,"text":1459},{"id":1481,"depth":83,"text":1481,"children":5506},[5507],{"id":1520,"depth":115,"text":1521},{"id":1527,"depth":83,"text":1527,"children":5509},[5510],{"id":1819,"depth":115,"text":1819,"children":5511},[5512],{"id":2411,"depth":142,"text":2412},{"id":2608,"depth":83,"text":2608,"children":5514},[5515,5516],{"id":3317,"depth":115,"text":3318},{"id":3438,"depth":115,"text":3439,"children":5517},[5518,5519,5520],{"id":3793,"depth":142,"text":3794},{"id":3891,"depth":142,"text":3891},{"id":4134,"depth":142,"text":4134},{"id":4202,"depth":83,"text":4202},{"id":4384,"depth":83,"text":4384},{"id":5403,"depth":83,"text":5403},{"id":5495,"depth":83,"text":5495},[1423],"2026-03-04",{},"\u002Farticles\u002Fjs-canvas-wave",{"title":1438,"description":1438},"articles\u002Fjs-canvas-wave",[1433],"_mix\u002Fex01-768x299.png","M7BMAJ4ad0W1ocZaATiSTEMNK1g6YP1t_HrYUpkyahQ",{"id":5535,"title":5536,"body":5537,"category":9192,"createdAt":9193,"description":9194,"extension":1426,"index":1427,"meta":9195,"navigation":340,"path":9196,"publish":340,"seo":9197,"series":1427,"seriesTitle":1427,"stem":9198,"tag":9199,"thumbnail":9200,"updatedAt":9193,"__hash__":9201},"articles\u002Farticles\u002Ffilepond-crop-vue.md","Vue filepondとCropper.jsの連携とファイルアップロード",{"type":10,"value":5538,"toc":9172},[5539,5548,5551,5555,5564,5787,5790,5804,5807,5810,5813,5816,5823,5834,5840,5847,5851,5858,5931,5945,5965,5969,5976,6422,6425,6428,6431,6446,6469,6717,6727,6731,6734,6740,6746,6915,6919,6922,7393,7416,7421,7424,7438,7441,7444,7819,7831,7834,7837,7851,7854,7864,7996,8015,8018,8021,8024,8027,8036,8437,8443,8449,8465,8476,8479,8492,9141,9144,9147,9150,9157,9160,9169],[13,5540,5541,5542,5547],{},"こんにちはjunです。皆さんはファイルアップロードをJSで実装したことありますか？最近のwebサービスではアバターだったり、記事に画像のせるなど画像をアップロードするのが当たり前になっています。そのため仕事でもファイルのアップロード機能を実装しますが、Ajaxを用いてのFileインターフェースの扱いなどUIの構築が面倒なので、ライブラリを使っています。私は",[1450,5543,5546],{"href":5544,"rel":5545},"https:\u002F\u002Fpqina.nl\u002Ffilepond\u002F",[1454],"filepond","と言うものを使用して、複数アップロードやファイルのフロントバリデーションを実装します。VanillaだけでなくVue.jsやReactとの連携がしやすいので重宝しています。",[13,5549,5550],{},"今回の記事は「Filepondのアップロードを行う前にフロントでトリミングしたものをアップロードする」という単純にアップロードするだけでなく、画像を加工してからUPする機能を実装します。Line,Facebook、Twitterなどではプロフィール画像をアップロードする際に、どんな写真でも任意の範囲で１：１でトリミングできる機能があります。意外とユーザーは自身のスマホでトリミングする機能があるのを知らなかったり、機器によっては提供されていないor面倒だったりします。UX的な観点からもフロントでのトリミング機能があると便利です。",[729,5552],{":src":5553,":width":5554,":center":1322},"'filepond-crop-vue\u002Ftwitter.png'","'50%'",[13,5556,5557,5558,5563],{},"この機能を実装するにあたり、",[1450,5559,5562],{"href":5560,"rel":5561},"https:\u002F\u002Ffengyuanchen.github.io\u002Fcropperjs\u002F",[1454],"Cropper.js","のVue版を使用します。アップロードはfilepondを使用するので両者ライブラリの連携を行います。意外と苦労したので記事にしたいと思います。フロントエンド はVue CLIを用います。バックエンド側の解説はしません。バージョン情報は以下の通りです。",[42,5565,5569],{"className":5566,"code":5567,"language":5568,"meta":47,"style":47},"language-json shiki shiki-themes material-theme-ocean","\"devDependencies\": {\n    \"vue\": \"^2.6.12\",\n    \"vue-filepond\": \"^6.0.3\",\n},\n\"dependencies\": {\n    \"filepond\": \"^4.30.3\",\n    \"filepond-plugin-file-validate-type\": \"^1.2.6\",\n    \"filepond-plugin-image-crop\": \"^2.0.6\",\n    \"filepond-plugin-image-edit\": \"^1.6.3\",\n    \"filepond-plugin-image-preview\": \"^4.6.10\",\n    \"filepond-plugin-image-transform\": \"^3.8.7\",\n    \"vue-cropperjs\": \"^4.2.0\",\n}\n","json",[49,5570,5571,5585,5605,5625,5631,5644,5663,5683,5703,5723,5743,5763,5783],{"__ignoreMap":47},[52,5572,5573,5575,5578,5580,5583],{"class":54,"line":55},[52,5574,72],{"class":58},[52,5576,5577],{"class":75},"devDependencies",[52,5579,72],{"class":58},[52,5581,5582],{"class":105},": ",[52,5584,364],{"class":58},[52,5586,5587,5590,5592,5594,5596,5598,5601,5603],{"class":54,"line":83},[52,5588,5589],{"class":58},"    \"",[52,5591,204],{"class":65},[52,5593,72],{"class":58},[52,5595,373],{"class":58},[52,5597,3013],{"class":58},[52,5599,5600],{"class":75},"^2.6.12",[52,5602,72],{"class":58},[52,5604,384],{"class":58},[52,5606,5607,5609,5612,5614,5616,5618,5621,5623],{"class":54,"line":115},[52,5608,5589],{"class":58},[52,5610,5611],{"class":65},"vue-filepond",[52,5613,72],{"class":58},[52,5615,373],{"class":58},[52,5617,3013],{"class":58},[52,5619,5620],{"class":75},"^6.0.3",[52,5622,72],{"class":58},[52,5624,384],{"class":58},[52,5626,5627,5629],{"class":54,"line":142},[52,5628,1311],{"class":58},[52,5630,384],{"class":105},[52,5632,5633,5635,5638,5640,5642],{"class":54,"line":169},[52,5634,72],{"class":58},[52,5636,5637],{"class":75},"dependencies",[52,5639,72],{"class":58},[52,5641,5582],{"class":105},[52,5643,364],{"class":58},[52,5645,5646,5648,5650,5652,5654,5656,5659,5661],{"class":54,"line":302},[52,5647,5589],{"class":58},[52,5649,5546],{"class":65},[52,5651,72],{"class":58},[52,5653,373],{"class":58},[52,5655,3013],{"class":58},[52,5657,5658],{"class":75},"^4.30.3",[52,5660,72],{"class":58},[52,5662,384],{"class":58},[52,5664,5665,5667,5670,5672,5674,5676,5679,5681],{"class":54,"line":308},[52,5666,5589],{"class":58},[52,5668,5669],{"class":65},"filepond-plugin-file-validate-type",[52,5671,72],{"class":58},[52,5673,373],{"class":58},[52,5675,3013],{"class":58},[52,5677,5678],{"class":75},"^1.2.6",[52,5680,72],{"class":58},[52,5682,384],{"class":58},[52,5684,5685,5687,5690,5692,5694,5696,5699,5701],{"class":54,"line":318},[52,5686,5589],{"class":58},[52,5688,5689],{"class":65},"filepond-plugin-image-crop",[52,5691,72],{"class":58},[52,5693,373],{"class":58},[52,5695,3013],{"class":58},[52,5697,5698],{"class":75},"^2.0.6",[52,5700,72],{"class":58},[52,5702,384],{"class":58},[52,5704,5705,5707,5710,5712,5714,5716,5719,5721],{"class":54,"line":328},[52,5706,5589],{"class":58},[52,5708,5709],{"class":65},"filepond-plugin-image-edit",[52,5711,72],{"class":58},[52,5713,373],{"class":58},[52,5715,3013],{"class":58},[52,5717,5718],{"class":75},"^1.6.3",[52,5720,72],{"class":58},[52,5722,384],{"class":58},[52,5724,5725,5727,5730,5732,5734,5736,5739,5741],{"class":54,"line":337},[52,5726,5589],{"class":58},[52,5728,5729],{"class":65},"filepond-plugin-image-preview",[52,5731,72],{"class":58},[52,5733,373],{"class":58},[52,5735,3013],{"class":58},[52,5737,5738],{"class":75},"^4.6.10",[52,5740,72],{"class":58},[52,5742,384],{"class":58},[52,5744,5745,5747,5750,5752,5754,5756,5759,5761],{"class":54,"line":344},[52,5746,5589],{"class":58},[52,5748,5749],{"class":65},"filepond-plugin-image-transform",[52,5751,72],{"class":58},[52,5753,373],{"class":58},[52,5755,3013],{"class":58},[52,5757,5758],{"class":75},"^3.8.7",[52,5760,72],{"class":58},[52,5762,384],{"class":58},[52,5764,5765,5767,5770,5772,5774,5776,5779,5781],{"class":54,"line":354},[52,5766,5589],{"class":58},[52,5768,5769],{"class":65},"vue-cropperjs",[52,5771,72],{"class":58},[52,5773,373],{"class":58},[52,5775,3013],{"class":58},[52,5777,5778],{"class":75},"^4.2.0",[52,5780,72],{"class":58},[52,5782,384],{"class":58},[52,5784,5785],{"class":54,"line":367},[52,5786,536],{"class":58},[17,5788,5789],{"id":5789},"実装目標",[2489,5791,5792,5795,5798,5801],{},[1467,5793,5794],{},"任意の画像をブラウザにアップロード",[1467,5796,5797],{},"画像を編集できるUIを用意し、任意でトリミングできる様にする",[1467,5799,5800],{},"任意の位置、倍率でオリジナルの画像を１：１でトリミング",[1467,5802,5803],{},"トリミングした画像をサーバーにアップロードする。",[13,5805,5806],{},"以上の機能を持ったアップローダーが実装目標です。",[17,5808,5809],{"id":5809},"初期設定",[13,5811,5812],{},"ではまず最初に必要なライブラリをインストールしていきましょう。とりあえず今はVue CLIの設定とfilepondだけインストールしましょう。Vue CLIのセットアップ解説は省きます。",[1518,5814,5815],{"id":5815},"ライブラリインストール",[42,5817,5821],{"className":5818,"code":5820,"language":452},[5819],"language-text","vue create filepond_cropper\ncd filepond_cropper\n\nnpm install filepond filepond-plugin-file-validate-type filepond-plugin-image-crop filepond-plugin-image-preview --save\nnpm install vue-filepond@6.0.3 --save-dev\n",[49,5822,5820],{"__ignoreMap":47},[13,5824,5825,5826,5828,5829],{},"このインストールで気をつけたいのが",[49,5827,5611],{},"のバージョンです。そのままインストールするとVue3に対応した最新版がインストールされ、Vue2系では動作しません。",[1450,5830,5833],{"href":5831,"rel":5832},"https:\u002F\u002Fgithub.com\u002Fpqina\u002Fvue-filepond",[1454],"本家Githubでも注意書きがあります。",[5835,5836,5837],"blockquote",{},[13,5838,5839],{},"If you want to use Vue FilePond with Vue 2, please use v6 of this plugin.",[13,5841,5842,5843,5846],{},"そのため ",[49,5844,5845],{},"npm install vue-filepond@6.0.3 --save-dev","としてバージョン６を入れる様にしてください。",[1518,5848,5850],{"id":5849},"vueレンダリング準備","Vueレンダリング準備",[13,5852,5853,5854,5857],{},"ライブラリのインストールが終わりましたら、最初にある",[49,5855,5856],{},"src\u002Fmain.js","でvueでfilepondを使用できる様にします。",[42,5859,5862],{"className":1249,"code":5860,"filename":5861,"language":1251,"meta":47,"style":47},"import Vue from 'vue'\n\nimport vueFilePond from 'vue-filepond';\nimport FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type\u002Fdist\u002Ffilepond-plugin-file-validate-type.esm.js';\nimport FilePondPluginImagePreview from 'filepond-plugin-image-preview\u002Fdist\u002Ffilepond-plugin-image-preview.esm.js';\nimport FilePondPluginImageCrop from 'filepond-plugin-image-crop\u002Fdist\u002Ffilepond-plugin-image-crop.esm';\nconst FilePond = vueFilePond(FilePondPluginFileValidateType, FilePondPluginImagePreview,FilePondPluginImageCrop);\nVue.component(FilePond);\n\nVue.config.productionTip = false\n\nnew Vue({\n  render: h => h(App),\n}).$mount('#app')\n","main.js",[49,5863,5864,5869,5873,5878,5883,5888,5893,5898,5903,5907,5912,5916,5921,5926],{"__ignoreMap":47},[52,5865,5866],{"class":54,"line":55},[52,5867,5868],{},"import Vue from 'vue'\n",[52,5870,5871],{"class":54,"line":83},[52,5872,341],{"emptyLinePlaceholder":340},[52,5874,5875],{"class":54,"line":115},[52,5876,5877],{},"import vueFilePond from 'vue-filepond';\n",[52,5879,5880],{"class":54,"line":142},[52,5881,5882],{},"import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type\u002Fdist\u002Ffilepond-plugin-file-validate-type.esm.js';\n",[52,5884,5885],{"class":54,"line":169},[52,5886,5887],{},"import FilePondPluginImagePreview from 'filepond-plugin-image-preview\u002Fdist\u002Ffilepond-plugin-image-preview.esm.js';\n",[52,5889,5890],{"class":54,"line":302},[52,5891,5892],{},"import FilePondPluginImageCrop from 'filepond-plugin-image-crop\u002Fdist\u002Ffilepond-plugin-image-crop.esm';\n",[52,5894,5895],{"class":54,"line":308},[52,5896,5897],{},"const FilePond = vueFilePond(FilePondPluginFileValidateType, FilePondPluginImagePreview,FilePondPluginImageCrop);\n",[52,5899,5900],{"class":54,"line":318},[52,5901,5902],{},"Vue.component(FilePond);\n",[52,5904,5905],{"class":54,"line":328},[52,5906,341],{"emptyLinePlaceholder":340},[52,5908,5909],{"class":54,"line":337},[52,5910,5911],{},"Vue.config.productionTip = false\n",[52,5913,5914],{"class":54,"line":344},[52,5915,341],{"emptyLinePlaceholder":340},[52,5917,5918],{"class":54,"line":354},[52,5919,5920],{},"new Vue({\n",[52,5922,5923],{"class":54,"line":367},[52,5924,5925],{},"  render: h => h(App),\n",[52,5927,5928],{"class":54,"line":387},[52,5929,5930],{},"}).$mount('#app')\n",[13,5932,5933,5936,5937,5940,5941,5944],{},[49,5934,5935],{},"vueFilePond","があれば一応すぐに使用できますがここではfilepondのプラグインを使用します。プラグインを",[49,5938,5939],{},"import","して ",[49,5942,5943],{},"vueFilePond()","の引数に当てていきます。",[1464,5946,5947,5953,5959],{},[1467,5948,5949,5952],{},[49,5950,5951],{},"FilePondPluginFileValidateType","はファイルの拡張子を検証します。ここでは画像（jpg,png,gif）以外を許可しない様にします。",[1467,5954,5955,5958],{},[49,5956,5957],{},"FilePondPluginImagePreview","はブラウザにアップロードしたファイルをプレビューできる様にします。",[1467,5960,5961,5964],{},[49,5962,5963],{},"FilePondPluginImageCrop","はブラウザにアップロードしたファイルを任意のアスペクト比でトリミングしたプレビューを表示します。（実際にトリミングはしてくれない。現状はあくまでトリミング情報を提供してプレビュー表示を変えるだけ）",[17,5966,5968],{"id":5967},"まずはfilepondにアップロードできるようにする","まずはFilepondにアップロードできるようにする",[13,5970,5971,5972,5975],{},"プラグインを読み込み、",[49,5973,5974],{},"src\u002FApp.vue","を編集します。",[42,5977,5979],{"className":202,"code":5978,"filename":5974,"language":204,"meta":47,"style":47},"\u003Ctemplate>\n    \u003Cdiv>\n        \u003Cfile-pond ref=\"filepond\" v-bind=\"attrs\"\u002F>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\u003Cscript>\nimport ja from 'filepond\u002Flocale\u002Fja-ja';\nimport VueCropper from 'vue-cropperjs';\nexport default {\n    name:'fileuploader',\n    data(){\n        return{\n            files:[],\n        }\n    },\n    methods:{\n        onError(res){\n            console.error(res)\n        },\n    },\n    computed:{\n        attrs(){\n            let apiserver = '\u002Fapi\u002Fv1\u002Ffile\u002Favater';\n            return {\n                allowMultiple:false,\n                'accepted-file-types':'image\u002Fjpeg,image\u002Fpng,image\u002Fgif',\n                instantUpload:false,\n                imageCropAspectRatio:'1:1',\n                server:{\n                    process: {\n                        url:apiserver,\n                        method: 'POST',\n                        withCredentials: true,\n                        onerror:(response) => this.onError(response)\n                    },\n                },\n                ...ja\n            }\n        }\n    },\n}\n\u003C\u002Fscript>\n",[49,5980,5981,5989,5998,6031,6039,6047,6055,6075,6092,6101,6117,6124,6131,6143,6147,6152,6159,6171,6187,6192,6196,6203,6210,6229,6236,6248,6269,6280,6296,6303,6312,6324,6340,6352,6380,6385,6390,6398,6402,6406,6410,6414],{"__ignoreMap":47},[52,5982,5983,5985,5987],{"class":54,"line":55},[52,5984,59],{"class":58},[52,5986,213],{"class":62},[52,5988,80],{"class":58},[52,5990,5991,5993,5996],{"class":54,"line":83},[52,5992,1575],{"class":58},[52,5994,5995],{"class":62},"div",[52,5997,80],{"class":58},[52,5999,6000,6002,6005,6008,6010,6012,6014,6016,6019,6021,6023,6026,6028],{"class":54,"line":115},[52,6001,1585],{"class":58},[52,6003,6004],{"class":62},"file-pond",[52,6006,6007],{"class":65}," ref",[52,6009,69],{"class":58},[52,6011,72],{"class":58},[52,6013,5546],{"class":75},[52,6015,72],{"class":58},[52,6017,6018],{"class":65}," v-bind",[52,6020,69],{"class":58},[52,6022,72],{"class":58},[52,6024,6025],{"class":75},"attrs",[52,6027,72],{"class":58},[52,6029,6030],{"class":58},"\u002F>\n",[52,6032,6033,6035,6037],{"class":54,"line":142},[52,6034,1717],{"class":58},[52,6036,5995],{"class":62},[52,6038,80],{"class":58},[52,6040,6041,6043,6045],{"class":54,"line":169},[52,6042,108],{"class":58},[52,6044,213],{"class":62},[52,6046,80],{"class":58},[52,6048,6049,6051,6053],{"class":54,"line":302},[52,6050,59],{"class":58},[52,6052,349],{"class":62},[52,6054,80],{"class":58},[52,6056,6057,6059,6062,6065,6068,6071,6073],{"class":54,"line":308},[52,6058,5939],{"class":360},[52,6060,6061],{"class":105}," ja ",[52,6063,6064],{"class":360},"from",[52,6066,6067],{"class":58}," '",[52,6069,6070],{"class":75},"filepond\u002Flocale\u002Fja-ja",[52,6072,376],{"class":58},[52,6074,1007],{"class":58},[52,6076,6077,6079,6082,6084,6086,6088,6090],{"class":54,"line":318},[52,6078,5939],{"class":360},[52,6080,6081],{"class":105}," VueCropper ",[52,6083,6064],{"class":360},[52,6085,6067],{"class":58},[52,6087,5769],{"class":75},[52,6089,376],{"class":58},[52,6091,1007],{"class":58},[52,6093,6094,6096,6098],{"class":54,"line":328},[52,6095,357],{"class":360},[52,6097,361],{"class":360},[52,6099,6100],{"class":58}," {\n",[52,6102,6103,6106,6108,6110,6113,6115],{"class":54,"line":337},[52,6104,6105],{"class":62},"    name",[52,6107,373],{"class":58},[52,6109,376],{"class":58},[52,6111,6112],{"class":75},"fileuploader",[52,6114,376],{"class":58},[52,6116,384],{"class":58},[52,6118,6119,6122],{"class":54,"line":344},[52,6120,6121],{"class":62},"    data",[52,6123,1838],{"class":58},[52,6125,6126,6129],{"class":54,"line":354},[52,6127,6128],{"class":360},"        return",[52,6130,364],{"class":58},[52,6132,6133,6136,6138,6141],{"class":54,"line":367},[52,6134,6135],{"class":62},"            files",[52,6137,373],{"class":58},[52,6139,6140],{"class":62},"[]",[52,6142,384],{"class":58},[52,6144,6145],{"class":54,"line":387},[52,6146,4814],{"class":58},[52,6148,6149],{"class":54,"line":415},[52,6150,6151],{"class":58},"    },\n",[52,6153,6154,6157],{"class":54,"line":427},[52,6155,6156],{"class":62},"    methods",[52,6158,924],{"class":58},[52,6160,6161,6164,6166,6169],{"class":54,"line":435},[52,6162,6163],{"class":62},"        onError",[52,6165,932],{"class":58},[52,6167,6168],{"class":986},"res",[52,6170,2855],{"class":58},[52,6172,6173,6176,6178,6181,6183,6185],{"class":54,"line":446},[52,6174,6175],{"class":105},"            console",[52,6177,957],{"class":58},[52,6179,6180],{"class":418},"error",[52,6182,932],{"class":62},[52,6184,6168],{"class":105},[52,6186,1015],{"class":62},[52,6188,6189],{"class":54,"line":480},[52,6190,6191],{"class":58},"        },\n",[52,6193,6194],{"class":54,"line":509},[52,6195,6151],{"class":58},[52,6197,6198,6201],{"class":54,"line":539},[52,6199,6200],{"class":62},"    computed",[52,6202,924],{"class":58},[52,6204,6205,6208],{"class":54,"line":547},[52,6206,6207],{"class":62},"        attrs",[52,6209,1838],{"class":58},[52,6211,6212,6215,6218,6220,6222,6225,6227],{"class":54,"line":553},[52,6213,6214],{"class":65},"            let",[52,6216,6217],{"class":105}," apiserver",[52,6219,951],{"class":58},[52,6221,6067],{"class":58},[52,6223,6224],{"class":75},"\u002Fapi\u002Fv1\u002Ffile\u002Favater",[52,6226,376],{"class":58},[52,6228,1007],{"class":58},[52,6230,6231,6234],{"class":54,"line":559},[52,6232,6233],{"class":360},"            return",[52,6235,6100],{"class":58},[52,6237,6238,6241,6243,6246],{"class":54,"line":564},[52,6239,6240],{"class":62},"                allowMultiple",[52,6242,373],{"class":58},[52,6244,1326],{"class":6245},"sbqyR",[52,6247,384],{"class":58},[52,6249,6250,6253,6256,6258,6260,6262,6265,6267],{"class":54,"line":569},[52,6251,6252],{"class":58},"                '",[52,6254,6255],{"class":62},"accepted-file-types",[52,6257,376],{"class":58},[52,6259,373],{"class":58},[52,6261,376],{"class":58},[52,6263,6264],{"class":75},"image\u002Fjpeg,image\u002Fpng,image\u002Fgif",[52,6266,376],{"class":58},[52,6268,384],{"class":58},[52,6270,6271,6274,6276,6278],{"class":54,"line":1106},[52,6272,6273],{"class":62},"                instantUpload",[52,6275,373],{"class":58},[52,6277,1326],{"class":6245},[52,6279,384],{"class":58},[52,6281,6282,6285,6287,6289,6292,6294],{"class":54,"line":1135},[52,6283,6284],{"class":62},"                imageCropAspectRatio",[52,6286,373],{"class":58},[52,6288,376],{"class":58},[52,6290,6291],{"class":75},"1:1",[52,6293,376],{"class":58},[52,6295,384],{"class":58},[52,6297,6298,6301],{"class":54,"line":1164},[52,6299,6300],{"class":62},"                server",[52,6302,924],{"class":58},[52,6304,6305,6308,6310],{"class":54,"line":4},[52,6306,6307],{"class":62},"                    process",[52,6309,373],{"class":58},[52,6311,6100],{"class":58},[52,6313,6314,6317,6319,6322],{"class":54,"line":1199},[52,6315,6316],{"class":62},"                        url",[52,6318,373],{"class":58},[52,6320,6321],{"class":105},"apiserver",[52,6323,384],{"class":58},[52,6325,6326,6329,6331,6333,6336,6338],{"class":54,"line":1204},[52,6327,6328],{"class":62},"                        method",[52,6330,373],{"class":58},[52,6332,6067],{"class":58},[52,6334,6335],{"class":75},"POST",[52,6337,376],{"class":58},[52,6339,384],{"class":58},[52,6341,6342,6345,6347,6350],{"class":54,"line":1209},[52,6343,6344],{"class":62},"                        withCredentials",[52,6346,373],{"class":58},[52,6348,6349],{"class":6245}," true",[52,6351,384],{"class":58},[52,6353,6354,6357,6360,6363,6365,6368,6371,6374,6376,6378],{"class":54,"line":1214},[52,6355,6356],{"class":418},"                        onerror",[52,6358,6359],{"class":58},":(",[52,6361,6362],{"class":986},"response",[52,6364,938],{"class":58},[52,6366,6367],{"class":65}," =>",[52,6369,6370],{"class":58}," this.",[52,6372,6373],{"class":418},"onError",[52,6375,932],{"class":62},[52,6377,6362],{"class":105},[52,6379,1015],{"class":62},[52,6381,6382],{"class":54,"line":1219},[52,6383,6384],{"class":58},"                    },\n",[52,6386,6387],{"class":54,"line":3216},[52,6388,6389],{"class":58},"                },\n",[52,6391,6392,6395],{"class":54,"line":3237},[52,6393,6394],{"class":58},"                ...",[52,6396,6397],{"class":105},"ja\n",[52,6399,6400],{"class":54,"line":3251},[52,6401,1665],{"class":58},[52,6403,6404],{"class":54,"line":3256},[52,6405,4814],{"class":58},[52,6407,6408],{"class":54,"line":3281},[52,6409,6151],{"class":58},[52,6411,6412],{"class":54,"line":3295},[52,6413,536],{"class":58},[52,6415,6416,6418,6420],{"class":54,"line":4922},[52,6417,108],{"class":58},[52,6419,349],{"class":62},[52,6421,80],{"class":58},[13,6423,6424],{},"上記の通りでfilepondのコンポーネント表示されます。（スタイルなどは適宜調整してください）",[729,6426],{":src":6427,":width":5554,":center":1322},"'filepond-crop-vue\u002Ffilepond.png'",[1518,6429,6430],{"id":6430},"設定内容",[13,6432,6433,6434,6437,6438,6441,6442,6445],{},"filepondの公式ではテンプレートに",[49,6435,6436],{},"props","を設定していますが日本語化や色々設定が多いので、",[49,6439,6440],{},"computed","に記載した方がいいです。また日本語化する場合は",[49,6443,6444],{},"import ja from 'filepond\u002Flocale\u002Fja-ja';"," とインポートして展開します。",[13,6447,6448,6449,6451,6452,6457,6458,6461,6462,4209,6465,6468],{},"そしてこれらの",[49,6450,6436],{},"は",[1450,6453,6456],{"href":6454,"rel":6455},"https:\u002F\u002Fpqina.nl\u002Ffilepond\u002Fdocs\u002Fapi\u002Finstance\u002Fproperties\u002F",[1454],"本家vanilla版ドキュメント","で記載されている",[49,6459,6460],{},"properties","と同じ項目です。またfilepondのメソッドにアクセスする場合は",[49,6463,6464],{},"this.$refs.pond",[49,6466,6467],{},"ref","の値を使用します。今回設定した項目の解説は以下の通りです。",[42,6470,6473],{"className":6471,"code":6472,"language":1433,"meta":47,"style":47},"language-js shiki shiki-themes material-theme-ocean","return {\n    \u002F\u002F 複数ファイルのアップロードを許可するか\n    allowMultiple:false,\n\n    \u002F\u002F 許可するmime\n    'accepted-file-types':'image\u002Fjpeg,image\u002Fpng,image\u002Fgif',\n\n    \u002F\u002F ブラウザにアップしたとき、すぐにサーバーにアップロードするか。デフォルトはTrueなので注意\n    instantUpload:false,\n\n    \u002F\u002F FilePondPluginImageCrop有効時のアスペクト比。\n    imageCropAspectRatio:'1:1',\n\n    \u002F\u002F アップロードする際のajaxの設定\n    server:{\n        \u002F\u002F アップロードするときの設定\n        process: {\n            \u002F\u002F アップロード先URL\n            url:apiserver,\n\n            \u002F\u002F アップロードのメソッド（processはデフォルトでPOST）\n            method: 'POST',\n\n            \u002F\u002F withCredentialsを有効化\n            withCredentials: true,\n\n            \u002F\u002F エラー時のコールバック\n            onerror:(response) => this.onError(response)\n        },\n    },\n\n    \u002F\u002F 日本語化\n    ...ja\n}\n",[49,6474,6475,6482,6487,6498,6502,6507,6526,6530,6535,6546,6550,6555,6570,6574,6579,6586,6591,6600,6605,6616,6620,6625,6640,6644,6649,6660,6664,6669,6689,6693,6697,6701,6706,6713],{"__ignoreMap":47},[52,6476,6477,6480],{"class":54,"line":55},[52,6478,6479],{"class":360},"return",[52,6481,6100],{"class":58},[52,6483,6484],{"class":54,"line":83},[52,6485,6486],{"class":411},"    \u002F\u002F 複数ファイルのアップロードを許可するか\n",[52,6488,6489,6492,6494,6496],{"class":54,"line":115},[52,6490,6491],{"class":62},"    allowMultiple",[52,6493,373],{"class":58},[52,6495,1326],{"class":6245},[52,6497,384],{"class":58},[52,6499,6500],{"class":54,"line":142},[52,6501,341],{"emptyLinePlaceholder":340},[52,6503,6504],{"class":54,"line":169},[52,6505,6506],{"class":411},"    \u002F\u002F 許可するmime\n",[52,6508,6509,6512,6514,6516,6518,6520,6522,6524],{"class":54,"line":302},[52,6510,6511],{"class":58},"    '",[52,6513,6255],{"class":62},[52,6515,376],{"class":58},[52,6517,373],{"class":58},[52,6519,376],{"class":58},[52,6521,6264],{"class":75},[52,6523,376],{"class":58},[52,6525,384],{"class":58},[52,6527,6528],{"class":54,"line":308},[52,6529,341],{"emptyLinePlaceholder":340},[52,6531,6532],{"class":54,"line":318},[52,6533,6534],{"class":411},"    \u002F\u002F ブラウザにアップしたとき、すぐにサーバーにアップロードするか。デフォルトはTrueなので注意\n",[52,6536,6537,6540,6542,6544],{"class":54,"line":328},[52,6538,6539],{"class":62},"    instantUpload",[52,6541,373],{"class":58},[52,6543,1326],{"class":6245},[52,6545,384],{"class":58},[52,6547,6548],{"class":54,"line":337},[52,6549,341],{"emptyLinePlaceholder":340},[52,6551,6552],{"class":54,"line":344},[52,6553,6554],{"class":411},"    \u002F\u002F FilePondPluginImageCrop有効時のアスペクト比。\n",[52,6556,6557,6560,6562,6564,6566,6568],{"class":54,"line":354},[52,6558,6559],{"class":62},"    imageCropAspectRatio",[52,6561,373],{"class":58},[52,6563,376],{"class":58},[52,6565,6291],{"class":75},[52,6567,376],{"class":58},[52,6569,384],{"class":58},[52,6571,6572],{"class":54,"line":367},[52,6573,341],{"emptyLinePlaceholder":340},[52,6575,6576],{"class":54,"line":387},[52,6577,6578],{"class":411},"    \u002F\u002F アップロードする際のajaxの設定\n",[52,6580,6581,6584],{"class":54,"line":415},[52,6582,6583],{"class":62},"    server",[52,6585,924],{"class":58},[52,6587,6588],{"class":54,"line":427},[52,6589,6590],{"class":411},"        \u002F\u002F アップロードするときの設定\n",[52,6592,6593,6596,6598],{"class":54,"line":435},[52,6594,6595],{"class":62},"        process",[52,6597,373],{"class":58},[52,6599,6100],{"class":58},[52,6601,6602],{"class":54,"line":446},[52,6603,6604],{"class":411},"            \u002F\u002F アップロード先URL\n",[52,6606,6607,6610,6612,6614],{"class":54,"line":480},[52,6608,6609],{"class":62},"            url",[52,6611,373],{"class":58},[52,6613,6321],{"class":105},[52,6615,384],{"class":58},[52,6617,6618],{"class":54,"line":509},[52,6619,341],{"emptyLinePlaceholder":340},[52,6621,6622],{"class":54,"line":539},[52,6623,6624],{"class":411},"            \u002F\u002F アップロードのメソッド（processはデフォルトでPOST）\n",[52,6626,6627,6630,6632,6634,6636,6638],{"class":54,"line":547},[52,6628,6629],{"class":62},"            method",[52,6631,373],{"class":58},[52,6633,6067],{"class":58},[52,6635,6335],{"class":75},[52,6637,376],{"class":58},[52,6639,384],{"class":58},[52,6641,6642],{"class":54,"line":553},[52,6643,341],{"emptyLinePlaceholder":340},[52,6645,6646],{"class":54,"line":559},[52,6647,6648],{"class":411},"            \u002F\u002F withCredentialsを有効化\n",[52,6650,6651,6654,6656,6658],{"class":54,"line":564},[52,6652,6653],{"class":62},"            withCredentials",[52,6655,373],{"class":58},[52,6657,6349],{"class":6245},[52,6659,384],{"class":58},[52,6661,6662],{"class":54,"line":569},[52,6663,341],{"emptyLinePlaceholder":340},[52,6665,6666],{"class":54,"line":1106},[52,6667,6668],{"class":411},"            \u002F\u002F エラー時のコールバック\n",[52,6670,6671,6674,6676,6678,6680,6682,6684,6686],{"class":54,"line":1135},[52,6672,6673],{"class":418},"            onerror",[52,6675,6359],{"class":58},[52,6677,6362],{"class":986},[52,6679,938],{"class":58},[52,6681,6367],{"class":65},[52,6683,6370],{"class":58},[52,6685,6373],{"class":418},[52,6687,6688],{"class":105},"(response)\n",[52,6690,6691],{"class":54,"line":1164},[52,6692,6191],{"class":58},[52,6694,6695],{"class":54,"line":4},[52,6696,6151],{"class":58},[52,6698,6699],{"class":54,"line":1199},[52,6700,341],{"emptyLinePlaceholder":340},[52,6702,6703],{"class":54,"line":1204},[52,6704,6705],{"class":411},"    \u002F\u002F 日本語化\n",[52,6707,6708,6711],{"class":54,"line":1209},[52,6709,6710],{"class":58},"    ...",[52,6712,6397],{"class":105},[52,6714,6715],{"class":54,"line":1214},[52,6716,536],{"class":58},[13,6718,6719,6720,6723,6724,6726],{},"ひとまず上記の設定があり、サーバー側でURLが利用可能であれば一応アップロードが可能です。ただしこれだけではトリミングされません。（プレビューはトリミングされて表示されますが）\n",[49,6721,6722],{},"instantUpload","はデフォルトで",[49,6725,1322],{},"であり、トリミング前にアップロードされますので注意が必要です。",[17,6728,6730],{"id":6729},"filepondとvue-cropperへの連携","Filepondとvue-cropperへの連携",[13,6732,6733],{},"ではアップロードはできたのでcropperと連携しましょう。新しくライブラリをインストールします。",[42,6735,6738],{"className":6736,"code":6737,"language":452},[5819],"npm install filepond-plugin-image-edit filepond-plugin-image-transform vue-cropperjs --save\n",[49,6739,6737],{"__ignoreMap":47},[13,6741,6742,6743,6745],{},"cropperのVue版とfilepondの編集APIとトリミングしてくれるプラグインをインストールします。",[49,6744,5861],{},"も変更します。",[42,6747,6749],{"className":6471,"code":6748,"filename":5861,"language":1433,"meta":47,"style":47},"import vueFilePond from 'vue-filepond';\nimport FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type\u002Fdist\u002Ffilepond-plugin-file-validate-type.esm.js';\nimport FilePondPluginImagePreview from 'filepond-plugin-image-preview\u002Fdist\u002Ffilepond-plugin-image-preview.esm.js';\nimport FilePondPluginImageCrop from 'filepond-plugin-image-crop\u002Fdist\u002Ffilepond-plugin-image-crop.esm';\nimport FilePondPluginImageEdit from 'filepond-plugin-image-edit\u002Fdist\u002Ffilepond-plugin-image-edit.esm';　\u002F\u002F 追加\nimport FilePondPluginImageTransform from 'filepond-plugin-image-transform\u002Fdist\u002Ffilepond-plugin-image-transform.esm';　\u002F\u002F 追加\nconst FilePond = vueFilePond(FilePondPluginFileValidateType, FilePondPluginImagePreview,FilePondPluginImageCrop,FilePondPluginImageEdit,FilePondPluginImageTransform);\nVue.component(FilePond);\n",[49,6750,6751,6768,6786,6804,6822,6843,6863,6900],{"__ignoreMap":47},[52,6752,6753,6755,6758,6760,6762,6764,6766],{"class":54,"line":55},[52,6754,5939],{"class":360},[52,6756,6757],{"class":105}," vueFilePond ",[52,6759,6064],{"class":360},[52,6761,6067],{"class":58},[52,6763,5611],{"class":75},[52,6765,376],{"class":58},[52,6767,1007],{"class":58},[52,6769,6770,6772,6775,6777,6779,6782,6784],{"class":54,"line":83},[52,6771,5939],{"class":360},[52,6773,6774],{"class":105}," FilePondPluginFileValidateType ",[52,6776,6064],{"class":360},[52,6778,6067],{"class":58},[52,6780,6781],{"class":75},"filepond-plugin-file-validate-type\u002Fdist\u002Ffilepond-plugin-file-validate-type.esm.js",[52,6783,376],{"class":58},[52,6785,1007],{"class":58},[52,6787,6788,6790,6793,6795,6797,6800,6802],{"class":54,"line":115},[52,6789,5939],{"class":360},[52,6791,6792],{"class":105}," FilePondPluginImagePreview ",[52,6794,6064],{"class":360},[52,6796,6067],{"class":58},[52,6798,6799],{"class":75},"filepond-plugin-image-preview\u002Fdist\u002Ffilepond-plugin-image-preview.esm.js",[52,6801,376],{"class":58},[52,6803,1007],{"class":58},[52,6805,6806,6808,6811,6813,6815,6818,6820],{"class":54,"line":142},[52,6807,5939],{"class":360},[52,6809,6810],{"class":105}," FilePondPluginImageCrop ",[52,6812,6064],{"class":360},[52,6814,6067],{"class":58},[52,6816,6817],{"class":75},"filepond-plugin-image-crop\u002Fdist\u002Ffilepond-plugin-image-crop.esm",[52,6819,376],{"class":58},[52,6821,1007],{"class":58},[52,6823,6824,6826,6829,6831,6833,6836,6838,6840],{"class":54,"line":169},[52,6825,5939],{"class":360},[52,6827,6828],{"class":105}," FilePondPluginImageEdit ",[52,6830,6064],{"class":360},[52,6832,6067],{"class":58},[52,6834,6835],{"class":75},"filepond-plugin-image-edit\u002Fdist\u002Ffilepond-plugin-image-edit.esm",[52,6837,376],{"class":58},[52,6839,3077],{"class":58},[52,6841,6842],{"class":411},"　\u002F\u002F 追加\n",[52,6844,6845,6847,6850,6852,6854,6857,6859,6861],{"class":54,"line":302},[52,6846,5939],{"class":360},[52,6848,6849],{"class":105}," FilePondPluginImageTransform ",[52,6851,6064],{"class":360},[52,6853,6067],{"class":58},[52,6855,6856],{"class":75},"filepond-plugin-image-transform\u002Fdist\u002Ffilepond-plugin-image-transform.esm",[52,6858,376],{"class":58},[52,6860,3077],{"class":58},[52,6862,6842],{"class":411},[52,6864,6865,6868,6871,6873,6876,6879,6881,6884,6886,6888,6890,6893,6895,6898],{"class":54,"line":308},[52,6866,6867],{"class":65},"const",[52,6869,6870],{"class":105}," FilePond ",[52,6872,69],{"class":58},[52,6874,6875],{"class":418}," vueFilePond",[52,6877,6878],{"class":105},"(FilePondPluginFileValidateType",[52,6880,408],{"class":58},[52,6882,6883],{"class":105}," FilePondPluginImagePreview",[52,6885,408],{"class":58},[52,6887,5963],{"class":105},[52,6889,408],{"class":58},[52,6891,6892],{"class":105},"FilePondPluginImageEdit",[52,6894,408],{"class":58},[52,6896,6897],{"class":105},"FilePondPluginImageTransform)",[52,6899,1007],{"class":58},[52,6901,6902,6905,6907,6910,6913],{"class":54,"line":318},[52,6903,6904],{"class":105},"Vue",[52,6906,957],{"class":58},[52,6908,6909],{"class":418},"component",[52,6911,6912],{"class":105},"(FilePond)",[52,6914,1007],{"class":58},[1518,6916,6918],{"id":6917},"編集uiの表示","編集UIの表示",[13,6920,6921],{},"まず編集ボタンを表示できる様にします。先程のfilepondの設定に編集用コールバックの設定を追加します。",[42,6923,6925],{"className":202,"code":6924,"filename":5974,"language":204,"meta":47,"style":47},"\u003Cscript>\n\u002F\u002F 部分省略\nexport default {\n    name:'fileuploader',\n    data(){\n        return{\n            files:[],\n        }\n    },\n    methods:{\n        \u002F\u002F ...\n    },\n    computed:{\n        \u002F\u002F 追加\n        editor(){\n            return {\n                \u002F\u002F Called by FilePond to edit the image\n                \u002F\u002F - should open your image editor\n                \u002F\u002F - receives file object and image edit instructions\n                open: (file, instructions) => {\n                \u002F\u002F open editor here\n\n                },\n\n                \u002F\u002F Callback set by FilePond\n                \u002F\u002F - should be called by the editor when user confirms editing\n                \u002F\u002F - should receive output object, resulting edit information\n                onconfirm: (data) => {},\n\n                \u002F\u002F Callback set by FilePond\n                \u002F\u002F - should be called by the editor when user cancels editing\n                oncancel: () => {},\n\n                \u002F\u002F Callback set by FilePond\n                \u002F\u002F - should be called by the editor when user closes the editor\n                onclose: () => {\n                },\n            }\n        },\n        attrs(){\n            let apiserver = '\u002Fapi\u002Fv1\u002Ffile\u002Favater';\n            return {\n                imageEditEditor:this.editor, \u002F\u002F 追加\n                allowMultiple:false,\n                'accepted-file-types':'image\u002Fjpeg,image\u002Fpng,image\u002Fgif',\n                instantUpload:false,\n                imageCropAspectRatio:'1:1',\n                server:{\n                    process: {\n                        url:apiserver,\n                        method: 'POST',\n                        withCredentials: true,\n                        onerror:(response) => this.onError(response)\n                    },\n                },\n                ...ja\n            }\n        },\n        \u002F\u002F ...\n    },\n}\n\u003C\u002Fscript>\n",[49,6926,6927,6935,6940,6948,6962,6968,6974,6984,6988,6992,6998,7003,7007,7013,7018,7025,7031,7036,7041,7046,7069,7074,7078,7082,7086,7091,7096,7101,7120,7124,7128,7133,7147,7151,7155,7160,7173,7177,7181,7185,7191,7207,7213,7229,7239,7257,7267,7281,7287,7295,7305,7319,7329,7351,7355,7359,7365,7369,7373,7377,7381,7385],{"__ignoreMap":47},[52,6928,6929,6931,6933],{"class":54,"line":55},[52,6930,59],{"class":58},[52,6932,349],{"class":62},[52,6934,80],{"class":58},[52,6936,6937],{"class":54,"line":83},[52,6938,6939],{"class":411},"\u002F\u002F 部分省略\n",[52,6941,6942,6944,6946],{"class":54,"line":115},[52,6943,357],{"class":360},[52,6945,361],{"class":360},[52,6947,6100],{"class":58},[52,6949,6950,6952,6954,6956,6958,6960],{"class":54,"line":142},[52,6951,6105],{"class":62},[52,6953,373],{"class":58},[52,6955,376],{"class":58},[52,6957,6112],{"class":75},[52,6959,376],{"class":58},[52,6961,384],{"class":58},[52,6963,6964,6966],{"class":54,"line":169},[52,6965,6121],{"class":62},[52,6967,1838],{"class":58},[52,6969,6970,6972],{"class":54,"line":302},[52,6971,6128],{"class":360},[52,6973,364],{"class":58},[52,6975,6976,6978,6980,6982],{"class":54,"line":308},[52,6977,6135],{"class":62},[52,6979,373],{"class":58},[52,6981,6140],{"class":62},[52,6983,384],{"class":58},[52,6985,6986],{"class":54,"line":318},[52,6987,4814],{"class":58},[52,6989,6990],{"class":54,"line":328},[52,6991,6151],{"class":58},[52,6993,6994,6996],{"class":54,"line":337},[52,6995,6156],{"class":62},[52,6997,924],{"class":58},[52,6999,7000],{"class":54,"line":344},[52,7001,7002],{"class":411},"        \u002F\u002F ...\n",[52,7004,7005],{"class":54,"line":354},[52,7006,6151],{"class":58},[52,7008,7009,7011],{"class":54,"line":367},[52,7010,6200],{"class":62},[52,7012,924],{"class":58},[52,7014,7015],{"class":54,"line":387},[52,7016,7017],{"class":411},"        \u002F\u002F 追加\n",[52,7019,7020,7023],{"class":54,"line":415},[52,7021,7022],{"class":62},"        editor",[52,7024,1838],{"class":58},[52,7026,7027,7029],{"class":54,"line":427},[52,7028,6233],{"class":360},[52,7030,6100],{"class":58},[52,7032,7033],{"class":54,"line":435},[52,7034,7035],{"class":411},"                \u002F\u002F Called by FilePond to edit the image\n",[52,7037,7038],{"class":54,"line":446},[52,7039,7040],{"class":411},"                \u002F\u002F - should open your image editor\n",[52,7042,7043],{"class":54,"line":480},[52,7044,7045],{"class":411},"                \u002F\u002F - receives file object and image edit instructions\n",[52,7047,7048,7051,7053,7055,7058,7060,7063,7065,7067],{"class":54,"line":509},[52,7049,7050],{"class":418},"                open",[52,7052,373],{"class":58},[52,7054,1913],{"class":58},[52,7056,7057],{"class":986},"file",[52,7059,408],{"class":58},[52,7061,7062],{"class":986}," instructions",[52,7064,938],{"class":58},[52,7066,6367],{"class":65},[52,7068,6100],{"class":58},[52,7070,7071],{"class":54,"line":539},[52,7072,7073],{"class":411},"                \u002F\u002F open editor here\n",[52,7075,7076],{"class":54,"line":547},[52,7077,341],{"emptyLinePlaceholder":340},[52,7079,7080],{"class":54,"line":553},[52,7081,6389],{"class":58},[52,7083,7084],{"class":54,"line":559},[52,7085,341],{"emptyLinePlaceholder":340},[52,7087,7088],{"class":54,"line":564},[52,7089,7090],{"class":411},"                \u002F\u002F Callback set by FilePond\n",[52,7092,7093],{"class":54,"line":569},[52,7094,7095],{"class":411},"                \u002F\u002F - should be called by the editor when user confirms editing\n",[52,7097,7098],{"class":54,"line":1106},[52,7099,7100],{"class":411},"                \u002F\u002F - should receive output object, resulting edit information\n",[52,7102,7103,7106,7108,7110,7113,7115,7117],{"class":54,"line":1135},[52,7104,7105],{"class":418},"                onconfirm",[52,7107,373],{"class":58},[52,7109,1913],{"class":58},[52,7111,7112],{"class":986},"data",[52,7114,938],{"class":58},[52,7116,6367],{"class":65},[52,7118,7119],{"class":58}," {},\n",[52,7121,7122],{"class":54,"line":1164},[52,7123,341],{"emptyLinePlaceholder":340},[52,7125,7126],{"class":54,"line":4},[52,7127,7090],{"class":411},[52,7129,7130],{"class":54,"line":1199},[52,7131,7132],{"class":411},"                \u002F\u002F - should be called by the editor when user cancels editing\n",[52,7134,7135,7138,7140,7143,7145],{"class":54,"line":1204},[52,7136,7137],{"class":418},"                oncancel",[52,7139,373],{"class":58},[52,7141,7142],{"class":58}," ()",[52,7144,6367],{"class":65},[52,7146,7119],{"class":58},[52,7148,7149],{"class":54,"line":1209},[52,7150,341],{"emptyLinePlaceholder":340},[52,7152,7153],{"class":54,"line":1214},[52,7154,7090],{"class":411},[52,7156,7157],{"class":54,"line":1219},[52,7158,7159],{"class":411},"                \u002F\u002F - should be called by the editor when user closes the editor\n",[52,7161,7162,7165,7167,7169,7171],{"class":54,"line":3216},[52,7163,7164],{"class":418},"                onclose",[52,7166,373],{"class":58},[52,7168,7142],{"class":58},[52,7170,6367],{"class":65},[52,7172,6100],{"class":58},[52,7174,7175],{"class":54,"line":3237},[52,7176,6389],{"class":58},[52,7178,7179],{"class":54,"line":3251},[52,7180,1665],{"class":58},[52,7182,7183],{"class":54,"line":3256},[52,7184,6191],{"class":58},[52,7186,7187,7189],{"class":54,"line":3281},[52,7188,6207],{"class":62},[52,7190,1838],{"class":58},[52,7192,7193,7195,7197,7199,7201,7203,7205],{"class":54,"line":3295},[52,7194,6214],{"class":65},[52,7196,6217],{"class":105},[52,7198,951],{"class":58},[52,7200,6067],{"class":58},[52,7202,6224],{"class":75},[52,7204,376],{"class":58},[52,7206,1007],{"class":58},[52,7208,7209,7211],{"class":54,"line":4922},[52,7210,6233],{"class":360},[52,7212,6100],{"class":58},[52,7214,7215,7218,7221,7224,7226],{"class":54,"line":4943},[52,7216,7217],{"class":62},"                imageEditEditor",[52,7219,7220],{"class":58},":this.",[52,7222,7223],{"class":105},"editor",[52,7225,408],{"class":58},[52,7227,7228],{"class":411}," \u002F\u002F 追加\n",[52,7230,7231,7233,7235,7237],{"class":54,"line":4961},[52,7232,6240],{"class":62},[52,7234,373],{"class":58},[52,7236,1326],{"class":6245},[52,7238,384],{"class":58},[52,7240,7241,7243,7245,7247,7249,7251,7253,7255],{"class":54,"line":4978},[52,7242,6252],{"class":58},[52,7244,6255],{"class":62},[52,7246,376],{"class":58},[52,7248,373],{"class":58},[52,7250,376],{"class":58},[52,7252,6264],{"class":75},[52,7254,376],{"class":58},[52,7256,384],{"class":58},[52,7258,7259,7261,7263,7265],{"class":54,"line":4983},[52,7260,6273],{"class":62},[52,7262,373],{"class":58},[52,7264,1326],{"class":6245},[52,7266,384],{"class":58},[52,7268,7269,7271,7273,7275,7277,7279],{"class":54,"line":5004},[52,7270,6284],{"class":62},[52,7272,373],{"class":58},[52,7274,376],{"class":58},[52,7276,6291],{"class":75},[52,7278,376],{"class":58},[52,7280,384],{"class":58},[52,7282,7283,7285],{"class":54,"line":5053},[52,7284,6300],{"class":62},[52,7286,924],{"class":58},[52,7288,7289,7291,7293],{"class":54,"line":5058},[52,7290,6307],{"class":62},[52,7292,373],{"class":58},[52,7294,6100],{"class":58},[52,7296,7297,7299,7301,7303],{"class":54,"line":5063},[52,7298,6316],{"class":62},[52,7300,373],{"class":58},[52,7302,6321],{"class":105},[52,7304,384],{"class":58},[52,7306,7307,7309,7311,7313,7315,7317],{"class":54,"line":5096},[52,7308,6328],{"class":62},[52,7310,373],{"class":58},[52,7312,6067],{"class":58},[52,7314,6335],{"class":75},[52,7316,376],{"class":58},[52,7318,384],{"class":58},[52,7320,7321,7323,7325,7327],{"class":54,"line":5109},[52,7322,6344],{"class":62},[52,7324,373],{"class":58},[52,7326,6349],{"class":6245},[52,7328,384],{"class":58},[52,7330,7331,7333,7335,7337,7339,7341,7343,7345,7347,7349],{"class":54,"line":5128},[52,7332,6356],{"class":418},[52,7334,6359],{"class":58},[52,7336,6362],{"class":986},[52,7338,938],{"class":58},[52,7340,6367],{"class":65},[52,7342,6370],{"class":58},[52,7344,6373],{"class":418},[52,7346,932],{"class":62},[52,7348,6362],{"class":105},[52,7350,1015],{"class":62},[52,7352,7353],{"class":54,"line":5141},[52,7354,6384],{"class":58},[52,7356,7357],{"class":54,"line":5162},[52,7358,6389],{"class":58},[52,7360,7361,7363],{"class":54,"line":5167},[52,7362,6394],{"class":58},[52,7364,6397],{"class":105},[52,7366,7367],{"class":54,"line":5203},[52,7368,1665],{"class":58},[52,7370,7371],{"class":54,"line":5253},[52,7372,6191],{"class":58},[52,7374,7375],{"class":54,"line":5279},[52,7376,7002],{"class":411},[52,7378,7379],{"class":54,"line":5284},[52,7380,6151],{"class":58},[52,7382,7383],{"class":54,"line":5289},[52,7384,536],{"class":58},[52,7386,7387,7389,7391],{"class":54,"line":5310},[52,7388,108],{"class":58},[52,7390,349],{"class":62},[52,7392,80],{"class":58},[13,7394,7395,7397,7398,7401,7402,7404,7405,7407,7408,7410,7411],{},[49,7396,7223],{},"というプロパティーを追加しました。そしてfilepondの設定に",[49,7399,7400],{},"imageEditEditor","を追加して、",[49,7403,7223],{},"を参照する様にします。この",[49,7406,7400],{},"がないと編集UIが出現しません。",[49,7409,7223],{},"の雛形は",[1450,7412,7415],{"href":7413,"rel":7414},"https:\u002F\u002Fpqina.nl\u002Ffilepond\u002Fdocs\u002Fapi\u002Fplugins\u002Fimage-edit#integrating-other-editors",[1454],"本家ドキュメントから参照できます。",[13,7417,7418,7420],{},[49,7419,7400],{},"をしますと下図の赤矢印の様に編集ボタンが出現します。",[729,7422],{":src":7423,":width":5554,":center":1322},"'filepond-crop-vue\u002Feditui.png'",[13,7425,7426,7427,7429,7430,7433,7434,7437],{},"この編集ボタンをクリックした際は",[49,7428,7223],{},"の",[49,7431,7432],{},"open()=>{}","コールバックが実行されます。この際に",[49,7435,7436],{},"cropper","に画像データを渡し、起動する様にすればトリミングUIを表示できる様になります。",[1518,7439,7440],{"id":7440},"cropperのレンダリング",[13,7442,7443],{},"設定の前にfilepondのコンポーネントを表示する様にします。",[42,7445,7447],{"className":202,"code":7446,"filename":5974,"language":204,"meta":47,"style":47},"\u003Ctemplate>\n\u003Cdiv>\n    \u003Cfile-pond v-bind=\"attrs\"\u002F>\n    \u003Cvue-cropper\n        ref=\"cropper\"\n        :src=\"imgSrc\"\n        :aspect-ratio=\"1 \u002F 1\"\n        :view-mode=\"1\"\n    >\n    \u003C\u002Fvue-cropper>\n    \u003Cbutton variant=\"primary\" @click=\"crop\">crop\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\u003Cscript>\nimport ja from 'filepond\u002Flocale\u002Fja-ja';\nimport VueCropper from 'vue-cropperjs';　 \u002F\u002F 追加\nexport default {\n    name:'fileuploader',\n    data(){\n        return{\n            files:[],\n            imgSrc:'', \u002F\u002F 追加\n        }\n    },\n    methods:{\n        onError(res){\n            \u002F\u002F ...\n        },\n        crop(){\n            \u002F\u002F 追加\n            \u002F\u002F とりあえずこのままにしておいてください。\n        }\n    },\n    computed:{\n        \u002F\u002F ...\n    },\n    components:{\n        VueCropper \u002F\u002F 追加\n    }\n}\n\u003C\u002Fscript>\n",[49,7448,7449,7457,7465,7483,7490,7503,7517,7531,7544,7549,7558,7599,7607,7615,7623,7639,7658,7666,7680,7686,7692,7702,7716,7720,7724,7730,7740,7745,7749,7756,7761,7766,7770,7774,7780,7784,7788,7795,7803,7807,7811],{"__ignoreMap":47},[52,7450,7451,7453,7455],{"class":54,"line":55},[52,7452,59],{"class":58},[52,7454,213],{"class":62},[52,7456,80],{"class":58},[52,7458,7459,7461,7463],{"class":54,"line":83},[52,7460,59],{"class":58},[52,7462,5995],{"class":62},[52,7464,80],{"class":58},[52,7466,7467,7469,7471,7473,7475,7477,7479,7481],{"class":54,"line":115},[52,7468,1575],{"class":58},[52,7470,6004],{"class":62},[52,7472,6018],{"class":65},[52,7474,69],{"class":58},[52,7476,72],{"class":58},[52,7478,6025],{"class":75},[52,7480,72],{"class":58},[52,7482,6030],{"class":58},[52,7484,7485,7487],{"class":54,"line":142},[52,7486,1575],{"class":58},[52,7488,7489],{"class":62},"vue-cropper\n",[52,7491,7492,7495,7497,7499,7501],{"class":54,"line":169},[52,7493,7494],{"class":65},"        ref",[52,7496,69],{"class":58},[52,7498,72],{"class":58},[52,7500,7436],{"class":75},[52,7502,266],{"class":58},[52,7504,7505,7508,7510,7512,7515],{"class":54,"line":302},[52,7506,7507],{"class":65},"        :src",[52,7509,69],{"class":58},[52,7511,72],{"class":58},[52,7513,7514],{"class":75},"imgSrc",[52,7516,266],{"class":58},[52,7518,7519,7522,7524,7526,7529],{"class":54,"line":308},[52,7520,7521],{"class":65},"        :aspect-ratio",[52,7523,69],{"class":58},[52,7525,72],{"class":58},[52,7527,7528],{"class":75},"1 \u002F 1",[52,7530,266],{"class":58},[52,7532,7533,7536,7538,7540,7542],{"class":54,"line":318},[52,7534,7535],{"class":65},"        :view-mode",[52,7537,69],{"class":58},[52,7539,72],{"class":58},[52,7541,31],{"class":75},[52,7543,266],{"class":58},[52,7545,7546],{"class":54,"line":328},[52,7547,7548],{"class":58},"    >\n",[52,7550,7551,7553,7556],{"class":54,"line":337},[52,7552,1717],{"class":58},[52,7554,7555],{"class":62},"vue-cropper",[52,7557,80],{"class":58},[52,7559,7560,7562,7565,7568,7570,7572,7575,7577,7580,7582,7584,7587,7589,7591,7593,7595,7597],{"class":54,"line":344},[52,7561,1575],{"class":58},[52,7563,7564],{"class":62},"button",[52,7566,7567],{"class":65}," variant",[52,7569,69],{"class":58},[52,7571,72],{"class":58},[52,7573,7574],{"class":75},"primary",[52,7576,72],{"class":58},[52,7578,7579],{"class":65}," @click",[52,7581,69],{"class":58},[52,7583,72],{"class":58},[52,7585,7586],{"class":75},"crop",[52,7588,72],{"class":58},[52,7590,102],{"class":58},[52,7592,7586],{"class":105},[52,7594,108],{"class":58},[52,7596,7564],{"class":62},[52,7598,80],{"class":58},[52,7600,7601,7603,7605],{"class":54,"line":354},[52,7602,108],{"class":58},[52,7604,5995],{"class":62},[52,7606,80],{"class":58},[52,7608,7609,7611,7613],{"class":54,"line":367},[52,7610,108],{"class":58},[52,7612,213],{"class":62},[52,7614,80],{"class":58},[52,7616,7617,7619,7621],{"class":54,"line":387},[52,7618,59],{"class":58},[52,7620,349],{"class":62},[52,7622,80],{"class":58},[52,7624,7625,7627,7629,7631,7633,7635,7637],{"class":54,"line":415},[52,7626,5939],{"class":360},[52,7628,6061],{"class":105},[52,7630,6064],{"class":360},[52,7632,6067],{"class":58},[52,7634,6070],{"class":75},[52,7636,376],{"class":58},[52,7638,1007],{"class":58},[52,7640,7641,7643,7645,7647,7649,7651,7653,7655],{"class":54,"line":427},[52,7642,5939],{"class":360},[52,7644,6081],{"class":105},[52,7646,6064],{"class":360},[52,7648,6067],{"class":58},[52,7650,5769],{"class":75},[52,7652,376],{"class":58},[52,7654,3077],{"class":58},[52,7656,7657],{"class":411},"　 \u002F\u002F 追加\n",[52,7659,7660,7662,7664],{"class":54,"line":435},[52,7661,357],{"class":360},[52,7663,361],{"class":360},[52,7665,6100],{"class":58},[52,7667,7668,7670,7672,7674,7676,7678],{"class":54,"line":446},[52,7669,6105],{"class":62},[52,7671,373],{"class":58},[52,7673,376],{"class":58},[52,7675,6112],{"class":75},[52,7677,376],{"class":58},[52,7679,384],{"class":58},[52,7681,7682,7684],{"class":54,"line":480},[52,7683,6121],{"class":62},[52,7685,1838],{"class":58},[52,7687,7688,7690],{"class":54,"line":509},[52,7689,6128],{"class":360},[52,7691,364],{"class":58},[52,7693,7694,7696,7698,7700],{"class":54,"line":539},[52,7695,6135],{"class":62},[52,7697,373],{"class":58},[52,7699,6140],{"class":62},[52,7701,384],{"class":58},[52,7703,7704,7707,7709,7712,7714],{"class":54,"line":547},[52,7705,7706],{"class":62},"            imgSrc",[52,7708,373],{"class":58},[52,7710,7711],{"class":58},"''",[52,7713,408],{"class":58},[52,7715,7228],{"class":411},[52,7717,7718],{"class":54,"line":553},[52,7719,4814],{"class":58},[52,7721,7722],{"class":54,"line":559},[52,7723,6151],{"class":58},[52,7725,7726,7728],{"class":54,"line":564},[52,7727,6156],{"class":62},[52,7729,924],{"class":58},[52,7731,7732,7734,7736,7738],{"class":54,"line":569},[52,7733,6163],{"class":62},[52,7735,932],{"class":58},[52,7737,6168],{"class":986},[52,7739,2855],{"class":58},[52,7741,7742],{"class":54,"line":1106},[52,7743,7744],{"class":411},"            \u002F\u002F ...\n",[52,7746,7747],{"class":54,"line":1135},[52,7748,6191],{"class":58},[52,7750,7751,7754],{"class":54,"line":1164},[52,7752,7753],{"class":62},"        crop",[52,7755,1838],{"class":58},[52,7757,7758],{"class":54,"line":4},[52,7759,7760],{"class":411},"            \u002F\u002F 追加\n",[52,7762,7763],{"class":54,"line":1199},[52,7764,7765],{"class":411},"            \u002F\u002F とりあえずこのままにしておいてください。\n",[52,7767,7768],{"class":54,"line":1204},[52,7769,4814],{"class":58},[52,7771,7772],{"class":54,"line":1209},[52,7773,6151],{"class":58},[52,7775,7776,7778],{"class":54,"line":1214},[52,7777,6200],{"class":62},[52,7779,924],{"class":58},[52,7781,7782],{"class":54,"line":1219},[52,7783,7002],{"class":411},[52,7785,7786],{"class":54,"line":3216},[52,7787,6151],{"class":58},[52,7789,7790,7793],{"class":54,"line":3237},[52,7791,7792],{"class":62},"    components",[52,7794,924],{"class":58},[52,7796,7797,7800],{"class":54,"line":3251},[52,7798,7799],{"class":105},"        VueCropper ",[52,7801,7802],{"class":411},"\u002F\u002F 追加\n",[52,7804,7805],{"class":54,"line":3256},[52,7806,2110],{"class":58},[52,7808,7809],{"class":54,"line":3281},[52,7810,536],{"class":58},[52,7812,7813,7815,7817],{"class":54,"line":3295},[52,7814,108],{"class":58},[52,7816,349],{"class":62},[52,7818,80],{"class":58},[13,7820,7821,7824,7825,7827,7828,7830],{},[49,7822,7823],{},"import VueCropper from 'vue-cropperjs';","にてvue-cropperのコンポーネントを配置します。",[49,7826,7555],{},"はトリミングのUIを提供し、",[49,7829,7564],{},"はトリミングの実行を行ってfilepondにデータを渡すメソッドに連絡します。\nひとまず以下の様に表示されると思います。",[729,7832],{":src":7833,":width":5554,":center":1322},"'filepond-crop-vue\u002Fcropper-init.png'",[13,7835,7836],{},"なおvue-cropperのコンポーネントに設定した項目は以下の通りです。",[1464,7838,7839,7845],{},[1467,7840,7841,7844],{},[49,7842,7843],{},"aspect-ratio",":トリミングUIのアスペクト比です。",[1467,7846,7847,7850],{},[49,7848,7849],{},"view-mode",":ビューモードと呼ばれるトリミングの選択範囲を制御できます。デフォルトは0であり制限がなく、透明の領域までトリミングできてしまします。詳しくはそれぞれ設定してみて挙動を確認してください。",[1518,7852,7853],{"id":7853},"filepondからcropperへ画像を渡す",[13,7855,7856,7857,7859,7860,7863],{},"それでは編集ボタンを押したらvue-cropperへ画像を渡す様にしましょう。実際のサービスではモーダルなどを出したりした方がいいかもしれませんが、面倒なので今はそのまま表示します。",[49,7858,7223],{},"をの",[49,7861,7862],{},"open","コールバックを編集します。",[42,7865,7867],{"className":6471,"code":7866,"language":1433,"meta":47,"style":47},"editor(){\n    return {\n        \u002F\u002F Called by FilePond to edit the image\n        \u002F\u002F - should open your image editor\n        \u002F\u002F - receives file object and image edit instructions\n        open: (file, instructions) => {\n        \u002F\u002F open editor here\n            const objectURL = URL.createObjectURL(file)\n            this.imgSet =objectURL;\n            this.$refs.cropper.replace(objectURL);\n        },\n},\n",[49,7868,7869,7877,7884,7889,7894,7899,7920,7925,7949,7964,7988,7992],{"__ignoreMap":47},[52,7870,7871,7873,7875],{"class":54,"line":55},[52,7872,7223],{"class":418},[52,7874,422],{"class":105},[52,7876,364],{"class":58},[52,7878,7879,7882],{"class":54,"line":83},[52,7880,7881],{"class":360},"    return",[52,7883,6100],{"class":58},[52,7885,7886],{"class":54,"line":115},[52,7887,7888],{"class":411},"        \u002F\u002F Called by FilePond to edit the image\n",[52,7890,7891],{"class":54,"line":142},[52,7892,7893],{"class":411},"        \u002F\u002F - should open your image editor\n",[52,7895,7896],{"class":54,"line":169},[52,7897,7898],{"class":411},"        \u002F\u002F - receives file object and image edit instructions\n",[52,7900,7901,7904,7906,7908,7910,7912,7914,7916,7918],{"class":54,"line":302},[52,7902,7903],{"class":418},"        open",[52,7905,373],{"class":58},[52,7907,1913],{"class":58},[52,7909,7057],{"class":986},[52,7911,408],{"class":58},[52,7913,7062],{"class":986},[52,7915,938],{"class":58},[52,7917,6367],{"class":65},[52,7919,6100],{"class":58},[52,7921,7922],{"class":54,"line":308},[52,7923,7924],{"class":411},"        \u002F\u002F open editor here\n",[52,7926,7927,7930,7933,7935,7938,7940,7943,7945,7947],{"class":54,"line":318},[52,7928,7929],{"class":65},"            const",[52,7931,7932],{"class":105}," objectURL",[52,7934,951],{"class":58},[52,7936,7937],{"class":105}," URL",[52,7939,957],{"class":58},[52,7941,7942],{"class":418},"createObjectURL",[52,7944,932],{"class":62},[52,7946,7057],{"class":105},[52,7948,1015],{"class":62},[52,7950,7951,7954,7957,7959,7962],{"class":54,"line":328},[52,7952,7953],{"class":58},"            this.",[52,7955,7956],{"class":105},"imgSet",[52,7958,951],{"class":58},[52,7960,7961],{"class":105},"objectURL",[52,7963,1007],{"class":58},[52,7965,7966,7968,7971,7973,7975,7977,7980,7982,7984,7986],{"class":54,"line":337},[52,7967,7953],{"class":58},[52,7969,7970],{"class":105},"$refs",[52,7972,957],{"class":58},[52,7974,7436],{"class":105},[52,7976,957],{"class":58},[52,7978,7979],{"class":418},"replace",[52,7981,932],{"class":62},[52,7983,7961],{"class":105},[52,7985,938],{"class":62},[52,7987,1007],{"class":58},[52,7989,7990],{"class":54,"line":344},[52,7991,6191],{"class":58},[52,7993,7994],{"class":54,"line":354},[52,7995,477],{"class":58},[13,7997,7998,8000,8001,8003,8004,8007,8008,8010,8011,8014],{},[49,7999,7862],{},"コールバックは２つの引数があり、",[49,8002,7057],{},"はアップロードされたFileオブジェクトです。instructionsは編集する前のfilepondの編集情報です。fileオブジェクトから",[49,8005,8006],{},"URL.createObjectURL(file)","を使用することでブラウザアップロード（filepond）された画像をURLで参照できる様になります。そして",[49,8009,7514],{},"に代入し、そして",[49,8012,8013],{},"cropper.replace(objectURL);","を使用してトリミングの画像を差し替えます。",[13,8016,8017],{},"上記の様に編集して、編集ボタンを押すとトリミングUIが表示される様になり、任意のトリミング位置を設定できる様になります。",[729,8019],{":src":8020,":width":5554,":center":1322},"'filepond-crop-vue\u002Fcroppable.png'",[1518,8022,8023],{"id":8023},"cropperからfilepondへ情報を渡す",[13,8025,8026],{},"トリミングUIでは任意の範囲と位置を設定できます。トリミングした画像をfilepondに適用するためには、vue-cropperで選択したトリミングの位置や範囲などのオブジェクトをfilepondに渡すことで実現できます。（cropperで画像を加工するわけではない！）",[13,8028,8029,8030,7429,8032,8035],{},"データを渡すときは",[49,8031,7223],{},[49,8033,8034],{},"onconfirm","コールバックを利用します。vue-cropperでトリミングした際にコールバックを呼び出します。",[42,8037,8039],{"className":202,"code":8038,"filename":5974,"language":204,"meta":47,"style":47},"\u003Ctemplate>\n\u003Cdiv>\n    \u003Cfile-pond v-bind=\"attrs\"\u002F>\n    \u003Cvue-cropper\n        ref=\"cropper\"\n        :src=\"imgSrc\"\n        :aspect-ratio=\"1 \u002F 1\"\n        :view-mode=\"1\"\n    >\n    \u003C\u002Fvue-cropper>\n    \u003Cbutton variant=\"primary\" @click=\"crop\">crop\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\u003Cscript>\nimport ja from 'filepond\u002Flocale\u002Fja-ja';\nimport VueCropper from 'vue-cropperjs';　 \u002F\u002F 追加\nexport default {\n    name:'fileuploader',\n    data(){\n        return{\n            files:[],\n            imgSrc:'', \u002F\u002F 追加\n        }\n    },\n    methods:{\n        onError(res){\n            \u002F\u002F ...\n        },\n        crop(){\n            \u002F\u002F 追加\n            this.editor.onconfirm()\n        }\n    },\n    computed:{\n        editor(){\n            return{\n                \u002F\u002F ...\n                onconfirm: (data) => {},\n                \u002F\u002F ...\n            }\n        }\n    },\n    components:{\n        VueCropper \u002F\u002F 追加\n    }\n}\n\u003C\u002Fscript>\n",[49,8040,8041,8049,8057,8075,8081,8093,8105,8117,8129,8133,8141,8177,8185,8193,8201,8217,8235,8243,8257,8263,8269,8279,8291,8295,8299,8305,8315,8319,8323,8329,8333,8346,8350,8354,8360,8366,8372,8377,8393,8397,8401,8405,8409,8415,8421,8425,8429],{"__ignoreMap":47},[52,8042,8043,8045,8047],{"class":54,"line":55},[52,8044,59],{"class":58},[52,8046,213],{"class":62},[52,8048,80],{"class":58},[52,8050,8051,8053,8055],{"class":54,"line":83},[52,8052,59],{"class":58},[52,8054,5995],{"class":62},[52,8056,80],{"class":58},[52,8058,8059,8061,8063,8065,8067,8069,8071,8073],{"class":54,"line":115},[52,8060,1575],{"class":58},[52,8062,6004],{"class":62},[52,8064,6018],{"class":65},[52,8066,69],{"class":58},[52,8068,72],{"class":58},[52,8070,6025],{"class":75},[52,8072,72],{"class":58},[52,8074,6030],{"class":58},[52,8076,8077,8079],{"class":54,"line":142},[52,8078,1575],{"class":58},[52,8080,7489],{"class":62},[52,8082,8083,8085,8087,8089,8091],{"class":54,"line":169},[52,8084,7494],{"class":65},[52,8086,69],{"class":58},[52,8088,72],{"class":58},[52,8090,7436],{"class":75},[52,8092,266],{"class":58},[52,8094,8095,8097,8099,8101,8103],{"class":54,"line":302},[52,8096,7507],{"class":65},[52,8098,69],{"class":58},[52,8100,72],{"class":58},[52,8102,7514],{"class":75},[52,8104,266],{"class":58},[52,8106,8107,8109,8111,8113,8115],{"class":54,"line":308},[52,8108,7521],{"class":65},[52,8110,69],{"class":58},[52,8112,72],{"class":58},[52,8114,7528],{"class":75},[52,8116,266],{"class":58},[52,8118,8119,8121,8123,8125,8127],{"class":54,"line":318},[52,8120,7535],{"class":65},[52,8122,69],{"class":58},[52,8124,72],{"class":58},[52,8126,31],{"class":75},[52,8128,266],{"class":58},[52,8130,8131],{"class":54,"line":328},[52,8132,7548],{"class":58},[52,8134,8135,8137,8139],{"class":54,"line":337},[52,8136,1717],{"class":58},[52,8138,7555],{"class":62},[52,8140,80],{"class":58},[52,8142,8143,8145,8147,8149,8151,8153,8155,8157,8159,8161,8163,8165,8167,8169,8171,8173,8175],{"class":54,"line":344},[52,8144,1575],{"class":58},[52,8146,7564],{"class":62},[52,8148,7567],{"class":65},[52,8150,69],{"class":58},[52,8152,72],{"class":58},[52,8154,7574],{"class":75},[52,8156,72],{"class":58},[52,8158,7579],{"class":65},[52,8160,69],{"class":58},[52,8162,72],{"class":58},[52,8164,7586],{"class":75},[52,8166,72],{"class":58},[52,8168,102],{"class":58},[52,8170,7586],{"class":105},[52,8172,108],{"class":58},[52,8174,7564],{"class":62},[52,8176,80],{"class":58},[52,8178,8179,8181,8183],{"class":54,"line":354},[52,8180,108],{"class":58},[52,8182,5995],{"class":62},[52,8184,80],{"class":58},[52,8186,8187,8189,8191],{"class":54,"line":367},[52,8188,108],{"class":58},[52,8190,213],{"class":62},[52,8192,80],{"class":58},[52,8194,8195,8197,8199],{"class":54,"line":387},[52,8196,59],{"class":58},[52,8198,349],{"class":62},[52,8200,80],{"class":58},[52,8202,8203,8205,8207,8209,8211,8213,8215],{"class":54,"line":415},[52,8204,5939],{"class":360},[52,8206,6061],{"class":105},[52,8208,6064],{"class":360},[52,8210,6067],{"class":58},[52,8212,6070],{"class":75},[52,8214,376],{"class":58},[52,8216,1007],{"class":58},[52,8218,8219,8221,8223,8225,8227,8229,8231,8233],{"class":54,"line":427},[52,8220,5939],{"class":360},[52,8222,6081],{"class":105},[52,8224,6064],{"class":360},[52,8226,6067],{"class":58},[52,8228,5769],{"class":75},[52,8230,376],{"class":58},[52,8232,3077],{"class":58},[52,8234,7657],{"class":411},[52,8236,8237,8239,8241],{"class":54,"line":435},[52,8238,357],{"class":360},[52,8240,361],{"class":360},[52,8242,6100],{"class":58},[52,8244,8245,8247,8249,8251,8253,8255],{"class":54,"line":446},[52,8246,6105],{"class":62},[52,8248,373],{"class":58},[52,8250,376],{"class":58},[52,8252,6112],{"class":75},[52,8254,376],{"class":58},[52,8256,384],{"class":58},[52,8258,8259,8261],{"class":54,"line":480},[52,8260,6121],{"class":62},[52,8262,1838],{"class":58},[52,8264,8265,8267],{"class":54,"line":509},[52,8266,6128],{"class":360},[52,8268,364],{"class":58},[52,8270,8271,8273,8275,8277],{"class":54,"line":539},[52,8272,6135],{"class":62},[52,8274,373],{"class":58},[52,8276,6140],{"class":62},[52,8278,384],{"class":58},[52,8280,8281,8283,8285,8287,8289],{"class":54,"line":547},[52,8282,7706],{"class":62},[52,8284,373],{"class":58},[52,8286,7711],{"class":58},[52,8288,408],{"class":58},[52,8290,7228],{"class":411},[52,8292,8293],{"class":54,"line":553},[52,8294,4814],{"class":58},[52,8296,8297],{"class":54,"line":559},[52,8298,6151],{"class":58},[52,8300,8301,8303],{"class":54,"line":564},[52,8302,6156],{"class":62},[52,8304,924],{"class":58},[52,8306,8307,8309,8311,8313],{"class":54,"line":569},[52,8308,6163],{"class":62},[52,8310,932],{"class":58},[52,8312,6168],{"class":986},[52,8314,2855],{"class":58},[52,8316,8317],{"class":54,"line":1106},[52,8318,7744],{"class":411},[52,8320,8321],{"class":54,"line":1135},[52,8322,6191],{"class":58},[52,8324,8325,8327],{"class":54,"line":1164},[52,8326,7753],{"class":62},[52,8328,1838],{"class":58},[52,8330,8331],{"class":54,"line":4},[52,8332,7760],{"class":411},[52,8334,8335,8337,8339,8341,8343],{"class":54,"line":1199},[52,8336,7953],{"class":58},[52,8338,7223],{"class":105},[52,8340,957],{"class":58},[52,8342,8034],{"class":418},[52,8344,8345],{"class":62},"()\n",[52,8347,8348],{"class":54,"line":1204},[52,8349,4814],{"class":58},[52,8351,8352],{"class":54,"line":1209},[52,8353,6151],{"class":58},[52,8355,8356,8358],{"class":54,"line":1214},[52,8357,6200],{"class":62},[52,8359,924],{"class":58},[52,8361,8362,8364],{"class":54,"line":1219},[52,8363,7022],{"class":62},[52,8365,1838],{"class":58},[52,8367,8368,8370],{"class":54,"line":3216},[52,8369,6233],{"class":360},[52,8371,364],{"class":58},[52,8373,8374],{"class":54,"line":3237},[52,8375,8376],{"class":411},"                \u002F\u002F ...\n",[52,8378,8379,8381,8383,8385,8387,8389,8391],{"class":54,"line":3251},[52,8380,7105],{"class":418},[52,8382,373],{"class":58},[52,8384,1913],{"class":58},[52,8386,7112],{"class":986},[52,8388,938],{"class":58},[52,8390,6367],{"class":65},[52,8392,7119],{"class":58},[52,8394,8395],{"class":54,"line":3256},[52,8396,8376],{"class":411},[52,8398,8399],{"class":54,"line":3281},[52,8400,1665],{"class":58},[52,8402,8403],{"class":54,"line":3295},[52,8404,4814],{"class":58},[52,8406,8407],{"class":54,"line":4922},[52,8408,6151],{"class":58},[52,8410,8411,8413],{"class":54,"line":4943},[52,8412,7792],{"class":62},[52,8414,924],{"class":58},[52,8416,8417,8419],{"class":54,"line":4961},[52,8418,7799],{"class":105},[52,8420,7802],{"class":411},[52,8422,8423],{"class":54,"line":4978},[52,8424,2110],{"class":58},[52,8426,8427],{"class":54,"line":4983},[52,8428,536],{"class":58},[52,8430,8431,8433,8435],{"class":54,"line":5004},[52,8432,108],{"class":58},[52,8434,349],{"class":62},[52,8436,80],{"class":58},[13,8438,8439,8440,8442],{},"この時",[49,8441,8034],{},"に渡すデータは以下の様に定義する必要があります。",[42,8444,8447],{"className":8445,"code":8446,"language":452},[5819],"{\n    data: {\n        \u002F\u002F This is the same as the instructions object\n        crop: {\n            center: {\n                x: .5,\n                y: .5\n            },\n            flip: {\n                horizontal: false,\n                vertical: false\n            },\n            zoom: 1,\n            rotation: 0,\n            aspectRatio: null\n        }\n    }\n}\n",[49,8448,8446],{"__ignoreMap":47},[13,8450,8451,8452,7429,8454,8457,8458,8461,8462,8464],{},"このデータは",[49,8453,7223],{},[49,8455,8456],{},"open(file, instructions)","コールバックの",[49,8459,8460],{},"instructions","と同じ構造です。filepondはこの",[49,8463,8460],{},"の値を変更することで、プレビューの画像表示や実際にアップロードする画像のトリミングなどが実行されます。",[13,8466,8467,8468,8471,8472,8475],{},"そのためvue-cropperのトリミングによる座標位置や、トリミング範囲上記のデータに合わせて設定する必要があります。vue-cropperには",[49,8469,8470],{},"getCanvasData()","や",[49,8473,8474],{},"getData()","などのメソッドがあるため、それらを用いてvue-cropperのデータをコールバックを通してfilepondへ渡します。",[2409,8477,8478],{"id":8478},"計算を行う",[13,8480,8481,8482,8487,8488,8491],{},"しかしfilepondに渡すデータは結構クセがあり、cropperから取得できる情報の何をどこに当てはめればいいのかわかりません。",[1450,8483,8486],{"href":8484,"rel":8485},"https:\u002F\u002Fgithub.com\u002Fpqina\u002Ffilepond-plugin-image-edit\u002Fissues\u002F1",[1454],"github issueにてcropper.jsとの連携に関するissue","を発見し、今回はその値を用いることにします。",[49,8489,8490],{},"crop()","を以下の様に変更します。",[42,8493,8495],{"className":6471,"code":8494,"language":1433,"meta":47,"style":47},"crop(){\n    \u002F\u002F https:\u002F\u002Fgithub.com\u002Fpqina\u002Ffilepond-plugin-image-edit\u002Fissues\u002F1\n    \u002F* Constants. *\u002F\n    const canvasData = this.$refs.cropper.getCanvasData() \u002F\u002F Cropperjs method getCanvasData()\n    const cropData = this.$refs.cropper.getData() \u002F\u002F Cropperjs method getData()\n\n    \u002F* Ratio of selected crop area. *\u002F\n    const cropAreaRatio = cropData.height \u002F cropData.width\n\n    \u002F* Center point of crop area in percent. *\u002F\n    const percentX = (cropData.x + cropData.width \u002F 2) \u002F canvasData.naturalWidth\n    const percentY = (cropData.y + cropData.height \u002F 2) \u002F canvasData.naturalHeight\n\n    \u002F* Calculate available space round image center position. *\u002F\n    const cx = percentX > 0.5 ? 1 - percentX : percentX\n    const cy = percentY > 0.5 ? 1 - percentY : percentY\n\n    \u002F* Calculate image rectangle respecting space round image from crop area. *\u002F\n    let width = canvasData.naturalWidth\n    let height = width * cropAreaRatio\n    if (height > canvasData.naturalHeight) {\n    height = canvasData.naturalHeight\n    width = height \u002F cropAreaRatio\n    }\n    const rectWidth = cx * 2 * width\n    const rectHeight = cy * 2 * height\n\n    \u002F* Calculate zoom. *\u002F\n    const zoom = Math.max(rectWidth \u002F cropData.width, rectHeight \u002F cropData.height)\n    this.editor.onconfirm({\n    data: {\n        crop: {\n            center: {\n                x: percentX,\n                y: percentY\n            },\n            flip: {\n                horizontal: cropData.scaleX \u003C 0,\n                vertical: cropData.scaleY \u003C 0\n            },\n            zoom: zoom,\n            rotation: (Math.PI \u002F 180) * cropData.rotate,\n            aspectRatio: cropAreaRatio\n        }\n    }\n    })\n}\n",[49,8496,8497,8505,8510,8515,8544,8571,8575,8580,8605,8609,8614,8657,8698,8702,8707,8739,8767,8771,8776,8791,8808,8830,8843,8856,8860,8880,8900,8904,8909,8953,8967,8975,8983,8992,9003,9012,9017,9026,9046,9065,9069,9080,9113,9122,9126,9130,9137],{"__ignoreMap":47},[52,8498,8499,8501,8503],{"class":54,"line":55},[52,8500,7586],{"class":418},[52,8502,422],{"class":105},[52,8504,364],{"class":58},[52,8506,8507],{"class":54,"line":83},[52,8508,8509],{"class":411},"    \u002F\u002F https:\u002F\u002Fgithub.com\u002Fpqina\u002Ffilepond-plugin-image-edit\u002Fissues\u002F1\n",[52,8511,8512],{"class":54,"line":115},[52,8513,8514],{"class":411},"    \u002F* Constants. *\u002F\n",[52,8516,8517,8520,8523,8525,8527,8529,8531,8533,8535,8538,8541],{"class":54,"line":142},[52,8518,8519],{"class":65},"    const",[52,8521,8522],{"class":105}," canvasData",[52,8524,951],{"class":58},[52,8526,6370],{"class":58},[52,8528,7970],{"class":105},[52,8530,957],{"class":58},[52,8532,7436],{"class":105},[52,8534,957],{"class":58},[52,8536,8537],{"class":418},"getCanvasData",[52,8539,8540],{"class":62},"() ",[52,8542,8543],{"class":411},"\u002F\u002F Cropperjs method getCanvasData()\n",[52,8545,8546,8548,8551,8553,8555,8557,8559,8561,8563,8566,8568],{"class":54,"line":169},[52,8547,8519],{"class":65},[52,8549,8550],{"class":105}," cropData",[52,8552,951],{"class":58},[52,8554,6370],{"class":58},[52,8556,7970],{"class":105},[52,8558,957],{"class":58},[52,8560,7436],{"class":105},[52,8562,957],{"class":58},[52,8564,8565],{"class":418},"getData",[52,8567,8540],{"class":62},[52,8569,8570],{"class":411},"\u002F\u002F Cropperjs method getData()\n",[52,8572,8573],{"class":54,"line":302},[52,8574,341],{"emptyLinePlaceholder":340},[52,8576,8577],{"class":54,"line":308},[52,8578,8579],{"class":411},"    \u002F* Ratio of selected crop area. *\u002F\n",[52,8581,8582,8584,8587,8589,8591,8593,8595,8598,8600,8602],{"class":54,"line":318},[52,8583,8519],{"class":65},[52,8585,8586],{"class":105}," cropAreaRatio",[52,8588,951],{"class":58},[52,8590,8550],{"class":105},[52,8592,957],{"class":58},[52,8594,2001],{"class":105},[52,8596,8597],{"class":58}," \u002F",[52,8599,8550],{"class":105},[52,8601,957],{"class":58},[52,8603,8604],{"class":105},"width\n",[52,8606,8607],{"class":54,"line":328},[52,8608,341],{"emptyLinePlaceholder":340},[52,8610,8611],{"class":54,"line":337},[52,8612,8613],{"class":411},"    \u002F* Center point of crop area in percent. *\u002F\n",[52,8615,8616,8618,8621,8623,8625,8628,8630,8632,8635,8637,8639,8641,8643,8646,8648,8650,8652,8654],{"class":54,"line":344},[52,8617,8519],{"class":65},[52,8619,8620],{"class":105}," percentX",[52,8622,951],{"class":58},[52,8624,1913],{"class":62},[52,8626,8627],{"class":105},"cropData",[52,8629,957],{"class":58},[52,8631,3154],{"class":105},[52,8633,8634],{"class":58}," +",[52,8636,8550],{"class":105},[52,8638,957],{"class":58},[52,8640,1973],{"class":105},[52,8642,8597],{"class":58},[52,8644,8645],{"class":1646}," 2",[52,8647,3098],{"class":62},[52,8649,2016],{"class":58},[52,8651,8522],{"class":105},[52,8653,957],{"class":58},[52,8655,8656],{"class":105},"naturalWidth\n",[52,8658,8659,8661,8664,8666,8668,8670,8672,8675,8677,8679,8681,8683,8685,8687,8689,8691,8693,8695],{"class":54,"line":354},[52,8660,8519],{"class":65},[52,8662,8663],{"class":105}," percentY",[52,8665,951],{"class":58},[52,8667,1913],{"class":62},[52,8669,8627],{"class":105},[52,8671,957],{"class":58},[52,8673,8674],{"class":105},"y",[52,8676,8634],{"class":58},[52,8678,8550],{"class":105},[52,8680,957],{"class":58},[52,8682,2001],{"class":105},[52,8684,8597],{"class":58},[52,8686,8645],{"class":1646},[52,8688,3098],{"class":62},[52,8690,2016],{"class":58},[52,8692,8522],{"class":105},[52,8694,957],{"class":58},[52,8696,8697],{"class":105},"naturalHeight\n",[52,8699,8700],{"class":54,"line":367},[52,8701,341],{"emptyLinePlaceholder":340},[52,8703,8704],{"class":54,"line":387},[52,8705,8706],{"class":411},"    \u002F* Calculate available space round image center position. *\u002F\n",[52,8708,8709,8711,8714,8716,8718,8721,8724,8727,8729,8731,8733,8736],{"class":54,"line":415},[52,8710,8519],{"class":65},[52,8712,8713],{"class":105}," cx",[52,8715,951],{"class":58},[52,8717,8620],{"class":105},[52,8719,8720],{"class":58}," >",[52,8722,8723],{"class":1646}," 0.5",[52,8725,8726],{"class":58}," ?",[52,8728,3095],{"class":1646},[52,8730,3113],{"class":58},[52,8732,8620],{"class":105},[52,8734,8735],{"class":58}," :",[52,8737,8738],{"class":105}," percentX\n",[52,8740,8741,8743,8746,8748,8750,8752,8754,8756,8758,8760,8762,8764],{"class":54,"line":427},[52,8742,8519],{"class":65},[52,8744,8745],{"class":105}," cy",[52,8747,951],{"class":58},[52,8749,8663],{"class":105},[52,8751,8720],{"class":58},[52,8753,8723],{"class":1646},[52,8755,8726],{"class":58},[52,8757,3095],{"class":1646},[52,8759,3113],{"class":58},[52,8761,8663],{"class":105},[52,8763,8735],{"class":58},[52,8765,8766],{"class":105}," percentY\n",[52,8768,8769],{"class":54,"line":435},[52,8770,341],{"emptyLinePlaceholder":340},[52,8772,8773],{"class":54,"line":446},[52,8774,8775],{"class":411},"    \u002F* Calculate image rectangle respecting space round image from crop area. *\u002F\n",[52,8777,8778,8780,8783,8785,8787,8789],{"class":54,"line":480},[52,8779,945],{"class":65},[52,8781,8782],{"class":105}," width",[52,8784,951],{"class":58},[52,8786,8522],{"class":105},[52,8788,957],{"class":58},[52,8790,8656],{"class":105},[52,8792,8793,8795,8798,8800,8802,8805],{"class":54,"line":509},[52,8794,945],{"class":65},[52,8796,8797],{"class":105}," height",[52,8799,951],{"class":58},[52,8801,8782],{"class":105},[52,8803,8804],{"class":58}," *",[52,8806,8807],{"class":105}," cropAreaRatio\n",[52,8809,8810,8813,8815,8817,8819,8821,8823,8826,8828],{"class":54,"line":539},[52,8811,8812],{"class":360},"    if",[52,8814,1913],{"class":62},[52,8816,2001],{"class":105},[52,8818,8720],{"class":58},[52,8820,8522],{"class":105},[52,8822,957],{"class":58},[52,8824,8825],{"class":105},"naturalHeight",[52,8827,3098],{"class":62},[52,8829,364],{"class":58},[52,8831,8832,8835,8837,8839,8841],{"class":54,"line":547},[52,8833,8834],{"class":105},"    height",[52,8836,951],{"class":58},[52,8838,8522],{"class":105},[52,8840,957],{"class":58},[52,8842,8697],{"class":105},[52,8844,8845,8848,8850,8852,8854],{"class":54,"line":553},[52,8846,8847],{"class":105},"    width",[52,8849,951],{"class":58},[52,8851,8797],{"class":105},[52,8853,8597],{"class":58},[52,8855,8807],{"class":105},[52,8857,8858],{"class":54,"line":559},[52,8859,2110],{"class":58},[52,8861,8862,8864,8867,8869,8871,8873,8875,8877],{"class":54,"line":564},[52,8863,8519],{"class":65},[52,8865,8866],{"class":105}," rectWidth",[52,8868,951],{"class":58},[52,8870,8713],{"class":105},[52,8872,8804],{"class":58},[52,8874,8645],{"class":1646},[52,8876,8804],{"class":58},[52,8878,8879],{"class":105}," width\n",[52,8881,8882,8884,8887,8889,8891,8893,8895,8897],{"class":54,"line":569},[52,8883,8519],{"class":65},[52,8885,8886],{"class":105}," rectHeight",[52,8888,951],{"class":58},[52,8890,8745],{"class":105},[52,8892,8804],{"class":58},[52,8894,8645],{"class":1646},[52,8896,8804],{"class":58},[52,8898,8899],{"class":105}," height\n",[52,8901,8902],{"class":54,"line":1106},[52,8903,341],{"emptyLinePlaceholder":340},[52,8905,8906],{"class":54,"line":1135},[52,8907,8908],{"class":411},"    \u002F* Calculate zoom. *\u002F\n",[52,8910,8911,8913,8916,8918,8921,8923,8926,8928,8931,8933,8935,8937,8939,8941,8943,8945,8947,8949,8951],{"class":54,"line":1164},[52,8912,8519],{"class":65},[52,8914,8915],{"class":105}," zoom",[52,8917,951],{"class":58},[52,8919,8920],{"class":105}," Math",[52,8922,957],{"class":58},[52,8924,8925],{"class":418},"max",[52,8927,932],{"class":62},[52,8929,8930],{"class":105},"rectWidth",[52,8932,8597],{"class":58},[52,8934,8550],{"class":105},[52,8936,957],{"class":58},[52,8938,1973],{"class":105},[52,8940,408],{"class":58},[52,8942,8886],{"class":105},[52,8944,8597],{"class":58},[52,8946,8550],{"class":105},[52,8948,957],{"class":58},[52,8950,2001],{"class":105},[52,8952,1015],{"class":62},[52,8954,8955,8957,8959,8961,8963,8965],{"class":54,"line":4},[52,8956,1052],{"class":58},[52,8958,7223],{"class":105},[52,8960,957],{"class":58},[52,8962,8034],{"class":418},[52,8964,932],{"class":62},[52,8966,364],{"class":58},[52,8968,8969,8971,8973],{"class":54,"line":1199},[52,8970,6121],{"class":62},[52,8972,373],{"class":58},[52,8974,6100],{"class":58},[52,8976,8977,8979,8981],{"class":54,"line":1204},[52,8978,7753],{"class":62},[52,8980,373],{"class":58},[52,8982,6100],{"class":58},[52,8984,8985,8988,8990],{"class":54,"line":1209},[52,8986,8987],{"class":62},"            center",[52,8989,373],{"class":58},[52,8991,6100],{"class":58},[52,8993,8994,8997,8999,9001],{"class":54,"line":1214},[52,8995,8996],{"class":62},"                x",[52,8998,373],{"class":58},[52,9000,8620],{"class":105},[52,9002,384],{"class":58},[52,9004,9005,9008,9010],{"class":54,"line":1219},[52,9006,9007],{"class":62},"                y",[52,9009,373],{"class":58},[52,9011,8766],{"class":105},[52,9013,9014],{"class":54,"line":3216},[52,9015,9016],{"class":58},"            },\n",[52,9018,9019,9022,9024],{"class":54,"line":3237},[52,9020,9021],{"class":62},"            flip",[52,9023,373],{"class":58},[52,9025,6100],{"class":58},[52,9027,9028,9031,9033,9035,9037,9040,9042,9044],{"class":54,"line":3251},[52,9029,9030],{"class":62},"                horizontal",[52,9032,373],{"class":58},[52,9034,8550],{"class":105},[52,9036,957],{"class":58},[52,9038,9039],{"class":105},"scaleX",[52,9041,86],{"class":58},[52,9043,2749],{"class":1646},[52,9045,384],{"class":58},[52,9047,9048,9051,9053,9055,9057,9060,9062],{"class":54,"line":3256},[52,9049,9050],{"class":62},"                vertical",[52,9052,373],{"class":58},[52,9054,8550],{"class":105},[52,9056,957],{"class":58},[52,9058,9059],{"class":105},"scaleY",[52,9061,86],{"class":58},[52,9063,9064],{"class":1646}," 0\n",[52,9066,9067],{"class":54,"line":3281},[52,9068,9016],{"class":58},[52,9070,9071,9074,9076,9078],{"class":54,"line":3295},[52,9072,9073],{"class":62},"            zoom",[52,9075,373],{"class":58},[52,9077,8915],{"class":105},[52,9079,384],{"class":58},[52,9081,9082,9085,9087,9089,9091,9093,9095,9097,9100,9102,9104,9106,9108,9111],{"class":54,"line":4922},[52,9083,9084],{"class":62},"            rotation",[52,9086,373],{"class":58},[52,9088,1913],{"class":62},[52,9090,3121],{"class":105},[52,9092,957],{"class":58},[52,9094,3136],{"class":105},[52,9096,8597],{"class":58},[52,9098,9099],{"class":1646}," 180",[52,9101,3098],{"class":62},[52,9103,3118],{"class":58},[52,9105,8550],{"class":105},[52,9107,957],{"class":58},[52,9109,9110],{"class":105},"rotate",[52,9112,384],{"class":58},[52,9114,9115,9118,9120],{"class":54,"line":4943},[52,9116,9117],{"class":62},"            aspectRatio",[52,9119,373],{"class":58},[52,9121,8807],{"class":105},[52,9123,9124],{"class":54,"line":4961},[52,9125,4814],{"class":58},[52,9127,9128],{"class":54,"line":4978},[52,9129,2110],{"class":58},[52,9131,9132,9135],{"class":54,"line":4983},[52,9133,9134],{"class":58},"    }",[52,9136,1015],{"class":62},[52,9138,9139],{"class":54,"line":5004},[52,9140,536],{"class":58},[17,9142,9143],{"id":9143},"アップロードを行う",[13,9145,9146],{},"これでvue-cropperのトリミング情報をfilepondに渡すことができる様になりました。実際にトリミング範囲を指定してcropボタンをクリックしますと、その位置と範囲にしたがってトリミングされた範囲がプレビューされます。",[729,9148],{":src":9149,":width":5554,":center":1322},"'filepond-crop-vue\u002Fcropped.png'",[13,9151,9152,9153,9156],{},"そして最後にアップロードボタンをクリックすると渡されたデータを元に、filepondが元画像のトリミングを行いアップロードします。トリミングしてもらうためには",[49,9154,9155],{},"FilePondPluginImageTransform","がインストールされ、有効になっている必要があります。サーバー側では１：１にトリミングされた画像を確認することができます。",[17,9158,9159],{"id":9159},"まとめ",[13,9161,9162,9163,9168],{},"filepondは主にブラウザアップロードと実際のトリミング処理を行います。そしてトリミング箇所の指定は別のライブラリを用いて実装する必要があります。filepondは",[1450,9164,9167],{"href":9165,"rel":9166},"https:\u002F\u002Fpqina.nl\u002Fpintura\u002F",[1454],"Doka Image Editor","という有償ライブラリとの互換性を第一にしているそうで、cropper.jsとの連携と特に計算が方法がわかりませんでした。ひとまず上記の方法でトリミングとアップロード機能が実装できました。後はUIを整えてあげれば完成です。",[1413,9170,9171],{},"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 pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .sbqyR, html code.shiki .sbqyR{--shiki-default:#FF9CAC}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}",{"title":47,"searchDepth":115,"depth":115,"links":9173},[9174,9175,9179,9182,9190,9191],{"id":5789,"depth":83,"text":5789},{"id":5809,"depth":83,"text":5809,"children":9176},[9177,9178],{"id":5815,"depth":115,"text":5815},{"id":5849,"depth":115,"text":5850},{"id":5967,"depth":83,"text":5968,"children":9180},[9181],{"id":6430,"depth":115,"text":6430},{"id":6729,"depth":83,"text":6730,"children":9183},[9184,9185,9186,9187],{"id":6917,"depth":115,"text":6918},{"id":7440,"depth":115,"text":7440},{"id":7853,"depth":115,"text":7853},{"id":8023,"depth":115,"text":8023,"children":9188},[9189],{"id":8478,"depth":142,"text":8478},{"id":9143,"depth":83,"text":9143},{"id":9159,"depth":83,"text":9159},[1423],"2025-12-09","Vueでブラウザでのトリミング機能とファイルアップロードを実装する",{},"\u002Farticles\u002Ffilepond-crop-vue",{"title":5536,"description":9194},"articles\u002Ffilepond-crop-vue",[1433,204],"filepond-crop-vue\u002Fthumbnail.png","an_V9C83Uuzs33hco6VOdrE9TtIqJVF5teLfyG0kaaE",{"id":9203,"title":9204,"body":9205,"category":14072,"createdAt":14073,"description":9204,"extension":1426,"index":1427,"meta":14074,"navigation":340,"path":14075,"publish":340,"seo":14076,"series":1427,"seriesTitle":1427,"stem":14077,"tag":14078,"thumbnail":1427,"updatedAt":14073,"__hash__":14079},"articles\u002Farticles\u002Feditorjs-repeatable.md","Editor.jsで反復入力できるブロックを作る（バニラJS）",{"type":10,"value":9206,"toc":14054},[9207,9210,9213,9216,9219,9222,9225,9239,9242,9246,9254,9257,9260,9263,9266,9269,9273,9282,9296,9299,9823,9831,9834,9837,9840,9852,9855,9858,9861,9864,9867,9871,9882,10312,10319,10365,10397,10400,10403,10406,10415,10418,10564,10571,10580,10586,10600,10703,10706,10709,10712,10927,10941,10944,10954,10957,10972,11129,11135,11298,11301,11304,11307,11781,11791,11797,11800,11817,12469,12472,12475,12481,12487,12490,12493,12496,12577,12580,12583,13079,13283,13309,13527,13553,13656,13662,13665,13668,13675,13678,13681,13684,13687,13697,13812,13824,13992,14006,14008,14011,14029,14038,14044,14051],[13,9208,9209],{},"こんにちはjunです。今回の記事は気になっているブラウザテキストエディターであるeditor.jsについての記事です。WYSINGの一種ですが、シンプルながらもckeditorやtinymceに変わるエディターになるのでは？と思いながら、カスタムブロックを作成しましので記事にしたいと思います。",[13,9211,9212],{},"なぜeditor.jsなのかという経緯や背景から解説しますので、さっさとタイトルの実装内容を知りたい場合は「今回作るもの」からご覧ください。",[17,9214,9215],{"id":9215},"背景",[13,9217,9218],{},"webシステムを作る際にはプレーンテキストのサポートだけでなく、リッチテキストエディタをサポートして欲しいという声があります。リッチテキストがあれば太字、リンク、色、画像などをhtmlを知らずともリッチなコンテンツをワードを使うようにユーザーが実装できるようになります。（こんなやつです↓）",[729,9220],{":src":9221,":width":732},"'editorjs-repeatable\u002Fsample-wysing.png'",[13,9223,9224],{},"ワードで作るような文章ベースの内容であればckeditorやtinymceで問題はありませんが、以下のようなデメリットや要件での難しさがあります。",[1464,9226,9227,9230,9233,9236],{},[1467,9228,9229],{},"生成されるコンテンツがHTMLであるため、webブラウザ意外に表示（レンダリング）が難しい。",[1467,9231,9232],{},"構造化したデータ、特定のフォーマットにしたがった入力やパーツを表現、制御UIを作成することが難しい。",[1467,9234,9235],{},"複雑なビューに対するデータモデルの入力。",[1467,9237,9238],{},"複雑なビューを見たまま編集する（wixみたいな感じ）。",[13,9240,9241],{},"「構造化したデータ、特定のフォーマット」というのは表示したいビューコンポーネントに対する入力項目のことです。例えば以下のbootstrapのカードを見てみてください。",[729,9243],{":src":9244,":width":9245},"'editorjs-repeatable\u002Fbootsrap-sample.png'","'200px'",[13,9247,9248,9249,9253],{},"お客様が「このカードを自分達でテキスト、画像を選択して任意のページ、箇所で表示できるようにしたい」という",[9250,9251,9252],"del",{},"めんどうな","要件があったとします。自由な入力をサポートするので、管理画面での固定的なフォームでは難しそうです。しかし、リッチテキストでは画像の選択や、テキスト部分の制御、カード自体の表示と削除など難しと思います。",[13,9255,9256],{},"さらに以下のようなアコーディオンはどうでしょうか？",[729,9258],{":src":9259,":width":732},"'editorjs-repeatable\u002Fbootsrap-accordion.png'",[13,9261,9262],{},"タイトル、テキスト意外にも「複数個」入力しないといけない、１つのコンポーネントにn個のフォーマットデータを入力できるようにする必要があります。",[13,9264,9265],{},"これが「構造化したデータ、特定のフォーマットにしたがった入力やパーツを表現、制御UI」でしたり、「複雑なビューを見たまま編集する」が難しいという意味です。wordpressでもグーデンベルクというコンポーネントレベルで記事を作成することが主流になり、webコンテンツの編集はワードで作成する様な文章的な内容からリッチなビューをサポートするのが要件になりつつあります。",[13,9267,9268],{},"その場合、従来のWYSINGでは実装が難しいです。そのためWYSINGでHTMLを作成してそれを保存、表示するのではなくて構造化されたデータをベースに表示、編集、出力できるようにしたものがeditor.jsです。",[17,9270,9272],{"id":9271},"editorjsの特徴","editor.jsの特徴",[13,9274,9275,9276,9281],{},"詳細は",[1450,9277,9280],{"href":9278,"rel":9279},"https:\u002F\u002Feditorjs.io\u002F",[1454],"公式サイトを参照","ですがざっとあげるとすれば",[2489,9283,9284,9287,9290,9293],{},[1467,9285,9286],{},"出力されるデータが配列。（永続化はJSONにする）",[1467,9288,9289],{},"特定のプロパティーに沿った要素で出力する。",[1467,9291,9292],{},"カスタマイズがしやすい。",[1467,9294,9295],{},"挿入するブロックを都度選択し、データを入力する。",[13,9297,9298],{},"といった感じです。取得されるデータは以下のようになっています。",[42,9300,9302],{"className":5566,"code":9301,"language":5568,"meta":47,"style":47},"{\n    \"time\" : 1654313680224,\n    \"blocks\" : [\n        {\n            \"id\" : \"gy0oTOBqL2\",\n            \"type\" : \"header\",\n            \"data\" : {\n                \"text\" : \"Editor.js\",\n                \"level\" : 2\n            }\n        },\n        {\n            \"id\" : \"AOOa_CMrPW\",\n            \"type\" : \"list\",\n            \"data\" : {\n                \"style\" : \"unordered\",\n                \"items\" : [\n                    \"It is a block-styled editor\",\n                    \"It returns clean data output in JSON\",\n                    \"Designed to be extendable and pluggable with a simple API\"\n                ]\n            }\n        },\n        {\n            \"id\" : \"6crsAbriK8\",\n            \"type\" : \"header\",\n            \"data\" : {\n                \"text\" : \"What does it mean clean data output\",\n                \"level\" : 3\n            }\n        },\n        {\n            \"id\" : \"GBhduZdsZW\",\n            \"type\" : \"image\",\n            \"data\" : {\n                \"file\" : {\n                    \"url\" : \"https:\u002F\u002Fcodex.so\u002Fpublic\u002Fapp\u002Fimg\u002Fexternal\u002Fcodex2x.png\"\n                },\n                \"caption\" : \"\",\n                \"withBorder\" : false,\n                \"stretched\" : false,\n                \"withBackground\" : false\n            }\n        }\n    ]\n}\n",[49,9303,9304,9308,9324,9338,9343,9364,9384,9396,9416,9430,9434,9438,9442,9461,9480,9492,9511,9524,9536,9547,9556,9561,9565,9569,9573,9592,9610,9622,9641,9654,9658,9662,9666,9685,9703,9715,9727,9745,9749,9765,9779,9792,9806,9810,9814,9819],{"__ignoreMap":47},[52,9305,9306],{"class":54,"line":55},[52,9307,364],{"class":58},[52,9309,9310,9312,9315,9317,9319,9322],{"class":54,"line":83},[52,9311,5589],{"class":58},[52,9313,9314],{"class":65},"time",[52,9316,72],{"class":58},[52,9318,8735],{"class":58},[52,9320,9321],{"class":1646}," 1654313680224",[52,9323,384],{"class":58},[52,9325,9326,9328,9331,9333,9335],{"class":54,"line":115},[52,9327,5589],{"class":58},[52,9329,9330],{"class":65},"blocks",[52,9332,72],{"class":58},[52,9334,8735],{"class":58},[52,9336,9337],{"class":58}," [\n",[52,9339,9340],{"class":54,"line":142},[52,9341,9342],{"class":58},"        {\n",[52,9344,9345,9348,9351,9353,9355,9357,9360,9362],{"class":54,"line":169},[52,9346,9347],{"class":58},"            \"",[52,9349,9350],{"class":370},"id",[52,9352,72],{"class":58},[52,9354,8735],{"class":58},[52,9356,3013],{"class":58},[52,9358,9359],{"class":75},"gy0oTOBqL2",[52,9361,72],{"class":58},[52,9363,384],{"class":58},[52,9365,9366,9368,9371,9373,9375,9377,9380,9382],{"class":54,"line":302},[52,9367,9347],{"class":58},[52,9369,9370],{"class":370},"type",[52,9372,72],{"class":58},[52,9374,8735],{"class":58},[52,9376,3013],{"class":58},[52,9378,9379],{"class":75},"header",[52,9381,72],{"class":58},[52,9383,384],{"class":58},[52,9385,9386,9388,9390,9392,9394],{"class":54,"line":308},[52,9387,9347],{"class":58},[52,9389,7112],{"class":370},[52,9391,72],{"class":58},[52,9393,8735],{"class":58},[52,9395,6100],{"class":58},[52,9397,9398,9401,9403,9405,9407,9409,9412,9414],{"class":54,"line":318},[52,9399,9400],{"class":58},"                \"",[52,9402,452],{"class":1646},[52,9404,72],{"class":58},[52,9406,8735],{"class":58},[52,9408,3013],{"class":58},[52,9410,9411],{"class":75},"Editor.js",[52,9413,72],{"class":58},[52,9415,384],{"class":58},[52,9417,9418,9420,9423,9425,9427],{"class":54,"line":328},[52,9419,9400],{"class":58},[52,9421,9422],{"class":1646},"level",[52,9424,72],{"class":58},[52,9426,8735],{"class":58},[52,9428,9429],{"class":1646}," 2\n",[52,9431,9432],{"class":54,"line":337},[52,9433,1665],{"class":58},[52,9435,9436],{"class":54,"line":344},[52,9437,6191],{"class":58},[52,9439,9440],{"class":54,"line":354},[52,9441,9342],{"class":58},[52,9443,9444,9446,9448,9450,9452,9454,9457,9459],{"class":54,"line":367},[52,9445,9347],{"class":58},[52,9447,9350],{"class":370},[52,9449,72],{"class":58},[52,9451,8735],{"class":58},[52,9453,3013],{"class":58},[52,9455,9456],{"class":75},"AOOa_CMrPW",[52,9458,72],{"class":58},[52,9460,384],{"class":58},[52,9462,9463,9465,9467,9469,9471,9473,9476,9478],{"class":54,"line":387},[52,9464,9347],{"class":58},[52,9466,9370],{"class":370},[52,9468,72],{"class":58},[52,9470,8735],{"class":58},[52,9472,3013],{"class":58},[52,9474,9475],{"class":75},"list",[52,9477,72],{"class":58},[52,9479,384],{"class":58},[52,9481,9482,9484,9486,9488,9490],{"class":54,"line":415},[52,9483,9347],{"class":58},[52,9485,7112],{"class":370},[52,9487,72],{"class":58},[52,9489,8735],{"class":58},[52,9491,6100],{"class":58},[52,9493,9494,9496,9498,9500,9502,9504,9507,9509],{"class":54,"line":427},[52,9495,9400],{"class":58},[52,9497,1413],{"class":1646},[52,9499,72],{"class":58},[52,9501,8735],{"class":58},[52,9503,3013],{"class":58},[52,9505,9506],{"class":75},"unordered",[52,9508,72],{"class":58},[52,9510,384],{"class":58},[52,9512,9513,9515,9518,9520,9522],{"class":54,"line":435},[52,9514,9400],{"class":58},[52,9516,9517],{"class":1646},"items",[52,9519,72],{"class":58},[52,9521,8735],{"class":58},[52,9523,9337],{"class":58},[52,9525,9526,9529,9532,9534],{"class":54,"line":446},[52,9527,9528],{"class":58},"                    \"",[52,9530,9531],{"class":75},"It is a block-styled editor",[52,9533,72],{"class":58},[52,9535,384],{"class":58},[52,9537,9538,9540,9543,9545],{"class":54,"line":480},[52,9539,9528],{"class":58},[52,9541,9542],{"class":75},"It returns clean data output in JSON",[52,9544,72],{"class":58},[52,9546,384],{"class":58},[52,9548,9549,9551,9554],{"class":54,"line":509},[52,9550,9528],{"class":58},[52,9552,9553],{"class":75},"Designed to be extendable and pluggable with a simple API",[52,9555,266],{"class":58},[52,9557,9558],{"class":54,"line":539},[52,9559,9560],{"class":58},"                ]\n",[52,9562,9563],{"class":54,"line":547},[52,9564,1665],{"class":58},[52,9566,9567],{"class":54,"line":553},[52,9568,6191],{"class":58},[52,9570,9571],{"class":54,"line":559},[52,9572,9342],{"class":58},[52,9574,9575,9577,9579,9581,9583,9585,9588,9590],{"class":54,"line":564},[52,9576,9347],{"class":58},[52,9578,9350],{"class":370},[52,9580,72],{"class":58},[52,9582,8735],{"class":58},[52,9584,3013],{"class":58},[52,9586,9587],{"class":75},"6crsAbriK8",[52,9589,72],{"class":58},[52,9591,384],{"class":58},[52,9593,9594,9596,9598,9600,9602,9604,9606,9608],{"class":54,"line":569},[52,9595,9347],{"class":58},[52,9597,9370],{"class":370},[52,9599,72],{"class":58},[52,9601,8735],{"class":58},[52,9603,3013],{"class":58},[52,9605,9379],{"class":75},[52,9607,72],{"class":58},[52,9609,384],{"class":58},[52,9611,9612,9614,9616,9618,9620],{"class":54,"line":1106},[52,9613,9347],{"class":58},[52,9615,7112],{"class":370},[52,9617,72],{"class":58},[52,9619,8735],{"class":58},[52,9621,6100],{"class":58},[52,9623,9624,9626,9628,9630,9632,9634,9637,9639],{"class":54,"line":1135},[52,9625,9400],{"class":58},[52,9627,452],{"class":1646},[52,9629,72],{"class":58},[52,9631,8735],{"class":58},[52,9633,3013],{"class":58},[52,9635,9636],{"class":75},"What does it mean clean data output",[52,9638,72],{"class":58},[52,9640,384],{"class":58},[52,9642,9643,9645,9647,9649,9651],{"class":54,"line":1164},[52,9644,9400],{"class":58},[52,9646,9422],{"class":1646},[52,9648,72],{"class":58},[52,9650,8735],{"class":58},[52,9652,9653],{"class":1646}," 3\n",[52,9655,9656],{"class":54,"line":4},[52,9657,1665],{"class":58},[52,9659,9660],{"class":54,"line":1199},[52,9661,6191],{"class":58},[52,9663,9664],{"class":54,"line":1204},[52,9665,9342],{"class":58},[52,9667,9668,9670,9672,9674,9676,9678,9681,9683],{"class":54,"line":1209},[52,9669,9347],{"class":58},[52,9671,9350],{"class":370},[52,9673,72],{"class":58},[52,9675,8735],{"class":58},[52,9677,3013],{"class":58},[52,9679,9680],{"class":75},"GBhduZdsZW",[52,9682,72],{"class":58},[52,9684,384],{"class":58},[52,9686,9687,9689,9691,9693,9695,9697,9699,9701],{"class":54,"line":1214},[52,9688,9347],{"class":58},[52,9690,9370],{"class":370},[52,9692,72],{"class":58},[52,9694,8735],{"class":58},[52,9696,3013],{"class":58},[52,9698,2053],{"class":75},[52,9700,72],{"class":58},[52,9702,384],{"class":58},[52,9704,9705,9707,9709,9711,9713],{"class":54,"line":1219},[52,9706,9347],{"class":58},[52,9708,7112],{"class":370},[52,9710,72],{"class":58},[52,9712,8735],{"class":58},[52,9714,6100],{"class":58},[52,9716,9717,9719,9721,9723,9725],{"class":54,"line":3216},[52,9718,9400],{"class":58},[52,9720,7057],{"class":1646},[52,9722,72],{"class":58},[52,9724,8735],{"class":58},[52,9726,6100],{"class":58},[52,9728,9729,9731,9734,9736,9738,9740,9743],{"class":54,"line":3237},[52,9730,9528],{"class":58},[52,9732,9733],{"class":62},"url",[52,9735,72],{"class":58},[52,9737,8735],{"class":58},[52,9739,3013],{"class":58},[52,9741,9742],{"class":75},"https:\u002F\u002Fcodex.so\u002Fpublic\u002Fapp\u002Fimg\u002Fexternal\u002Fcodex2x.png",[52,9744,266],{"class":58},[52,9746,9747],{"class":54,"line":3251},[52,9748,6389],{"class":58},[52,9750,9751,9753,9756,9758,9760,9763],{"class":54,"line":3256},[52,9752,9400],{"class":58},[52,9754,9755],{"class":1646},"caption",[52,9757,72],{"class":58},[52,9759,8735],{"class":58},[52,9761,9762],{"class":58}," \"\"",[52,9764,384],{"class":58},[52,9766,9767,9769,9772,9774,9776],{"class":54,"line":3281},[52,9768,9400],{"class":58},[52,9770,9771],{"class":1646},"withBorder",[52,9773,72],{"class":58},[52,9775,8735],{"class":58},[52,9777,9778],{"class":58}," false,\n",[52,9780,9781,9783,9786,9788,9790],{"class":54,"line":3295},[52,9782,9400],{"class":58},[52,9784,9785],{"class":1646},"stretched",[52,9787,72],{"class":58},[52,9789,8735],{"class":58},[52,9791,9778],{"class":58},[52,9793,9794,9796,9799,9801,9803],{"class":54,"line":4922},[52,9795,9400],{"class":58},[52,9797,9798],{"class":1646},"withBackground",[52,9800,72],{"class":58},[52,9802,8735],{"class":58},[52,9804,9805],{"class":58}," false\n",[52,9807,9808],{"class":54,"line":4943},[52,9809,1665],{"class":58},[52,9811,9812],{"class":54,"line":4961},[52,9813,4814],{"class":58},[52,9815,9816],{"class":54,"line":4978},[52,9817,9818],{"class":58},"    ]\n",[52,9820,9821],{"class":54,"line":4983},[52,9822,536],{"class":58},[13,9824,9825,9827,9828,9830],{},[49,9826,9330],{},"という要素はいかにそれぞれのブロックのデータが入り、",[49,9829,9370],{},"によって種類を識別して決まったプロパティーが取得されます。",[1518,9832,9833],{"id":9833},"editorjsのメリット",[13,9835,9836],{},"メリットは従来のwysingで管理できなかったような複雑なビューや構造化したデータをクライアント側で簡単に入力できるようになったこと、そしてそのデータをJSONで管理できることです。スキーマを定義してバリデーションもしやすいですし、JSONなので配布もしやすいです。ブロックのカスタマイズや作成も簡単に行えるので、拡張性も高いです。今回は「作成」のみ行います。",[1518,9838,9839],{"id":9839},"editorjsのデメリット",[13,9841,9842,9843,9848,9849,9851],{},"デメリットは取得されるデータがJSONのため、レンダリングをしたい際は専用のパーサを作成する必要がります。node.jsであれば",[1450,9844,9847],{"href":9845,"rel":9846},"https:\u002F\u002Fgithub.com\u002Fpavittarx\u002Feditorjs-html",[1454],"editorjs-html","というものを使用したりして自前でhtmlに変換する処理が必要です。基本的には",[49,9850,9330],{},"をforeachして、typeをswitchにて分岐させて特定のhtml文字列を作成するといった感じです。",[13,9853,9854],{},"また当たり前ですがバリデーションも実装する必要があります。従来のwysingではphp-purifier,html_sanitizerなどを使用して特定のタグや属性をフィルタして保存するなどを行います。editor.jsは構造化したデータが大切になるので、永続化処理時にあらかじめ定義したプロパティーであるか、構造であるかをチェックするシステムは自前で実装する必要があります。（ここはまた今度書く予定です。）",[13,9856,9857],{},"まだ公式から言語ごとのパーサーやバリデーターはまだ安定したものが出ていない印象です。（2022年6月当時）",[17,9859,9860],{"id":9860},"今回作るもの",[13,9862,9863],{},"上記のような特徴を持ったeditor.jsですが、今回は例に出したアコーディオンを作ってみようかと思います。タイトルと本文（太字、斜体、リンクをサポート）を複数個に増減可能で、順番も変えられるようにします。",[13,9865,9866],{},"使う場面としては「よくある質問」みたいなページです。editor.jsの説明をメインに行うため、デザインや動きは最低限となっています。バニラJSで作成し、サンプルはこちらに置いておきました。",[17,9868,9870],{"id":9869},"足場となるhtmljscssを作成","足場となるhtml,js,cssを作成",[13,9872,9873,9874,408,9876,408,9878,9881],{},"今回は",[49,9875,1535],{},[49,9877,1538],{},[49,9879,9880],{},"style.css","の３つだけを使用します。editor.jsはCDNから引っ張り、webpackなどは使用しません。",[42,9883,9885],{"className":44,"code":9884,"filename":1535,"language":46,"meta":47,"style":47},"\u003C!DOCTYPE html>\n\u003Chtml lang=\"ja\">\n\u003Chead>\n    \u003Cmeta charset=\"UTF-8\">\n    \u003Cmeta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    \u003Clink href=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbootstrap@5.0.2\u002Fdist\u002Fcss\u002Fbootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-EVSTQN3\u002FazprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC\" crossorigin=\"anonymous\">\n    \u003Ctitle>editorjs\u003C\u002Ftitle>\n    \u003Clink rel=\"stylesheet\" href=\".\u002Fstyle.css\">\n\u003C\u002Fhead>\n\u003Cbody>\n    \u003Cdiv class=\"container\">\n        \u003Cdiv class=\"mt-5 border bg-light\">\n            \u003Cdiv id=\"editorjs\">\u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003Cdiv class=\"mt-2\">\n            \u003Cpre id=\"data\">\u003C\u002Fpre>\n        \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@editorjs\u002Feditorjs@latest\">\u003C\u002Fscript>\n\u003Cscript src=\".\u002Fapp.js\">\u003C\u002Fscript>\n\u003C\u002Fhtml>\n",[49,9886,9887,9897,9917,9925,9944,9976,10006,10063,10080,10109,10117,10125,10145,10164,10186,10194,10213,10235,10243,10251,10259,10282,10304],{"__ignoreMap":47},[52,9888,9889,9891,9893,9895],{"class":54,"line":55},[52,9890,1554],{"class":58},[52,9892,1557],{"class":62},[52,9894,1560],{"class":65},[52,9896,80],{"class":58},[52,9898,9899,9901,9903,9906,9908,9910,9913,9915],{"class":54,"line":83},[52,9900,59],{"class":58},[52,9902,46],{"class":62},[52,9904,9905],{"class":65}," lang",[52,9907,69],{"class":58},[52,9909,72],{"class":58},[52,9911,9912],{"class":75},"ja",[52,9914,72],{"class":58},[52,9916,80],{"class":58},[52,9918,9919,9921,9923],{"class":54,"line":115},[52,9920,59],{"class":58},[52,9922,1578],{"class":62},[52,9924,80],{"class":58},[52,9926,9927,9929,9931,9933,9935,9937,9940,9942],{"class":54,"line":142},[52,9928,1575],{"class":58},[52,9930,1588],{"class":62},[52,9932,1591],{"class":65},[52,9934,69],{"class":58},[52,9936,72],{"class":58},[52,9938,9939],{"class":75},"UTF-8",[52,9941,72],{"class":58},[52,9943,80],{"class":58},[52,9945,9946,9948,9950,9953,9955,9957,9960,9962,9965,9967,9969,9972,9974],{"class":54,"line":169},[52,9947,1575],{"class":58},[52,9949,1588],{"class":62},[52,9951,9952],{"class":65}," http-equiv",[52,9954,69],{"class":58},[52,9956,72],{"class":58},[52,9958,9959],{"class":75},"X-UA-Compatible",[52,9961,72],{"class":58},[52,9963,9964],{"class":65}," content",[52,9966,69],{"class":58},[52,9968,72],{"class":58},[52,9970,9971],{"class":75},"IE=edge",[52,9973,72],{"class":58},[52,9975,80],{"class":58},[52,9977,9978,9980,9982,9984,9986,9988,9991,9993,9995,9997,9999,10002,10004],{"class":54,"line":302},[52,9979,1575],{"class":58},[52,9981,1588],{"class":62},[52,9983,66],{"class":65},[52,9985,69],{"class":58},[52,9987,72],{"class":58},[52,9989,9990],{"class":75},"viewport",[52,9992,72],{"class":58},[52,9994,9964],{"class":65},[52,9996,69],{"class":58},[52,9998,72],{"class":58},[52,10000,10001],{"class":75},"width=device-width, initial-scale=1.0",[52,10003,72],{"class":58},[52,10005,80],{"class":58},[52,10007,10008,10010,10013,10016,10018,10020,10023,10025,10028,10030,10032,10035,10037,10040,10042,10044,10047,10049,10052,10054,10056,10059,10061],{"class":54,"line":308},[52,10009,1575],{"class":58},[52,10011,10012],{"class":62},"link",[52,10014,10015],{"class":65}," href",[52,10017,69],{"class":58},[52,10019,72],{"class":58},[52,10021,10022],{"class":75},"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbootstrap@5.0.2\u002Fdist\u002Fcss\u002Fbootstrap.min.css",[52,10024,72],{"class":58},[52,10026,10027],{"class":65}," rel",[52,10029,69],{"class":58},[52,10031,72],{"class":58},[52,10033,10034],{"class":75},"stylesheet",[52,10036,72],{"class":58},[52,10038,10039],{"class":65}," integrity",[52,10041,69],{"class":58},[52,10043,72],{"class":58},[52,10045,10046],{"class":75},"sha384-EVSTQN3\u002FazprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC",[52,10048,72],{"class":58},[52,10050,10051],{"class":65}," crossorigin",[52,10053,69],{"class":58},[52,10055,72],{"class":58},[52,10057,10058],{"class":75},"anonymous",[52,10060,72],{"class":58},[52,10062,80],{"class":58},[52,10064,10065,10067,10069,10071,10074,10076,10078],{"class":54,"line":318},[52,10066,1575],{"class":58},[52,10068,1609],{"class":62},[52,10070,102],{"class":58},[52,10072,10073],{"class":105},"editorjs",[52,10075,108],{"class":58},[52,10077,1609],{"class":62},[52,10079,80],{"class":58},[52,10081,10082,10084,10086,10088,10090,10092,10094,10096,10098,10100,10102,10105,10107],{"class":54,"line":328},[52,10083,1575],{"class":58},[52,10085,10012],{"class":62},[52,10087,10027],{"class":65},[52,10089,69],{"class":58},[52,10091,72],{"class":58},[52,10093,10034],{"class":75},[52,10095,72],{"class":58},[52,10097,10015],{"class":65},[52,10099,69],{"class":58},[52,10101,72],{"class":58},[52,10103,10104],{"class":75},".\u002Fstyle.css",[52,10106,72],{"class":58},[52,10108,80],{"class":58},[52,10110,10111,10113,10115],{"class":54,"line":337},[52,10112,108],{"class":58},[52,10114,1578],{"class":62},[52,10116,80],{"class":58},[52,10118,10119,10121,10123],{"class":54,"line":344},[52,10120,59],{"class":58},[52,10122,1728],{"class":62},[52,10124,80],{"class":58},[52,10126,10127,10129,10131,10134,10136,10138,10141,10143],{"class":54,"line":354},[52,10128,1575],{"class":58},[52,10130,5995],{"class":62},[52,10132,10133],{"class":65}," class",[52,10135,69],{"class":58},[52,10137,72],{"class":58},[52,10139,10140],{"class":75},"container",[52,10142,72],{"class":58},[52,10144,80],{"class":58},[52,10146,10147,10149,10151,10153,10155,10157,10160,10162],{"class":54,"line":367},[52,10148,1585],{"class":58},[52,10150,5995],{"class":62},[52,10152,10133],{"class":65},[52,10154,69],{"class":58},[52,10156,72],{"class":58},[52,10158,10159],{"class":75},"mt-5 border bg-light",[52,10161,72],{"class":58},[52,10163,80],{"class":58},[52,10165,10166,10168,10170,10172,10174,10176,10178,10180,10182,10184],{"class":54,"line":387},[52,10167,1744],{"class":58},[52,10169,5995],{"class":62},[52,10171,1750],{"class":65},[52,10173,69],{"class":58},[52,10175,72],{"class":58},[52,10177,10073],{"class":75},[52,10179,72],{"class":58},[52,10181,1761],{"class":58},[52,10183,5995],{"class":62},[52,10185,80],{"class":58},[52,10187,10188,10190,10192],{"class":54,"line":415},[52,10189,1708],{"class":58},[52,10191,5995],{"class":62},[52,10193,80],{"class":58},[52,10195,10196,10198,10200,10202,10204,10206,10209,10211],{"class":54,"line":427},[52,10197,1585],{"class":58},[52,10199,5995],{"class":62},[52,10201,10133],{"class":65},[52,10203,69],{"class":58},[52,10205,72],{"class":58},[52,10207,10208],{"class":75},"mt-2",[52,10210,72],{"class":58},[52,10212,80],{"class":58},[52,10214,10215,10217,10219,10221,10223,10225,10227,10229,10231,10233],{"class":54,"line":435},[52,10216,1744],{"class":58},[52,10218,42],{"class":62},[52,10220,1750],{"class":65},[52,10222,69],{"class":58},[52,10224,72],{"class":58},[52,10226,7112],{"class":75},[52,10228,72],{"class":58},[52,10230,1761],{"class":58},[52,10232,42],{"class":62},[52,10234,80],{"class":58},[52,10236,10237,10239,10241],{"class":54,"line":446},[52,10238,1708],{"class":58},[52,10240,5995],{"class":62},[52,10242,80],{"class":58},[52,10244,10245,10247,10249],{"class":54,"line":480},[52,10246,1717],{"class":58},[52,10248,5995],{"class":62},[52,10250,80],{"class":58},[52,10252,10253,10255,10257],{"class":54,"line":509},[52,10254,108],{"class":58},[52,10256,1728],{"class":62},[52,10258,80],{"class":58},[52,10260,10261,10263,10265,10267,10269,10271,10274,10276,10278,10280],{"class":54,"line":539},[52,10262,59],{"class":58},[52,10264,349],{"class":62},[52,10266,1782],{"class":65},[52,10268,69],{"class":58},[52,10270,72],{"class":58},[52,10272,10273],{"class":75},"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@editorjs\u002Feditorjs@latest",[52,10275,72],{"class":58},[52,10277,1761],{"class":58},[52,10279,349],{"class":62},[52,10281,80],{"class":58},[52,10283,10284,10286,10288,10290,10292,10294,10296,10298,10300,10302],{"class":54,"line":547},[52,10285,59],{"class":58},[52,10287,349],{"class":62},[52,10289,1782],{"class":65},[52,10291,69],{"class":58},[52,10293,72],{"class":58},[52,10295,1789],{"class":75},[52,10297,72],{"class":58},[52,10299,1761],{"class":58},[52,10301,349],{"class":62},[52,10303,80],{"class":58},[52,10305,10306,10308,10310],{"class":54,"line":553},[52,10307,108],{"class":58},[52,10309,46],{"class":62},[52,10311,80],{"class":58},[13,10313,10314,10315,10318],{},"editor.js側はひとまず以下のようにして、",[49,10316,10317],{},"\u003Cdiv id=\"editorjs\">\u003C\u002Fdiv>","にエディターがマウントされるようにします。",[42,10320,10322],{"className":6471,"code":10321,"filename":1538,"language":1433,"meta":47,"style":47},"const editor = new EditorJS({\n    holder: 'editorjs',\n});\n",[49,10323,10324,10342,10357],{"__ignoreMap":47},[52,10325,10326,10328,10331,10333,10335,10338,10340],{"class":54,"line":55},[52,10327,6867],{"class":65},[52,10329,10330],{"class":105}," editor ",[52,10332,69],{"class":58},[52,10334,1936],{"class":58},[52,10336,10337],{"class":418}," EditorJS",[52,10339,932],{"class":105},[52,10341,364],{"class":58},[52,10343,10344,10347,10349,10351,10353,10355],{"class":54,"line":83},[52,10345,10346],{"class":62},"    holder",[52,10348,373],{"class":58},[52,10350,6067],{"class":58},[52,10352,10073],{"class":75},[52,10354,376],{"class":58},[52,10356,384],{"class":58},[52,10358,10359,10361,10363],{"class":54,"line":115},[52,10360,1311],{"class":58},[52,10362,938],{"class":105},[52,10364,1007],{"class":58},[42,10366,10370],{"className":10367,"code":10368,"filename":9880,"language":10369,"meta":47,"style":47},"language-css shiki shiki-themes material-theme-ocean",".ce-block__content{\n    background-color: white;\n}\n","css",[49,10371,10372,10381,10393],{"__ignoreMap":47},[52,10373,10374,10376,10379],{"class":54,"line":55},[52,10375,957],{"class":58},[52,10377,10378],{"class":370},"ce-block__content",[52,10380,364],{"class":58},[52,10382,10383,10386,10388,10391],{"class":54,"line":83},[52,10384,10385],{"class":1640},"    background-color",[52,10387,373],{"class":58},[52,10389,10390],{"class":105}," white",[52,10392,1007],{"class":58},[52,10394,10395],{"class":54,"line":115},[52,10396,536],{"class":58},[13,10398,10399],{},"マウントされると以下の画像のように、editor.jsがマウントされて初期状態ではテキストブロックだけが入力できます。",[729,10401],{":src":10402,":width":732},"'editorjs-repeatable\u002Finit-mounted.png'",[17,10404,10405],{"id":10405},"基本のメソッド",[13,10407,10408,10409,10414],{},"実装には",[1450,10410,10413],{"href":10411,"rel":10412},"https:\u002F\u002Feditorjs.io\u002Fthe-first-plugin",[1454],"editor.jsのドキュメント","を参考にして解説します。ドキュメントの方も合わせて作ってみることをお勧めします。",[13,10416,10417],{},"editor.jsにてブロックを実装する場合以下のメソッドを実装します。",[42,10419,10421],{"className":6471,"code":10420,"language":1433,"meta":47,"style":47},"class Accordion{\n    \u002F\u002F editor jsにブロックの情報を渡す\n    static get toolbox() {\n        return{\n            title: 'YourBlockName',\n            icon: '\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" viewBox=\"0 0 576 512\">\u003C!--! Font Awesome Pro 6.1.1 by @fontawesome - https:\u002F\u002Ffontawesome.com License - https:\u002F\u002Ffontawesome.com\u002Flicense (Commercial License) Copyright 2022 Fonticons, Inc. -->\u003Cpath d=\"M528 32H144c-26.51 0-48 21.49-48 48v256c0 26.51 21.49 48 48 48H528c26.51 0 48-21.49 48-48v-256C576 53.49 554.5 32 528 32zM223.1 96c17.68 0 32 14.33 32 32S241.7 160 223.1 160c-17.67 0-32-14.33-32-32S206.3 96 223.1 96zM494.1 311.6C491.3 316.8 485.9 320 480 320H192c-6.023 0-11.53-3.379-14.26-8.75c-2.73-5.367-2.215-11.81 1.332-16.68l70-96C252.1 194.4 256.9 192 262 192c5.111 0 9.916 2.441 12.93 6.574l22.35 30.66l62.74-94.11C362.1 130.7 367.1 128 373.3 128c5.348 0 10.34 2.672 13.31 7.125l106.7 160C496.6 300 496.9 306.3 494.1 311.6zM456 432H120c-39.7 0-72-32.3-72-72v-240C48 106.8 37.25 96 24 96S0 106.8 0 120v240C0 426.2 53.83 480 120 480h336c13.25 0 24-10.75 24-24S469.3 432 456 432z\"\u002F>\u003C\u002Fsvg>'\n        }\n    } \n\n    constructor({ data,api,config }){\n    }\n\n    render(){}\n\n    save(blockContent){}\n}\n",[49,10422,10423,10433,10438,10453,10459,10475,10490,10494,10500,10504,10527,10531,10535,10543,10547,10560],{"__ignoreMap":47},[52,10424,10425,10428,10431],{"class":54,"line":55},[52,10426,10427],{"class":65},"class",[52,10429,10430],{"class":370}," Accordion",[52,10432,364],{"class":58},[52,10434,10435],{"class":54,"line":83},[52,10436,10437],{"class":411},"    \u002F\u002F editor jsにブロックの情報を渡す\n",[52,10439,10440,10443,10446,10449,10451],{"class":54,"line":115},[52,10441,10442],{"class":65},"    static",[52,10444,10445],{"class":65}," get",[52,10447,10448],{"class":62}," toolbox",[52,10450,422],{"class":58},[52,10452,6100],{"class":58},[52,10454,10455,10457],{"class":54,"line":142},[52,10456,6128],{"class":360},[52,10458,364],{"class":58},[52,10460,10461,10464,10466,10468,10471,10473],{"class":54,"line":169},[52,10462,10463],{"class":62},"            title",[52,10465,373],{"class":58},[52,10467,6067],{"class":58},[52,10469,10470],{"class":75},"YourBlockName",[52,10472,376],{"class":58},[52,10474,384],{"class":58},[52,10476,10477,10480,10482,10484,10487],{"class":54,"line":302},[52,10478,10479],{"class":62},"            icon",[52,10481,373],{"class":58},[52,10483,6067],{"class":58},[52,10485,10486],{"class":75},"\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" viewBox=\"0 0 576 512\">\u003C!--! Font Awesome Pro 6.1.1 by @fontawesome - https:\u002F\u002Ffontawesome.com License - https:\u002F\u002Ffontawesome.com\u002Flicense (Commercial License) Copyright 2022 Fonticons, Inc. -->\u003Cpath d=\"M528 32H144c-26.51 0-48 21.49-48 48v256c0 26.51 21.49 48 48 48H528c26.51 0 48-21.49 48-48v-256C576 53.49 554.5 32 528 32zM223.1 96c17.68 0 32 14.33 32 32S241.7 160 223.1 160c-17.67 0-32-14.33-32-32S206.3 96 223.1 96zM494.1 311.6C491.3 316.8 485.9 320 480 320H192c-6.023 0-11.53-3.379-14.26-8.75c-2.73-5.367-2.215-11.81 1.332-16.68l70-96C252.1 194.4 256.9 192 262 192c5.111 0 9.916 2.441 12.93 6.574l22.35 30.66l62.74-94.11C362.1 130.7 367.1 128 373.3 128c5.348 0 10.34 2.672 13.31 7.125l106.7 160C496.6 300 496.9 306.3 494.1 311.6zM456 432H120c-39.7 0-72-32.3-72-72v-240C48 106.8 37.25 96 24 96S0 106.8 0 120v240C0 426.2 53.83 480 120 480h336c13.25 0 24-10.75 24-24S469.3 432 456 432z\"\u002F>\u003C\u002Fsvg>",[52,10488,10489],{"class":58},"'\n",[52,10491,10492],{"class":54,"line":308},[52,10493,4814],{"class":58},[52,10495,10496,10498],{"class":54,"line":318},[52,10497,9134],{"class":58},[52,10499,283],{"class":105},[52,10501,10502],{"class":54,"line":328},[52,10503,341],{"emptyLinePlaceholder":340},[52,10505,10506,10509,10512,10514,10516,10519,10521,10524],{"class":54,"line":337},[52,10507,10508],{"class":65},"    constructor",[52,10510,10511],{"class":58},"({",[52,10513,419],{"class":986},[52,10515,408],{"class":58},[52,10517,10518],{"class":986},"api",[52,10520,408],{"class":58},[52,10522,10523],{"class":986},"config",[52,10525,10526],{"class":58}," }){\n",[52,10528,10529],{"class":54,"line":344},[52,10530,2110],{"class":58},[52,10532,10533],{"class":54,"line":354},[52,10534,341],{"emptyLinePlaceholder":340},[52,10536,10537,10540],{"class":54,"line":367},[52,10538,10539],{"class":62},"    render",[52,10541,10542],{"class":58},"(){}\n",[52,10544,10545],{"class":54,"line":387},[52,10546,341],{"emptyLinePlaceholder":340},[52,10548,10549,10552,10554,10557],{"class":54,"line":415},[52,10550,10551],{"class":62},"    save",[52,10553,932],{"class":58},[52,10555,10556],{"class":986},"blockContent",[52,10558,10559],{"class":58},"){}\n",[52,10561,10562],{"class":54,"line":427},[52,10563,536],{"class":58},[13,10565,10566,10567,10570],{},"とりあえずこれら３つがあればブロックは実装できます。",[49,10568,10569],{},"static get toolbox()","はeditor.jsが呼び出し、ツールボックスのアイコンやブロック名の表示を行います。アイコンはsvgのタグまたは、画像タグを指定します。widthとheightは50ぐらいが適切です。",[13,10572,10573,10574,10576,10577,10579],{},"コンストラクターにはeditor.jsより、このブロックの初期データ",[49,10575,7112],{},"、とeditor.jsのコアにアクセスできる",[49,10578,10518],{},"が渡されます。",[13,10581,10582,10585],{},[49,10583,10584],{},"render()","はブロックが初期化された時に最初に呼び出され、ブロックのUIビューのhtmlを返すようにします。",[13,10587,10588,10591,10592,6451,10594,10596,10597,10599],{},[49,10589,10590],{},"save(blockContent){}","はエディターの保存時に呼ばれ、このブロッククラスの値を出力します。引数の",[49,10593,10556],{},[49,10595,10584],{},"にて渡されたDOMです。このメソッドはオブジェクトを返すようにします。上記の例でいう、",[49,10598,7112],{},"の箇所を出力します。",[42,10601,10603],{"className":5566,"code":10602,"language":5568,"meta":47,"style":47},"{\n    \"id\" : \"6crsAbriK8\",\n    \"type\" : \"header\",\n    \u002F\u002F save で出力する\n    \"data\" : {\n        \"text\" : \"What does it mean clean data output\",\n        \"level\" : 3\n    }\n},\n",[49,10604,10605,10609,10627,10645,10650,10662,10681,10693,10697],{"__ignoreMap":47},[52,10606,10607],{"class":54,"line":55},[52,10608,364],{"class":58},[52,10610,10611,10613,10615,10617,10619,10621,10623,10625],{"class":54,"line":83},[52,10612,5589],{"class":58},[52,10614,9350],{"class":65},[52,10616,72],{"class":58},[52,10618,8735],{"class":58},[52,10620,3013],{"class":58},[52,10622,9587],{"class":75},[52,10624,72],{"class":58},[52,10626,384],{"class":58},[52,10628,10629,10631,10633,10635,10637,10639,10641,10643],{"class":54,"line":115},[52,10630,5589],{"class":58},[52,10632,9370],{"class":65},[52,10634,72],{"class":58},[52,10636,8735],{"class":58},[52,10638,3013],{"class":58},[52,10640,9379],{"class":75},[52,10642,72],{"class":58},[52,10644,384],{"class":58},[52,10646,10647],{"class":54,"line":142},[52,10648,10649],{"class":411},"    \u002F\u002F save で出力する\n",[52,10651,10652,10654,10656,10658,10660],{"class":54,"line":169},[52,10653,5589],{"class":58},[52,10655,7112],{"class":65},[52,10657,72],{"class":58},[52,10659,8735],{"class":58},[52,10661,6100],{"class":58},[52,10663,10664,10667,10669,10671,10673,10675,10677,10679],{"class":54,"line":302},[52,10665,10666],{"class":58},"        \"",[52,10668,452],{"class":370},[52,10670,72],{"class":58},[52,10672,8735],{"class":58},[52,10674,3013],{"class":58},[52,10676,9636],{"class":75},[52,10678,72],{"class":58},[52,10680,384],{"class":58},[52,10682,10683,10685,10687,10689,10691],{"class":54,"line":308},[52,10684,10666],{"class":58},[52,10686,9422],{"class":370},[52,10688,72],{"class":58},[52,10690,8735],{"class":58},[52,10692,9653],{"class":1646},[52,10694,10695],{"class":54,"line":318},[52,10696,2110],{"class":58},[52,10698,10699,10701],{"class":54,"line":328},[52,10700,1311],{"class":58},[52,10702,384],{"class":105},[13,10704,10705],{},"とりあえず上記の４つのメソッドはブロックを作成する上で重要なものになりますので、ひとまず把握しておいてください。",[17,10707,10708],{"id":10708},"実装",[13,10710,10711],{},"とりあえずEditor.js上でブロックを選択できるようにしましょう。上記のクラスとメソッドを実装してDOMにマウントします。",[42,10713,10715],{"className":6471,"code":10714,"language":1433,"meta":47,"style":47},"class Accordion{\n    \u002F\u002F editor jsにブロックの情報を渡す\n    static get toolbox() {\n        return{\n            title: 'アコーディオン',\n            icon: 'svg' \u002F\u002F 長いので省略\n        }\n    } \n\n    constructor({ data,api,config }){\n    }\n\n    render(){}\n\n    save(blockContent){}\n}\n\nconst editor = new EditorJS({\n    holder: 'editorjs',\n    tools: { \n        accordion : {class:Accordion,inlineToolbar: true}\n    }\n});\n",[49,10716,10717,10725,10729,10741,10747,10762,10778,10782,10788,10792,10810,10814,10818,10824,10828,10838,10842,10846,10862,10876,10888,10915,10919],{"__ignoreMap":47},[52,10718,10719,10721,10723],{"class":54,"line":55},[52,10720,10427],{"class":65},[52,10722,10430],{"class":370},[52,10724,364],{"class":58},[52,10726,10727],{"class":54,"line":83},[52,10728,10437],{"class":411},[52,10730,10731,10733,10735,10737,10739],{"class":54,"line":115},[52,10732,10442],{"class":65},[52,10734,10445],{"class":65},[52,10736,10448],{"class":62},[52,10738,422],{"class":58},[52,10740,6100],{"class":58},[52,10742,10743,10745],{"class":54,"line":142},[52,10744,6128],{"class":360},[52,10746,364],{"class":58},[52,10748,10749,10751,10753,10755,10758,10760],{"class":54,"line":169},[52,10750,10463],{"class":62},[52,10752,373],{"class":58},[52,10754,6067],{"class":58},[52,10756,10757],{"class":75},"アコーディオン",[52,10759,376],{"class":58},[52,10761,384],{"class":58},[52,10763,10764,10766,10768,10770,10773,10775],{"class":54,"line":302},[52,10765,10479],{"class":62},[52,10767,373],{"class":58},[52,10769,6067],{"class":58},[52,10771,10772],{"class":75},"svg",[52,10774,376],{"class":58},[52,10776,10777],{"class":411}," \u002F\u002F 長いので省略\n",[52,10779,10780],{"class":54,"line":308},[52,10781,4814],{"class":58},[52,10783,10784,10786],{"class":54,"line":318},[52,10785,9134],{"class":58},[52,10787,283],{"class":105},[52,10789,10790],{"class":54,"line":328},[52,10791,341],{"emptyLinePlaceholder":340},[52,10793,10794,10796,10798,10800,10802,10804,10806,10808],{"class":54,"line":337},[52,10795,10508],{"class":65},[52,10797,10511],{"class":58},[52,10799,419],{"class":986},[52,10801,408],{"class":58},[52,10803,10518],{"class":986},[52,10805,408],{"class":58},[52,10807,10523],{"class":986},[52,10809,10526],{"class":58},[52,10811,10812],{"class":54,"line":344},[52,10813,2110],{"class":58},[52,10815,10816],{"class":54,"line":354},[52,10817,341],{"emptyLinePlaceholder":340},[52,10819,10820,10822],{"class":54,"line":367},[52,10821,10539],{"class":62},[52,10823,10542],{"class":58},[52,10825,10826],{"class":54,"line":387},[52,10827,341],{"emptyLinePlaceholder":340},[52,10829,10830,10832,10834,10836],{"class":54,"line":415},[52,10831,10551],{"class":62},[52,10833,932],{"class":58},[52,10835,10556],{"class":986},[52,10837,10559],{"class":58},[52,10839,10840],{"class":54,"line":427},[52,10841,536],{"class":58},[52,10843,10844],{"class":54,"line":435},[52,10845,341],{"emptyLinePlaceholder":340},[52,10847,10848,10850,10852,10854,10856,10858,10860],{"class":54,"line":446},[52,10849,6867],{"class":65},[52,10851,10330],{"class":105},[52,10853,69],{"class":58},[52,10855,1936],{"class":58},[52,10857,10337],{"class":418},[52,10859,932],{"class":105},[52,10861,364],{"class":58},[52,10863,10864,10866,10868,10870,10872,10874],{"class":54,"line":480},[52,10865,10346],{"class":62},[52,10867,373],{"class":58},[52,10869,6067],{"class":58},[52,10871,10073],{"class":75},[52,10873,376],{"class":58},[52,10875,384],{"class":58},[52,10877,10878,10881,10883,10886],{"class":54,"line":509},[52,10879,10880],{"class":62},"    tools",[52,10882,373],{"class":58},[52,10884,10885],{"class":58}," {",[52,10887,283],{"class":105},[52,10889,10890,10893,10895,10897,10899,10901,10904,10906,10909,10911,10913],{"class":54,"line":539},[52,10891,10892],{"class":62},"        accordion ",[52,10894,373],{"class":58},[52,10896,10885],{"class":58},[52,10898,10427],{"class":62},[52,10900,373],{"class":58},[52,10902,10903],{"class":105},"Accordion",[52,10905,408],{"class":58},[52,10907,10908],{"class":62},"inlineToolbar",[52,10910,373],{"class":58},[52,10912,6349],{"class":6245},[52,10914,536],{"class":58},[52,10916,10917],{"class":54,"line":547},[52,10918,2110],{"class":58},[52,10920,10921,10923,10925],{"class":54,"line":553},[52,10922,1311],{"class":58},[52,10924,938],{"class":105},[52,10926,1007],{"class":58},[13,10928,10929,10932,10933,10936,10937,10940],{},[49,10930,10931],{},"holder","のIDに指定したDOMにEditor.jsがマウントされます。そして",[49,10934,10935],{},"tools","に追加したいカスタムブロックのクラスを指定します。後でも解説しますが、インラインツールバーという斜体や太字などを使用できるようにする場合は",[49,10938,10939],{},"inlineToolbar:true","を指定します。",[729,10942],{":src":10943,":width":732},"'editorjs-repeatable\u002Fset1.png'",[13,10945,10946,10947,10949,10950,10953],{},"この時にブロックをリストから選択した時にeditor.jsにマウントされます。この時に",[49,10948,10584],{},"でreturnされた",[49,10951,10952],{},"Element","がマウントされます。まずは要素を追加するボタンを用意しましょう。",[1518,10955,10956],{"id":10956},"追加ボタンのレンダリング",[13,10958,10959,10960,10963,10964,10949,10966,10968,10969,10971],{},"ブロックを追加したい際にまずブロックの",[49,10961,10962],{},"constructor","が呼ばれます。そして",[49,10965,10584],{},[49,10967,10952],{},"がマウントされます。今は追加ですが、後ほど保存した値からブロックをレンダーする処理を追加することもあるので、",[49,10970,10962],{},"で処理処理を記述します。",[42,10973,10975],{"className":6471,"code":10974,"language":1433,"meta":47,"style":47},"class Accordion{\n    static get toolbox() {\n        return {\n          title: 'アコーディオン',\n          icon: 'svg'\n        };\n    }\n\n    constructor({data,config,api}){\n        const accordionData = data.itmes || [];\n        this.createParentWrapper();\n    }\n\n    render(){}\n\n    createParentWrapper(){}\n}\n",[49,10976,10977,10985,10997,11003,11018,11031,11036,11040,11044,11063,11088,11100,11104,11108,11114,11118,11125],{"__ignoreMap":47},[52,10978,10979,10981,10983],{"class":54,"line":55},[52,10980,10427],{"class":65},[52,10982,10430],{"class":370},[52,10984,364],{"class":58},[52,10986,10987,10989,10991,10993,10995],{"class":54,"line":83},[52,10988,10442],{"class":65},[52,10990,10445],{"class":65},[52,10992,10448],{"class":62},[52,10994,422],{"class":58},[52,10996,6100],{"class":58},[52,10998,10999,11001],{"class":54,"line":115},[52,11000,6128],{"class":360},[52,11002,6100],{"class":58},[52,11004,11005,11008,11010,11012,11014,11016],{"class":54,"line":142},[52,11006,11007],{"class":62},"          title",[52,11009,373],{"class":58},[52,11011,6067],{"class":58},[52,11013,10757],{"class":75},[52,11015,376],{"class":58},[52,11017,384],{"class":58},[52,11019,11020,11023,11025,11027,11029],{"class":54,"line":169},[52,11021,11022],{"class":62},"          icon",[52,11024,373],{"class":58},[52,11026,6067],{"class":58},[52,11028,10772],{"class":75},[52,11030,10489],{"class":58},[52,11032,11033],{"class":54,"line":302},[52,11034,11035],{"class":58},"        };\n",[52,11037,11038],{"class":54,"line":308},[52,11039,2110],{"class":58},[52,11041,11042],{"class":54,"line":318},[52,11043,341],{"emptyLinePlaceholder":340},[52,11045,11046,11048,11050,11052,11054,11056,11058,11060],{"class":54,"line":328},[52,11047,10508],{"class":65},[52,11049,10511],{"class":58},[52,11051,7112],{"class":986},[52,11053,408],{"class":58},[52,11055,10523],{"class":986},[52,11057,408],{"class":58},[52,11059,10518],{"class":986},[52,11061,11062],{"class":58},"}){\n",[52,11064,11065,11068,11071,11073,11075,11077,11080,11083,11086],{"class":54,"line":337},[52,11066,11067],{"class":65},"        const",[52,11069,11070],{"class":105}," accordionData",[52,11072,951],{"class":58},[52,11074,419],{"class":105},[52,11076,957],{"class":58},[52,11078,11079],{"class":105},"itmes",[52,11081,11082],{"class":58}," ||",[52,11084,11085],{"class":62}," []",[52,11087,1007],{"class":58},[52,11089,11090,11093,11096,11098],{"class":54,"line":344},[52,11091,11092],{"class":58},"        this.",[52,11094,11095],{"class":418},"createParentWrapper",[52,11097,422],{"class":62},[52,11099,1007],{"class":58},[52,11101,11102],{"class":54,"line":354},[52,11103,2110],{"class":58},[52,11105,11106],{"class":54,"line":367},[52,11107,341],{"emptyLinePlaceholder":340},[52,11109,11110,11112],{"class":54,"line":387},[52,11111,10539],{"class":62},[52,11113,10542],{"class":58},[52,11115,11116],{"class":54,"line":415},[52,11117,341],{"emptyLinePlaceholder":340},[52,11119,11120,11123],{"class":54,"line":427},[52,11121,11122],{"class":62},"    createParentWrapper",[52,11124,10542],{"class":58},[52,11126,11127],{"class":54,"line":435},[52,11128,536],{"class":58},[13,11130,11131,11134],{},[49,11132,11133],{},"createParentWrapper()","では最終的にアコーディオンのDOMを作成します。",[42,11136,11138],{"className":6471,"code":11137,"language":1433,"meta":47,"style":47},"createParentWrapper(){\n    const wrapper = document.createElement(\"div\");\n    const parentId = \"parent-\" + this.randomID();\n    wrapper.classList.add(\"accordion\");\n    wrapper.id = parentId;\n    wrapper.innerHTML = `\n    \u003Cbutton class=\"accordion-add btn btn-success d-block btn-sm\">\n    追加する\n    \u003C\u002Fbutton>\n    `;\n    this.wrapper = wrapper;\n}\n",[49,11139,11140,11148,11176,11203,11231,11245,11259,11264,11269,11274,11281,11294],{"__ignoreMap":47},[52,11141,11142,11144,11146],{"class":54,"line":55},[52,11143,11095],{"class":418},[52,11145,422],{"class":105},[52,11147,364],{"class":58},[52,11149,11150,11152,11155,11157,11159,11161,11164,11166,11168,11170,11172,11174],{"class":54,"line":83},[52,11151,8519],{"class":65},[52,11153,11154],{"class":105}," wrapper",[52,11156,951],{"class":58},[52,11158,1851],{"class":105},[52,11160,957],{"class":58},[52,11162,11163],{"class":418},"createElement",[52,11165,932],{"class":62},[52,11167,72],{"class":58},[52,11169,5995],{"class":75},[52,11171,72],{"class":58},[52,11173,938],{"class":62},[52,11175,1007],{"class":58},[52,11177,11178,11180,11183,11185,11187,11190,11192,11194,11196,11199,11201],{"class":54,"line":115},[52,11179,8519],{"class":65},[52,11181,11182],{"class":105}," parentId",[52,11184,951],{"class":58},[52,11186,3013],{"class":58},[52,11188,11189],{"class":75},"parent-",[52,11191,72],{"class":58},[52,11193,8634],{"class":58},[52,11195,6370],{"class":58},[52,11197,11198],{"class":418},"randomID",[52,11200,422],{"class":62},[52,11202,1007],{"class":58},[52,11204,11205,11208,11210,11213,11215,11218,11220,11222,11225,11227,11229],{"class":54,"line":142},[52,11206,11207],{"class":105},"    wrapper",[52,11209,957],{"class":58},[52,11211,11212],{"class":105},"classList",[52,11214,957],{"class":58},[52,11216,11217],{"class":418},"add",[52,11219,932],{"class":62},[52,11221,72],{"class":58},[52,11223,11224],{"class":75},"accordion",[52,11226,72],{"class":58},[52,11228,938],{"class":62},[52,11230,1007],{"class":58},[52,11232,11233,11235,11237,11239,11241,11243],{"class":54,"line":169},[52,11234,11207],{"class":105},[52,11236,957],{"class":58},[52,11238,9350],{"class":105},[52,11240,951],{"class":58},[52,11242,11182],{"class":105},[52,11244,1007],{"class":58},[52,11246,11247,11249,11251,11254,11256],{"class":54,"line":302},[52,11248,11207],{"class":105},[52,11250,957],{"class":58},[52,11252,11253],{"class":105},"innerHTML",[52,11255,951],{"class":58},[52,11257,11258],{"class":58}," `\n",[52,11260,11261],{"class":54,"line":308},[52,11262,11263],{"class":75},"    \u003Cbutton class=\"accordion-add btn btn-success d-block btn-sm\">\n",[52,11265,11266],{"class":54,"line":318},[52,11267,11268],{"class":75},"    追加する\n",[52,11270,11271],{"class":54,"line":328},[52,11272,11273],{"class":75},"    \u003C\u002Fbutton>\n",[52,11275,11276,11279],{"class":54,"line":337},[52,11277,11278],{"class":58},"    `",[52,11280,1007],{"class":58},[52,11282,11283,11285,11288,11290,11292],{"class":54,"line":344},[52,11284,1052],{"class":58},[52,11286,11287],{"class":105},"wrapper",[52,11289,951],{"class":58},[52,11291,11154],{"class":105},[52,11293,1007],{"class":58},[52,11295,11296],{"class":54,"line":354},[52,11297,536],{"class":58},[13,11299,11300],{},"こうすると追加するボタンが追加されたはずです。",[729,11302],{":src":11303,":width":732},"'editorjs-repeatable\u002Fset2.png'",[13,11305,11306],{},"この追加するボタンを押したらアコーディオンの要素が追加されるようにします。",[42,11308,11310],{"className":6471,"code":11309,"language":1433,"meta":47,"style":47},"randomID(){\n    let result           = '';\n    let characters       = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\n    let charactersLength = characters.length;\n    for ( let i = 0; i \u003C 10; i++ ) {\n        result += characters.charAt(Math.floor(Math.random() * \n        charactersLength));\n    }\n    return result;\n}\n\ncreateParentWrapper(){\n    const wrapper = document.createElement(\"div\");\n    const parentId = \"parent-\" + this.randomID();\n    wrapper.classList.add(\"accordion\");\n    wrapper.id = parentId;\n    wrapper.innerHTML = `\n    \u003Cbutton class=\"accordion-add btn btn-success d-block btn-sm\">\n    追加する\n    \u003C\u002Fbutton>\n    `;\n\n    wrapper.querySelector(\".accordion-add\").addEventListener(\"click\",()=>{\n        this.addItemEle();\n    });\n    this.wrapper = wrapper;\n}\n\naddItemEle(title=\"\",content=\"\"){\n    this.wrapper.insertBefore(this.generateItemEle(title,content),this.wrapper.querySelector(\".accordion-add\"))\n}\n\ngenerateItemEle(title,content){}\n",[49,11311,11312,11320,11335,11354,11372,11409,11447,11456,11460,11468,11472,11476,11484,11510,11534,11558,11572,11584,11588,11592,11596,11602,11606,11647,11658,11666,11678,11682,11686,11711,11759,11763,11767],{"__ignoreMap":47},[52,11313,11314,11316,11318],{"class":54,"line":55},[52,11315,11198],{"class":418},[52,11317,422],{"class":105},[52,11319,364],{"class":58},[52,11321,11322,11324,11327,11330,11333],{"class":54,"line":83},[52,11323,945],{"class":65},[52,11325,11326],{"class":105}," result",[52,11328,11329],{"class":58},"           =",[52,11331,11332],{"class":58}," ''",[52,11334,1007],{"class":58},[52,11336,11337,11339,11342,11345,11347,11350,11352],{"class":54,"line":115},[52,11338,945],{"class":65},[52,11340,11341],{"class":105}," characters",[52,11343,11344],{"class":58},"       =",[52,11346,6067],{"class":58},[52,11348,11349],{"class":75},"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",[52,11351,376],{"class":58},[52,11353,1007],{"class":58},[52,11355,11356,11358,11361,11363,11365,11367,11370],{"class":54,"line":142},[52,11357,945],{"class":65},[52,11359,11360],{"class":105}," charactersLength",[52,11362,951],{"class":58},[52,11364,11341],{"class":105},[52,11366,957],{"class":58},[52,11368,11369],{"class":105},"length",[52,11371,1007],{"class":58},[52,11373,11374,11376,11379,11381,11384,11386,11388,11390,11392,11394,11397,11399,11401,11404,11407],{"class":54,"line":169},[52,11375,3063],{"class":360},[52,11377,11378],{"class":62}," ( ",[52,11380,1258],{"class":65},[52,11382,11383],{"class":105}," i",[52,11385,951],{"class":58},[52,11387,2749],{"class":1646},[52,11389,3077],{"class":58},[52,11391,11383],{"class":105},[52,11393,86],{"class":58},[52,11395,11396],{"class":1646}," 10",[52,11398,3077],{"class":58},[52,11400,11383],{"class":105},[52,11402,11403],{"class":58},"++",[52,11405,11406],{"class":62}," ) ",[52,11408,364],{"class":58},[52,11410,11411,11414,11416,11418,11420,11423,11425,11427,11429,11432,11434,11436,11438,11441,11443,11445],{"class":54,"line":302},[52,11412,11413],{"class":105},"        result",[52,11415,4299],{"class":58},[52,11417,11341],{"class":105},[52,11419,957],{"class":58},[52,11421,11422],{"class":418},"charAt",[52,11424,932],{"class":62},[52,11426,3121],{"class":105},[52,11428,957],{"class":58},[52,11430,11431],{"class":418},"floor",[52,11433,932],{"class":62},[52,11435,3121],{"class":105},[52,11437,957],{"class":58},[52,11439,11440],{"class":418},"random",[52,11442,8540],{"class":62},[52,11444,3118],{"class":58},[52,11446,283],{"class":62},[52,11448,11449,11452,11454],{"class":54,"line":308},[52,11450,11451],{"class":105},"        charactersLength",[52,11453,3157],{"class":62},[52,11455,1007],{"class":58},[52,11457,11458],{"class":54,"line":318},[52,11459,2110],{"class":58},[52,11461,11462,11464,11466],{"class":54,"line":328},[52,11463,7881],{"class":360},[52,11465,11326],{"class":105},[52,11467,1007],{"class":58},[52,11469,11470],{"class":54,"line":337},[52,11471,536],{"class":58},[52,11473,11474],{"class":54,"line":344},[52,11475,341],{"emptyLinePlaceholder":340},[52,11477,11478,11480,11482],{"class":54,"line":354},[52,11479,11095],{"class":418},[52,11481,422],{"class":105},[52,11483,364],{"class":58},[52,11485,11486,11488,11490,11492,11494,11496,11498,11500,11502,11504,11506,11508],{"class":54,"line":367},[52,11487,8519],{"class":65},[52,11489,11154],{"class":105},[52,11491,951],{"class":58},[52,11493,1851],{"class":105},[52,11495,957],{"class":58},[52,11497,11163],{"class":418},[52,11499,932],{"class":62},[52,11501,72],{"class":58},[52,11503,5995],{"class":75},[52,11505,72],{"class":58},[52,11507,938],{"class":62},[52,11509,1007],{"class":58},[52,11511,11512,11514,11516,11518,11520,11522,11524,11526,11528,11530,11532],{"class":54,"line":387},[52,11513,8519],{"class":65},[52,11515,11182],{"class":105},[52,11517,951],{"class":58},[52,11519,3013],{"class":58},[52,11521,11189],{"class":75},[52,11523,72],{"class":58},[52,11525,8634],{"class":58},[52,11527,6370],{"class":58},[52,11529,11198],{"class":418},[52,11531,422],{"class":62},[52,11533,1007],{"class":58},[52,11535,11536,11538,11540,11542,11544,11546,11548,11550,11552,11554,11556],{"class":54,"line":415},[52,11537,11207],{"class":105},[52,11539,957],{"class":58},[52,11541,11212],{"class":105},[52,11543,957],{"class":58},[52,11545,11217],{"class":418},[52,11547,932],{"class":62},[52,11549,72],{"class":58},[52,11551,11224],{"class":75},[52,11553,72],{"class":58},[52,11555,938],{"class":62},[52,11557,1007],{"class":58},[52,11559,11560,11562,11564,11566,11568,11570],{"class":54,"line":427},[52,11561,11207],{"class":105},[52,11563,957],{"class":58},[52,11565,9350],{"class":105},[52,11567,951],{"class":58},[52,11569,11182],{"class":105},[52,11571,1007],{"class":58},[52,11573,11574,11576,11578,11580,11582],{"class":54,"line":435},[52,11575,11207],{"class":105},[52,11577,957],{"class":58},[52,11579,11253],{"class":105},[52,11581,951],{"class":58},[52,11583,11258],{"class":58},[52,11585,11586],{"class":54,"line":446},[52,11587,11263],{"class":75},[52,11589,11590],{"class":54,"line":480},[52,11591,11268],{"class":75},[52,11593,11594],{"class":54,"line":509},[52,11595,11273],{"class":75},[52,11597,11598,11600],{"class":54,"line":539},[52,11599,11278],{"class":58},[52,11601,1007],{"class":58},[52,11603,11604],{"class":54,"line":547},[52,11605,341],{"emptyLinePlaceholder":340},[52,11607,11608,11610,11612,11615,11617,11619,11622,11624,11626,11628,11631,11633,11635,11638,11640,11643,11645],{"class":54,"line":553},[52,11609,11207],{"class":105},[52,11611,957],{"class":58},[52,11613,11614],{"class":418},"querySelector",[52,11616,932],{"class":62},[52,11618,72],{"class":58},[52,11620,11621],{"class":75},".accordion-add",[52,11623,72],{"class":58},[52,11625,938],{"class":62},[52,11627,957],{"class":58},[52,11629,11630],{"class":418},"addEventListener",[52,11632,932],{"class":62},[52,11634,72],{"class":58},[52,11636,11637],{"class":75},"click",[52,11639,72],{"class":58},[52,11641,11642],{"class":58},",()",[52,11644,990],{"class":65},[52,11646,364],{"class":58},[52,11648,11649,11651,11654,11656],{"class":54,"line":559},[52,11650,11092],{"class":58},[52,11652,11653],{"class":418},"addItemEle",[52,11655,422],{"class":62},[52,11657,1007],{"class":58},[52,11659,11660,11662,11664],{"class":54,"line":564},[52,11661,9134],{"class":58},[52,11663,938],{"class":62},[52,11665,1007],{"class":58},[52,11667,11668,11670,11672,11674,11676],{"class":54,"line":569},[52,11669,1052],{"class":58},[52,11671,11287],{"class":105},[52,11673,951],{"class":58},[52,11675,11154],{"class":105},[52,11677,1007],{"class":58},[52,11679,11680],{"class":54,"line":1106},[52,11681,536],{"class":58},[52,11683,11684],{"class":54,"line":1135},[52,11685,341],{"emptyLinePlaceholder":340},[52,11687,11688,11690,11693,11695,11698,11700,11703,11705,11707,11709],{"class":54,"line":1164},[52,11689,11653],{"class":418},[52,11691,11692],{"class":105},"(title",[52,11694,69],{"class":58},[52,11696,11697],{"class":58},"\"\"",[52,11699,408],{"class":58},[52,11701,11702],{"class":105},"content",[52,11704,69],{"class":58},[52,11706,11697],{"class":58},[52,11708,938],{"class":105},[52,11710,364],{"class":58},[52,11712,11713,11715,11717,11719,11722,11724,11726,11729,11731,11733,11735,11737,11739,11742,11744,11746,11748,11750,11752,11754,11756],{"class":54,"line":4},[52,11714,1052],{"class":58},[52,11716,11287],{"class":105},[52,11718,957],{"class":58},[52,11720,11721],{"class":418},"insertBefore",[52,11723,932],{"class":62},[52,11725,1370],{"class":58},[52,11727,11728],{"class":418},"generateItemEle",[52,11730,932],{"class":62},[52,11732,1609],{"class":105},[52,11734,408],{"class":58},[52,11736,11702],{"class":105},[52,11738,938],{"class":62},[52,11740,11741],{"class":58},",this.",[52,11743,11287],{"class":105},[52,11745,957],{"class":58},[52,11747,11614],{"class":418},[52,11749,932],{"class":62},[52,11751,72],{"class":58},[52,11753,11621],{"class":75},[52,11755,72],{"class":58},[52,11757,11758],{"class":62},"))\n",[52,11760,11761],{"class":54,"line":1199},[52,11762,536],{"class":58},[52,11764,11765],{"class":54,"line":1204},[52,11766,341],{"emptyLinePlaceholder":340},[52,11768,11769,11771,11773,11775,11778],{"class":54,"line":1209},[52,11770,11728],{"class":418},[52,11772,11692],{"class":105},[52,11774,408],{"class":58},[52,11776,11777],{"class":105},"content)",[52,11779,11780],{"class":58},"{}\n",[13,11782,11783,11786,11787,11790],{},[49,11784,11785],{},"addItemEle()","を呼ぶことでアイテムのDOMが挿入されるようにします。DOM自体は",[49,11788,11789],{},"generateItemEle()","で生成されるようにします。",[13,11792,11793,11794,11796],{},"次は",[49,11795,11789],{},"で生成されるDOMの記述を作成します。",[1518,11798,11799],{"id":11799},"要素の生成",[13,11801,11802,11804,11805,11808,11809,4209,11811,11813,11814,11816],{},[49,11803,11789],{},"では",[49,11806,11807],{},"document.createElement","を使用してbootstrap通りのHTMLを作成します。",[49,11810,1609],{},[49,11812,11702],{},"ではリッチテキストを挿入するために",[49,11815,11253],{},"で入れています。実際に表示する際はXSSの危険があるので入力値の検証が必要です。",[42,11818,11820],{"className":6471,"code":11819,"language":1433,"meta":47,"style":47},"generateItemEle(title,content){\n        const itemWrapper = document.createElement(\"div\");\n        const wrapperId = this.randomID();\n        itemWrapper.id = wrapperId;\n        itemWrapper.classList.add(\"accordion-item\");\n\n        const itemHeaderWrapper = document.createElement(\"h2\");\n        itemHeaderWrapper.classList.add(\"accordion-header\");\n        const collapseId = wrapperId+\"-collapse\";\n        itemHeaderWrapper.innerHTML = `\n        \u003Cbutton class=\"accordion-button\" type=\"button\" data-bs-toggle=\"collapse\">\n            \u003Cdiv class=\"accordion-button-wrapper\">${title}\u003C\u002Fdiv>\n        \u003C\u002Fbutton>\n        `;\n        itemHeaderWrapper.querySelector('.accordion-button-wrapper').contentEditable = true;\n\n        const itemBodyWrapper = document.createElement(\"div\");\n        itemBodyWrapper.classList.add(\"accordion-collapse\",\"collapse\",\"show\");\n        itemBodyWrapper.id = collapseId;\n        itemBodyWrapper.innerHTML = `\n        \u003Cdiv class=\"accordion-body\">\n            \u003Cp class=\"accordion-body-input\">${content}\u003C\u002Fp>\n            \u003Cdiv class=\"mt-2\">\n            \u003Cbutton class=\"accordion-delete btn btn-danger btn-sm\">削除\u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        `;\n        itemBodyWrapper.querySelector('.accordion-body-input').contentEditable = true;\n        itemBodyWrapper.querySelector('.accordion-delete').addEventListener('click',()=>{\n            if(window.confirm(\"削除してもよろしいですか？\")) itemWrapper.remove();\n        });\n\n        itemWrapper.appendChild(itemHeaderWrapper);\n        itemWrapper.appendChild(itemBodyWrapper);\n        return itemWrapper;\n        \u002F*\n        生成イメージ\n        \u003Cdiv class=\"accordion-item\">\n            \u003Ch2 class=\"accordion-header\" id=\"headingOne\">\n                \u003Cbutton class=\"accordion-button\" type=\"button\" data-bs-toggle=\"collapse\" >\n                    \u003Cdiv class=\"accordion-button-wrapper\">${title}\u003C\u002Fdiv>\n                \u003C\u002Fbutton>\n            \u003C\u002Fh2>\n            \u003Cdiv id=\"${wrapperId}-collaps\" class=\"accordion-collapse collapse show\">\n                \u003Cdiv class=\"accordion-body\">\n                    \u003Cp class=\"accordion-body-input\">${content}\u003C\u002Fp>\n                    \u003Cbutton class=\"accordion-delete btn btn-danger mt-2 btn-sm\">\n                    削除\n                    \u003C\u002Fbutton>\n                \u003C\u002Fdiv>\n            \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        *\u002F\n    }\n",[49,11821,11822,11834,11861,11878,11893,11918,11922,11949,11975,11997,12009,12014,12029,12034,12041,12071,12075,12102,12146,12160,12172,12177,12191,12196,12201,12206,12211,12217,12246,12283,12321,12330,12334,12352,12369,12377,12382,12387,12392,12397,12402,12407,12412,12417,12422,12427,12432,12437,12442,12447,12452,12456,12460,12465],{"__ignoreMap":47},[52,11823,11824,11826,11828,11830,11832],{"class":54,"line":55},[52,11825,11728],{"class":418},[52,11827,11692],{"class":105},[52,11829,408],{"class":58},[52,11831,11777],{"class":105},[52,11833,364],{"class":58},[52,11835,11836,11838,11841,11843,11845,11847,11849,11851,11853,11855,11857,11859],{"class":54,"line":83},[52,11837,11067],{"class":65},[52,11839,11840],{"class":105}," itemWrapper",[52,11842,951],{"class":58},[52,11844,1851],{"class":105},[52,11846,957],{"class":58},[52,11848,11163],{"class":418},[52,11850,932],{"class":62},[52,11852,72],{"class":58},[52,11854,5995],{"class":75},[52,11856,72],{"class":58},[52,11858,938],{"class":62},[52,11860,1007],{"class":58},[52,11862,11863,11865,11868,11870,11872,11874,11876],{"class":54,"line":115},[52,11864,11067],{"class":65},[52,11866,11867],{"class":105}," wrapperId",[52,11869,951],{"class":58},[52,11871,6370],{"class":58},[52,11873,11198],{"class":418},[52,11875,422],{"class":62},[52,11877,1007],{"class":58},[52,11879,11880,11883,11885,11887,11889,11891],{"class":54,"line":142},[52,11881,11882],{"class":105},"        itemWrapper",[52,11884,957],{"class":58},[52,11886,9350],{"class":105},[52,11888,951],{"class":58},[52,11890,11867],{"class":105},[52,11892,1007],{"class":58},[52,11894,11895,11897,11899,11901,11903,11905,11907,11909,11912,11914,11916],{"class":54,"line":169},[52,11896,11882],{"class":105},[52,11898,957],{"class":58},[52,11900,11212],{"class":105},[52,11902,957],{"class":58},[52,11904,11217],{"class":418},[52,11906,932],{"class":62},[52,11908,72],{"class":58},[52,11910,11911],{"class":75},"accordion-item",[52,11913,72],{"class":58},[52,11915,938],{"class":62},[52,11917,1007],{"class":58},[52,11919,11920],{"class":54,"line":302},[52,11921,341],{"emptyLinePlaceholder":340},[52,11923,11924,11926,11929,11931,11933,11935,11937,11939,11941,11943,11945,11947],{"class":54,"line":308},[52,11925,11067],{"class":65},[52,11927,11928],{"class":105}," itemHeaderWrapper",[52,11930,951],{"class":58},[52,11932,1851],{"class":105},[52,11934,957],{"class":58},[52,11936,11163],{"class":418},[52,11938,932],{"class":62},[52,11940,72],{"class":58},[52,11942,17],{"class":75},[52,11944,72],{"class":58},[52,11946,938],{"class":62},[52,11948,1007],{"class":58},[52,11950,11951,11954,11956,11958,11960,11962,11964,11966,11969,11971,11973],{"class":54,"line":318},[52,11952,11953],{"class":105},"        itemHeaderWrapper",[52,11955,957],{"class":58},[52,11957,11212],{"class":105},[52,11959,957],{"class":58},[52,11961,11217],{"class":418},[52,11963,932],{"class":62},[52,11965,72],{"class":58},[52,11967,11968],{"class":75},"accordion-header",[52,11970,72],{"class":58},[52,11972,938],{"class":62},[52,11974,1007],{"class":58},[52,11976,11977,11979,11982,11984,11986,11988,11990,11993,11995],{"class":54,"line":328},[52,11978,11067],{"class":65},[52,11980,11981],{"class":105}," collapseId",[52,11983,951],{"class":58},[52,11985,11867],{"class":105},[52,11987,3151],{"class":58},[52,11989,72],{"class":58},[52,11991,11992],{"class":75},"-collapse",[52,11994,72],{"class":58},[52,11996,1007],{"class":58},[52,11998,11999,12001,12003,12005,12007],{"class":54,"line":337},[52,12000,11953],{"class":105},[52,12002,957],{"class":58},[52,12004,11253],{"class":105},[52,12006,951],{"class":58},[52,12008,11258],{"class":58},[52,12010,12011],{"class":54,"line":344},[52,12012,12013],{"class":75},"        \u003Cbutton class=\"accordion-button\" type=\"button\" data-bs-toggle=\"collapse\">\n",[52,12015,12016,12019,12022,12024,12026],{"class":54,"line":354},[52,12017,12018],{"class":75},"            \u003Cdiv class=\"accordion-button-wrapper\">",[52,12020,12021],{"class":58},"${",[52,12023,1609],{"class":105},[52,12025,1311],{"class":58},[52,12027,12028],{"class":75},"\u003C\u002Fdiv>\n",[52,12030,12031],{"class":54,"line":367},[52,12032,12033],{"class":75},"        \u003C\u002Fbutton>\n",[52,12035,12036,12039],{"class":54,"line":387},[52,12037,12038],{"class":58},"        `",[52,12040,1007],{"class":58},[52,12042,12043,12045,12047,12049,12051,12053,12056,12058,12060,12062,12065,12067,12069],{"class":54,"line":415},[52,12044,11953],{"class":105},[52,12046,957],{"class":58},[52,12048,11614],{"class":418},[52,12050,932],{"class":62},[52,12052,376],{"class":58},[52,12054,12055],{"class":75},".accordion-button-wrapper",[52,12057,376],{"class":58},[52,12059,938],{"class":62},[52,12061,957],{"class":58},[52,12063,12064],{"class":105},"contentEditable",[52,12066,951],{"class":58},[52,12068,6349],{"class":6245},[52,12070,1007],{"class":58},[52,12072,12073],{"class":54,"line":427},[52,12074,341],{"emptyLinePlaceholder":340},[52,12076,12077,12079,12082,12084,12086,12088,12090,12092,12094,12096,12098,12100],{"class":54,"line":435},[52,12078,11067],{"class":65},[52,12080,12081],{"class":105}," itemBodyWrapper",[52,12083,951],{"class":58},[52,12085,1851],{"class":105},[52,12087,957],{"class":58},[52,12089,11163],{"class":418},[52,12091,932],{"class":62},[52,12093,72],{"class":58},[52,12095,5995],{"class":75},[52,12097,72],{"class":58},[52,12099,938],{"class":62},[52,12101,1007],{"class":58},[52,12103,12104,12107,12109,12111,12113,12115,12117,12119,12122,12124,12126,12128,12131,12133,12135,12137,12140,12142,12144],{"class":54,"line":446},[52,12105,12106],{"class":105},"        itemBodyWrapper",[52,12108,957],{"class":58},[52,12110,11212],{"class":105},[52,12112,957],{"class":58},[52,12114,11217],{"class":418},[52,12116,932],{"class":62},[52,12118,72],{"class":58},[52,12120,12121],{"class":75},"accordion-collapse",[52,12123,72],{"class":58},[52,12125,408],{"class":58},[52,12127,72],{"class":58},[52,12129,12130],{"class":75},"collapse",[52,12132,72],{"class":58},[52,12134,408],{"class":58},[52,12136,72],{"class":58},[52,12138,12139],{"class":75},"show",[52,12141,72],{"class":58},[52,12143,938],{"class":62},[52,12145,1007],{"class":58},[52,12147,12148,12150,12152,12154,12156,12158],{"class":54,"line":480},[52,12149,12106],{"class":105},[52,12151,957],{"class":58},[52,12153,9350],{"class":105},[52,12155,951],{"class":58},[52,12157,11981],{"class":105},[52,12159,1007],{"class":58},[52,12161,12162,12164,12166,12168,12170],{"class":54,"line":509},[52,12163,12106],{"class":105},[52,12165,957],{"class":58},[52,12167,11253],{"class":105},[52,12169,951],{"class":58},[52,12171,11258],{"class":58},[52,12173,12174],{"class":54,"line":539},[52,12175,12176],{"class":75},"        \u003Cdiv class=\"accordion-body\">\n",[52,12178,12179,12182,12184,12186,12188],{"class":54,"line":547},[52,12180,12181],{"class":75},"            \u003Cp class=\"accordion-body-input\">",[52,12183,12021],{"class":58},[52,12185,11702],{"class":105},[52,12187,1311],{"class":58},[52,12189,12190],{"class":75},"\u003C\u002Fp>\n",[52,12192,12193],{"class":54,"line":553},[52,12194,12195],{"class":75},"            \u003Cdiv class=\"mt-2\">\n",[52,12197,12198],{"class":54,"line":559},[52,12199,12200],{"class":75},"            \u003Cbutton class=\"accordion-delete btn btn-danger btn-sm\">削除\u003C\u002Fbutton>\n",[52,12202,12203],{"class":54,"line":564},[52,12204,12205],{"class":75},"            \u003C\u002Fdiv>\n",[52,12207,12208],{"class":54,"line":569},[52,12209,12210],{"class":75},"        \u003C\u002Fdiv>\n",[52,12212,12213,12215],{"class":54,"line":1106},[52,12214,12038],{"class":58},[52,12216,1007],{"class":58},[52,12218,12219,12221,12223,12225,12227,12229,12232,12234,12236,12238,12240,12242,12244],{"class":54,"line":1135},[52,12220,12106],{"class":105},[52,12222,957],{"class":58},[52,12224,11614],{"class":418},[52,12226,932],{"class":62},[52,12228,376],{"class":58},[52,12230,12231],{"class":75},".accordion-body-input",[52,12233,376],{"class":58},[52,12235,938],{"class":62},[52,12237,957],{"class":58},[52,12239,12064],{"class":105},[52,12241,951],{"class":58},[52,12243,6349],{"class":6245},[52,12245,1007],{"class":58},[52,12247,12248,12250,12252,12254,12256,12258,12261,12263,12265,12267,12269,12271,12273,12275,12277,12279,12281],{"class":54,"line":1164},[52,12249,12106],{"class":105},[52,12251,957],{"class":58},[52,12253,11614],{"class":418},[52,12255,932],{"class":62},[52,12257,376],{"class":58},[52,12259,12260],{"class":75},".accordion-delete",[52,12262,376],{"class":58},[52,12264,938],{"class":62},[52,12266,957],{"class":58},[52,12268,11630],{"class":418},[52,12270,932],{"class":62},[52,12272,376],{"class":58},[52,12274,11637],{"class":75},[52,12276,376],{"class":58},[52,12278,11642],{"class":58},[52,12280,990],{"class":65},[52,12282,364],{"class":58},[52,12284,12285,12288,12290,12292,12294,12297,12299,12301,12304,12306,12309,12312,12314,12317,12319],{"class":54,"line":4},[52,12286,12287],{"class":360},"            if",[52,12289,932],{"class":62},[52,12291,1983],{"class":105},[52,12293,957],{"class":58},[52,12295,12296],{"class":418},"confirm",[52,12298,932],{"class":62},[52,12300,72],{"class":58},[52,12302,12303],{"class":75},"削除してもよろしいですか？",[52,12305,72],{"class":58},[52,12307,12308],{"class":62},")) ",[52,12310,12311],{"class":105},"itemWrapper",[52,12313,957],{"class":58},[52,12315,12316],{"class":418},"remove",[52,12318,422],{"class":62},[52,12320,1007],{"class":58},[52,12322,12323,12326,12328],{"class":54,"line":1199},[52,12324,12325],{"class":58},"        }",[52,12327,938],{"class":62},[52,12329,1007],{"class":58},[52,12331,12332],{"class":54,"line":1204},[52,12333,341],{"emptyLinePlaceholder":340},[52,12335,12336,12338,12340,12343,12345,12348,12350],{"class":54,"line":1209},[52,12337,11882],{"class":105},[52,12339,957],{"class":58},[52,12341,12342],{"class":418},"appendChild",[52,12344,932],{"class":62},[52,12346,12347],{"class":105},"itemHeaderWrapper",[52,12349,938],{"class":62},[52,12351,1007],{"class":58},[52,12353,12354,12356,12358,12360,12362,12365,12367],{"class":54,"line":1214},[52,12355,11882],{"class":105},[52,12357,957],{"class":58},[52,12359,12342],{"class":418},[52,12361,932],{"class":62},[52,12363,12364],{"class":105},"itemBodyWrapper",[52,12366,938],{"class":62},[52,12368,1007],{"class":58},[52,12370,12371,12373,12375],{"class":54,"line":1219},[52,12372,6128],{"class":360},[52,12374,11840],{"class":105},[52,12376,1007],{"class":58},[52,12378,12379],{"class":54,"line":3216},[52,12380,12381],{"class":411},"        \u002F*\n",[52,12383,12384],{"class":54,"line":3237},[52,12385,12386],{"class":411},"        生成イメージ\n",[52,12388,12389],{"class":54,"line":3251},[52,12390,12391],{"class":411},"        \u003Cdiv class=\"accordion-item\">\n",[52,12393,12394],{"class":54,"line":3256},[52,12395,12396],{"class":411},"            \u003Ch2 class=\"accordion-header\" id=\"headingOne\">\n",[52,12398,12399],{"class":54,"line":3281},[52,12400,12401],{"class":411},"                \u003Cbutton class=\"accordion-button\" type=\"button\" data-bs-toggle=\"collapse\" >\n",[52,12403,12404],{"class":54,"line":3295},[52,12405,12406],{"class":411},"                    \u003Cdiv class=\"accordion-button-wrapper\">${title}\u003C\u002Fdiv>\n",[52,12408,12409],{"class":54,"line":4922},[52,12410,12411],{"class":411},"                \u003C\u002Fbutton>\n",[52,12413,12414],{"class":54,"line":4943},[52,12415,12416],{"class":411},"            \u003C\u002Fh2>\n",[52,12418,12419],{"class":54,"line":4961},[52,12420,12421],{"class":411},"            \u003Cdiv id=\"${wrapperId}-collaps\" class=\"accordion-collapse collapse show\">\n",[52,12423,12424],{"class":54,"line":4978},[52,12425,12426],{"class":411},"                \u003Cdiv class=\"accordion-body\">\n",[52,12428,12429],{"class":54,"line":4983},[52,12430,12431],{"class":411},"                    \u003Cp class=\"accordion-body-input\">${content}\u003C\u002Fp>\n",[52,12433,12434],{"class":54,"line":5004},[52,12435,12436],{"class":411},"                    \u003Cbutton class=\"accordion-delete btn btn-danger mt-2 btn-sm\">\n",[52,12438,12439],{"class":54,"line":5053},[52,12440,12441],{"class":411},"                    削除\n",[52,12443,12444],{"class":54,"line":5058},[52,12445,12446],{"class":411},"                    \u003C\u002Fbutton>\n",[52,12448,12449],{"class":54,"line":5063},[52,12450,12451],{"class":411},"                \u003C\u002Fdiv>\n",[52,12453,12454],{"class":54,"line":5096},[52,12455,12205],{"class":411},[52,12457,12458],{"class":54,"line":5109},[52,12459,12210],{"class":411},[52,12461,12462],{"class":54,"line":5128},[52,12463,12464],{"class":411},"        *\u002F\n",[52,12466,12467],{"class":54,"line":5141},[52,12468,2110],{"class":58},[13,12470,12471],{},"そしてアイテムのHTMLを返すようにし、wrapperに挿入します。ここまで実装し、ボタンをクリックした時に以下のようにアコーディオンが表示されます。",[729,12473],{":src":12474,":width":732},"'editorjs-repeatable\u002Fset3.png'",[13,12476,12477,12478,12480],{},"また",[49,12479,12064],{},"をtrueにすることで、",[42,12482,12485],{"className":12483,"code":12484,"language":452},[5819],"itemHeaderWrapper.querySelector('.accordion-button-wrapper').contentEditable = true;\nitemBodyWrapper.querySelector('.accordion-body-input').contentEditable = true;\n",[49,12486,12484],{"__ignoreMap":47},[13,12488,12489],{},"以下の図のように文字の入力がinputなしでできるようになります。",[729,12491],{":src":12492,":width":732},"'editorjs-repeatable\u002Fset4.png'",[13,12494,12495],{},"そして以下のように削除ボタンにイベントリスナーを実装することで要素の削除ができるようになります。",[42,12497,12499],{"className":6471,"code":12498,"language":1433,"meta":47,"style":47},"itemBodyWrapper.querySelector('.accordion-delete').addEventListener('click',()=>{\n    if(window.confirm(\"削除してもよろしいですか？\")) itemWrapper.remove();\n});\n",[49,12500,12501,12537,12569],{"__ignoreMap":47},[52,12502,12503,12505,12507,12509,12511,12513,12515,12517,12519,12521,12523,12525,12527,12529,12531,12533,12535],{"class":54,"line":55},[52,12504,12364],{"class":105},[52,12506,957],{"class":58},[52,12508,11614],{"class":418},[52,12510,932],{"class":105},[52,12512,376],{"class":58},[52,12514,12260],{"class":75},[52,12516,376],{"class":58},[52,12518,938],{"class":105},[52,12520,957],{"class":58},[52,12522,11630],{"class":418},[52,12524,932],{"class":105},[52,12526,376],{"class":58},[52,12528,11637],{"class":75},[52,12530,376],{"class":58},[52,12532,11642],{"class":58},[52,12534,990],{"class":65},[52,12536,364],{"class":58},[52,12538,12539,12541,12543,12545,12547,12549,12551,12553,12555,12557,12559,12561,12563,12565,12567],{"class":54,"line":83},[52,12540,8812],{"class":360},[52,12542,932],{"class":62},[52,12544,1983],{"class":105},[52,12546,957],{"class":58},[52,12548,12296],{"class":418},[52,12550,932],{"class":62},[52,12552,72],{"class":58},[52,12554,12303],{"class":75},[52,12556,72],{"class":58},[52,12558,12308],{"class":62},[52,12560,12311],{"class":105},[52,12562,957],{"class":58},[52,12564,12316],{"class":418},[52,12566,422],{"class":62},[52,12568,1007],{"class":58},[52,12570,12571,12573,12575],{"class":54,"line":115},[52,12572,1311],{"class":58},[52,12574,938],{"class":105},[52,12576,1007],{"class":58},[1518,12578,12579],{"id":12579},"保存処理",[13,12581,12582],{},"保存ボタンをクリックした時に入力した内容を出力できるようにします。",[42,12584,12586],{"className":44,"code":12585,"language":46,"meta":47,"style":47},"\u003C!DOCTYPE html>\n\u003Chtml lang=\"ja\">\n\u003Chead>\n    \u003Cmeta charset=\"UTF-8\">\n    \u003Cmeta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    \u003Clink href=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbootstrap@5.0.2\u002Fdist\u002Fcss\u002Fbootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-EVSTQN3\u002FazprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC\" crossorigin=\"anonymous\">\n    \u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbootstrap@5.2.2\u002Fdist\u002Fjs\u002Fbootstrap.bundle.min.js\" integrity=\"sha384-OERcA2EqjJCMA+\u002F3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo\u002F\u002Ff6V8Qbsw3\" crossorigin=\"anonymous\">\u003C\u002Fscript>\n    \u003Ctitle>editorjs\u003C\u002Ftitle>\n    \u003Clink rel=\"stylesheet\" href=\".\u002Fstyle.css\">\n\u003C\u002Fhead>\n\u003Cbody>\n    \u003Cdiv class=\"container\">\n        \u003Cdiv class=\"mt-5 border bg-light\">\n            \u003Cdiv id=\"editorjs\">\u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003C!-- 追加 -->\n        \u003Cdiv class=\"mt-2\">　\n            \u003Cbutton id=\"save\" class=\"btn btn-primary\">保存＆出力\u003C\u002Fbutton>\n            \u003Cpre id=\"data\">\u003C\u002Fpre>\n        \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@editorjs\u002Feditorjs@latest\">\u003C\u002Fscript>\n\u003Cscript src=\".\u002Fapp.js\">\u003C\u002Fscript>\n\u003C\u002Fhtml>\n",[49,12587,12588,12598,12616,12624,12642,12670,12698,12746,12790,12806,12834,12842,12850,12868,12886,12908,12916,12921,12942,12981,13003,13011,13019,13027,13049,13071],{"__ignoreMap":47},[52,12589,12590,12592,12594,12596],{"class":54,"line":55},[52,12591,1554],{"class":58},[52,12593,1557],{"class":62},[52,12595,1560],{"class":65},[52,12597,80],{"class":58},[52,12599,12600,12602,12604,12606,12608,12610,12612,12614],{"class":54,"line":83},[52,12601,59],{"class":58},[52,12603,46],{"class":62},[52,12605,9905],{"class":65},[52,12607,69],{"class":58},[52,12609,72],{"class":58},[52,12611,9912],{"class":75},[52,12613,72],{"class":58},[52,12615,80],{"class":58},[52,12617,12618,12620,12622],{"class":54,"line":115},[52,12619,59],{"class":58},[52,12621,1578],{"class":62},[52,12623,80],{"class":58},[52,12625,12626,12628,12630,12632,12634,12636,12638,12640],{"class":54,"line":142},[52,12627,1575],{"class":58},[52,12629,1588],{"class":62},[52,12631,1591],{"class":65},[52,12633,69],{"class":58},[52,12635,72],{"class":58},[52,12637,9939],{"class":75},[52,12639,72],{"class":58},[52,12641,80],{"class":58},[52,12643,12644,12646,12648,12650,12652,12654,12656,12658,12660,12662,12664,12666,12668],{"class":54,"line":169},[52,12645,1575],{"class":58},[52,12647,1588],{"class":62},[52,12649,9952],{"class":65},[52,12651,69],{"class":58},[52,12653,72],{"class":58},[52,12655,9959],{"class":75},[52,12657,72],{"class":58},[52,12659,9964],{"class":65},[52,12661,69],{"class":58},[52,12663,72],{"class":58},[52,12665,9971],{"class":75},[52,12667,72],{"class":58},[52,12669,80],{"class":58},[52,12671,12672,12674,12676,12678,12680,12682,12684,12686,12688,12690,12692,12694,12696],{"class":54,"line":302},[52,12673,1575],{"class":58},[52,12675,1588],{"class":62},[52,12677,66],{"class":65},[52,12679,69],{"class":58},[52,12681,72],{"class":58},[52,12683,9990],{"class":75},[52,12685,72],{"class":58},[52,12687,9964],{"class":65},[52,12689,69],{"class":58},[52,12691,72],{"class":58},[52,12693,10001],{"class":75},[52,12695,72],{"class":58},[52,12697,80],{"class":58},[52,12699,12700,12702,12704,12706,12708,12710,12712,12714,12716,12718,12720,12722,12724,12726,12728,12730,12732,12734,12736,12738,12740,12742,12744],{"class":54,"line":308},[52,12701,1575],{"class":58},[52,12703,10012],{"class":62},[52,12705,10015],{"class":65},[52,12707,69],{"class":58},[52,12709,72],{"class":58},[52,12711,10022],{"class":75},[52,12713,72],{"class":58},[52,12715,10027],{"class":65},[52,12717,69],{"class":58},[52,12719,72],{"class":58},[52,12721,10034],{"class":75},[52,12723,72],{"class":58},[52,12725,10039],{"class":65},[52,12727,69],{"class":58},[52,12729,72],{"class":58},[52,12731,10046],{"class":75},[52,12733,72],{"class":58},[52,12735,10051],{"class":65},[52,12737,69],{"class":58},[52,12739,72],{"class":58},[52,12741,10058],{"class":75},[52,12743,72],{"class":58},[52,12745,80],{"class":58},[52,12747,12748,12750,12752,12754,12756,12758,12761,12763,12765,12767,12769,12772,12774,12776,12778,12780,12782,12784,12786,12788],{"class":54,"line":318},[52,12749,1575],{"class":58},[52,12751,349],{"class":62},[52,12753,1782],{"class":65},[52,12755,69],{"class":58},[52,12757,72],{"class":58},[52,12759,12760],{"class":75},"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fbootstrap@5.2.2\u002Fdist\u002Fjs\u002Fbootstrap.bundle.min.js",[52,12762,72],{"class":58},[52,12764,10039],{"class":65},[52,12766,69],{"class":58},[52,12768,72],{"class":58},[52,12770,12771],{"class":75},"sha384-OERcA2EqjJCMA+\u002F3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo\u002F\u002Ff6V8Qbsw3",[52,12773,72],{"class":58},[52,12775,10051],{"class":65},[52,12777,69],{"class":58},[52,12779,72],{"class":58},[52,12781,10058],{"class":75},[52,12783,72],{"class":58},[52,12785,1761],{"class":58},[52,12787,349],{"class":62},[52,12789,80],{"class":58},[52,12791,12792,12794,12796,12798,12800,12802,12804],{"class":54,"line":328},[52,12793,1575],{"class":58},[52,12795,1609],{"class":62},[52,12797,102],{"class":58},[52,12799,10073],{"class":105},[52,12801,108],{"class":58},[52,12803,1609],{"class":62},[52,12805,80],{"class":58},[52,12807,12808,12810,12812,12814,12816,12818,12820,12822,12824,12826,12828,12830,12832],{"class":54,"line":337},[52,12809,1575],{"class":58},[52,12811,10012],{"class":62},[52,12813,10027],{"class":65},[52,12815,69],{"class":58},[52,12817,72],{"class":58},[52,12819,10034],{"class":75},[52,12821,72],{"class":58},[52,12823,10015],{"class":65},[52,12825,69],{"class":58},[52,12827,72],{"class":58},[52,12829,10104],{"class":75},[52,12831,72],{"class":58},[52,12833,80],{"class":58},[52,12835,12836,12838,12840],{"class":54,"line":344},[52,12837,108],{"class":58},[52,12839,1578],{"class":62},[52,12841,80],{"class":58},[52,12843,12844,12846,12848],{"class":54,"line":354},[52,12845,59],{"class":58},[52,12847,1728],{"class":62},[52,12849,80],{"class":58},[52,12851,12852,12854,12856,12858,12860,12862,12864,12866],{"class":54,"line":367},[52,12853,1575],{"class":58},[52,12855,5995],{"class":62},[52,12857,10133],{"class":65},[52,12859,69],{"class":58},[52,12861,72],{"class":58},[52,12863,10140],{"class":75},[52,12865,72],{"class":58},[52,12867,80],{"class":58},[52,12869,12870,12872,12874,12876,12878,12880,12882,12884],{"class":54,"line":387},[52,12871,1585],{"class":58},[52,12873,5995],{"class":62},[52,12875,10133],{"class":65},[52,12877,69],{"class":58},[52,12879,72],{"class":58},[52,12881,10159],{"class":75},[52,12883,72],{"class":58},[52,12885,80],{"class":58},[52,12887,12888,12890,12892,12894,12896,12898,12900,12902,12904,12906],{"class":54,"line":415},[52,12889,1744],{"class":58},[52,12891,5995],{"class":62},[52,12893,1750],{"class":65},[52,12895,69],{"class":58},[52,12897,72],{"class":58},[52,12899,10073],{"class":75},[52,12901,72],{"class":58},[52,12903,1761],{"class":58},[52,12905,5995],{"class":62},[52,12907,80],{"class":58},[52,12909,12910,12912,12914],{"class":54,"line":427},[52,12911,1708],{"class":58},[52,12913,5995],{"class":62},[52,12915,80],{"class":58},[52,12917,12918],{"class":54,"line":435},[52,12919,12920],{"class":411},"        \u003C!-- 追加 -->\n",[52,12922,12923,12925,12927,12929,12931,12933,12935,12937,12939],{"class":54,"line":446},[52,12924,1585],{"class":58},[52,12926,5995],{"class":62},[52,12928,10133],{"class":65},[52,12930,69],{"class":58},[52,12932,72],{"class":58},[52,12934,10208],{"class":75},[52,12936,72],{"class":58},[52,12938,102],{"class":58},[52,12940,12941],{"class":105},"　\n",[52,12943,12944,12946,12948,12950,12952,12954,12957,12959,12961,12963,12965,12968,12970,12972,12975,12977,12979],{"class":54,"line":480},[52,12945,1744],{"class":58},[52,12947,7564],{"class":62},[52,12949,1750],{"class":65},[52,12951,69],{"class":58},[52,12953,72],{"class":58},[52,12955,12956],{"class":75},"save",[52,12958,72],{"class":58},[52,12960,10133],{"class":65},[52,12962,69],{"class":58},[52,12964,72],{"class":58},[52,12966,12967],{"class":75},"btn btn-primary",[52,12969,72],{"class":58},[52,12971,102],{"class":58},[52,12973,12974],{"class":105},"保存＆出力",[52,12976,108],{"class":58},[52,12978,7564],{"class":62},[52,12980,80],{"class":58},[52,12982,12983,12985,12987,12989,12991,12993,12995,12997,12999,13001],{"class":54,"line":509},[52,12984,1744],{"class":58},[52,12986,42],{"class":62},[52,12988,1750],{"class":65},[52,12990,69],{"class":58},[52,12992,72],{"class":58},[52,12994,7112],{"class":75},[52,12996,72],{"class":58},[52,12998,1761],{"class":58},[52,13000,42],{"class":62},[52,13002,80],{"class":58},[52,13004,13005,13007,13009],{"class":54,"line":539},[52,13006,1708],{"class":58},[52,13008,5995],{"class":62},[52,13010,80],{"class":58},[52,13012,13013,13015,13017],{"class":54,"line":547},[52,13014,1717],{"class":58},[52,13016,5995],{"class":62},[52,13018,80],{"class":58},[52,13020,13021,13023,13025],{"class":54,"line":553},[52,13022,108],{"class":58},[52,13024,1728],{"class":62},[52,13026,80],{"class":58},[52,13028,13029,13031,13033,13035,13037,13039,13041,13043,13045,13047],{"class":54,"line":559},[52,13030,59],{"class":58},[52,13032,349],{"class":62},[52,13034,1782],{"class":65},[52,13036,69],{"class":58},[52,13038,72],{"class":58},[52,13040,10273],{"class":75},[52,13042,72],{"class":58},[52,13044,1761],{"class":58},[52,13046,349],{"class":62},[52,13048,80],{"class":58},[52,13050,13051,13053,13055,13057,13059,13061,13063,13065,13067,13069],{"class":54,"line":564},[52,13052,59],{"class":58},[52,13054,349],{"class":62},[52,13056,1782],{"class":65},[52,13058,69],{"class":58},[52,13060,72],{"class":58},[52,13062,1789],{"class":75},[52,13064,72],{"class":58},[52,13066,1761],{"class":58},[52,13068,349],{"class":62},[52,13070,80],{"class":58},[52,13072,13073,13075,13077],{"class":54,"line":569},[52,13074,108],{"class":58},[52,13076,46],{"class":62},[52,13078,80],{"class":58},[42,13080,13082],{"className":6471,"code":13081,"language":1433,"meta":47,"style":47},"const pre = document.getElementById(\"data\");\ndocument.getElementById(\"save\").addEventListener(\"click\",()=>{\n    editor.save().then((outputData) => {\n        pre.textContent = JSON.stringify(outputData, null , \"\\t\");\n    }).catch((error) => {\n        console.log('Saving failed: ', error)\n    });\n})\n",[49,13083,13084,13111,13148,13177,13220,13243,13269,13277],{"__ignoreMap":47},[52,13085,13086,13088,13091,13093,13095,13097,13099,13101,13103,13105,13107,13109],{"class":54,"line":55},[52,13087,6867],{"class":65},[52,13089,13090],{"class":105}," pre ",[52,13092,69],{"class":58},[52,13094,1851],{"class":105},[52,13096,957],{"class":58},[52,13098,1856],{"class":418},[52,13100,932],{"class":105},[52,13102,72],{"class":58},[52,13104,7112],{"class":75},[52,13106,72],{"class":58},[52,13108,938],{"class":105},[52,13110,1007],{"class":58},[52,13112,13113,13116,13118,13120,13122,13124,13126,13128,13130,13132,13134,13136,13138,13140,13142,13144,13146],{"class":54,"line":83},[52,13114,13115],{"class":105},"document",[52,13117,957],{"class":58},[52,13119,1856],{"class":418},[52,13121,932],{"class":105},[52,13123,72],{"class":58},[52,13125,12956],{"class":75},[52,13127,72],{"class":58},[52,13129,938],{"class":105},[52,13131,957],{"class":58},[52,13133,11630],{"class":418},[52,13135,932],{"class":105},[52,13137,72],{"class":58},[52,13139,11637],{"class":75},[52,13141,72],{"class":58},[52,13143,11642],{"class":58},[52,13145,990],{"class":65},[52,13147,364],{"class":58},[52,13149,13150,13153,13155,13157,13159,13161,13164,13166,13168,13171,13173,13175],{"class":54,"line":115},[52,13151,13152],{"class":105},"    editor",[52,13154,957],{"class":58},[52,13156,12956],{"class":418},[52,13158,422],{"class":62},[52,13160,957],{"class":58},[52,13162,13163],{"class":418},"then",[52,13165,932],{"class":62},[52,13167,932],{"class":58},[52,13169,13170],{"class":986},"outputData",[52,13172,938],{"class":58},[52,13174,6367],{"class":65},[52,13176,6100],{"class":58},[52,13178,13179,13182,13184,13187,13189,13192,13194,13197,13199,13201,13203,13206,13209,13211,13214,13216,13218],{"class":54,"line":142},[52,13180,13181],{"class":105},"        pre",[52,13183,957],{"class":58},[52,13185,13186],{"class":105},"textContent",[52,13188,951],{"class":58},[52,13190,13191],{"class":105}," JSON",[52,13193,957],{"class":58},[52,13195,13196],{"class":418},"stringify",[52,13198,932],{"class":62},[52,13200,13170],{"class":105},[52,13202,408],{"class":58},[52,13204,13205],{"class":58}," null",[52,13207,13208],{"class":58}," ,",[52,13210,3013],{"class":58},[52,13212,13213],{"class":105},"\\t",[52,13215,72],{"class":58},[52,13217,938],{"class":62},[52,13219,1007],{"class":58},[52,13221,13222,13224,13226,13228,13231,13233,13235,13237,13239,13241],{"class":54,"line":169},[52,13223,9134],{"class":58},[52,13225,938],{"class":62},[52,13227,957],{"class":58},[52,13229,13230],{"class":418},"catch",[52,13232,932],{"class":62},[52,13234,932],{"class":58},[52,13236,6180],{"class":986},[52,13238,938],{"class":58},[52,13240,6367],{"class":65},[52,13242,6100],{"class":58},[52,13244,13245,13248,13250,13253,13255,13257,13260,13262,13264,13267],{"class":54,"line":302},[52,13246,13247],{"class":105},"        console",[52,13249,957],{"class":58},[52,13251,13252],{"class":418},"log",[52,13254,932],{"class":62},[52,13256,376],{"class":58},[52,13258,13259],{"class":75},"Saving failed: ",[52,13261,376],{"class":58},[52,13263,408],{"class":58},[52,13265,13266],{"class":105}," error",[52,13268,1015],{"class":62},[52,13270,13271,13273,13275],{"class":54,"line":308},[52,13272,9134],{"class":58},[52,13274,938],{"class":62},[52,13276,1007],{"class":58},[52,13278,13279,13281],{"class":54,"line":318},[52,13280,1311],{"class":58},[52,13282,1015],{"class":105},[13,13284,13285,13286,13289,13290,13295,13296,13298,13299,13302,13303,13305,13306,13308],{},"editor.jsでブロックのデータを出力したい時は",[49,13287,13288],{},"editor.save()","を使用します。Promiseなので非同期でデータが出力されます。今回はJSONで出力するようにしました。",[1450,13291,13294],{"href":13292,"rel":13293},"https:\u002F\u002Fapps.jun-app.com\u002Feditorjs-sample\u002F",[1454],"デモサイト","で確認できます。",[49,13297,13288],{},"では全ブロッククラスの",[49,13300,13301],{},"save()","が呼ばれるため、",[49,13304,10903],{},"クラスでも",[49,13307,13301],{},"メソッドを実装します。",[42,13310,13312],{"className":6471,"code":13311,"language":1433,"meta":47,"style":47},"save(blockContent){\n    const contents = blockContent.querySelectorAll(\".accordion-item\");\n    const itmes = Array.prototype.map.call(contents, (item)=>{\n        const title = item.querySelector(\".accordion-button-wrapper\");\n        const content = item.querySelector(\".accordion-body-input\");\n\n        return {\n            title:title.innerHTML || \"\",\n            content:content.innerHTML || \"\",\n        };\n    })\n    return {\n        itmes\n    }\n}\n",[49,13313,13314,13323,13353,13397,13425,13451,13455,13461,13479,13498,13502,13508,13514,13519,13523],{"__ignoreMap":47},[52,13315,13316,13318,13321],{"class":54,"line":55},[52,13317,12956],{"class":418},[52,13319,13320],{"class":105},"(blockContent)",[52,13322,364],{"class":58},[52,13324,13325,13327,13330,13332,13335,13337,13340,13342,13344,13347,13349,13351],{"class":54,"line":83},[52,13326,8519],{"class":65},[52,13328,13329],{"class":105}," contents",[52,13331,951],{"class":58},[52,13333,13334],{"class":105}," blockContent",[52,13336,957],{"class":58},[52,13338,13339],{"class":418},"querySelectorAll",[52,13341,932],{"class":62},[52,13343,72],{"class":58},[52,13345,13346],{"class":75},".accordion-item",[52,13348,72],{"class":58},[52,13350,938],{"class":62},[52,13352,1007],{"class":58},[52,13354,13355,13357,13360,13362,13365,13367,13370,13372,13374,13376,13379,13381,13384,13386,13388,13391,13393,13395],{"class":54,"line":115},[52,13356,8519],{"class":65},[52,13358,13359],{"class":105}," itmes",[52,13361,951],{"class":58},[52,13363,13364],{"class":370}," Array",[52,13366,957],{"class":58},[52,13368,13369],{"class":105},"prototype",[52,13371,957],{"class":58},[52,13373,1031],{"class":105},[52,13375,957],{"class":58},[52,13377,13378],{"class":418},"call",[52,13380,932],{"class":62},[52,13382,13383],{"class":105},"contents",[52,13385,408],{"class":58},[52,13387,1913],{"class":58},[52,13389,13390],{"class":986},"item",[52,13392,938],{"class":58},[52,13394,990],{"class":65},[52,13396,364],{"class":58},[52,13398,13399,13401,13404,13406,13409,13411,13413,13415,13417,13419,13421,13423],{"class":54,"line":142},[52,13400,11067],{"class":65},[52,13402,13403],{"class":105}," title",[52,13405,951],{"class":58},[52,13407,13408],{"class":105}," item",[52,13410,957],{"class":58},[52,13412,11614],{"class":418},[52,13414,932],{"class":62},[52,13416,72],{"class":58},[52,13418,12055],{"class":75},[52,13420,72],{"class":58},[52,13422,938],{"class":62},[52,13424,1007],{"class":58},[52,13426,13427,13429,13431,13433,13435,13437,13439,13441,13443,13445,13447,13449],{"class":54,"line":169},[52,13428,11067],{"class":65},[52,13430,9964],{"class":105},[52,13432,951],{"class":58},[52,13434,13408],{"class":105},[52,13436,957],{"class":58},[52,13438,11614],{"class":418},[52,13440,932],{"class":62},[52,13442,72],{"class":58},[52,13444,12231],{"class":75},[52,13446,72],{"class":58},[52,13448,938],{"class":62},[52,13450,1007],{"class":58},[52,13452,13453],{"class":54,"line":302},[52,13454,341],{"emptyLinePlaceholder":340},[52,13456,13457,13459],{"class":54,"line":308},[52,13458,6128],{"class":360},[52,13460,6100],{"class":58},[52,13462,13463,13465,13467,13469,13471,13473,13475,13477],{"class":54,"line":318},[52,13464,10463],{"class":62},[52,13466,373],{"class":58},[52,13468,1609],{"class":105},[52,13470,957],{"class":58},[52,13472,11253],{"class":105},[52,13474,11082],{"class":58},[52,13476,9762],{"class":58},[52,13478,384],{"class":58},[52,13480,13481,13484,13486,13488,13490,13492,13494,13496],{"class":54,"line":328},[52,13482,13483],{"class":62},"            content",[52,13485,373],{"class":58},[52,13487,11702],{"class":105},[52,13489,957],{"class":58},[52,13491,11253],{"class":105},[52,13493,11082],{"class":58},[52,13495,9762],{"class":58},[52,13497,384],{"class":58},[52,13499,13500],{"class":54,"line":337},[52,13501,11035],{"class":58},[52,13503,13504,13506],{"class":54,"line":344},[52,13505,9134],{"class":58},[52,13507,1015],{"class":62},[52,13509,13510,13512],{"class":54,"line":354},[52,13511,7881],{"class":360},[52,13513,6100],{"class":58},[52,13515,13516],{"class":54,"line":367},[52,13517,13518],{"class":105},"        itmes\n",[52,13520,13521],{"class":54,"line":387},[52,13522,2110],{"class":58},[52,13524,13525],{"class":54,"line":415},[52,13526,536],{"class":58},[13,13528,13529,6451,13531,13533,13534,13537,13538,13541,13542,4209,13544,7429,13546,13549,13550,13552],{},[49,13530,10556],{},[49,13532,10584],{},"で表示しているwrapperのDOMが渡されます。今回のクラスの場合、",[49,13535,13536],{},"this.wrapper","とほぼ同じです。save()で行うことはブロックのDOMからアコーディオンのタイトルと内容を取得します。複数個のアコーディオン要素があるので",[49,13539,13540],{},"blockContent.querySelectorAll(\".accordion-item\")","でNodeListを取得し、",[49,13543,12055],{},[49,13545,12231],{},[49,13547,13548],{},"innnerHtml","を取得して、",[49,13551,13301],{},"で返したいオブジェクトに当てはめます。",[42,13554,13556],{"className":6471,"code":13555,"language":1433,"meta":47,"style":47},"return {\n    items\n};\n\u002F\u002F ↓\n\u002F\u002F return {\n\u002F\u002F     items:[\n\u002F\u002F         {\n\u002F\u002F             title:\"...\",\n\u002F\u002F             content:\"...\",\n\u002F\u002F         },\n\u002F\u002F         {\n\u002F\u002F             title:\"...\",\n\u002F\u002F             content:\"...\",\n\u002F\u002F         },\n\u002F\u002F         {\n\u002F\u002F             title:\"...\",\n\u002F\u002F             content:\"...\",\n\u002F\u002F         },\n\u002F\u002F         ...\n\u002F\u002F     ]\n\u002F\u002F };\n",[49,13557,13558,13564,13569,13574,13579,13584,13589,13594,13599,13604,13609,13613,13617,13621,13625,13629,13633,13637,13641,13646,13651],{"__ignoreMap":47},[52,13559,13560,13562],{"class":54,"line":55},[52,13561,6479],{"class":360},[52,13563,6100],{"class":58},[52,13565,13566],{"class":54,"line":83},[52,13567,13568],{"class":105},"    items\n",[52,13570,13571],{"class":54,"line":115},[52,13572,13573],{"class":58},"};\n",[52,13575,13576],{"class":54,"line":142},[52,13577,13578],{"class":411},"\u002F\u002F ↓\n",[52,13580,13581],{"class":54,"line":169},[52,13582,13583],{"class":411},"\u002F\u002F return {\n",[52,13585,13586],{"class":54,"line":302},[52,13587,13588],{"class":411},"\u002F\u002F     items:[\n",[52,13590,13591],{"class":54,"line":308},[52,13592,13593],{"class":411},"\u002F\u002F         {\n",[52,13595,13596],{"class":54,"line":318},[52,13597,13598],{"class":411},"\u002F\u002F             title:\"...\",\n",[52,13600,13601],{"class":54,"line":328},[52,13602,13603],{"class":411},"\u002F\u002F             content:\"...\",\n",[52,13605,13606],{"class":54,"line":337},[52,13607,13608],{"class":411},"\u002F\u002F         },\n",[52,13610,13611],{"class":54,"line":344},[52,13612,13593],{"class":411},[52,13614,13615],{"class":54,"line":354},[52,13616,13598],{"class":411},[52,13618,13619],{"class":54,"line":367},[52,13620,13603],{"class":411},[52,13622,13623],{"class":54,"line":387},[52,13624,13608],{"class":411},[52,13626,13627],{"class":54,"line":415},[52,13628,13593],{"class":411},[52,13630,13631],{"class":54,"line":427},[52,13632,13598],{"class":411},[52,13634,13635],{"class":54,"line":435},[52,13636,13603],{"class":411},[52,13638,13639],{"class":54,"line":446},[52,13640,13608],{"class":411},[52,13642,13643],{"class":54,"line":480},[52,13644,13645],{"class":411},"\u002F\u002F         ...\n",[52,13647,13648],{"class":54,"line":509},[52,13649,13650],{"class":411},"\u002F\u002F     ]\n",[52,13652,13653],{"class":54,"line":539},[52,13654,13655],{"class":411},"\u002F\u002F };\n",[13,13657,13658,13661],{},[1450,13659,13294],{"href":13292,"rel":13660},[1454],"でアコーディオンを作成し、文字を入力して「保存・出力」をクリックすると上記のオブジェクトをJSON化したものが表示されると思います。",[729,13663],{":src":13664,":width":732},"'editorjs-repeatable\u002Fset5.png'",[1518,13666,13667],{"id":13667},"インラインツールをサポートする",[13,13669,13670,13671,13674],{},"冒頭で",[49,13672,13673],{},"inlineToolbar: true","を指定するとリッチテキストを使用できるようになり、文章に対して太字、斜体、リンクを付与するツールが出現するようになります。",[729,13676],{":src":13677,":width":732},"'editorjs-repeatable\u002Fset6.png'",[13,13679,13680],{},"リッチテキストを保存、再現する場合はinnetHtmlをget、setする必要があります。",[1518,13682,13683],{"id":13683},"データを渡された時の処理",[13,13685,13686],{},"上記まではフォームを表示し、エディタ上で文章を入力して、保存・出力しました。しかし実際の用途では保存したデータから再度エディタ上に表示できるようにしましょう。",[13,13688,13689,13690,13693,13694,13696],{},"エディタにデータを渡す時は",[49,13691,13692],{},"Editor","クラスに",[49,13695,7112],{},"プロパティーに出力したデータを設定すれば大丈夫です。",[42,13698,13700],{"className":6471,"code":13699,"language":1433,"meta":47,"style":47},"const editor = new EditorJS({\n    holder: 'editorjs',\n    tools: { \n        accordion : {class:Accordion,inlineToolbar: true}\n    },\n\n    \u002F\u002F ここ\n    data:JSON.parse('出力したデータ')\n});\n",[49,13701,13702,13718,13732,13742,13766,13770,13774,13779,13804],{"__ignoreMap":47},[52,13703,13704,13706,13708,13710,13712,13714,13716],{"class":54,"line":55},[52,13705,6867],{"class":65},[52,13707,10330],{"class":105},[52,13709,69],{"class":58},[52,13711,1936],{"class":58},[52,13713,10337],{"class":418},[52,13715,932],{"class":105},[52,13717,364],{"class":58},[52,13719,13720,13722,13724,13726,13728,13730],{"class":54,"line":83},[52,13721,10346],{"class":62},[52,13723,373],{"class":58},[52,13725,6067],{"class":58},[52,13727,10073],{"class":75},[52,13729,376],{"class":58},[52,13731,384],{"class":58},[52,13733,13734,13736,13738,13740],{"class":54,"line":115},[52,13735,10880],{"class":62},[52,13737,373],{"class":58},[52,13739,10885],{"class":58},[52,13741,283],{"class":105},[52,13743,13744,13746,13748,13750,13752,13754,13756,13758,13760,13762,13764],{"class":54,"line":142},[52,13745,10892],{"class":62},[52,13747,373],{"class":58},[52,13749,10885],{"class":58},[52,13751,10427],{"class":62},[52,13753,373],{"class":58},[52,13755,10903],{"class":105},[52,13757,408],{"class":58},[52,13759,10908],{"class":62},[52,13761,373],{"class":58},[52,13763,6349],{"class":6245},[52,13765,536],{"class":58},[52,13767,13768],{"class":54,"line":169},[52,13769,6151],{"class":58},[52,13771,13772],{"class":54,"line":302},[52,13773,341],{"emptyLinePlaceholder":340},[52,13775,13776],{"class":54,"line":308},[52,13777,13778],{"class":411},"    \u002F\u002F ここ\n",[52,13780,13781,13783,13785,13788,13790,13793,13795,13797,13800,13802],{"class":54,"line":318},[52,13782,6121],{"class":62},[52,13784,373],{"class":58},[52,13786,13787],{"class":105},"JSON",[52,13789,957],{"class":58},[52,13791,13792],{"class":418},"parse",[52,13794,932],{"class":105},[52,13796,376],{"class":58},[52,13798,13799],{"class":75},"出力したデータ",[52,13801,376],{"class":58},[52,13803,1015],{"class":105},[52,13805,13806,13808,13810],{"class":54,"line":328},[52,13807,1311],{"class":58},[52,13809,938],{"class":105},[52,13811,1007],{"class":58},[13,13813,13814,13815,13817,13818,13820,13821,13823],{},"JSONで保存した時はパースでします。JSON内の",[49,13816,9330],{},"内の各ブロックの",[49,13819,9370],{},"から判別してブロッククラスを当てはめてくれます。そしてこのデータはブロッククラスのコンストラクタに",[49,13822,7112],{},"という引数で渡されます。",[42,13825,13827],{"className":6471,"code":13826,"language":1433,"meta":47,"style":47},"class Accordion{\n    constructor({data,config,api}){\n        \u002F\u002F これ！\n        const accordionData = data.itmes || [];\n\n        this.createParentWrapper();\n\n        \u002F\u002F データ当てはめ\n        if(accordionData.length > 0){\n            accordionData.forEach(element => {\n                this.addItemEle(element.title,element.content);\n            });\n        }\n    }\n}\n",[49,13828,13829,13837,13855,13860,13880,13884,13894,13898,13903,13925,13944,13971,13980,13984,13988],{"__ignoreMap":47},[52,13830,13831,13833,13835],{"class":54,"line":55},[52,13832,10427],{"class":65},[52,13834,10430],{"class":370},[52,13836,364],{"class":58},[52,13838,13839,13841,13843,13845,13847,13849,13851,13853],{"class":54,"line":83},[52,13840,10508],{"class":65},[52,13842,10511],{"class":58},[52,13844,7112],{"class":986},[52,13846,408],{"class":58},[52,13848,10523],{"class":986},[52,13850,408],{"class":58},[52,13852,10518],{"class":986},[52,13854,11062],{"class":58},[52,13856,13857],{"class":54,"line":115},[52,13858,13859],{"class":411},"        \u002F\u002F これ！\n",[52,13861,13862,13864,13866,13868,13870,13872,13874,13876,13878],{"class":54,"line":142},[52,13863,11067],{"class":65},[52,13865,11070],{"class":105},[52,13867,951],{"class":58},[52,13869,419],{"class":105},[52,13871,957],{"class":58},[52,13873,11079],{"class":105},[52,13875,11082],{"class":58},[52,13877,11085],{"class":62},[52,13879,1007],{"class":58},[52,13881,13882],{"class":54,"line":169},[52,13883,341],{"emptyLinePlaceholder":340},[52,13885,13886,13888,13890,13892],{"class":54,"line":302},[52,13887,11092],{"class":58},[52,13889,11095],{"class":418},[52,13891,422],{"class":62},[52,13893,1007],{"class":58},[52,13895,13896],{"class":54,"line":308},[52,13897,341],{"emptyLinePlaceholder":340},[52,13899,13900],{"class":54,"line":318},[52,13901,13902],{"class":411},"        \u002F\u002F データ当てはめ\n",[52,13904,13905,13908,13910,13913,13915,13917,13919,13921,13923],{"class":54,"line":328},[52,13906,13907],{"class":360},"        if",[52,13909,932],{"class":62},[52,13911,13912],{"class":105},"accordionData",[52,13914,957],{"class":58},[52,13916,11369],{"class":105},[52,13918,8720],{"class":58},[52,13920,2749],{"class":1646},[52,13922,938],{"class":62},[52,13924,364],{"class":58},[52,13926,13927,13930,13932,13935,13937,13940,13942],{"class":54,"line":337},[52,13928,13929],{"class":105},"            accordionData",[52,13931,957],{"class":58},[52,13933,13934],{"class":418},"forEach",[52,13936,932],{"class":62},[52,13938,13939],{"class":986},"element",[52,13941,6367],{"class":65},[52,13943,6100],{"class":58},[52,13945,13946,13949,13951,13953,13955,13957,13959,13961,13963,13965,13967,13969],{"class":54,"line":344},[52,13947,13948],{"class":58},"                this.",[52,13950,11653],{"class":418},[52,13952,932],{"class":62},[52,13954,13939],{"class":105},[52,13956,957],{"class":58},[52,13958,1609],{"class":105},[52,13960,408],{"class":58},[52,13962,13939],{"class":105},[52,13964,957],{"class":58},[52,13966,11702],{"class":105},[52,13968,938],{"class":62},[52,13970,1007],{"class":58},[52,13972,13973,13976,13978],{"class":54,"line":354},[52,13974,13975],{"class":58},"            }",[52,13977,938],{"class":62},[52,13979,1007],{"class":58},[52,13981,13982],{"class":54,"line":367},[52,13983,4814],{"class":58},[52,13985,13986],{"class":54,"line":387},[52,13987,2110],{"class":58},[52,13989,13990],{"class":54,"line":415},[52,13991,536],{"class":58},[13,13993,13994,13995,723,13998,14001,14002,14005],{},"そしてデータが存在する場合、",[49,13996,13997],{},"data.itmes",[49,13999,14000],{},"foreach","で",[49,14003,14004],{},"this.addItemEle()","を通じてフォームのDOMを生成します。",[17,14007,9159],{"id":9159},[13,14009,14010],{},"Editor.jsにおける反復入力できるブロックの作成方法は以上となります。editor.jsでのブロック作成の基本は",[2489,14012,14013,14018,14024],{},[1467,14014,14015,14017],{},[49,14016,10584],{},"でブロックのDOMを返す。",[1467,14019,14020,14023],{},[49,14021,14022],{},"constructor()","で初期処理を行う。",[1467,14025,14026,14028],{},[49,14027,13301],{},"でブロックのDOMから値を取得してオブジェクトで返す。",[13,14030,14031,14032,14034,14035,14037],{},"以上となります。簡単のブロックの場合、",[49,14033,10584],{},"に",[49,14036,11789],{},"で作成したようなDOM生成のコードを書いてもいいですが、今回は要素の追加・削除が必要なためメソッドを分けました。",[13,14039,14040,14041,14043],{},"ただし",[49,14042,11789],{},"の中を見ると結構ごちゃごちゃしています。vue.jsやreact.jsに慣れた人にとってみると、saveでの値の取得が面倒に感じるかもしれません。DOMの生成部分は正直undersocre.jsのtemplateなどを使用してデータバインディングを使用した方がより複雑なブロックを作成できると思います。今度はundersocre.jsと組み合わせて複雑な入力形態をとれるようにしてみます。",[13,14045,14046,14047,14050],{},"今回作成したコードは",[1450,14048,13294],{"href":13292,"rel":14049},[1454],"からご確認ください。",[1413,14052,14053],{},"html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}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 pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s6YsC, html code.shiki .s6YsC{--shiki-default:#B2CCD6}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sbqyR, html code.shiki .sbqyR{--shiki-default:#FF9CAC}",{"title":47,"searchDepth":115,"depth":115,"links":14055},[14056,14057,14061,14062,14063,14064,14071],{"id":9215,"depth":83,"text":9215},{"id":9271,"depth":83,"text":9272,"children":14058},[14059,14060],{"id":9833,"depth":115,"text":9833},{"id":9839,"depth":115,"text":9839},{"id":9860,"depth":83,"text":9860},{"id":9869,"depth":83,"text":9870},{"id":10405,"depth":83,"text":10405},{"id":10708,"depth":83,"text":10708,"children":14065},[14066,14067,14068,14069,14070],{"id":10956,"depth":115,"text":10956},{"id":11799,"depth":115,"text":11799},{"id":12579,"depth":115,"text":12579},{"id":13667,"depth":115,"text":13667},{"id":13683,"depth":115,"text":13683},{"id":9159,"depth":83,"text":9159},[1423],"2025-10-21",{},"\u002Farticles\u002Feditorjs-repeatable",{"title":9204,"description":9204},"articles\u002Feditorjs-repeatable",[1433],"fqFXMzrX-J2WaHaq3V6xcHNSXu0M84XC2Fj6Q3A2fvY",{"id":14081,"title":14082,"body":14083,"category":15423,"createdAt":15424,"description":15425,"extension":1426,"index":1427,"meta":15426,"navigation":340,"path":15427,"publish":340,"seo":15428,"series":1427,"seriesTitle":1427,"stem":15429,"tag":15430,"thumbnail":15432,"updatedAt":1427,"__hash__":15433},"articles\u002Farticles\u002F2024_09_22_line_great_circle_antimeridian_cutting.md","turf.jsを用いたメルカトル図法地図上の線の大圏航路補正・逆子午線分割",{"type":10,"value":14084,"toc":15416},[14085,14088,14091,14095,14098,14101,14104,14107,14110,14113,14116,14119,14122,14125,14128,14131,14134,14137,14141,14144,14148,14151,14817,14820,15413],[13,14086,14087],{},"私がメルカトル図法を使用した地図上で、mapboxに2点以上の線を引く処理を実装していたとき、線が直線的に描画されるという問題が発生しました。さらに、経度180度の逆子午線を跨ぐ場合、mapboxでは大回りの線が描かれてしまい、その修正も必要でした。例えば、東京→ロサンゼルスの２点を結ぶと、東経180度を越えるのでなく、西回りでぐるっと線が引かれてしまいました。",[13,14089,14090],{},"本記事では、JavaScriptライブラリであるturf.jsを用いて、補正と分割を行った表示用のパスを算出手法について紹介します。",[17,14092,14094],{"id":14093},"大圏航路とはなぜ補正が必要","大圏航路とは？なぜ補正が必要？",[13,14096,14097],{},"大圏航路（Great Circle Route）は、地球上の2点を最短距離で結ぶ経路です。なんとなく２点を結んだ直線が最短経路と思ってしましますが、実際はそうではありません。よく見る平面の地図はメルカトル図法という方法で表現されており、引いた線が実際の地球上の経路、距離、面積と一致しません。これは地球が球面であり、球面上の線を平面上の線の描画と一致しないからです。例えば、東京→デリーの最短ルートは実際以下の通りで、直線的に結んだものではありません。（左が補正なしの直線）",[729,14099],{":src":14100,":width":9245},"'2024_09_22_line_great_circle_antimeridian_cutting\u002Ffig2.png'",[729,14102],{":src":14103,":width":9245},"'2024_09_22_line_great_circle_antimeridian_cutting\u002Ffig1.png'",[13,14105,14106],{},"メルカトル図法は、緯度が高くなるほど距離の比率が歪むため、大圏航路を描くと地図上で曲線として表示されるのが特徴です。しかし、通常のGeoJSONなどを使用して単純に線を引くと、2点間を直線で結んでしまい、大圏航路としての正確さが失れます。",[13,14108,14109],{},"これは2点の距離が離れるほど、平面上に引いた直線と実際の線と乖離します。短い距離、少なくとも日本列島ぐらいであれば問題ありませんが、大陸レベルだったり海路・空路を表現するときはその乖離が顕著になります。",[13,14111,14112],{},"海路・空路を記載するような地図はこの補正を考慮しないといけません。わかりやすくすると以下の通りです。",[729,14114],{":src":14115,":width":9245},"'2024_09_22_line_great_circle_antimeridian_cutting\u002Ffig5.png'",[729,14117],{":src":14118,":width":9245},"'2024_09_22_line_great_circle_antimeridian_cutting\u002Ffig6.png'",[17,14120,14121],{"id":14121},"逆子午線を越えるときには分割が必要",[13,14123,14124],{},"逆子午線とは、経度180度の線を指します。mapboxで経度180度を跨ぐ線を描こうとすると、通常の経路補正では大回りで描かれてしまい、直感的に不自然になります。",[729,14126],{":src":14127,":width":9245},"'2024_09_22_line_great_circle_antimeridian_cutting\u002Ffig3.png'",[729,14129],{":src":14130,":width":9245},"'2024_09_22_line_great_circle_antimeridian_cutting\u002Ffig4.png'",[13,14132,14133],{},"そのため、逆子午線を越える線を正しく描画するには、線を分割して複数のLineStringやMultiLineStringに分ける処理を行いました。図としてこのような感じです。",[729,14135],{":src":14136,":width":9245},"'2024_09_22_line_great_circle_antimeridian_cutting\u002Ffig7.png'",[17,14138,14140],{"id":14139},"基本的にturfjsを使えば解決できる","基本的にturf.jsを使えば解決できる",[13,14142,14143],{},"JavaScriptのturf.jsライブラリを用いることで、これらの問題を解決できます。turf.jsはGeoJSONデータの操作に強力な機能を提供しており、大圏航路の計算を行うとき、180度を越えるとき自動的に分割したパスを渡してくれます。",[1518,14145,14147],{"id":14146},"_2点間に補正点を算出","2点間に補正点を算出",[13,14149,14150],{},"まず、2点間の線を大圏航路で補正するために、turf.jsのturf.greatCircle関数を使用しました。この関数を使うことで2点間を大圏航路で補正し、曲線的な追加のパスを生成できます。\n180度を超えない2点の場合、補正した緯度経度の配列が戻ります。180度を越える場合、180度で分割した2つのlineの緯度経度配列が戻ります。",[42,14152,14156],{"className":14153,"code":14154,"language":14155,"meta":47,"style":47},"language-ts shiki shiki-themes material-theme-ocean","import * as turf from '@turf\u002Fturf';\nimport { Feature } from \"geojson\";\n\nconst path = [lat:number,lng:number][]\nconst displayPath: [number, number][][] = [];\n\n\u002F\u002F 各ラインのパスごとに大円航路を計算したパスを追加\nfor (let i = 0; i \u003C path.length - 1; i++) {\n    const startPoint = path[i];\n    const endPoint = path[i + 1];\n\n    const currentPoint = turf.point([startPoint[1], startPoint[0]]); \u002F\u002F turf.js は経度・緯度と入力したり戻ってくるので注意。\n    const nextPoint = turf.point([endPoint[1], endPoint[0]]);\n\n    const greatCircle = turf.greatCircle(currentPoint, nextPoint, { npoints: 30 }); \u002F\u002F npoints が追加する補正点。多いほど滑らか\n\n    const coordinates = greatCircle.geometry.coordinates;\n\n    \u002F\u002F LineString の場合\n    if (Array.isArray(coordinates[0]) && !Array.isArray(coordinates[0][0])) {\n        displayPath.push(coordinates as [number, number][]);\n    } \n    \u002F\u002F MultiLineString の場合\n    else if (Array.isArray(coordinates[0][0])) {\n        coordinates.forEach((segment) => {\n            displayPath.push(segment as [number, number][]);\n        });\n    }\n}\n\nconst geojson = {\n    type: \"Feature\",\n    properties: {},\n    geometry: {\n        type: \"MultiLineString\",\n        coordinates: displayPath,\n    },\n} as Feature;\n\nreturn geojson;\n","ts",[49,14157,14158,14181,14205,14209,14226,14255,14259,14264,14308,14328,14351,14355,14400,14438,14442,14487,14491,14514,14518,14523,14578,14607,14613,14618,14650,14672,14699,14707,14711,14715,14719,14730,14746,14755,14764,14780,14790,14794,14804,14808],{"__ignoreMap":47},[52,14159,14160,14162,14164,14167,14170,14172,14174,14177,14179],{"class":54,"line":55},[52,14161,5939],{"class":360},[52,14163,8804],{"class":58},[52,14165,14166],{"class":360}," as",[52,14168,14169],{"class":105}," turf ",[52,14171,6064],{"class":360},[52,14173,6067],{"class":58},[52,14175,14176],{"class":75},"@turf\u002Fturf",[52,14178,376],{"class":58},[52,14180,1007],{"class":58},[52,14182,14183,14185,14187,14190,14193,14196,14198,14201,14203],{"class":54,"line":83},[52,14184,5939],{"class":360},[52,14186,10885],{"class":58},[52,14188,14189],{"class":105}," Feature",[52,14191,14192],{"class":58}," }",[52,14194,14195],{"class":360}," from",[52,14197,3013],{"class":58},[52,14199,14200],{"class":75},"geojson",[52,14202,72],{"class":58},[52,14204,1007],{"class":58},[52,14206,14207],{"class":54,"line":115},[52,14208,341],{"emptyLinePlaceholder":340},[52,14210,14211,14213,14216,14218,14221,14223],{"class":54,"line":142},[52,14212,6867],{"class":65},[52,14214,14215],{"class":105}," path ",[52,14217,69],{"class":58},[52,14219,14220],{"class":105}," [lat:number",[52,14222,408],{"class":58},[52,14224,14225],{"class":105},"lng:number][]\n",[52,14227,14228,14230,14233,14235,14238,14241,14243,14246,14249,14251,14253],{"class":54,"line":169},[52,14229,6867],{"class":65},[52,14231,14232],{"class":105}," displayPath",[52,14234,373],{"class":58},[52,14236,14237],{"class":105}," [",[52,14239,14240],{"class":370},"number",[52,14242,408],{"class":58},[52,14244,14245],{"class":370}," number",[52,14247,14248],{"class":105},"][][] ",[52,14250,69],{"class":58},[52,14252,11085],{"class":105},[52,14254,1007],{"class":58},[52,14256,14257],{"class":54,"line":302},[52,14258,341],{"emptyLinePlaceholder":340},[52,14260,14261],{"class":54,"line":308},[52,14262,14263],{"class":411},"\u002F\u002F 各ラインのパスごとに大円航路を計算したパスを追加\n",[52,14265,14266,14269,14271,14273,14276,14278,14280,14282,14284,14286,14289,14291,14294,14296,14298,14300,14302,14304,14306],{"class":54,"line":318},[52,14267,14268],{"class":360},"for",[52,14270,1913],{"class":105},[52,14272,1258],{"class":65},[52,14274,14275],{"class":105}," i ",[52,14277,69],{"class":58},[52,14279,2749],{"class":1646},[52,14281,3077],{"class":58},[52,14283,14275],{"class":105},[52,14285,59],{"class":58},[52,14287,14288],{"class":105}," path",[52,14290,957],{"class":58},[52,14292,14293],{"class":105},"length ",[52,14295,2700],{"class":58},[52,14297,3095],{"class":1646},[52,14299,3077],{"class":58},[52,14301,11383],{"class":105},[52,14303,11403],{"class":58},[52,14305,3098],{"class":105},[52,14307,364],{"class":58},[52,14309,14310,14312,14315,14317,14319,14321,14324,14326],{"class":54,"line":328},[52,14311,8519],{"class":65},[52,14313,14314],{"class":105}," startPoint",[52,14316,951],{"class":58},[52,14318,14288],{"class":105},[52,14320,395],{"class":62},[52,14322,14323],{"class":105},"i",[52,14325,405],{"class":62},[52,14327,1007],{"class":58},[52,14329,14330,14332,14335,14337,14339,14341,14343,14345,14347,14349],{"class":54,"line":337},[52,14331,8519],{"class":65},[52,14333,14334],{"class":105}," endPoint",[52,14336,951],{"class":58},[52,14338,14288],{"class":105},[52,14340,395],{"class":62},[52,14342,14323],{"class":105},[52,14344,8634],{"class":58},[52,14346,3095],{"class":1646},[52,14348,405],{"class":62},[52,14350,1007],{"class":58},[52,14352,14353],{"class":54,"line":344},[52,14354,341],{"emptyLinePlaceholder":340},[52,14356,14357,14359,14362,14364,14367,14369,14372,14375,14378,14380,14382,14384,14386,14388,14390,14392,14395,14397],{"class":54,"line":354},[52,14358,8519],{"class":65},[52,14360,14361],{"class":105}," currentPoint",[52,14363,951],{"class":58},[52,14365,14366],{"class":105}," turf",[52,14368,957],{"class":58},[52,14370,14371],{"class":418},"point",[52,14373,14374],{"class":62},"([",[52,14376,14377],{"class":105},"startPoint",[52,14379,395],{"class":62},[52,14381,31],{"class":1646},[52,14383,405],{"class":62},[52,14385,408],{"class":58},[52,14387,14314],{"class":105},[52,14389,395],{"class":62},[52,14391,1647],{"class":1646},[52,14393,14394],{"class":62},"]])",[52,14396,3077],{"class":58},[52,14398,14399],{"class":411}," \u002F\u002F turf.js は経度・緯度と入力したり戻ってくるので注意。\n",[52,14401,14402,14404,14407,14409,14411,14413,14415,14417,14420,14422,14424,14426,14428,14430,14432,14434,14436],{"class":54,"line":367},[52,14403,8519],{"class":65},[52,14405,14406],{"class":105}," nextPoint",[52,14408,951],{"class":58},[52,14410,14366],{"class":105},[52,14412,957],{"class":58},[52,14414,14371],{"class":418},[52,14416,14374],{"class":62},[52,14418,14419],{"class":105},"endPoint",[52,14421,395],{"class":62},[52,14423,31],{"class":1646},[52,14425,405],{"class":62},[52,14427,408],{"class":58},[52,14429,14334],{"class":105},[52,14431,395],{"class":62},[52,14433,1647],{"class":1646},[52,14435,14394],{"class":62},[52,14437,1007],{"class":58},[52,14439,14440],{"class":54,"line":387},[52,14441,341],{"emptyLinePlaceholder":340},[52,14443,14444,14446,14449,14451,14453,14455,14458,14460,14463,14465,14467,14469,14471,14474,14476,14478,14480,14482,14484],{"class":54,"line":415},[52,14445,8519],{"class":65},[52,14447,14448],{"class":105}," greatCircle",[52,14450,951],{"class":58},[52,14452,14366],{"class":105},[52,14454,957],{"class":58},[52,14456,14457],{"class":418},"greatCircle",[52,14459,932],{"class":62},[52,14461,14462],{"class":105},"currentPoint",[52,14464,408],{"class":58},[52,14466,14406],{"class":105},[52,14468,408],{"class":58},[52,14470,10885],{"class":58},[52,14472,14473],{"class":62}," npoints",[52,14475,373],{"class":58},[52,14477,2721],{"class":1646},[52,14479,14192],{"class":58},[52,14481,938],{"class":62},[52,14483,3077],{"class":58},[52,14485,14486],{"class":411}," \u002F\u002F npoints が追加する補正点。多いほど滑らか\n",[52,14488,14489],{"class":54,"line":427},[52,14490,341],{"emptyLinePlaceholder":340},[52,14492,14493,14495,14498,14500,14502,14504,14507,14509,14512],{"class":54,"line":435},[52,14494,8519],{"class":65},[52,14496,14497],{"class":105}," coordinates",[52,14499,951],{"class":58},[52,14501,14448],{"class":105},[52,14503,957],{"class":58},[52,14505,14506],{"class":105},"geometry",[52,14508,957],{"class":58},[52,14510,14511],{"class":105},"coordinates",[52,14513,1007],{"class":58},[52,14515,14516],{"class":54,"line":446},[52,14517,341],{"emptyLinePlaceholder":340},[52,14519,14520],{"class":54,"line":480},[52,14521,14522],{"class":411},"    \u002F\u002F LineString の場合\n",[52,14524,14525,14527,14529,14532,14534,14537,14539,14541,14543,14545,14548,14551,14554,14556,14558,14560,14562,14564,14566,14568,14571,14573,14576],{"class":54,"line":509},[52,14526,8812],{"class":360},[52,14528,1913],{"class":62},[52,14530,14531],{"class":105},"Array",[52,14533,957],{"class":58},[52,14535,14536],{"class":418},"isArray",[52,14538,932],{"class":62},[52,14540,14511],{"class":105},[52,14542,395],{"class":62},[52,14544,1647],{"class":1646},[52,14546,14547],{"class":62},"]) ",[52,14549,14550],{"class":58},"&&",[52,14552,14553],{"class":58}," !",[52,14555,14531],{"class":105},[52,14557,957],{"class":58},[52,14559,14536],{"class":418},[52,14561,932],{"class":62},[52,14563,14511],{"class":105},[52,14565,395],{"class":62},[52,14567,1647],{"class":1646},[52,14569,14570],{"class":62},"][",[52,14572,1647],{"class":1646},[52,14574,14575],{"class":62},"])) ",[52,14577,364],{"class":58},[52,14579,14580,14583,14585,14588,14590,14592,14594,14596,14598,14600,14602,14605],{"class":54,"line":539},[52,14581,14582],{"class":105},"        displayPath",[52,14584,957],{"class":58},[52,14586,14587],{"class":418},"push",[52,14589,932],{"class":62},[52,14591,14511],{"class":105},[52,14593,14166],{"class":360},[52,14595,14237],{"class":62},[52,14597,14240],{"class":370},[52,14599,408],{"class":58},[52,14601,14245],{"class":370},[52,14603,14604],{"class":62},"][])",[52,14606,1007],{"class":58},[52,14608,14609,14611],{"class":54,"line":547},[52,14610,9134],{"class":58},[52,14612,283],{"class":62},[52,14614,14615],{"class":54,"line":553},[52,14616,14617],{"class":411},"    \u002F\u002F MultiLineString の場合\n",[52,14619,14620,14623,14626,14628,14630,14632,14634,14636,14638,14640,14642,14644,14646,14648],{"class":54,"line":559},[52,14621,14622],{"class":360},"    else",[52,14624,14625],{"class":360}," if",[52,14627,1913],{"class":62},[52,14629,14531],{"class":105},[52,14631,957],{"class":58},[52,14633,14536],{"class":418},[52,14635,932],{"class":62},[52,14637,14511],{"class":105},[52,14639,395],{"class":62},[52,14641,1647],{"class":1646},[52,14643,14570],{"class":62},[52,14645,1647],{"class":1646},[52,14647,14575],{"class":62},[52,14649,364],{"class":58},[52,14651,14652,14655,14657,14659,14661,14663,14666,14668,14670],{"class":54,"line":564},[52,14653,14654],{"class":105},"        coordinates",[52,14656,957],{"class":58},[52,14658,13934],{"class":418},[52,14660,932],{"class":62},[52,14662,932],{"class":58},[52,14664,14665],{"class":986},"segment",[52,14667,938],{"class":58},[52,14669,6367],{"class":65},[52,14671,6100],{"class":58},[52,14673,14674,14677,14679,14681,14683,14685,14687,14689,14691,14693,14695,14697],{"class":54,"line":569},[52,14675,14676],{"class":105},"            displayPath",[52,14678,957],{"class":58},[52,14680,14587],{"class":418},[52,14682,932],{"class":62},[52,14684,14665],{"class":105},[52,14686,14166],{"class":360},[52,14688,14237],{"class":62},[52,14690,14240],{"class":370},[52,14692,408],{"class":58},[52,14694,14245],{"class":370},[52,14696,14604],{"class":62},[52,14698,1007],{"class":58},[52,14700,14701,14703,14705],{"class":54,"line":1106},[52,14702,12325],{"class":58},[52,14704,938],{"class":62},[52,14706,1007],{"class":58},[52,14708,14709],{"class":54,"line":1135},[52,14710,2110],{"class":58},[52,14712,14713],{"class":54,"line":1164},[52,14714,536],{"class":58},[52,14716,14717],{"class":54,"line":4},[52,14718,341],{"emptyLinePlaceholder":340},[52,14720,14721,14723,14726,14728],{"class":54,"line":1199},[52,14722,6867],{"class":65},[52,14724,14725],{"class":105}," geojson ",[52,14727,69],{"class":58},[52,14729,6100],{"class":58},[52,14731,14732,14735,14737,14739,14742,14744],{"class":54,"line":1204},[52,14733,14734],{"class":62},"    type",[52,14736,373],{"class":58},[52,14738,3013],{"class":58},[52,14740,14741],{"class":75},"Feature",[52,14743,72],{"class":58},[52,14745,384],{"class":58},[52,14747,14748,14751,14753],{"class":54,"line":1209},[52,14749,14750],{"class":62},"    properties",[52,14752,373],{"class":58},[52,14754,7119],{"class":58},[52,14756,14757,14760,14762],{"class":54,"line":1214},[52,14758,14759],{"class":62},"    geometry",[52,14761,373],{"class":58},[52,14763,6100],{"class":58},[52,14765,14766,14769,14771,14773,14776,14778],{"class":54,"line":1219},[52,14767,14768],{"class":62},"        type",[52,14770,373],{"class":58},[52,14772,3013],{"class":58},[52,14774,14775],{"class":75},"MultiLineString",[52,14777,72],{"class":58},[52,14779,384],{"class":58},[52,14781,14782,14784,14786,14788],{"class":54,"line":3216},[52,14783,14654],{"class":62},[52,14785,373],{"class":58},[52,14787,14232],{"class":105},[52,14789,384],{"class":58},[52,14791,14792],{"class":54,"line":3237},[52,14793,6151],{"class":58},[52,14795,14796,14798,14800,14802],{"class":54,"line":3251},[52,14797,1311],{"class":58},[52,14799,14166],{"class":360},[52,14801,14189],{"class":370},[52,14803,1007],{"class":58},[52,14805,14806],{"class":54,"line":3256},[52,14807,341],{"emptyLinePlaceholder":340},[52,14809,14810,14812,14815],{"class":54,"line":3281},[52,14811,6479],{"class":360},[52,14813,14814],{"class":105}," geojson",[52,14816,1007],{"class":58},[13,14818,14819],{},"わかりやすいように、180度を越える・超えないパターンでの大圏航路の補正です。",[42,14821,14823],{"className":6471,"code":14822,"language":1433,"meta":47,"style":47},"import * as turf from '@turf\u002Fturf';\n\nconst nonOver = turf.greatCircle(\n    [139.6503, 35.6762],  \u002F\u002F 東京の座標\n    [77.2090, 28.6139], \u002F\u002F デリーの座標\n    { npoints: 30 }\n);\n\nconsole.log(nonOver)\n\u002F**\n[\n  [ 139.6503, 35.67620000000001 ],\n  [ 137.45530635075792, 36.00345099838847 ],\n  [ 135.24315241142824, 36.29039534292487 ],\n  [ 133.01584313143206, 36.53630839532294 ],\n  [ 130.77553308674652, 36.74055732953035 ],\n  [ 128.52450866279412, 36.90260805727012 ],\n  [ 126.26516729530344, 37.022031252284606 ],\n  [ 123.99999414238111, 37.09850731283235 ],\n  [ 121.73153667001746, 37.131830125727525 ],\n  [ 119.46237772511748, 37.121909525547366 ],\n  [ 117.19510773852923, 37.0687723782786 ],\n  [ 114.93229674053903, 36.972562257930214 ],\n  [ 112.67646687998554, 36.83353772552399 ],\n  [ 110.43006611487587, 36.65206926027127 ],\n  [ 108.19544368887564, 36.42863493057574 ],\n  [ 105.97482792817907, 36.163814925900674 ],\n  [ 103.7703067926894, 35.858285097980165 ],\n  [ 101.58381150098748, 35.51280968026889 ],\n  [ 99.41710342759748, 35.12823336735064 ],\n  [ 97.27176435078754, 34.705472941209955 ],\n  [ 95.14919001606306, 34.245508629231686 ],\n  [ 93.05058687993017, 33.749375370334334 ],\n  [ 90.97697181428099, 33.21815415185492 ],\n  [ 88.92917448617246, 32.6529635619425 ],\n  [ 86.90784208162192, 32.05495168160128 ],\n  [ 84.91344601479491, 31.42528841842546 ],\n  [ 82.94629025402638, 30.7651583616406 ],\n  [ 81.00652090116452, 30.075754216293156 ],\n  [ 79.09413667798896, 29.358270854084918 ],\n  [ 77.209, 28.613900000000005 ]\n]\n*\u002F\n\nconst over = turf.greatCircle(\n    [139.6503, 35.6762],  \u002F\u002F 東京の座標\n    [-147.7164, 64.8378], \u002F\u002F アラスカの座標\n    { npoints: 30 }\n);\n\nconsole.log(over)\n\u002F*\n  [\n    [ 139.6503, 35.67620000000001 ],\n    [ 140.80178756980726, 37.16607636013253 ],\n    [ 141.99941083018345, 38.64436492965305 ],\n    [ 143.24727313996488, 40.10993078543407 ],\n    [ 144.54983478842868, 41.56151748104379 ],\n    [ 145.91194328157903, 42.9977313513525 ],\n    [ 147.33886324769875, 44.41702385324905 ],\n    [ 148.83630454782175, 45.81767177569259 ],\n    [ 150.41044655311683, 47.19775518537249 ],\n    [ 152.0679557258351, 48.55513303641521 ],\n    [ 153.8159925691613, 49.88741647693477 ],\n    [ 155.66220265313686, 51.19194004906208 ],\n    [ 157.6146847535422, 52.465731224424154 ],\n    [ 159.68192717003024, 53.705479070518045 ],\n    [ 161.87270110244128, 54.90750333482882 ],\n    [ 164.19589776721827, 56.06772589186521 ],\n    [ 166.66029413034695, 57.18164734362052 ],\n    [ 169.27423139783608, 58.24433259326387 ],\n    [ 172.0451917704491, 59.25041037671838 ],\n    [ 174.9792638386216, 60.19409291290832 ],\n    [ 178.08049701800238, 61.069222785803966 ],\n    [ 180, 61.53895140096846 ]\n  ],\n  [\n    [ -180, 61.53895140096846 ],\n    [ -178.64983787529644, 61.86935452669037 ],\n    [ -175.2140408098754, 62.587877615485425 ],\n    [ -171.618756489847, 63.21818519285117 ],\n    [ -167.87562800253605, 63.753888204781546 ],\n    [ -164.0017045661598, 64.18906791167207 ],\n    [ -160.0194735821535, 64.51855132598804 ],\n    [ -155.9564136218279, 64.73818576863974 ],\n    [ -151.84402691822368, 64.84508272020929 ],\n    [ -147.7164, 64.8378 ]\n  ]\n]\n*\u002F\n",[49,14824,14825,14845,14849,14867,14887,14906,14919,14925,14929,14941,14946,14950,14955,14960,14965,14970,14975,14980,14985,14990,14995,15000,15005,15010,15015,15020,15025,15030,15035,15040,15045,15050,15055,15060,15065,15070,15075,15080,15085,15090,15095,15100,15105,15110,15114,15131,15147,15168,15180,15186,15190,15201,15206,15211,15216,15221,15226,15231,15236,15241,15246,15251,15256,15261,15266,15271,15276,15281,15286,15291,15296,15302,15308,15314,15320,15326,15332,15337,15343,15349,15355,15361,15367,15373,15379,15385,15391,15397,15403,15408],{"__ignoreMap":47},[52,14826,14827,14829,14831,14833,14835,14837,14839,14841,14843],{"class":54,"line":55},[52,14828,5939],{"class":360},[52,14830,8804],{"class":58},[52,14832,14166],{"class":360},[52,14834,14169],{"class":105},[52,14836,6064],{"class":360},[52,14838,6067],{"class":58},[52,14840,14176],{"class":75},[52,14842,376],{"class":58},[52,14844,1007],{"class":58},[52,14846,14847],{"class":54,"line":83},[52,14848,341],{"emptyLinePlaceholder":340},[52,14850,14851,14853,14856,14858,14860,14862,14864],{"class":54,"line":115},[52,14852,6867],{"class":65},[52,14854,14855],{"class":105}," nonOver ",[52,14857,69],{"class":58},[52,14859,14366],{"class":105},[52,14861,957],{"class":58},[52,14863,14457],{"class":418},[52,14865,14866],{"class":105},"(\n",[52,14868,14869,14872,14875,14877,14880,14882,14884],{"class":54,"line":142},[52,14870,14871],{"class":105},"    [",[52,14873,14874],{"class":1646},"139.6503",[52,14876,408],{"class":58},[52,14878,14879],{"class":1646}," 35.6762",[52,14881,405],{"class":105},[52,14883,408],{"class":58},[52,14885,14886],{"class":411},"  \u002F\u002F 東京の座標\n",[52,14888,14889,14891,14894,14896,14899,14901,14903],{"class":54,"line":169},[52,14890,14871],{"class":105},[52,14892,14893],{"class":1646},"77.2090",[52,14895,408],{"class":58},[52,14897,14898],{"class":1646}," 28.6139",[52,14900,405],{"class":105},[52,14902,408],{"class":58},[52,14904,14905],{"class":411}," \u002F\u002F デリーの座標\n",[52,14907,14908,14911,14913,14915,14917],{"class":54,"line":302},[52,14909,14910],{"class":58},"    {",[52,14912,14473],{"class":62},[52,14914,373],{"class":58},[52,14916,2721],{"class":1646},[52,14918,1081],{"class":58},[52,14920,14921,14923],{"class":54,"line":308},[52,14922,938],{"class":105},[52,14924,1007],{"class":58},[52,14926,14927],{"class":54,"line":318},[52,14928,341],{"emptyLinePlaceholder":340},[52,14930,14931,14934,14936,14938],{"class":54,"line":328},[52,14932,14933],{"class":105},"console",[52,14935,957],{"class":58},[52,14937,13252],{"class":418},[52,14939,14940],{"class":105},"(nonOver)\n",[52,14942,14943],{"class":54,"line":337},[52,14944,14945],{"class":411},"\u002F**\n",[52,14947,14948],{"class":54,"line":344},[52,14949,443],{"class":411},[52,14951,14952],{"class":54,"line":354},[52,14953,14954],{"class":411},"  [ 139.6503, 35.67620000000001 ],\n",[52,14956,14957],{"class":54,"line":367},[52,14958,14959],{"class":411},"  [ 137.45530635075792, 36.00345099838847 ],\n",[52,14961,14962],{"class":54,"line":387},[52,14963,14964],{"class":411},"  [ 135.24315241142824, 36.29039534292487 ],\n",[52,14966,14967],{"class":54,"line":415},[52,14968,14969],{"class":411},"  [ 133.01584313143206, 36.53630839532294 ],\n",[52,14971,14972],{"class":54,"line":427},[52,14973,14974],{"class":411},"  [ 130.77553308674652, 36.74055732953035 ],\n",[52,14976,14977],{"class":54,"line":435},[52,14978,14979],{"class":411},"  [ 128.52450866279412, 36.90260805727012 ],\n",[52,14981,14982],{"class":54,"line":446},[52,14983,14984],{"class":411},"  [ 126.26516729530344, 37.022031252284606 ],\n",[52,14986,14987],{"class":54,"line":480},[52,14988,14989],{"class":411},"  [ 123.99999414238111, 37.09850731283235 ],\n",[52,14991,14992],{"class":54,"line":509},[52,14993,14994],{"class":411},"  [ 121.73153667001746, 37.131830125727525 ],\n",[52,14996,14997],{"class":54,"line":539},[52,14998,14999],{"class":411},"  [ 119.46237772511748, 37.121909525547366 ],\n",[52,15001,15002],{"class":54,"line":547},[52,15003,15004],{"class":411},"  [ 117.19510773852923, 37.0687723782786 ],\n",[52,15006,15007],{"class":54,"line":553},[52,15008,15009],{"class":411},"  [ 114.93229674053903, 36.972562257930214 ],\n",[52,15011,15012],{"class":54,"line":559},[52,15013,15014],{"class":411},"  [ 112.67646687998554, 36.83353772552399 ],\n",[52,15016,15017],{"class":54,"line":564},[52,15018,15019],{"class":411},"  [ 110.43006611487587, 36.65206926027127 ],\n",[52,15021,15022],{"class":54,"line":569},[52,15023,15024],{"class":411},"  [ 108.19544368887564, 36.42863493057574 ],\n",[52,15026,15027],{"class":54,"line":1106},[52,15028,15029],{"class":411},"  [ 105.97482792817907, 36.163814925900674 ],\n",[52,15031,15032],{"class":54,"line":1135},[52,15033,15034],{"class":411},"  [ 103.7703067926894, 35.858285097980165 ],\n",[52,15036,15037],{"class":54,"line":1164},[52,15038,15039],{"class":411},"  [ 101.58381150098748, 35.51280968026889 ],\n",[52,15041,15042],{"class":54,"line":4},[52,15043,15044],{"class":411},"  [ 99.41710342759748, 35.12823336735064 ],\n",[52,15046,15047],{"class":54,"line":1199},[52,15048,15049],{"class":411},"  [ 97.27176435078754, 34.705472941209955 ],\n",[52,15051,15052],{"class":54,"line":1204},[52,15053,15054],{"class":411},"  [ 95.14919001606306, 34.245508629231686 ],\n",[52,15056,15057],{"class":54,"line":1209},[52,15058,15059],{"class":411},"  [ 93.05058687993017, 33.749375370334334 ],\n",[52,15061,15062],{"class":54,"line":1214},[52,15063,15064],{"class":411},"  [ 90.97697181428099, 33.21815415185492 ],\n",[52,15066,15067],{"class":54,"line":1219},[52,15068,15069],{"class":411},"  [ 88.92917448617246, 32.6529635619425 ],\n",[52,15071,15072],{"class":54,"line":3216},[52,15073,15074],{"class":411},"  [ 86.90784208162192, 32.05495168160128 ],\n",[52,15076,15077],{"class":54,"line":3237},[52,15078,15079],{"class":411},"  [ 84.91344601479491, 31.42528841842546 ],\n",[52,15081,15082],{"class":54,"line":3251},[52,15083,15084],{"class":411},"  [ 82.94629025402638, 30.7651583616406 ],\n",[52,15086,15087],{"class":54,"line":3256},[52,15088,15089],{"class":411},"  [ 81.00652090116452, 30.075754216293156 ],\n",[52,15091,15092],{"class":54,"line":3281},[52,15093,15094],{"class":411},"  [ 79.09413667798896, 29.358270854084918 ],\n",[52,15096,15097],{"class":54,"line":3295},[52,15098,15099],{"class":411},"  [ 77.209, 28.613900000000005 ]\n",[52,15101,15102],{"class":54,"line":4922},[52,15103,15104],{"class":411},"]\n",[52,15106,15107],{"class":54,"line":4943},[52,15108,15109],{"class":411},"*\u002F\n",[52,15111,15112],{"class":54,"line":4961},[52,15113,341],{"emptyLinePlaceholder":340},[52,15115,15116,15118,15121,15123,15125,15127,15129],{"class":54,"line":4978},[52,15117,6867],{"class":65},[52,15119,15120],{"class":105}," over ",[52,15122,69],{"class":58},[52,15124,14366],{"class":105},[52,15126,957],{"class":58},[52,15128,14457],{"class":418},[52,15130,14866],{"class":105},[52,15132,15133,15135,15137,15139,15141,15143,15145],{"class":54,"line":4983},[52,15134,14871],{"class":105},[52,15136,14874],{"class":1646},[52,15138,408],{"class":58},[52,15140,14879],{"class":1646},[52,15142,405],{"class":105},[52,15144,408],{"class":58},[52,15146,14886],{"class":411},[52,15148,15149,15151,15153,15156,15158,15161,15163,15165],{"class":54,"line":5004},[52,15150,14871],{"class":105},[52,15152,2700],{"class":58},[52,15154,15155],{"class":1646},"147.7164",[52,15157,408],{"class":58},[52,15159,15160],{"class":1646}," 64.8378",[52,15162,405],{"class":105},[52,15164,408],{"class":58},[52,15166,15167],{"class":411}," \u002F\u002F アラスカの座標\n",[52,15169,15170,15172,15174,15176,15178],{"class":54,"line":5053},[52,15171,14910],{"class":58},[52,15173,14473],{"class":62},[52,15175,373],{"class":58},[52,15177,2721],{"class":1646},[52,15179,1081],{"class":58},[52,15181,15182,15184],{"class":54,"line":5058},[52,15183,938],{"class":105},[52,15185,1007],{"class":58},[52,15187,15188],{"class":54,"line":5063},[52,15189,341],{"emptyLinePlaceholder":340},[52,15191,15192,15194,15196,15198],{"class":54,"line":5096},[52,15193,14933],{"class":105},[52,15195,957],{"class":58},[52,15197,13252],{"class":418},[52,15199,15200],{"class":105},"(over)\n",[52,15202,15203],{"class":54,"line":5109},[52,15204,15205],{"class":411},"\u002F*\n",[52,15207,15208],{"class":54,"line":5128},[52,15209,15210],{"class":411},"  [\n",[52,15212,15213],{"class":54,"line":5141},[52,15214,15215],{"class":411},"    [ 139.6503, 35.67620000000001 ],\n",[52,15217,15218],{"class":54,"line":5162},[52,15219,15220],{"class":411},"    [ 140.80178756980726, 37.16607636013253 ],\n",[52,15222,15223],{"class":54,"line":5167},[52,15224,15225],{"class":411},"    [ 141.99941083018345, 38.64436492965305 ],\n",[52,15227,15228],{"class":54,"line":5203},[52,15229,15230],{"class":411},"    [ 143.24727313996488, 40.10993078543407 ],\n",[52,15232,15233],{"class":54,"line":5253},[52,15234,15235],{"class":411},"    [ 144.54983478842868, 41.56151748104379 ],\n",[52,15237,15238],{"class":54,"line":5279},[52,15239,15240],{"class":411},"    [ 145.91194328157903, 42.9977313513525 ],\n",[52,15242,15243],{"class":54,"line":5284},[52,15244,15245],{"class":411},"    [ 147.33886324769875, 44.41702385324905 ],\n",[52,15247,15248],{"class":54,"line":5289},[52,15249,15250],{"class":411},"    [ 148.83630454782175, 45.81767177569259 ],\n",[52,15252,15253],{"class":54,"line":5310},[52,15254,15255],{"class":411},"    [ 150.41044655311683, 47.19775518537249 ],\n",[52,15257,15258],{"class":54,"line":5331},[52,15259,15260],{"class":411},"    [ 152.0679557258351, 48.55513303641521 ],\n",[52,15262,15263],{"class":54,"line":5344},[52,15264,15265],{"class":411},"    [ 153.8159925691613, 49.88741647693477 ],\n",[52,15267,15268],{"class":54,"line":5349},[52,15269,15270],{"class":411},"    [ 155.66220265313686, 51.19194004906208 ],\n",[52,15272,15273],{"class":54,"line":5370},[52,15274,15275],{"class":411},"    [ 157.6146847535422, 52.465731224424154 ],\n",[52,15277,15278],{"class":54,"line":5383},[52,15279,15280],{"class":411},"    [ 159.68192717003024, 53.705479070518045 ],\n",[52,15282,15283],{"class":54,"line":5388},[52,15284,15285],{"class":411},"    [ 161.87270110244128, 54.90750333482882 ],\n",[52,15287,15288],{"class":54,"line":5393},[52,15289,15290],{"class":411},"    [ 164.19589776721827, 56.06772589186521 ],\n",[52,15292,15293],{"class":54,"line":5398},[52,15294,15295],{"class":411},"    [ 166.66029413034695, 57.18164734362052 ],\n",[52,15297,15299],{"class":54,"line":15298},71,[52,15300,15301],{"class":411},"    [ 169.27423139783608, 58.24433259326387 ],\n",[52,15303,15305],{"class":54,"line":15304},72,[52,15306,15307],{"class":411},"    [ 172.0451917704491, 59.25041037671838 ],\n",[52,15309,15311],{"class":54,"line":15310},73,[52,15312,15313],{"class":411},"    [ 174.9792638386216, 60.19409291290832 ],\n",[52,15315,15317],{"class":54,"line":15316},74,[52,15318,15319],{"class":411},"    [ 178.08049701800238, 61.069222785803966 ],\n",[52,15321,15323],{"class":54,"line":15322},75,[52,15324,15325],{"class":411},"    [ 180, 61.53895140096846 ]\n",[52,15327,15329],{"class":54,"line":15328},76,[52,15330,15331],{"class":411},"  ],\n",[52,15333,15335],{"class":54,"line":15334},77,[52,15336,15210],{"class":411},[52,15338,15340],{"class":54,"line":15339},78,[52,15341,15342],{"class":411},"    [ -180, 61.53895140096846 ],\n",[52,15344,15346],{"class":54,"line":15345},79,[52,15347,15348],{"class":411},"    [ -178.64983787529644, 61.86935452669037 ],\n",[52,15350,15352],{"class":54,"line":15351},80,[52,15353,15354],{"class":411},"    [ -175.2140408098754, 62.587877615485425 ],\n",[52,15356,15358],{"class":54,"line":15357},81,[52,15359,15360],{"class":411},"    [ -171.618756489847, 63.21818519285117 ],\n",[52,15362,15364],{"class":54,"line":15363},82,[52,15365,15366],{"class":411},"    [ -167.87562800253605, 63.753888204781546 ],\n",[52,15368,15370],{"class":54,"line":15369},83,[52,15371,15372],{"class":411},"    [ -164.0017045661598, 64.18906791167207 ],\n",[52,15374,15376],{"class":54,"line":15375},84,[52,15377,15378],{"class":411},"    [ -160.0194735821535, 64.51855132598804 ],\n",[52,15380,15382],{"class":54,"line":15381},85,[52,15383,15384],{"class":411},"    [ -155.9564136218279, 64.73818576863974 ],\n",[52,15386,15388],{"class":54,"line":15387},86,[52,15389,15390],{"class":411},"    [ -151.84402691822368, 64.84508272020929 ],\n",[52,15392,15394],{"class":54,"line":15393},87,[52,15395,15396],{"class":411},"    [ -147.7164, 64.8378 ]\n",[52,15398,15400],{"class":54,"line":15399},88,[52,15401,15402],{"class":411},"  ]\n",[52,15404,15406],{"class":54,"line":15405},89,[52,15407,15104],{"class":411},[52,15409,15411],{"class":54,"line":15410},90,[52,15412,15109],{"class":411},[1413,15414,15415],{},"html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}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);}",{"title":47,"searchDepth":115,"depth":115,"links":15417},[15418,15419,15420],{"id":14093,"depth":83,"text":14094},{"id":14121,"depth":83,"text":14121},{"id":14139,"depth":83,"text":14140,"children":15421},[15422],{"id":14146,"depth":115,"text":14147},[1423],"2024-09-22","平面の地図にまっすぐひいた線は最短の航路ではありません。",{},"\u002Farticles\u002F2024_09_22_line_great_circle_antimeridian_cutting",{"title":14082,"description":15425},"articles\u002F2024_09_22_line_great_circle_antimeridian_cutting",[1433,15431],"gis","2024_09_22_line_great_circle_antimeridian_cutting\u002Fthumbnail.webp","rjpze4IQqIg5qQZw4X3RE4NP18BGM-IbRT3T9c54h4c",{"id":15435,"title":15436,"body":15437,"category":16217,"createdAt":16218,"description":16219,"extension":1426,"index":1427,"meta":16220,"navigation":340,"path":16221,"publish":340,"seo":16222,"series":1427,"seriesTitle":1427,"stem":16223,"tag":16224,"thumbnail":16226,"updatedAt":1427,"__hash__":16227},"articles\u002Farticles\u002F2024_08_31_map_lib_price_guide.md","Webやネイティブアプリで地図表示・操作をしたいときのライブラリ選定、使用で考えたいこと",{"type":10,"value":15438,"toc":16183},[15439,15448,15451,15465,15468,15471,15474,15477,15480,15483,15486,15489,15493,15496,15499,15502,15505,15508,15511,15514,15517,15520,15523,15526,15529,15532,15535,15538,15542,15545,15548,15552,15555,15558,15561,15564,15568,15571,15574,15577,15580,15584,15587,15590,15593,15596,15599,15603,15606,15609,15612,15615,15619,15622,15625,15628,15631,15635,15638,15641,15644,15647,15651,15654,15657,15660,15663,15666,15669,15673,15676,16023,16027,16030,16049,16053,16056,16059,16062,16066,16069,16113,16117,16120,16124,16127,16152,16155,16158,16161,16164,16180],[13,15440,15441,15442,15447],{},"こんにちはjunです。私は個人開発で",[1450,15443,15446],{"href":15444,"rel":15445},"https:\u002F\u002Froute-share.net",[1454],"RouteShare","とよばれる地図を用いた地理情報・アクティビティ共有型webサービスを運営・開発しています。そのサービスではGoogle Map APIを使用して地図を表示したり、ユーザーが任意にピンをおいたりラインを引いたり、地図上から検索できるといった機能を持たせています。このように地図を表示したり、何かしら地図上で編集する機能などをサービスに加えたい時にGoogle Map APIやMapbxのSDKを使用することが多いと思います。しかし課金の体系がわからなかったり、意外と価格がすることもあります。",[13,15449,15450],{},"今回の記事では",[1464,15452,15453,15456,15459,15462],{},[1467,15454,15455],{},"地図ライブラリに必要なこと",[1467,15457,15458],{},"いかにしてwebなどで地図を表示するのか",[1467,15460,15461],{},"最低限の構成で低コストで運用する方法",[1467,15463,15464],{},"メジャーライブラリの課金体系",[13,15466,15467],{},"という４点について詳細な内容をお伝えしたいとおもいます。主にこれから地図を利用したアプリケーションを開発、運営したいと思う技術者向けの内容です。",[17,15469,15470],{"id":15470},"地図を表示して操作するということ",[13,15472,15473],{},"Google MapやMapboxなどの地図ライブラリを使用して地図を表示し、操作できることは当たり前のように感じるかもしれませんが、その裏側には複雑な仕組みが存在します。ここでは、その仕組みをわかりやすく解説します。",[1518,15475,15476],{"id":15476},"様々な操作に応じて指定した地図を表示する",[13,15478,15479],{},"地図アプリを使う際、ユーザーはズームイン・ズームアウト、パン（スワイプ）などの操作を行います。この時、地図ライブラリはその操作に応じて、表示する地図データを動的に更新します。例えば、ズームインすると、現在の中央緯度経度を基点に、より詳細な地図データを読み込み、表示範囲を狭めることで、より詳細な情報を表示します。",[1518,15481,15482],{"id":15482},"地図を表示するためにはタイル画像を読み込んでいる",[13,15484,15485],{},"地図データは大きな一枚の画像ではなく、「タイル」と呼ばれる小さな画像に分割されています。これにより、地図を表示する際には、ユーザーの画面に表示される部分だけを効率的に読み込むことができます。例えば、ズームインしたり、パンしたりするたびに、必要な範囲のタイル画像が新たに読み込まれ、表示されます。Googleのネットワークを見てみるとこのようにタイルごとの画像をリクエストされているのがわかります。",[729,15487],{":src":15488,":width":732},"'2024_08_31_map_lib_price_guide\u002Fmaptitle_req.png'",[1518,15490,15492],{"id":15491},"どこを表示しているのかをjsなどで制御し必要なタイルをリクエストする","どこを表示しているのかをJSなどで制御し、必要なタイルをリクエストする",[13,15494,15495],{},"地図ライブラリはJavaScriptなどを使って、現在の表示範囲やズームレベルを管理し、それに基づいて必要なタイル画像をサーバーにリクエストします。これにより、無駄なデータの読み込みを避け、効率的に地図を表示することができます。例えば、ユーザーが画面を移動させた際、表示範囲が変わったことを検知し、その範囲に対応するタイル画像を新たに取得して表示します。",[1518,15497,15498],{"id":15498},"球体の地球を平面で問題なく表示できるようにするための工夫",[13,15500,15501],{},"地球は球体ですが、地図は平面に表示されます。この変換には「メルカトル図法」などの投影法が使われています。メルカトル図法は、経緯度を一定の間隔で平面に投影することで、球体の地球を平面の地図として表示することができます。ただし、この方法では緯度が高いほど形が歪むため、特に高緯度地方では地図のスケールが大きくなります。",[1518,15503,15504],{"id":15504},"地図をスクラッチで開発する場合の考慮点",[13,15506,15507],{},"もし、地図表示機能をスクラッチで開発しようと考えると、いくつか非常に重要な技術や知識が必要となります。まず、地球は球体であるため、それを平面の地図として正確に表現するためには、地理計算技術や地学に関する深い知識が不可欠です。例えば、メルカトル図法などの投影法を用いて、経緯度を正確に平面に変換する必要があります。",[13,15509,15510],{},"さらに、ユーザーがズームイン・ズームアウト、パンなどの操作を行うたびに、その表示位置を正確に計算し、適切な地図データを表示するためのアルゴリズムが求められます。このような技術を実装するには、数学的な計算だけでなく、地理情報システム（GIS）に関する知識も必要です。",[13,15512,15513],{},"また、実際に地図を表示するためには、膨大な数のタイル画像を効率的に管理し、提供するためのタイルサーバも不可欠です。これを自前で構築するとなると、莫大なデータ容量を持つサーバと、それを高速に処理・配信するためのインフラが必要となります。例えば、地球全体をカバーするタイル画像をすべて用意した場合、その総データサイズは数百GBから数TBに達することがあります。",[13,15515,15516],{},"タイルサーバの必要枚数の計算\n地球全体をカバーするためのタイル画像の総枚数は、ズームレベルに依存します。タイルの総枚数は、ズームレベルz に対して次の式で計算されます：",[13,15518,15519],{},"タイル枚数 = 2^(2z)",[13,15521,15522],{},"例えば、ズームレベル0では1枚、ズームレベル1では4枚、ズームレベル2では16枚のタイルが必要です。ズームレベルが上がるごとに、必要なタイル枚数は指数関数的に増加します。ズームレベル10の場合、約1,048,576枚、ズームレベル15では約1,073,741,824枚ものタイルが必要となります。まして言語ごとに表示を変えるとならにさらに必要となります。",[13,15524,15525],{},"これらを保存し、ユーザーに迅速に提供するためには、高性能かつ大容量のストレージが必要となり、その運用コストも無視できません。静的でないサーバーサイドレンダリングでもサーバーパワーとキャッシュを工夫する知識が必要です。",[13,15527,15528],{},"特に、Web上で地図を描画する際には、CanvasやWebGLなどの描画技術が使われます。これにより、地図データをピクセル単位で描画し、ユーザーの操作に応じてリアルタイムで更新することが可能です。しかし、このような技術を一から実装することは非常に手間がかかり、パフォーマンスの最適化やブラウザ間の互換性を考慮する必要があります。",[13,15530,15531],{},"さらに、Google Mapを例にとると、短く・狭い範囲での２点は直線ですが広い範囲では曲線になります。実際は球面に沿ったまっすぐな線を引いていますが、それを平面に表した結果です。",[729,15533],{":src":15534,":width":732},"'2024_08_31_map_lib_price_guide\u002Fdistorted_map_example.png'",[13,15536,15537],{},"このように、広範囲での地図表示には特有の課題があり、それらを解決するための技術が必要となります。",[17,15539,15541],{"id":15540},"メジャーライブラリタイルサーバの仕組みと課金体系","メジャーライブラリ・タイルサーバの仕組みと課金体系",[13,15543,15544],{},"地図表示ライブラリやタイルサーバは、地図アプリケーションの開発において重要な要素です。ここでは、代表的なライブラリとタイルサーバの紹介とともに、それぞれのメリット、デメリット、制限事項、そして課金体系について解説します。なお2024年8月現在の内容です。",[1518,15546,15547],{"id":15547},"地図表示ライブラリ",[2409,15549,15551],{"id":15550},"google-map-sdk","Google Map SDK",[13,15553,15554],{},"メリット:世界中で広く使用され、信頼性が高い。多機能で詳細な地図データやストリートビューなど豊富なAPIを提供。ドキュメントが充実し、コミュニティサポートも豊富。SDK以外にも地理計算ライブラリが含まれており、高度なカスタマイズが可能。",[13,15556,15557],{},"デメリット:課金体系が比較的高額で、使用量が増えるとコストが急増する可能性がある。特定のデータや機能に制約がある場合がある。",[13,15559,15560],{},"制限事項:無料使用枠があるが、一定のリクエスト数を超えると課金が発生する。Googleの著作表示が必要。",[13,15562,15563],{},"課金体系:web・ネイティブ共に月間のSDKリクエスト数に基づく従量課金制。SDKのインスタンス作成時に課金され、料金はAPIごとに異なり、使用する機能によって変動する。",[2409,15565,15567],{"id":15566},"mapbox-sdk","Mapbox SDK",[13,15569,15570],{},"メリット:新興のプラットフォームで、Google Mapよりオープンかつデベロッパーフレンドリー。高度にカスタマイズ可能で、地図のデザインやデータ表示を柔軟にコントロールできる。料金体系がGoogle Mapより安価で、小規模なプロジェクトやスタートアップに適している。",[13,15572,15573],{},"デメリット:Google Mapに比べてコミュニティ規模が小さく、サポートリソースが限られる場合がある。人気が出た場合、Google Mapに近い価格になる可能性がある。",[13,15575,15576],{},"制限事項:商用利用時に無料枠を超えると従量課金が発生する。Mapboxの著作表示が必要。",[13,15578,15579],{},"課金体系:web版はSDKのインスタンス作成時をSDKリクエストとして課金、ネイティブ版はアクセストークンから計測されるアクティブユーザー数に基づく。",[2409,15581,15583],{"id":15582},"leafletjs","Leaflet.js",[13,15585,15586],{},"メリット:軽量でシンプルなオープンソースライブラリでカスタマイズ性が高い。大規模な地図アプリケーションだけでなく、軽量な地図表示にも適しており、無料で使用可能。",[13,15588,15589],{},"デメリット:標準機能は少なく、複雑な機能や高度なカスタマイズには他のプラグインやライブラリが必要。自前でタイルサーバを用意する必要がある場合がある。",[13,15591,15592],{},"制限事項:オープンソースのため、サポートはコミュニティに依存する部分が大きい。",[13,15594,15595],{},"課金体系:Leaflet.js自体は無料。ただし、タイルサーバ利用には別途コストが発生する場合がある。",[1518,15597,15598],{"id":15598},"タイルサーバ",[2409,15600,15602],{"id":15601},"maptiler","MapTiler",[13,15604,15605],{},"メリット:高品質な地図タイルを提供し、世界中の地図データにアクセス可能。カスタマイズが容易で商用利用もサポート。",[13,15607,15608],{},"デメリット:高度な機能や大規模な使用にはそれ相応のコストがかかる。",[13,15610,15611],{},"制限事項:商用利用には有料プランが必要。",[13,15613,15614],{},"課金体系:利用量に応じた従量課金制で、使用するタイル数やアクセス頻度に基づいて料金が決まる。",[2409,15616,15618],{"id":15617},"openstreetmap","OpenStreetMap",[13,15620,15621],{},"メリット:オープンソースプロジェクトであり、無料で利用可能。世界中のユーザーがデータを提供し、地図データが豊富。主にテスト時や地図を印刷する際に使用される。",[13,15623,15624],{},"デメリット:商用利用は基本的に不可能で使用には制限がある。データの正確性や一貫性にバラつきがある場合がある。アクセスしすぎるとブロックされる。",[13,15626,15627],{},"制限事項:大規模な商用プロジェクトでの利用は推奨されない。",[13,15629,15630],{},"課金体系:無料で使用可能だが、商用利用時には著作表示が必要。",[2409,15632,15634],{"id":15633},"mapbox-tile-server","Mapbox Tile Server",[13,15636,15637],{},"メリット:高品質な地図タイルを提供し、Mapbox SDKとの統合が容易。高度にカスタマイズ可能で、デザインや機能を自由に変更できる。",[13,15639,15640],{},"デメリット:使用量に応じてコストがかかるため、大規模なプロジェクトでは費用が高くなる可能性がある。",[13,15642,15643],{},"制限事項:無料使用枠があるが、商用利用には有料プランが必要。",[13,15645,15646],{},"課金体系:使用するタイル数やユーザー数に応じた従量課金制。",[2409,15648,15650],{"id":15649},"google-map-tile","Google Map Tile",[13,15652,15653],{},"メリット:信頼性が高く、Googleの地図データを利用可能。多機能で、他のGoogleサービスとの連携が容易。",[13,15655,15656],{},"デメリット:料金が高めで、使用量が増えるとコストが急増する可能性がある。",[13,15658,15659],{},"制限事項:一定の無料枠を超えると課金が発生する。",[13,15661,15662],{},"課金体系:タイルのリクエスト数に基づいた従量課金制。",[17,15664,15665],{"id":15665},"webでの最小の構成",[13,15667,15668],{},"Webアプリケーションで地図を表示する際、コストを抑えつつシンプルに構成するためには、軽量な地図ライブラリと信頼性の高いタイルサーバを選ぶことが重要です。ここでは、Leaflet.jsとMapboxのタイルサーバを使用した最小構成について説明します。",[1518,15670,15672],{"id":15671},"_1-leafletjsの導入","1. Leaflet.jsの導入",[13,15674,15675],{},"Leaflet.jsは、軽量でシンプルなオープンソースのJavaScriptライブラリです。基本的な地図表示機能を備えており、軽量でありながら高いカスタマイズ性を持っています。まずは、Leaflet.jsをプロジェクトに導入します。",[42,15677,15679],{"className":44,"code":15678,"language":46,"meta":47,"style":47},"\u003C!DOCTYPE html>\n\u003Chtml>\n\u003Chead>\n    \u003Ctitle>Leaflet Map\u003C\u002Ftitle>\n    \u003Clink rel=\"stylesheet\" href=\"https:\u002F\u002Funpkg.com\u002Fleaflet@1.7.1\u002Fdist\u002Fleaflet.css\" \u002F>\n    \u003Cscript src=\"https:\u002F\u002Funpkg.com\u002Fleaflet@1.7.1\u002Fdist\u002Fleaflet.js\">\u003C\u002Fscript>\n\u003C\u002Fhead>\n\u003Cbody>\n    \u003Cdiv id=\"map\" style=\"width: 600px; height: 400px;\">\u003C\u002Fdiv>\n    \u003Cscript>\n        var map = L.map('map').setView([51.505, -0.09], 13);\n\n        L.tileLayer('https:\u002F\u002Fapi.mapbox.com\u002Fstyles\u002Fv1\u002F{id}\u002Ftiles\u002F{z}\u002F{x}\u002F{y}?access_token=YOUR_MAPBOX_ACCESS_TOKEN', {\n            maxZoom: 18,\n            id: 'mapbox\u002Fstreets-v11',\n            tileSize: 512,\n            zoomOffset: -1,\n            accessToken: 'YOUR_MAPBOX_ACCESS_TOKEN'\n        }).addTo(map);\n    \u003C\u002Fscript>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[49,15680,15681,15691,15699,15707,15724,15754,15777,15785,15793,15827,15835,15889,15893,15916,15928,15944,15956,15969,15983,15999,16007,16015],{"__ignoreMap":47},[52,15682,15683,15685,15687,15689],{"class":54,"line":55},[52,15684,1554],{"class":58},[52,15686,1557],{"class":62},[52,15688,1560],{"class":65},[52,15690,80],{"class":58},[52,15692,15693,15695,15697],{"class":54,"line":83},[52,15694,59],{"class":58},[52,15696,46],{"class":62},[52,15698,80],{"class":58},[52,15700,15701,15703,15705],{"class":54,"line":115},[52,15702,59],{"class":58},[52,15704,1578],{"class":62},[52,15706,80],{"class":58},[52,15708,15709,15711,15713,15715,15718,15720,15722],{"class":54,"line":142},[52,15710,1575],{"class":58},[52,15712,1609],{"class":62},[52,15714,102],{"class":58},[52,15716,15717],{"class":105},"Leaflet Map",[52,15719,108],{"class":58},[52,15721,1609],{"class":62},[52,15723,80],{"class":58},[52,15725,15726,15728,15730,15732,15734,15736,15738,15740,15742,15744,15746,15749,15751],{"class":54,"line":169},[52,15727,1575],{"class":58},[52,15729,10012],{"class":62},[52,15731,10027],{"class":65},[52,15733,69],{"class":58},[52,15735,72],{"class":58},[52,15737,10034],{"class":75},[52,15739,72],{"class":58},[52,15741,10015],{"class":65},[52,15743,69],{"class":58},[52,15745,72],{"class":58},[52,15747,15748],{"class":75},"https:\u002F\u002Funpkg.com\u002Fleaflet@1.7.1\u002Fdist\u002Fleaflet.css",[52,15750,72],{"class":58},[52,15752,15753],{"class":58}," \u002F>\n",[52,15755,15756,15758,15760,15762,15764,15766,15769,15771,15773,15775],{"class":54,"line":302},[52,15757,1575],{"class":58},[52,15759,349],{"class":62},[52,15761,1782],{"class":65},[52,15763,69],{"class":58},[52,15765,72],{"class":58},[52,15767,15768],{"class":75},"https:\u002F\u002Funpkg.com\u002Fleaflet@1.7.1\u002Fdist\u002Fleaflet.js",[52,15770,72],{"class":58},[52,15772,1761],{"class":58},[52,15774,349],{"class":62},[52,15776,80],{"class":58},[52,15778,15779,15781,15783],{"class":54,"line":308},[52,15780,108],{"class":58},[52,15782,1578],{"class":62},[52,15784,80],{"class":58},[52,15786,15787,15789,15791],{"class":54,"line":318},[52,15788,59],{"class":58},[52,15790,1728],{"class":62},[52,15792,80],{"class":58},[52,15794,15795,15797,15799,15801,15803,15805,15807,15809,15812,15814,15816,15819,15821,15823,15825],{"class":54,"line":328},[52,15796,1575],{"class":58},[52,15798,5995],{"class":62},[52,15800,1750],{"class":65},[52,15802,69],{"class":58},[52,15804,72],{"class":58},[52,15806,1031],{"class":75},[52,15808,72],{"class":58},[52,15810,15811],{"class":65}," style",[52,15813,69],{"class":58},[52,15815,72],{"class":58},[52,15817,15818],{"class":75},"width: 600px; height: 400px;",[52,15820,72],{"class":58},[52,15822,1761],{"class":58},[52,15824,5995],{"class":62},[52,15826,80],{"class":58},[52,15828,15829,15831,15833],{"class":54,"line":337},[52,15830,1575],{"class":58},[52,15832,349],{"class":62},[52,15834,80],{"class":58},[52,15836,15837,15839,15842,15844,15847,15849,15851,15853,15855,15857,15859,15861,15863,15866,15868,15871,15873,15875,15878,15880,15882,15885,15887],{"class":54,"line":344},[52,15838,3105],{"class":65},[52,15840,15841],{"class":105}," map ",[52,15843,69],{"class":58},[52,15845,15846],{"class":105}," L",[52,15848,957],{"class":58},[52,15850,1031],{"class":418},[52,15852,932],{"class":105},[52,15854,376],{"class":58},[52,15856,1031],{"class":75},[52,15858,376],{"class":58},[52,15860,938],{"class":105},[52,15862,957],{"class":58},[52,15864,15865],{"class":418},"setView",[52,15867,14374],{"class":105},[52,15869,15870],{"class":1646},"51.505",[52,15872,408],{"class":58},[52,15874,3113],{"class":58},[52,15876,15877],{"class":1646},"0.09",[52,15879,405],{"class":105},[52,15881,408],{"class":58},[52,15883,15884],{"class":1646}," 13",[52,15886,938],{"class":105},[52,15888,1007],{"class":58},[52,15890,15891],{"class":54,"line":354},[52,15892,341],{"emptyLinePlaceholder":340},[52,15894,15895,15898,15900,15903,15905,15907,15910,15912,15914],{"class":54,"line":367},[52,15896,15897],{"class":105},"        L",[52,15899,957],{"class":58},[52,15901,15902],{"class":418},"tileLayer",[52,15904,932],{"class":105},[52,15906,376],{"class":58},[52,15908,15909],{"class":75},"https:\u002F\u002Fapi.mapbox.com\u002Fstyles\u002Fv1\u002F{id}\u002Ftiles\u002F{z}\u002F{x}\u002F{y}?access_token=YOUR_MAPBOX_ACCESS_TOKEN",[52,15911,376],{"class":58},[52,15913,408],{"class":58},[52,15915,6100],{"class":58},[52,15917,15918,15921,15923,15926],{"class":54,"line":387},[52,15919,15920],{"class":62},"            maxZoom",[52,15922,373],{"class":58},[52,15924,15925],{"class":1646}," 18",[52,15927,384],{"class":58},[52,15929,15930,15933,15935,15937,15940,15942],{"class":54,"line":415},[52,15931,15932],{"class":62},"            id",[52,15934,373],{"class":58},[52,15936,6067],{"class":58},[52,15938,15939],{"class":75},"mapbox\u002Fstreets-v11",[52,15941,376],{"class":58},[52,15943,384],{"class":58},[52,15945,15946,15949,15951,15954],{"class":54,"line":427},[52,15947,15948],{"class":62},"            tileSize",[52,15950,373],{"class":58},[52,15952,15953],{"class":1646}," 512",[52,15955,384],{"class":58},[52,15957,15958,15961,15963,15965,15967],{"class":54,"line":435},[52,15959,15960],{"class":62},"            zoomOffset",[52,15962,373],{"class":58},[52,15964,3113],{"class":58},[52,15966,31],{"class":1646},[52,15968,384],{"class":58},[52,15970,15971,15974,15976,15978,15981],{"class":54,"line":446},[52,15972,15973],{"class":62},"            accessToken",[52,15975,373],{"class":58},[52,15977,6067],{"class":58},[52,15979,15980],{"class":75},"YOUR_MAPBOX_ACCESS_TOKEN",[52,15982,10489],{"class":58},[52,15984,15985,15987,15989,15991,15994,15997],{"class":54,"line":480},[52,15986,12325],{"class":58},[52,15988,938],{"class":105},[52,15990,957],{"class":58},[52,15992,15993],{"class":418},"addTo",[52,15995,15996],{"class":105},"(map)",[52,15998,1007],{"class":58},[52,16000,16001,16003,16005],{"class":54,"line":509},[52,16002,1717],{"class":58},[52,16004,349],{"class":62},[52,16006,80],{"class":58},[52,16008,16009,16011,16013],{"class":54,"line":539},[52,16010,108],{"class":58},[52,16012,1728],{"class":62},[52,16014,80],{"class":58},[52,16016,16017,16019,16021],{"class":54,"line":547},[52,16018,108],{"class":58},[52,16020,46],{"class":62},[52,16022,80],{"class":58},[1518,16024,16026],{"id":16025},"_2-mapboxタイルサーバの契約と設定","2. Mapboxタイルサーバの契約と設定",[13,16028,16029],{},"Leaflet.js自体は無料で利用できますが、タイル画像を提供するサーバが必要です。ここでは、Mapboxのタイルサーバを契約します。Mapboxは信頼性が高く、カスタマイズ性に優れたタイルサーバを提供しています。また無料枠内であればタイルも無料で利用できます。",[2489,16031,16032,16040,16046],{},[1467,16033,16034,16039],{},[1450,16035,16038],{"href":16036,"rel":16037},"https:\u002F\u002Fwww.mapbox.com\u002F",[1454],"Mapboxの公式サイト","でアカウントを作成します。",[1467,16041,16042,16043,16045],{},"タイルを作成し、アクセストークンを取得し、上記のLeaflet.jsのコードにある ",[49,16044,15980],{}," の部分に取得したアクセストークンを入力します。",[1467,16047,16048],{},"必要に応じて、地図のスタイルやズームレベルをカスタマイズできます。",[1518,16050,16052],{"id":16051},"_3-最小構成のメリットと注意点","3. 最小構成のメリットと注意点",[13,16054,16055],{},"この最小構成では、Leaflet.jsの軽量なコードとMapboxの高品質なタイルサーバを組み合わせて、低コストでシンプルな地図表示を実現できます。ただし、商用利用の場合は、Mapboxの料金体系に注意し、利用量に応じたプランを選択する必要があります。\nまた、シンプルな構成であるため、複雑なカスタマイズや高負荷のユースケースには向いていない場合があります。プロジェクトの規模や要件に応じて、適切なライブラリやサーバ構成を検討することが重要です。",[17,16057,16058],{"id":16058},"部分的な仕様の場合",[13,16060,16061],{},"特定の地域や用途に限定した地図表示を行う場合、全世界をカバーする必要がないため、タイルアクセスを最適化することが可能です。ここでは、部分的な仕様における地図表示の方法について説明します。",[1518,16063,16065],{"id":16064},"_1-エリア制限によるタイルアクセスの節約","1. エリア制限によるタイルアクセスの節約",[13,16067,16068],{},"特定の地域のみを対象とする場合、表示できるエリアを緯度経度範囲とズームレベルで制限することで、無駄なタイルアクセスを減らし、コストを節約できます。例えば、都市単位での地図表示や、特定の観光地を中心にしたアプリケーションの場合、必要最小限のエリアだけを表示するよう設定することで、効率的なタイルアクセスが可能です。",[42,16070,16072],{"className":1249,"code":16071,"language":1251,"meta":47,"style":47},"var map = L.map('map', {\n    center: [35.6895, 139.6917], \u002F\u002F 東京の緯度経度\n    zoom: 12,\n    maxBounds: [\n        [35.0, 138.0], \u002F\u002F 南西端\n        [36.0, 140.0]  \u002F\u002F 北東端\n    ]\n});\n",[49,16073,16074,16079,16084,16089,16094,16099,16104,16108],{"__ignoreMap":47},[52,16075,16076],{"class":54,"line":55},[52,16077,16078],{},"var map = L.map('map', {\n",[52,16080,16081],{"class":54,"line":83},[52,16082,16083],{},"    center: [35.6895, 139.6917], \u002F\u002F 東京の緯度経度\n",[52,16085,16086],{"class":54,"line":115},[52,16087,16088],{},"    zoom: 12,\n",[52,16090,16091],{"class":54,"line":142},[52,16092,16093],{},"    maxBounds: [\n",[52,16095,16096],{"class":54,"line":169},[52,16097,16098],{},"        [35.0, 138.0], \u002F\u002F 南西端\n",[52,16100,16101],{"class":54,"line":302},[52,16102,16103],{},"        [36.0, 140.0]  \u002F\u002F 北東端\n",[52,16105,16106],{"class":54,"line":308},[52,16107,9818],{},[52,16109,16110],{"class":54,"line":318},[52,16111,16112],{},"});\n",[1518,16114,16116],{"id":16115},"_2-webでの利用leafletjsの活用","2. Webでの利用：Leaflet.jsの活用",[13,16118,16119],{},"Webアプリケーションにおいて、軽量でカスタマイズ性の高いLeaflet.jsを利用することは有効です。Leaflet.jsは、シンプルな実装で特定エリアの地図表示を行うことができ、タイルサーバへのアクセスを最小限に抑えることができます。",[1518,16121,16123],{"id":16122},"_3-日本国内のみの利用国土地理院のタイルサーバ","3. 日本国内のみの利用：国土地理院のタイルサーバ",[13,16125,16126],{},"日本国内のみを対象とする場合、国土地理院が提供するタイルサーバを利用することも一つの選択肢です。国土地理院のタイルは無料で利用可能で、日本の詳細な地図データを提供しています。Leaflet.jsと組み合わせることで、日本国内での特定エリアを効率的に表示することが可能です。",[42,16128,16130],{"className":1249,"code":16129,"language":1251,"meta":47,"style":47},"L.tileLayer('https:\u002F\u002Fcyberjapandata.gsi.go.jp\u002Fxyz\u002Fstd\u002F{z}\u002F{x}\u002F{y}.png', {\n    maxZoom: 18,\n    attribution: '© 国土地理院'\n}).addTo(map);\n",[49,16131,16132,16137,16142,16147],{"__ignoreMap":47},[52,16133,16134],{"class":54,"line":55},[52,16135,16136],{},"L.tileLayer('https:\u002F\u002Fcyberjapandata.gsi.go.jp\u002Fxyz\u002Fstd\u002F{z}\u002F{x}\u002F{y}.png', {\n",[52,16138,16139],{"class":54,"line":83},[52,16140,16141],{},"    maxZoom: 18,\n",[52,16143,16144],{"class":54,"line":115},[52,16145,16146],{},"    attribution: '© 国土地理院'\n",[52,16148,16149],{"class":54,"line":142},[52,16150,16151],{},"}).addTo(map);\n",[13,16153,16154],{},"このように、部分的な仕様の場合には、地図表示エリアを限定することで、コストを抑えつつ必要な情報を提供することが可能です。利用する地域や用途に応じて、最適な構成を選択してください。",[17,16156,16157],{"id":16157},"結論",[13,16159,16160],{},"とりあえず内容はこの通りです。記述した通り、地図表示というのはそれなりのサーバコストやライブラリの開発コストが高いため、ある意味一部のサービスの寡占状態になっています。しかし地図表示は非常にわかりやすく需要が多いためgoogle mapが高くても使わざる得ないような状態になっています。ですが、google map SDK以外にも上記の最小構成などである程度コストを節約することができるので、小さいサービスや自治体が地図を使用したいときなどに利用できます。ちなみにネイティブの場合はSDKに当たるものにleaflet.jsのようなものがないので難しいです。私もreact nativeで探したのですが見当たらない様子です。この辺は調べてまた見つかったら更新しようと思います。",[17,16162,16163],{"id":16163},"参考",[1464,16165,16166,16173],{},[1467,16167,16168],{},[1450,16169,16172],{"href":16170,"rel":16171},"https:\u002F\u002Fspeakerdeck.com\u002Fsmellman\u002Fguo-nei-xiang-ketairusabafalsegou-zhu-toyun-yong-nituite?slide=22",[1454],"国内向けタイルサーバの構築と運用について",[1467,16174,16175],{},[1450,16176,16179],{"href":16177,"rel":16178},"https:\u002F\u002Fwww.gsi.go.jp\u002FGIS\u002Fwhatisgis.html",[1454],"GISとは",[1413,16181,16182],{},"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 pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}",{"title":47,"searchDepth":115,"depth":115,"links":16184},[16185,16192,16205,16210,16215,16216],{"id":15470,"depth":83,"text":15470,"children":16186},[16187,16188,16189,16190,16191],{"id":15476,"depth":115,"text":15476},{"id":15482,"depth":115,"text":15482},{"id":15491,"depth":115,"text":15492},{"id":15498,"depth":115,"text":15498},{"id":15504,"depth":115,"text":15504},{"id":15540,"depth":83,"text":15541,"children":16193},[16194,16199],{"id":15547,"depth":115,"text":15547,"children":16195},[16196,16197,16198],{"id":15550,"depth":142,"text":15551},{"id":15566,"depth":142,"text":15567},{"id":15582,"depth":142,"text":15583},{"id":15598,"depth":115,"text":15598,"children":16200},[16201,16202,16203,16204],{"id":15601,"depth":142,"text":15602},{"id":15617,"depth":142,"text":15618},{"id":15633,"depth":142,"text":15634},{"id":15649,"depth":142,"text":15650},{"id":15665,"depth":83,"text":15665,"children":16206},[16207,16208,16209],{"id":15671,"depth":115,"text":15672},{"id":16025,"depth":115,"text":16026},{"id":16051,"depth":115,"text":16052},{"id":16058,"depth":83,"text":16058,"children":16211},[16212,16213,16214],{"id":16064,"depth":115,"text":16065},{"id":16115,"depth":115,"text":16116},{"id":16122,"depth":115,"text":16123},{"id":16157,"depth":83,"text":16157},{"id":16163,"depth":83,"text":16163},[1423],"2024-08-31","地図アプリで考えておくこと",{},"\u002Farticles\u002F2024_08_31_map_lib_price_guide",{"title":15436,"description":16219},"articles\u002F2024_08_31_map_lib_price_guide",[1433,16225],"native","2024_08_31_map_lib_price_guide\u002Fthumbnail.webp","UCWkKbRmKWP-9IKYE6ttGwipqTvfmV2bZ1WTCC2d2BA",{"id":16229,"title":16230,"body":16231,"category":16349,"createdAt":16351,"description":16230,"extension":1426,"index":1427,"meta":16352,"navigation":340,"path":16353,"publish":340,"seo":16354,"series":1427,"seriesTitle":1427,"stem":16355,"tag":16356,"thumbnail":1427,"updatedAt":1427,"__hash__":16357},"articles\u002Farticles\u002Fqueryselector-error-with-numeric.md","Document.querySelector() で先頭が数字のIDを指定するとエラーが起きる。",{"type":10,"value":16232,"toc":16347},[16233,16251,16257,16288,16294,16301,16309,16314,16331,16344],[13,16234,16235,16236,16239,16240,16243,16244,1393,16247,16250],{},"こんにちはjunです。最近、editor.jsだったり色々バニラJSを触る機会がありました。特定のNodeを取得する時に",[49,16237,16238],{},"document.querySelector()","を使用することでCSS・jqueryライクに要素を取得できます。今回はIDを持つ要素を",[49,16241,16242],{},"getByElementId()","を使わず、",[49,16245,16246],{},"querySelector()",[49,16248,16249],{},"querySelector(\"#~~~\")","と取得した時に遭遇したエラーについてです。",[13,16252,16253,16254,16256],{},"タイトルの通りなのですが、",[49,16255,16246],{},"で先頭が数字のIDを指定するとエラーが起きます。",[42,16258,16260],{"className":6471,"code":16259,"language":1433,"meta":47,"style":47},"document.querySelector(\"#16test\");\n\u002F\u002F VM490:1 Uncaught DOMException: Failed to execute 'querySelector' on 'Document': '#16test' is not a valid selector.\n",[49,16261,16262,16283],{"__ignoreMap":47},[52,16263,16264,16266,16268,16270,16272,16274,16277,16279,16281],{"class":54,"line":55},[52,16265,13115],{"class":105},[52,16267,957],{"class":58},[52,16269,11614],{"class":418},[52,16271,932],{"class":105},[52,16273,72],{"class":58},[52,16275,16276],{"class":75},"#16test",[52,16278,72],{"class":58},[52,16280,938],{"class":105},[52,16282,1007],{"class":58},[52,16284,16285],{"class":54,"line":83},[52,16286,16287],{"class":411},"\u002F\u002F VM490:1 Uncaught DOMException: Failed to execute 'querySelector' on 'Document': '#16test' is not a valid selector.\n",[13,16289,16290,16291,16293],{},"簡単にコンソールでチェックできます。「not a valid selector」の通り、有効なセレクタじゃないよと怒っています。なぜこんなことが起きるのかを調べたところ、",[49,16292,16246],{},"はCSSセレクタの仕様を使っており、CSSのIDセレクタは「#の後に数字をつけてはいけない」という仕様があるからです。",[13,16295,16296,16297],{},"Stackoverflow\n",[1450,16298,16299],{"href":16299,"rel":16300},"https:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F37270787\u002Funcaught-syntaxerror-failed-to-execute-queryselector-on-document",[1454],[13,16302,16303,16304],{},"上記のStackoverflowの回答が役に立ちました。HTML5の仕様としてはIDの最初の文字に数字を入れることは問題ありません。しかしCSS3のIDセレクタの仕様ではなんと、#のあとに数字を使用してはいけないと確かに書かれています。",[1450,16305,16308],{"href":16306,"rel":16307},"https:\u002F\u002Fwww.w3.org\u002FTR\u002FCSS2\u002Fsyndata.html#characters",[1454],"詳細はW3Cのこちらのページにあります。",[5835,16310,16311],{},[13,16312,16313],{},"they cannot start with a digit, two hyphens, or a hyphen followed by a digit.",[13,16315,16316,16317,16320,16321,16323,16324,16326,16327,16330],{},"第一の解決策は",[49,16318,16319],{},"getElementById()","を使うことです。そう思うと、本来DOMにIDを持つ要素は必ず一つなので",[49,16322,16319],{},"を使えばいいのに、なんで",[49,16325,16246],{},"を使っていたんだろうか？と思っていたら、ランダムに生成したIDをもつ要素配下のある子要素を取得する処理を実装する際、",[49,16328,16329],{},"querySelector(`#{id} .item`)","みたいな感じで実装してた時でした。",[13,16332,16333,16334,16337,16338,16340,16341,16343],{},"この場合ランダムに生成されるIDに数字が含まれないようにするか、",[49,16335,16336],{},"getElementById(id)?.querySelector(\".item\")","として一旦",[49,16339,16319],{},"を使ってから",[49,16342,16246],{},"を使うといいかなと思います。",[1413,16345,16346],{},"html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}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);}",{"title":47,"searchDepth":115,"depth":115,"links":16348},[],[16350],"ministack","2022-11-23",{},"\u002Farticles\u002Fqueryselector-error-with-numeric",{"title":16230,"description":16230},"articles\u002Fqueryselector-error-with-numeric",[46,1433],"-tKJHB-rOhRzOw2x_oCwOadM10j-DEzx-pvkwNQqzxA",{"id":16359,"title":16360,"body":16361,"category":17976,"createdAt":17977,"description":16360,"extension":1426,"index":1427,"meta":17978,"navigation":340,"path":17979,"publish":340,"seo":17980,"series":1427,"seriesTitle":1427,"stem":17981,"tag":17982,"thumbnail":17984,"updatedAt":1427,"__hash__":17985},"articles\u002Farticles\u002Flaravel-i18n-json-dump.md","Laravelで多言語用のJSONを出力するコマンドを作る",{"type":10,"value":16362,"toc":17964},[16363,16376,16408,16415,16429,16442,16446,16449,16455,16521,16528,16531,16539,16542,16545,16552,16555,16558,16568,16574,16577,16580,16583,16589,16596,16789,16796,16802,16805,16808,16815,16975,17002,17005,17008,17106,17126,17130,17164,17179,17182,17197,17211,17214,17217,17518,17521,17534,17542,17547,17550,17753,17951,17958,17961],[13,16364,16365,16366,16369,16370,2383,16373,16375],{},"こんにちはjunです。個人開発で多言語対応のLaravelアプリを作っています。多言語では各種言語の単語・文章のマッピング（辞書）をする配列・JSONを作成し、レンダリング時にメソッドを使用して文字を表示します。Laravelでは",[49,16367,16368],{},"resources\u002Flang"," 配下に",[49,16371,16372],{},"en",[49,16374,9912],{},"のようなディレクトリを作成し、その中に言語のマッピングをします。大体は以下のような配列を作ります。",[42,16377,16382],{"className":16378,"code":16379,"filename":16380,"language":16381,"meta":47,"style":47},"language-php shiki shiki-themes material-theme-ocean","\u003C?php\nreturn [\n    'login'=>'ログイン',\n    'logout'=>'ログアウト'\n]\n\n","resources\u002Flang\u002Fja\u002Fwords.php","php",[49,16383,16384,16389,16394,16399,16404],{"__ignoreMap":47},[52,16385,16386],{"class":54,"line":55},[52,16387,16388],{},"\u003C?php\n",[52,16390,16391],{"class":54,"line":83},[52,16392,16393],{},"return [\n",[52,16395,16396],{"class":54,"line":115},[52,16397,16398],{},"    'login'=>'ログイン',\n",[52,16400,16401],{"class":54,"line":142},[52,16402,16403],{},"    'logout'=>'ログアウト'\n",[52,16405,16406],{"class":54,"line":169},[52,16407,15104],{},[13,16409,16410,16411,16414],{},"そしてビューファイルなどで",[49,16412,16413],{},"__('words.login')","のように使用します。",[5995,16416,16420,16421,16424,16425,16428],{"className":16417},[16418,16419],"alert","alert-info","\n多言語のメソッドが",[49,16422,16423],{},"__","みたいな名称である理由としては、使いまくるので簡単な名称になっています。Vueとかでは",[49,16426,16427],{},"$t()","みたいなものを使用します。\n",[13,16430,16431,16432,16434,16435,2383,16438,16441],{},"上記のようなPHPファイルを作成してもできますが、",[49,16433,16368],{}," 直下に",[49,16436,16437],{},"en.json",[49,16439,16440],{},"ja.json","のようなJSONファイルを作成しても、多言語メソッドで呼び出せます。",[17,16443,16445],{"id":16444},"jsonの弱点","JSONの弱点",[13,16447,16448],{},"JSONで作るメリットはマッピングのデータを他のアプリでも利用できることです。例えば、Vue・ReactではVuei18n、react-i18nextなどを使用します。同じように多言語メソッドを使用します。その際のマッピングデータとしてJSONを使用します。であればJSONファイルを作っておくことで、Vueや外部へマッピングデータを提供しやすくなります。",[13,16450,16451,16452,16454],{},"ただしJSONで作成するとLaravel側で",[49,16453,16413],{},"のような呼び出しができません。この時JSONは以下のようになっています。",[42,16456,16458],{"className":5566,"code":16457,"language":5568,"meta":47,"style":47},"{\n    \"words\":{\n        \"login\":\"ログイン\",\n        \"logout\":\"ログアウト\"\n    }\n}\n",[49,16459,16460,16464,16475,16495,16513,16517],{"__ignoreMap":47},[52,16461,16462],{"class":54,"line":55},[52,16463,364],{"class":58},[52,16465,16466,16468,16471,16473],{"class":54,"line":83},[52,16467,5589],{"class":58},[52,16469,16470],{"class":65},"words",[52,16472,72],{"class":58},[52,16474,924],{"class":58},[52,16476,16477,16479,16482,16484,16486,16488,16491,16493],{"class":54,"line":115},[52,16478,10666],{"class":58},[52,16480,16481],{"class":370},"login",[52,16483,72],{"class":58},[52,16485,373],{"class":58},[52,16487,72],{"class":58},[52,16489,16490],{"class":75},"ログイン",[52,16492,72],{"class":58},[52,16494,384],{"class":58},[52,16496,16497,16499,16502,16504,16506,16508,16511],{"class":54,"line":142},[52,16498,10666],{"class":58},[52,16500,16501],{"class":370},"logout",[52,16503,72],{"class":58},[52,16505,373],{"class":58},[52,16507,72],{"class":58},[52,16509,16510],{"class":75},"ログアウト",[52,16512,266],{"class":58},[52,16514,16515],{"class":54,"line":169},[52,16516,2110],{"class":58},[52,16518,16519],{"class":54,"line":302},[52,16520,536],{"class":58},[13,16522,16523,16524,16527],{},"すべて一次元にしてしまうと後で混乱してしまうので、カスケードさせておくと良いです。Vuei18nでは",[49,16525,16526],{},"$t('words.login')","で呼び出せますが、なぜかLaravelではJSONのカスケード配下のキーを呼び出すことができません。",[13,16529,16530],{},"そのため",[1464,16532,16533,16536],{},[1467,16534,16535],{},"Laravel以外のアプリへの提供→JSON",[1467,16537,16538],{},"Laravelの多言語対応→PHP",[13,16540,16541],{},"という２重管理でそれぞれ設定しないといけなくなり、非効率的です。",[17,16543,16544],{"id":16544},"解決方法",[13,16546,16547,16548,16551],{},"解決方法としてはPHPで作成した配列をJSONにダンプすることです。そうすることでPHPファイルとJSONの言語ファイルを作成することができます。そこで今回の記事ではPHPの言語ファイルをカスケードさせたJSONに出力するような",[49,16549,16550],{},"artisan","コマンドを作ってみたいと思います。",[17,16553,16554],{"id":16554},"コマンドの実装",[1518,16556,16557],{"id":16557},"対応する言語ディレクトリ",[13,16559,16560,16561,16369,16563,2383,16565,16567],{},"まずはLaravelの言語ファイルのドキュメントの通り、",[49,16562,16368],{},[49,16564,16372],{},[49,16566,9912],{},"のようなディレクトリを作成しておきましょう。今回は英語と日本語にしておきます。",[42,16569,16572],{"className":16570,"code":16571,"language":452},[5819],"resources\n├── lang\n│   ├── en\n│   │   ├── auth.php\n│   │   ├── words.php\n│   │   └── exceptions.php\n│   └── ja\n│       ├── auth.php\n│       ├── words.php\n│       └── exceptions.php\n...\n",[49,16573,16571],{"__ignoreMap":47},[13,16575,16576],{},"そしてそのディレクトリごとにphpファイルを分けます。とりあえずこのようにしておきます。",[1518,16578,16579],{"id":16579},"コマンドのファイルを作成",[13,16581,16582],{},"それではカスタムのコマンドを作成しましょう。",[42,16584,16587],{"className":16585,"code":16586,"language":452},[5819],"php artisan make:command Dumplang\n",[49,16588,16586],{"__ignoreMap":47},[13,16590,16591,16592,16595],{},"コマンドもartisanで作成できます。",[49,16593,16594],{},"app\u002FConsole\u002FCommands","というディレクトリが作成され、そこにファイルが作成されます。",[42,16597,16599],{"className":16378,"code":16598,"language":16381,"meta":47,"style":47},"\u003C?php\n\nnamespace App\\Console\\Commands;\n\nuse Illuminate\\Console\\Command;\n\nclass Dumblang extends Command\n{\n    \u002F**\n     * The name and signature of the console command.\n     *\n     * @var string\n     *\u002F\n    protected $signature = 'lang:dump';\n\n    \u002F**\n     * The console command description.\n     *\n     * @var string\n     *\u002F\n    protected $description = 'Convert each php lang files to JSON files.';\n\n    \u002F**\n     * Create a new command instance.\n     *\n     * @return void\n     *\u002F\n    public function __construct()\n    {\n        parent::__construct();\n    }\n\n    \u002F**\n     * Execute the console command.\n     *\n     * @return int\n     *\u002F\n    public function handle()\n    {\n        \n    }\n}\n",[49,16600,16601,16605,16609,16614,16618,16623,16627,16632,16636,16641,16646,16651,16656,16661,16666,16670,16674,16679,16683,16687,16691,16696,16700,16704,16709,16713,16718,16722,16727,16732,16737,16741,16745,16749,16754,16758,16763,16767,16772,16776,16781,16785],{"__ignoreMap":47},[52,16602,16603],{"class":54,"line":55},[52,16604,16388],{},[52,16606,16607],{"class":54,"line":83},[52,16608,341],{"emptyLinePlaceholder":340},[52,16610,16611],{"class":54,"line":115},[52,16612,16613],{},"namespace App\\Console\\Commands;\n",[52,16615,16616],{"class":54,"line":142},[52,16617,341],{"emptyLinePlaceholder":340},[52,16619,16620],{"class":54,"line":169},[52,16621,16622],{},"use Illuminate\\Console\\Command;\n",[52,16624,16625],{"class":54,"line":302},[52,16626,341],{"emptyLinePlaceholder":340},[52,16628,16629],{"class":54,"line":308},[52,16630,16631],{},"class Dumblang extends Command\n",[52,16633,16634],{"class":54,"line":318},[52,16635,364],{},[52,16637,16638],{"class":54,"line":328},[52,16639,16640],{},"    \u002F**\n",[52,16642,16643],{"class":54,"line":337},[52,16644,16645],{},"     * The name and signature of the console command.\n",[52,16647,16648],{"class":54,"line":344},[52,16649,16650],{},"     *\n",[52,16652,16653],{"class":54,"line":354},[52,16654,16655],{},"     * @var string\n",[52,16657,16658],{"class":54,"line":367},[52,16659,16660],{},"     *\u002F\n",[52,16662,16663],{"class":54,"line":387},[52,16664,16665],{},"    protected $signature = 'lang:dump';\n",[52,16667,16668],{"class":54,"line":415},[52,16669,341],{"emptyLinePlaceholder":340},[52,16671,16672],{"class":54,"line":427},[52,16673,16640],{},[52,16675,16676],{"class":54,"line":435},[52,16677,16678],{},"     * The console command description.\n",[52,16680,16681],{"class":54,"line":446},[52,16682,16650],{},[52,16684,16685],{"class":54,"line":480},[52,16686,16655],{},[52,16688,16689],{"class":54,"line":509},[52,16690,16660],{},[52,16692,16693],{"class":54,"line":539},[52,16694,16695],{},"    protected $description = 'Convert each php lang files to JSON files.';\n",[52,16697,16698],{"class":54,"line":547},[52,16699,341],{"emptyLinePlaceholder":340},[52,16701,16702],{"class":54,"line":553},[52,16703,16640],{},[52,16705,16706],{"class":54,"line":559},[52,16707,16708],{},"     * Create a new command instance.\n",[52,16710,16711],{"class":54,"line":564},[52,16712,16650],{},[52,16714,16715],{"class":54,"line":569},[52,16716,16717],{},"     * @return void\n",[52,16719,16720],{"class":54,"line":1106},[52,16721,16660],{},[52,16723,16724],{"class":54,"line":1135},[52,16725,16726],{},"    public function __construct()\n",[52,16728,16729],{"class":54,"line":1164},[52,16730,16731],{},"    {\n",[52,16733,16734],{"class":54,"line":4},[52,16735,16736],{},"        parent::__construct();\n",[52,16738,16739],{"class":54,"line":1199},[52,16740,2110],{},[52,16742,16743],{"class":54,"line":1204},[52,16744,341],{"emptyLinePlaceholder":340},[52,16746,16747],{"class":54,"line":1209},[52,16748,16640],{},[52,16750,16751],{"class":54,"line":1214},[52,16752,16753],{},"     * Execute the console command.\n",[52,16755,16756],{"class":54,"line":1219},[52,16757,16650],{},[52,16759,16760],{"class":54,"line":3216},[52,16761,16762],{},"     * @return int\n",[52,16764,16765],{"class":54,"line":3237},[52,16766,16660],{},[52,16768,16769],{"class":54,"line":3251},[52,16770,16771],{},"    public function handle()\n",[52,16773,16774],{"class":54,"line":3256},[52,16775,16731],{},[52,16777,16778],{"class":54,"line":3281},[52,16779,16780],{},"        \n",[52,16782,16783],{"class":54,"line":3295},[52,16784,2110],{},[52,16786,16787],{"class":54,"line":4922},[52,16788,536],{},[13,16790,16791,16792,16795],{},"まずは上記のように、シグネチャと説明を記述します。シグネチャでは",[49,16793,16794],{},"lang:dump"," とすると",[42,16797,16800],{"className":16798,"code":16799,"language":452},[5819],"php artisan lang:dump\n",[49,16801,16799],{"__ignoreMap":47},[13,16803,16804],{},"と入力するとこのコマンドを実行できます。",[1518,16806,16807],{"id":16807},"言語ディレクトリからファイルを読み込む",[13,16809,16810,16811,16814],{},"コマンドの内容は",[49,16812,16813],{},"handle()","に記述します。全体は以下の通りです。",[42,16816,16818],{"className":16378,"code":16817,"language":16381,"meta":47,"style":47},"use Illuminate\\Support\\Facades\\File;\nuse Illuminate\\Support\\Facades\\Lang;\n\npublic function handle()\n{\n    $suports = [\"ja\",\"en\"];\n    \n    foreach($suports as $lng){\n        $langdir = resource_path('lang\u002F'.$lng);\n        if(is_dir($langdir)){\n            $files = scandir($langdir);\n            if($files === false) continue;\n\n            $files = array_filter($files,function($f){\n                return strpos($f,'.php') !== false;\n            });\n\n            $trans = [];\n            foreach($files as $f){\n                $content_key = str_replace('.php','',$f);\n                $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n                $trans[$content_key] = $content;\n            }\n\n            $json = json_encode($trans,JSON_UNESCAPED_UNICODE);\n            File::put(resource_path('lang\u002F'.$lng.'.json'),$json);\n            File::put(base_path('nuxt\u002Flang\u002F'.$lng.'.json'),$json);\n        }else{\n            print(\"Lang directory for ${$lng} dose not exisits\");\n        }\n    }\n    return Command::SUCCESS;\n}\n",[49,16819,16820,16825,16830,16834,16839,16843,16848,16852,16857,16862,16867,16872,16877,16881,16886,16891,16896,16900,16905,16910,16915,16920,16925,16929,16933,16938,16943,16948,16953,16958,16962,16966,16971],{"__ignoreMap":47},[52,16821,16822],{"class":54,"line":55},[52,16823,16824],{},"use Illuminate\\Support\\Facades\\File;\n",[52,16826,16827],{"class":54,"line":83},[52,16828,16829],{},"use Illuminate\\Support\\Facades\\Lang;\n",[52,16831,16832],{"class":54,"line":115},[52,16833,341],{"emptyLinePlaceholder":340},[52,16835,16836],{"class":54,"line":142},[52,16837,16838],{},"public function handle()\n",[52,16840,16841],{"class":54,"line":169},[52,16842,364],{},[52,16844,16845],{"class":54,"line":302},[52,16846,16847],{},"    $suports = [\"ja\",\"en\"];\n",[52,16849,16850],{"class":54,"line":308},[52,16851,2651],{},[52,16853,16854],{"class":54,"line":318},[52,16855,16856],{},"    foreach($suports as $lng){\n",[52,16858,16859],{"class":54,"line":328},[52,16860,16861],{},"        $langdir = resource_path('lang\u002F'.$lng);\n",[52,16863,16864],{"class":54,"line":337},[52,16865,16866],{},"        if(is_dir($langdir)){\n",[52,16868,16869],{"class":54,"line":344},[52,16870,16871],{},"            $files = scandir($langdir);\n",[52,16873,16874],{"class":54,"line":354},[52,16875,16876],{},"            if($files === false) continue;\n",[52,16878,16879],{"class":54,"line":367},[52,16880,341],{"emptyLinePlaceholder":340},[52,16882,16883],{"class":54,"line":387},[52,16884,16885],{},"            $files = array_filter($files,function($f){\n",[52,16887,16888],{"class":54,"line":415},[52,16889,16890],{},"                return strpos($f,'.php') !== false;\n",[52,16892,16893],{"class":54,"line":427},[52,16894,16895],{},"            });\n",[52,16897,16898],{"class":54,"line":435},[52,16899,341],{"emptyLinePlaceholder":340},[52,16901,16902],{"class":54,"line":446},[52,16903,16904],{},"            $trans = [];\n",[52,16906,16907],{"class":54,"line":480},[52,16908,16909],{},"            foreach($files as $f){\n",[52,16911,16912],{"class":54,"line":509},[52,16913,16914],{},"                $content_key = str_replace('.php','',$f);\n",[52,16916,16917],{"class":54,"line":539},[52,16918,16919],{},"                $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n",[52,16921,16922],{"class":54,"line":547},[52,16923,16924],{},"                $trans[$content_key] = $content;\n",[52,16926,16927],{"class":54,"line":553},[52,16928,1665],{},[52,16930,16931],{"class":54,"line":559},[52,16932,341],{"emptyLinePlaceholder":340},[52,16934,16935],{"class":54,"line":564},[52,16936,16937],{},"            $json = json_encode($trans,JSON_UNESCAPED_UNICODE);\n",[52,16939,16940],{"class":54,"line":569},[52,16941,16942],{},"            File::put(resource_path('lang\u002F'.$lng.'.json'),$json);\n",[52,16944,16945],{"class":54,"line":1106},[52,16946,16947],{},"            File::put(base_path('nuxt\u002Flang\u002F'.$lng.'.json'),$json);\n",[52,16949,16950],{"class":54,"line":1135},[52,16951,16952],{},"        }else{\n",[52,16954,16955],{"class":54,"line":1164},[52,16956,16957],{},"            print(\"Lang directory for ${$lng} dose not exisits\");\n",[52,16959,16960],{"class":54,"line":4},[52,16961,4814],{},[52,16963,16964],{"class":54,"line":1199},[52,16965,2110],{},[52,16967,16968],{"class":54,"line":1204},[52,16969,16970],{},"    return Command::SUCCESS;\n",[52,16972,16973],{"class":54,"line":1209},[52,16974,536],{},[42,16976,16978],{"className":16378,"code":16977,"language":16381,"meta":47,"style":47},"$suports = [\"ja\",\"en\"];\n\nforeach($suports as $lng){\n\n}\n",[49,16979,16980,16985,16989,16994,16998],{"__ignoreMap":47},[52,16981,16982],{"class":54,"line":55},[52,16983,16984],{},"$suports = [\"ja\",\"en\"];\n",[52,16986,16987],{"class":54,"line":83},[52,16988,341],{"emptyLinePlaceholder":340},[52,16990,16991],{"class":54,"line":115},[52,16992,16993],{},"foreach($suports as $lng){\n",[52,16995,16996],{"class":54,"line":142},[52,16997,341],{"emptyLinePlaceholder":340},[52,16999,17000],{"class":54,"line":169},[52,17001,536],{},[13,17003,17004],{},"まずは取得予定の言語の配列を用意します。それを再帰的に処理します。",[13,17006,17007],{},"元となる言語ディレクトリからファイルの一覧を取得します。",[42,17009,17011],{"className":16378,"code":17010,"language":16381,"meta":47,"style":47},"foreach($suports as $lng){\n    $langdir = resource_path('lang\u002F'.$lng);\n    if(is_dir($langdir)){\n        $files = scandir($langdir);\n        if($files === false) continue;\n\n        $files = array_filter($files,function($f){\n            return strpos($f,'.php') !== false;\n        });\n\n        $trans = [];\n        foreach($files as $f){\n            $content_key = str_replace('.php','',$f);\n            $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n            $trans[$content_key] = $content;\n        }\n\n        \u002F\u002F ....\n    }\n}\n",[49,17012,17013,17017,17022,17027,17032,17037,17041,17046,17051,17056,17060,17065,17070,17075,17080,17085,17089,17093,17098,17102],{"__ignoreMap":47},[52,17014,17015],{"class":54,"line":55},[52,17016,16993],{},[52,17018,17019],{"class":54,"line":83},[52,17020,17021],{},"    $langdir = resource_path('lang\u002F'.$lng);\n",[52,17023,17024],{"class":54,"line":115},[52,17025,17026],{},"    if(is_dir($langdir)){\n",[52,17028,17029],{"class":54,"line":142},[52,17030,17031],{},"        $files = scandir($langdir);\n",[52,17033,17034],{"class":54,"line":169},[52,17035,17036],{},"        if($files === false) continue;\n",[52,17038,17039],{"class":54,"line":302},[52,17040,341],{"emptyLinePlaceholder":340},[52,17042,17043],{"class":54,"line":308},[52,17044,17045],{},"        $files = array_filter($files,function($f){\n",[52,17047,17048],{"class":54,"line":318},[52,17049,17050],{},"            return strpos($f,'.php') !== false;\n",[52,17052,17053],{"class":54,"line":328},[52,17054,17055],{},"        });\n",[52,17057,17058],{"class":54,"line":337},[52,17059,341],{"emptyLinePlaceholder":340},[52,17061,17062],{"class":54,"line":344},[52,17063,17064],{},"        $trans = [];\n",[52,17066,17067],{"class":54,"line":354},[52,17068,17069],{},"        foreach($files as $f){\n",[52,17071,17072],{"class":54,"line":367},[52,17073,17074],{},"            $content_key = str_replace('.php','',$f);\n",[52,17076,17077],{"class":54,"line":387},[52,17078,17079],{},"            $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n",[52,17081,17082],{"class":54,"line":415},[52,17083,17084],{},"            $trans[$content_key] = $content;\n",[52,17086,17087],{"class":54,"line":427},[52,17088,4814],{},[52,17090,17091],{"class":54,"line":435},[52,17092,341],{"emptyLinePlaceholder":340},[52,17094,17095],{"class":54,"line":446},[52,17096,17097],{},"        \u002F\u002F ....\n",[52,17099,17100],{"class":54,"line":480},[52,17101,2110],{},[52,17103,17104],{"class":54,"line":509},[52,17105,536],{},[13,17107,17108,17111,17112,17115,17116,17118,17119,17121,17122,17125],{},[49,17109,17110],{},"resource_path('lang\u002F'.$lng)","で先程の言語ディレクトリのパスを取得し、",[49,17113,17114],{},"scandir()","を用いて内部のファイルを配列で取得します。",[49,17117,17114],{},"はphp以外のファイルや",[49,17120,957],{},"みたいなSELinuxが勝手に作る謎ファイルも読み取ってしまうので、",[49,17123,17124],{},"array_filter()","を用いてフィルターします。",[1518,17127,17129],{"id":17128},"一つの配列に打ち込んでjson化する","一つの配列に打ち込んでJSON化する",[42,17131,17133],{"className":16378,"code":17132,"language":16381,"meta":47,"style":47},"$trans = [];\nforeach($files as $f){\n    $content_key = str_replace('.php','',$f);\n    $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n    $trans[$content_key] = $content;\n}\n",[49,17134,17135,17140,17145,17150,17155,17160],{"__ignoreMap":47},[52,17136,17137],{"class":54,"line":55},[52,17138,17139],{},"$trans = [];\n",[52,17141,17142],{"class":54,"line":83},[52,17143,17144],{},"foreach($files as $f){\n",[52,17146,17147],{"class":54,"line":115},[52,17148,17149],{},"    $content_key = str_replace('.php','',$f);\n",[52,17151,17152],{"class":54,"line":142},[52,17153,17154],{},"    $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n",[52,17156,17157],{"class":54,"line":169},[52,17158,17159],{},"    $trans[$content_key] = $content;\n",[52,17161,17162],{"class":54,"line":302},[52,17163,536],{},[13,17165,17166,17167,17170,17171,17174,17175,17178],{},"JSONではphpファイル名を一次キーとして利用したいので ",[49,17168,17169],{},"str_replace('.php','',$f)","でファイル名を取得します。",[49,17172,17173],{},"include resource_path(\"lang\u002F$lng\u002F$f\")","でphpファイルの記述を取得ます。そしてJSONにする配列に、ファイル名をキーとして打ち込みます。",[49,17176,17177],{},"$trans[$content_key] = $content;"," それをスキャンした言語PHPファイル全てに行います。",[1518,17180,17181],{"id":17181},"ファイルを出力",[42,17183,17185],{"className":16378,"code":17184,"language":16381,"meta":47,"style":47},"$json = json_encode($trans,JSON_UNESCAPED_UNICODE);\nFile::put(resource_path('lang\u002F'.$lng.'.json'),$json);\n",[49,17186,17187,17192],{"__ignoreMap":47},[52,17188,17189],{"class":54,"line":55},[52,17190,17191],{},"$json = json_encode($trans,JSON_UNESCAPED_UNICODE);\n",[52,17193,17194],{"class":54,"line":83},[52,17195,17196],{},"File::put(resource_path('lang\u002F'.$lng.'.json'),$json);\n",[13,17198,17199,17200,17203,17204,17206,17207,17210],{},"そして１つにまとめた配列を",[49,17201,17202],{},"json_encode","をしておき、それを",[49,17205,16368],{}," 配下します。その際には",[49,17208,17209],{},"言語名.json","となるようにしておきます。",[17,17212,17213],{"id":17213},"全容と実行",[13,17215,17216],{},"コードは以下の通りです。",[42,17218,17220],{"className":16378,"code":17219,"language":16381,"meta":47,"style":47},"\u003C?php\n\nnamespace App\\Console\\Commands;\n\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\File;\nuse Illuminate\\Support\\Facades\\Lang;\n\nclass Dumblang extends Command\n{\n    \u002F**\n     * The name and signature of the console command.\n     *\n     * @var string\n     *\u002F\n    protected $signature = 'lang:dump';\n\n    \u002F**\n     * The console command description.\n     *\n     * @var string\n     *\u002F\n    protected $description = 'Convert each php lang files to JSON files.';\n\n    \u002F**\n     * Create a new command instance.\n     *\n     * @return void\n     *\u002F\n    public function __construct()\n    {\n        parent::__construct();\n    }\n\n    \u002F**\n     * Execute the console command.\n     *\n     * @return int\n     *\u002F\n    public function handle()\n    {\n        $suports = config('app.support_langs');\n        \n        foreach($suports as $lng){\n            $langdir = resource_path('lang\u002F'.$lng);\n            if(is_dir($langdir)){\n                $files = scandir($langdir);\n                if($files === false) continue;\n\n                $files = array_filter($files,function($f){\n                    return strpos($f,'.php') !== false;\n                });\n\n                $trans = [];\n                foreach($files as $f){\n                    $content_key = str_replace('.php','',$f);\n                    $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n                    $trans[$content_key] = $content;\n                }\n\n                $json = json_encode($trans,JSON_UNESCAPED_UNICODE);\n                File::put(resource_path('lang\u002F'.$lng.'.json'),$json);\n            }else{\n                print(\"Lang directory for ${$lng} dose not exisits\");\n            }\n        }\n        return Command::SUCCESS;\n    }\n}\n",[49,17221,17222,17226,17230,17234,17238,17242,17246,17250,17254,17258,17262,17266,17270,17274,17278,17282,17286,17290,17294,17298,17302,17306,17310,17314,17318,17322,17326,17330,17334,17338,17342,17346,17350,17354,17358,17362,17366,17370,17374,17378,17382,17386,17391,17395,17400,17405,17410,17415,17420,17424,17429,17434,17439,17443,17448,17453,17458,17463,17468,17473,17477,17482,17487,17492,17497,17501,17505,17510,17514],{"__ignoreMap":47},[52,17223,17224],{"class":54,"line":55},[52,17225,16388],{},[52,17227,17228],{"class":54,"line":83},[52,17229,341],{"emptyLinePlaceholder":340},[52,17231,17232],{"class":54,"line":115},[52,17233,16613],{},[52,17235,17236],{"class":54,"line":142},[52,17237,341],{"emptyLinePlaceholder":340},[52,17239,17240],{"class":54,"line":169},[52,17241,16622],{},[52,17243,17244],{"class":54,"line":302},[52,17245,16824],{},[52,17247,17248],{"class":54,"line":308},[52,17249,16829],{},[52,17251,17252],{"class":54,"line":318},[52,17253,341],{"emptyLinePlaceholder":340},[52,17255,17256],{"class":54,"line":328},[52,17257,16631],{},[52,17259,17260],{"class":54,"line":337},[52,17261,364],{},[52,17263,17264],{"class":54,"line":344},[52,17265,16640],{},[52,17267,17268],{"class":54,"line":354},[52,17269,16645],{},[52,17271,17272],{"class":54,"line":367},[52,17273,16650],{},[52,17275,17276],{"class":54,"line":387},[52,17277,16655],{},[52,17279,17280],{"class":54,"line":415},[52,17281,16660],{},[52,17283,17284],{"class":54,"line":427},[52,17285,16665],{},[52,17287,17288],{"class":54,"line":435},[52,17289,341],{"emptyLinePlaceholder":340},[52,17291,17292],{"class":54,"line":446},[52,17293,16640],{},[52,17295,17296],{"class":54,"line":480},[52,17297,16678],{},[52,17299,17300],{"class":54,"line":509},[52,17301,16650],{},[52,17303,17304],{"class":54,"line":539},[52,17305,16655],{},[52,17307,17308],{"class":54,"line":547},[52,17309,16660],{},[52,17311,17312],{"class":54,"line":553},[52,17313,16695],{},[52,17315,17316],{"class":54,"line":559},[52,17317,341],{"emptyLinePlaceholder":340},[52,17319,17320],{"class":54,"line":564},[52,17321,16640],{},[52,17323,17324],{"class":54,"line":569},[52,17325,16708],{},[52,17327,17328],{"class":54,"line":1106},[52,17329,16650],{},[52,17331,17332],{"class":54,"line":1135},[52,17333,16717],{},[52,17335,17336],{"class":54,"line":1164},[52,17337,16660],{},[52,17339,17340],{"class":54,"line":4},[52,17341,16726],{},[52,17343,17344],{"class":54,"line":1199},[52,17345,16731],{},[52,17347,17348],{"class":54,"line":1204},[52,17349,16736],{},[52,17351,17352],{"class":54,"line":1209},[52,17353,2110],{},[52,17355,17356],{"class":54,"line":1214},[52,17357,341],{"emptyLinePlaceholder":340},[52,17359,17360],{"class":54,"line":1219},[52,17361,16640],{},[52,17363,17364],{"class":54,"line":3216},[52,17365,16753],{},[52,17367,17368],{"class":54,"line":3237},[52,17369,16650],{},[52,17371,17372],{"class":54,"line":3251},[52,17373,16762],{},[52,17375,17376],{"class":54,"line":3256},[52,17377,16660],{},[52,17379,17380],{"class":54,"line":3281},[52,17381,16771],{},[52,17383,17384],{"class":54,"line":3295},[52,17385,16731],{},[52,17387,17388],{"class":54,"line":4922},[52,17389,17390],{},"        $suports = config('app.support_langs');\n",[52,17392,17393],{"class":54,"line":4943},[52,17394,16780],{},[52,17396,17397],{"class":54,"line":4961},[52,17398,17399],{},"        foreach($suports as $lng){\n",[52,17401,17402],{"class":54,"line":4978},[52,17403,17404],{},"            $langdir = resource_path('lang\u002F'.$lng);\n",[52,17406,17407],{"class":54,"line":4983},[52,17408,17409],{},"            if(is_dir($langdir)){\n",[52,17411,17412],{"class":54,"line":5004},[52,17413,17414],{},"                $files = scandir($langdir);\n",[52,17416,17417],{"class":54,"line":5053},[52,17418,17419],{},"                if($files === false) continue;\n",[52,17421,17422],{"class":54,"line":5058},[52,17423,341],{"emptyLinePlaceholder":340},[52,17425,17426],{"class":54,"line":5063},[52,17427,17428],{},"                $files = array_filter($files,function($f){\n",[52,17430,17431],{"class":54,"line":5096},[52,17432,17433],{},"                    return strpos($f,'.php') !== false;\n",[52,17435,17436],{"class":54,"line":5109},[52,17437,17438],{},"                });\n",[52,17440,17441],{"class":54,"line":5128},[52,17442,341],{"emptyLinePlaceholder":340},[52,17444,17445],{"class":54,"line":5141},[52,17446,17447],{},"                $trans = [];\n",[52,17449,17450],{"class":54,"line":5162},[52,17451,17452],{},"                foreach($files as $f){\n",[52,17454,17455],{"class":54,"line":5167},[52,17456,17457],{},"                    $content_key = str_replace('.php','',$f);\n",[52,17459,17460],{"class":54,"line":5203},[52,17461,17462],{},"                    $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n",[52,17464,17465],{"class":54,"line":5253},[52,17466,17467],{},"                    $trans[$content_key] = $content;\n",[52,17469,17470],{"class":54,"line":5279},[52,17471,17472],{},"                }\n",[52,17474,17475],{"class":54,"line":5284},[52,17476,341],{"emptyLinePlaceholder":340},[52,17478,17479],{"class":54,"line":5289},[52,17480,17481],{},"                $json = json_encode($trans,JSON_UNESCAPED_UNICODE);\n",[52,17483,17484],{"class":54,"line":5310},[52,17485,17486],{},"                File::put(resource_path('lang\u002F'.$lng.'.json'),$json);\n",[52,17488,17489],{"class":54,"line":5331},[52,17490,17491],{},"            }else{\n",[52,17493,17494],{"class":54,"line":5344},[52,17495,17496],{},"                print(\"Lang directory for ${$lng} dose not exisits\");\n",[52,17498,17499],{"class":54,"line":5349},[52,17500,1665],{},[52,17502,17503],{"class":54,"line":5370},[52,17504,4814],{},[52,17506,17507],{"class":54,"line":5383},[52,17508,17509],{},"        return Command::SUCCESS;\n",[52,17511,17512],{"class":54,"line":5388},[52,17513,2110],{},[52,17515,17516],{"class":54,"line":5393},[52,17517,536],{},[13,17519,17520],{},"ちょっと気をつける点としては",[1464,17522,17523,17529],{},[1467,17524,17525,17528],{},[49,17526,17527],{},"is_dir($langdir)"," で言語ディレクトリの存在チェック",[1467,17530,17531,17533],{},[49,17532,17114],{}," で取得したファイルをフィルタする",[13,17535,17536,17537,2383,17539,17541],{},"実行してみると",[49,17538,16440],{},[49,17540,16437],{},"というのが作成され、みてみると",[42,17543,17545],{"className":17544,"code":16571,"language":452},[5819],[49,17546,16571],{"__ignoreMap":47},[13,17548,17549],{},"↓",[42,17551,17553],{"className":5566,"code":17552,"filename":16440,"language":5568,"meta":47,"style":47},"{\n    \"auth\":{\n        \"login\":\"ログイン\",\n        \"logout\":\"ログアウト\",\n    },\n    \"words\":{\n        \"save\":\"保存\",\n        \"update\":\"更新\"\n    },\n    \"exceptions\":{\n        \"401\":\"ログインしてください。\",\n        \"model\":{\n            \"403\":\"このデータにアクセスできません。\",\n            \"404\":\"対応するデータが見つかりません。\"\n        }\n    }\n}\n",[49,17554,17555,17559,17570,17588,17606,17610,17620,17639,17657,17661,17672,17692,17703,17723,17741,17745,17749],{"__ignoreMap":47},[52,17556,17557],{"class":54,"line":55},[52,17558,364],{"class":58},[52,17560,17561,17563,17566,17568],{"class":54,"line":83},[52,17562,5589],{"class":58},[52,17564,17565],{"class":65},"auth",[52,17567,72],{"class":58},[52,17569,924],{"class":58},[52,17571,17572,17574,17576,17578,17580,17582,17584,17586],{"class":54,"line":115},[52,17573,10666],{"class":58},[52,17575,16481],{"class":370},[52,17577,72],{"class":58},[52,17579,373],{"class":58},[52,17581,72],{"class":58},[52,17583,16490],{"class":75},[52,17585,72],{"class":58},[52,17587,384],{"class":58},[52,17589,17590,17592,17594,17596,17598,17600,17602,17604],{"class":54,"line":142},[52,17591,10666],{"class":58},[52,17593,16501],{"class":370},[52,17595,72],{"class":58},[52,17597,373],{"class":58},[52,17599,72],{"class":58},[52,17601,16510],{"class":75},[52,17603,72],{"class":58},[52,17605,384],{"class":58},[52,17607,17608],{"class":54,"line":169},[52,17609,6151],{"class":58},[52,17611,17612,17614,17616,17618],{"class":54,"line":302},[52,17613,5589],{"class":58},[52,17615,16470],{"class":65},[52,17617,72],{"class":58},[52,17619,924],{"class":58},[52,17621,17622,17624,17626,17628,17630,17632,17635,17637],{"class":54,"line":308},[52,17623,10666],{"class":58},[52,17625,12956],{"class":370},[52,17627,72],{"class":58},[52,17629,373],{"class":58},[52,17631,72],{"class":58},[52,17633,17634],{"class":75},"保存",[52,17636,72],{"class":58},[52,17638,384],{"class":58},[52,17640,17641,17643,17646,17648,17650,17652,17655],{"class":54,"line":318},[52,17642,10666],{"class":58},[52,17644,17645],{"class":370},"update",[52,17647,72],{"class":58},[52,17649,373],{"class":58},[52,17651,72],{"class":58},[52,17653,17654],{"class":75},"更新",[52,17656,266],{"class":58},[52,17658,17659],{"class":54,"line":328},[52,17660,6151],{"class":58},[52,17662,17663,17665,17668,17670],{"class":54,"line":337},[52,17664,5589],{"class":58},[52,17666,17667],{"class":65},"exceptions",[52,17669,72],{"class":58},[52,17671,924],{"class":58},[52,17673,17674,17676,17679,17681,17683,17685,17688,17690],{"class":54,"line":344},[52,17675,10666],{"class":58},[52,17677,17678],{"class":370},"401",[52,17680,72],{"class":58},[52,17682,373],{"class":58},[52,17684,72],{"class":58},[52,17686,17687],{"class":75},"ログインしてください。",[52,17689,72],{"class":58},[52,17691,384],{"class":58},[52,17693,17694,17696,17699,17701],{"class":54,"line":354},[52,17695,10666],{"class":58},[52,17697,17698],{"class":370},"model",[52,17700,72],{"class":58},[52,17702,924],{"class":58},[52,17704,17705,17707,17710,17712,17714,17716,17719,17721],{"class":54,"line":367},[52,17706,9347],{"class":58},[52,17708,17709],{"class":1646},"403",[52,17711,72],{"class":58},[52,17713,373],{"class":58},[52,17715,72],{"class":58},[52,17717,17718],{"class":75},"このデータにアクセスできません。",[52,17720,72],{"class":58},[52,17722,384],{"class":58},[52,17724,17725,17727,17730,17732,17734,17736,17739],{"class":54,"line":387},[52,17726,9347],{"class":58},[52,17728,17729],{"class":1646},"404",[52,17731,72],{"class":58},[52,17733,373],{"class":58},[52,17735,72],{"class":58},[52,17737,17738],{"class":75},"対応するデータが見つかりません。",[52,17740,266],{"class":58},[52,17742,17743],{"class":54,"line":415},[52,17744,4814],{"class":58},[52,17746,17747],{"class":54,"line":427},[52,17748,2110],{"class":58},[52,17750,17751],{"class":54,"line":435},[52,17752,536],{"class":58},[42,17754,17756],{"className":5566,"code":17755,"filename":16437,"language":5568,"meta":47,"style":47},"{\n    \"auth\":{\n        \"login\":\"Login\",\n        \"logout\":\"Logout\",\n    },\n    \"words\":{\n        \"save\":\"Saving\",\n        \"update\":\"Updating\"\n    },\n    \"exceptions\":{\n        \"401\":\"Please login.\",\n        \"model\":{\n            \"403\":\"You can not access to this data.\",\n            \"404\":\"The data you request is not found.\"\n        }\n    }\n}\n",[49,17757,17758,17762,17772,17791,17810,17814,17824,17843,17860,17864,17874,17893,17903,17922,17939,17943,17947],{"__ignoreMap":47},[52,17759,17760],{"class":54,"line":55},[52,17761,364],{"class":58},[52,17763,17764,17766,17768,17770],{"class":54,"line":83},[52,17765,5589],{"class":58},[52,17767,17565],{"class":65},[52,17769,72],{"class":58},[52,17771,924],{"class":58},[52,17773,17774,17776,17778,17780,17782,17784,17787,17789],{"class":54,"line":115},[52,17775,10666],{"class":58},[52,17777,16481],{"class":370},[52,17779,72],{"class":58},[52,17781,373],{"class":58},[52,17783,72],{"class":58},[52,17785,17786],{"class":75},"Login",[52,17788,72],{"class":58},[52,17790,384],{"class":58},[52,17792,17793,17795,17797,17799,17801,17803,17806,17808],{"class":54,"line":142},[52,17794,10666],{"class":58},[52,17796,16501],{"class":370},[52,17798,72],{"class":58},[52,17800,373],{"class":58},[52,17802,72],{"class":58},[52,17804,17805],{"class":75},"Logout",[52,17807,72],{"class":58},[52,17809,384],{"class":58},[52,17811,17812],{"class":54,"line":169},[52,17813,6151],{"class":58},[52,17815,17816,17818,17820,17822],{"class":54,"line":302},[52,17817,5589],{"class":58},[52,17819,16470],{"class":65},[52,17821,72],{"class":58},[52,17823,924],{"class":58},[52,17825,17826,17828,17830,17832,17834,17836,17839,17841],{"class":54,"line":308},[52,17827,10666],{"class":58},[52,17829,12956],{"class":370},[52,17831,72],{"class":58},[52,17833,373],{"class":58},[52,17835,72],{"class":58},[52,17837,17838],{"class":75},"Saving",[52,17840,72],{"class":58},[52,17842,384],{"class":58},[52,17844,17845,17847,17849,17851,17853,17855,17858],{"class":54,"line":318},[52,17846,10666],{"class":58},[52,17848,17645],{"class":370},[52,17850,72],{"class":58},[52,17852,373],{"class":58},[52,17854,72],{"class":58},[52,17856,17857],{"class":75},"Updating",[52,17859,266],{"class":58},[52,17861,17862],{"class":54,"line":328},[52,17863,6151],{"class":58},[52,17865,17866,17868,17870,17872],{"class":54,"line":337},[52,17867,5589],{"class":58},[52,17869,17667],{"class":65},[52,17871,72],{"class":58},[52,17873,924],{"class":58},[52,17875,17876,17878,17880,17882,17884,17886,17889,17891],{"class":54,"line":344},[52,17877,10666],{"class":58},[52,17879,17678],{"class":370},[52,17881,72],{"class":58},[52,17883,373],{"class":58},[52,17885,72],{"class":58},[52,17887,17888],{"class":75},"Please login.",[52,17890,72],{"class":58},[52,17892,384],{"class":58},[52,17894,17895,17897,17899,17901],{"class":54,"line":354},[52,17896,10666],{"class":58},[52,17898,17698],{"class":370},[52,17900,72],{"class":58},[52,17902,924],{"class":58},[52,17904,17905,17907,17909,17911,17913,17915,17918,17920],{"class":54,"line":367},[52,17906,9347],{"class":58},[52,17908,17709],{"class":1646},[52,17910,72],{"class":58},[52,17912,373],{"class":58},[52,17914,72],{"class":58},[52,17916,17917],{"class":75},"You can not access to this data.",[52,17919,72],{"class":58},[52,17921,384],{"class":58},[52,17923,17924,17926,17928,17930,17932,17934,17937],{"class":54,"line":387},[52,17925,9347],{"class":58},[52,17927,17729],{"class":1646},[52,17929,72],{"class":58},[52,17931,373],{"class":58},[52,17933,72],{"class":58},[52,17935,17936],{"class":75},"The data you request is not found.",[52,17938,266],{"class":58},[52,17940,17941],{"class":54,"line":415},[52,17942,4814],{"class":58},[52,17944,17945],{"class":54,"line":427},[52,17946,2110],{"class":58},[52,17948,17949],{"class":54,"line":435},[52,17950,536],{"class":58},[13,17952,17953,17954,17957],{},"これでPHPファイルだけでJSONの言語ファイルも作成して、フロントでのVueの言語ファイルなどに提供することができるようになります。動的にこのJSONファイルは生成するので、バージョン管理をするときは",[49,17955,17956],{},".gitignore","で指定しておくといいです。そしてデプロイや更新時にはこのコマンドを打ち忘れないようにしましょう。",[13,17959,17960],{},"ちなみにですが、実際にやっていることは特定のディレクトリのPHPファイルの内容を取得して、それをJSONにして生成したファイルを置いているだけなので素のPHP、pythonやrubyとか他の言語でも行けると思いますよ。",[1413,17962,17963],{},"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 pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}",{"title":47,"searchDepth":115,"depth":115,"links":17965},[17966,17967,17968,17975],{"id":16444,"depth":83,"text":16445},{"id":16544,"depth":83,"text":16544},{"id":16554,"depth":83,"text":16554,"children":17969},[17970,17971,17972,17973,17974],{"id":16557,"depth":115,"text":16557},{"id":16579,"depth":115,"text":16579},{"id":16807,"depth":115,"text":16807},{"id":17128,"depth":115,"text":17129},{"id":17181,"depth":115,"text":17181},{"id":17213,"depth":83,"text":17213},[16350],"2022-05-22",{},"\u002Farticles\u002Flaravel-i18n-json-dump",{"title":16360,"description":16360},"articles\u002Flaravel-i18n-json-dump",[16381,17983,1433],"laravel","_common\u002Flaravel.png","tdUMQGHoIH7SI9TXJlID3MGccNOOjq6udWMjq_HTqC0",{"id":17987,"title":17988,"body":17989,"category":19014,"createdAt":19015,"description":19016,"extension":1426,"index":1427,"meta":19017,"navigation":340,"path":19018,"publish":340,"seo":19019,"series":1427,"seriesTitle":1427,"stem":19020,"tag":19021,"thumbnail":19023,"updatedAt":1427,"__hash__":19024},"articles\u002Farticles\u002Fnuxt-auth-middleware.md","Nuxt.jsのSSR・SPA時のフロント側の認証ビューを自前で実装する",{"type":10,"value":17990,"toc":19002},[17991,17994,17997,18000,18011,18014,18017,18020,18023,18026,18029,18032,18043,18047,18050,18092,18095,18155,18158,18165,18244,18247,18250,18351,18358,18390,18394,18399,18406,18502,18609,18613,18620,18624,18633,18636,18805,18808,18812,18819,18979,18982,18985,18988,18999],[13,17992,17993],{},"こんにちはjunです。webアプリを作成する時は大体、認証機能をつけることが多いです。LaravelやDjangoなどでは一発で行けますが、Nuxt.js、Vue.jsを使用したSPA、Node.jsでのSSRコンテンツを作成する場合は一捻り必要です。",[13,17995,17996],{},"Nuxt.jsなどで作成するアプリはバックエンドと独立し、都度バックエンドへのAPI通信の際にトークンを渡したりすることで認証が必要なAPIへアクセスしています。",[13,17998,17999],{},"ビュー側の処理でも",[1464,18001,18002,18005,18008],{},[1467,18003,18004],{},"ログインしているユーザーはこのように表示",[1467,18006,18007],{},"このルートはログインしているユーザーのみアクセス可能",[1467,18009,18010],{},"未ログインユーザーはログイン画面移動",[13,18012,18013],{},"といった処理が行われることが多いです。この記事では上記のような認証を用いたビューの表現、ルーターの設定をNuxt.jsでどう行うかの解説をしたいと思います。SPA（クライアントサイドレンダリング）とSSR（サーバーサイドレンダリング）の２パターンを実装します。ちなみにnuxt-authなどのライブラリは使用しません。",[13,18015,18016],{},"また、ログインメソッドの処理やリクエストヘッダにどうこうするとか、バックエンド側の処理については今回は解説しません。あくまでフロント側（Nuxt.js側）の表示やロジックに認証が必要な場合にどう実装するかについて解説するのみです。",[17,18018,18019],{"id":18019},"大まかな処理",[13,18021,18022],{},"まずこの実装を行うにあたりJWTやクッキーなどなんらかの認証トークンが取得できていること、またそれらの値を使用できることを前提として進めます。",[13,18024,18025],{},"LaravelやDjangoなどではリクエストを送る際にヘッダーにセッションIDを含むクッキーを送信することで、サーバー側でログインによるビューやロジックの分岐を行っています。",[13,18027,18028],{},"しかしNuxt.jsでSPAの場合はコンテンツをクライアント側で生成し、またSSRの場合はnode.jsのサーバーで行われます。すなわちセッションやユーザー情報を保存しているAPIサーバーから「どうにかしてNuxt.js側にユーザー情報を渡す」必要があります。",[13,18030,18031],{},"これから実装する内容はSSR、SPAどちらも以下の通りです。",[2489,18033,18034,18037,18040],{},[1467,18035,18036],{},"Nuxt.js側でユーザーの情報を保存する。",[1467,18038,18039],{},"ログインの是非はユーザー情報の有無で判断する。",[1467,18041,18042],{},"何かしらのタイミングでユーザー情報を取得するAPIを都度発行する。",[17,18044,18046],{"id":18045},"storeの調整","Storeの調整",[13,18048,18049],{},"ではまずStoreにて以下のようにuserステートを作成します。",[42,18051,18054],{"className":1249,"code":18052,"filename":18053,"language":1251,"meta":47,"style":47},"export const state = () => ({\n    user:null,\n});\n","store\u002Findex.js",[49,18055,18056,18076,18084],{"__ignoreMap":47},[52,18057,18058,18060,18063,18066,18068,18070,18072,18074],{"class":54,"line":55},[52,18059,357],{"class":360},[52,18061,18062],{"class":65}," const",[52,18064,18065],{"class":105}," state ",[52,18067,69],{"class":58},[52,18069,7142],{"class":58},[52,18071,6367],{"class":65},[52,18073,1913],{"class":105},[52,18075,364],{"class":58},[52,18077,18078,18081],{"class":54,"line":83},[52,18079,18080],{"class":62},"    user",[52,18082,18083],{"class":58},":null,\n",[52,18085,18086,18088,18090],{"class":54,"line":115},[52,18087,1311],{"class":58},[52,18089,938],{"class":105},[52,18091,1007],{"class":58},[13,18093,18094],{},"デフォルトではnullにしておきます。このuserステートがnullでなく、ユーザー情報のオブジェクトである場合をログイン状態とします。mutationなどでこのステートに値をセットできるように作っておきます。",[42,18096,18098],{"className":1249,"code":18097,"filename":18053,"language":1251,"meta":47,"style":47},"export const mutations = {\n    setUser(state,{user}){\n            state.user = user;\n    }\n}\n",[49,18099,18100,18113,18131,18147,18151],{"__ignoreMap":47},[52,18101,18102,18104,18106,18109,18111],{"class":54,"line":55},[52,18103,357],{"class":360},[52,18105,18062],{"class":65},[52,18107,18108],{"class":105}," mutations ",[52,18110,69],{"class":58},[52,18112,6100],{"class":58},[52,18114,18115,18118,18120,18123,18126,18129],{"class":54,"line":83},[52,18116,18117],{"class":62},"    setUser",[52,18119,932],{"class":58},[52,18121,18122],{"class":986},"state",[52,18124,18125],{"class":58},",{",[52,18127,18128],{"class":986},"user",[52,18130,11062],{"class":58},[52,18132,18133,18136,18138,18140,18142,18145],{"class":54,"line":115},[52,18134,18135],{"class":105},"            state",[52,18137,957],{"class":58},[52,18139,18128],{"class":105},[52,18141,951],{"class":58},[52,18143,18144],{"class":105}," user",[52,18146,1007],{"class":58},[52,18148,18149],{"class":54,"line":142},[52,18150,2110],{"class":58},[52,18152,18153],{"class":54,"line":169},[52,18154,536],{"class":58},[17,18156,18157],{"id":18157},"ミドルェアの作成",[13,18159,18160,18161,18164],{},"次に「ログインしたユーザーのみがアクセス可能なページ」を実装できるようにミドルウェアを実装します。",[49,18162,18163],{},"middleware\u002Fauth.js","を作成します。",[42,18166,18168],{"className":1249,"code":18167,"filename":18163,"language":1251,"meta":47,"style":47},"export default function ({ store, redirect }) {\n    if (!store.state.user) {\n        redirect('\u002Flogin');\n    }\n}\n",[49,18169,18170,18194,18218,18236,18240],{"__ignoreMap":47},[52,18171,18172,18174,18176,18178,18181,18184,18186,18189,18192],{"class":54,"line":55},[52,18173,357],{"class":360},[52,18175,361],{"class":360},[52,18177,2036],{"class":65},[52,18179,18180],{"class":58}," ({",[52,18182,18183],{"class":986}," store",[52,18185,408],{"class":58},[52,18187,18188],{"class":986}," redirect",[52,18190,18191],{"class":58}," })",[52,18193,6100],{"class":58},[52,18195,18196,18198,18200,18203,18206,18208,18210,18212,18214,18216],{"class":54,"line":83},[52,18197,8812],{"class":360},[52,18199,1913],{"class":62},[52,18201,18202],{"class":58},"!",[52,18204,18205],{"class":105},"store",[52,18207,957],{"class":58},[52,18209,18122],{"class":105},[52,18211,957],{"class":58},[52,18213,18128],{"class":105},[52,18215,3098],{"class":62},[52,18217,364],{"class":58},[52,18219,18220,18223,18225,18227,18230,18232,18234],{"class":54,"line":115},[52,18221,18222],{"class":418},"        redirect",[52,18224,932],{"class":62},[52,18226,376],{"class":58},[52,18228,18229],{"class":75},"\u002Flogin",[52,18231,376],{"class":58},[52,18233,938],{"class":62},[52,18235,1007],{"class":58},[52,18237,18238],{"class":54,"line":142},[52,18239,2110],{"class":58},[52,18241,18242],{"class":54,"line":169},[52,18243,536],{"class":58},[13,18245,18246],{},"単純にStoreのUserステートがnullかどうかでログインページに飛ばすようにしています。",[13,18248,18249],{},"ページコンポーネントでは以下のようにしてミドルウェアを有効にします。",[42,18251,18254],{"className":202,"code":18252,"filename":18253,"language":204,"meta":47,"style":47},"\u003Ctemplate>\n    \u003Cdiv>\n        auth\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\u003Cscript>\nexport default {\n    name:\"home\",\n    middleware:\"auth\",\n}\n\u003C\u002Fscript>\n","pages\u002Fauth\u002Findex.vue",[49,18255,18256,18264,18272,18277,18285,18293,18301,18309,18324,18339,18343],{"__ignoreMap":47},[52,18257,18258,18260,18262],{"class":54,"line":55},[52,18259,59],{"class":58},[52,18261,213],{"class":62},[52,18263,80],{"class":58},[52,18265,18266,18268,18270],{"class":54,"line":83},[52,18267,1575],{"class":58},[52,18269,5995],{"class":62},[52,18271,80],{"class":58},[52,18273,18274],{"class":54,"line":115},[52,18275,18276],{"class":105},"        auth\n",[52,18278,18279,18281,18283],{"class":54,"line":142},[52,18280,1717],{"class":58},[52,18282,5995],{"class":62},[52,18284,80],{"class":58},[52,18286,18287,18289,18291],{"class":54,"line":169},[52,18288,108],{"class":58},[52,18290,213],{"class":62},[52,18292,80],{"class":58},[52,18294,18295,18297,18299],{"class":54,"line":302},[52,18296,59],{"class":58},[52,18298,349],{"class":62},[52,18300,80],{"class":58},[52,18302,18303,18305,18307],{"class":54,"line":308},[52,18304,357],{"class":360},[52,18306,361],{"class":360},[52,18308,6100],{"class":58},[52,18310,18311,18313,18315,18317,18320,18322],{"class":54,"line":318},[52,18312,6105],{"class":62},[52,18314,373],{"class":58},[52,18316,72],{"class":58},[52,18318,18319],{"class":75},"home",[52,18321,72],{"class":58},[52,18323,384],{"class":58},[52,18325,18326,18329,18331,18333,18335,18337],{"class":54,"line":328},[52,18327,18328],{"class":62},"    middleware",[52,18330,373],{"class":58},[52,18332,72],{"class":58},[52,18334,17565],{"class":75},[52,18336,72],{"class":58},[52,18338,384],{"class":58},[52,18340,18341],{"class":54,"line":337},[52,18342,536],{"class":58},[52,18344,18345,18347,18349],{"class":54,"line":344},[52,18346,108],{"class":58},[52,18348,349],{"class":62},[52,18350,80],{"class":58},[13,18352,18353,18354,18357],{},"こうすることでログインが必要なページを実装することができます。または",[49,18355,18356],{},"nuxt.config.js"," にて以下のように設定することで全てのページに認証ミドルウェアを適用できます。",[42,18359,18361],{"className":1249,"code":18360,"filename":18356,"language":1251,"meta":47,"style":47},"router: {\n    middleware: 'auth',\n},\n",[49,18362,18363,18372,18386],{"__ignoreMap":47},[52,18364,18365,18368,18370],{"class":54,"line":55},[52,18366,18367],{"class":370},"router",[52,18369,373],{"class":58},[52,18371,6100],{"class":58},[52,18373,18374,18376,18378,18380,18382,18384],{"class":54,"line":83},[52,18375,18328],{"class":370},[52,18377,373],{"class":58},[52,18379,6067],{"class":58},[52,18381,17565],{"class":75},[52,18383,376],{"class":58},[52,18385,384],{"class":58},[52,18387,18388],{"class":54,"line":115},[52,18389,477],{"class":58},[1518,18391,18393],{"id":18392},"特定のページディレクトリを除く場合","特定のページ、ディレクトリを除く場合",[13,18395,18396,18398],{},[49,18397,18356],{}," にてグローバルな認証ミドルウェアを実装できますが、未ログインでもアクセス可能なページや、未ログインでないと閲覧できないページ（ログインページなど）では不便です。",[13,18400,18401,18402,18405],{},"個別にミドルウェアを作成してページごとに設定してオーバーライドすることも可能ですが、私はよく以下のように",[49,18403,18404],{},"auth.js","実装しています。",[42,18407,18409],{"className":1249,"code":18408,"filename":18163,"language":1251,"meta":47,"style":47},"\u002F\u002F 特定のページの認証を外す\nexport default function ({ store, redirect }) {\n    if (!store.state.user && route.fullPath !== '\u002Flogin') {\n        redirect('\u002Flogin');\n    }\n}\n",[49,18410,18411,18416,18436,18478,18494,18498],{"__ignoreMap":47},[52,18412,18413],{"class":54,"line":55},[52,18414,18415],{"class":411},"\u002F\u002F 特定のページの認証を外す\n",[52,18417,18418,18420,18422,18424,18426,18428,18430,18432,18434],{"class":54,"line":83},[52,18419,357],{"class":360},[52,18421,361],{"class":360},[52,18423,2036],{"class":65},[52,18425,18180],{"class":58},[52,18427,18183],{"class":986},[52,18429,408],{"class":58},[52,18431,18188],{"class":986},[52,18433,18191],{"class":58},[52,18435,6100],{"class":58},[52,18437,18438,18440,18442,18444,18446,18448,18450,18452,18454,18457,18460,18462,18465,18468,18470,18472,18474,18476],{"class":54,"line":115},[52,18439,8812],{"class":360},[52,18441,1913],{"class":62},[52,18443,18202],{"class":58},[52,18445,18205],{"class":105},[52,18447,957],{"class":58},[52,18449,18122],{"class":105},[52,18451,957],{"class":58},[52,18453,18128],{"class":105},[52,18455,18456],{"class":58}," &&",[52,18458,18459],{"class":105}," route",[52,18461,957],{"class":58},[52,18463,18464],{"class":105},"fullPath",[52,18466,18467],{"class":58}," !==",[52,18469,6067],{"class":58},[52,18471,18229],{"class":75},[52,18473,376],{"class":58},[52,18475,3098],{"class":62},[52,18477,364],{"class":58},[52,18479,18480,18482,18484,18486,18488,18490,18492],{"class":54,"line":142},[52,18481,18222],{"class":418},[52,18483,932],{"class":62},[52,18485,376],{"class":58},[52,18487,18229],{"class":75},[52,18489,376],{"class":58},[52,18491,938],{"class":62},[52,18493,1007],{"class":58},[52,18495,18496],{"class":54,"line":169},[52,18497,2110],{"class":58},[52,18499,18500],{"class":54,"line":302},[52,18501,536],{"class":58},[42,18503,18505],{"className":1249,"code":18504,"filename":18163,"language":1251,"meta":47,"style":47},"\u002F\u002F 特定のディレクトリ配下を外す\nexport default function ({ store, redirect }) {\n    if (!store.state.user && route.fullPath.indexOf('\u002Fpublic') != -1) {\n        redirect('\u002Flogin');\n    }\n}\n",[49,18506,18507,18512,18532,18585,18601,18605],{"__ignoreMap":47},[52,18508,18509],{"class":54,"line":55},[52,18510,18511],{"class":411},"\u002F\u002F 特定のディレクトリ配下を外す\n",[52,18513,18514,18516,18518,18520,18522,18524,18526,18528,18530],{"class":54,"line":83},[52,18515,357],{"class":360},[52,18517,361],{"class":360},[52,18519,2036],{"class":65},[52,18521,18180],{"class":58},[52,18523,18183],{"class":986},[52,18525,408],{"class":58},[52,18527,18188],{"class":986},[52,18529,18191],{"class":58},[52,18531,6100],{"class":58},[52,18533,18534,18536,18538,18540,18542,18544,18546,18548,18550,18552,18554,18556,18558,18560,18563,18565,18567,18570,18572,18574,18577,18579,18581,18583],{"class":54,"line":115},[52,18535,8812],{"class":360},[52,18537,1913],{"class":62},[52,18539,18202],{"class":58},[52,18541,18205],{"class":105},[52,18543,957],{"class":58},[52,18545,18122],{"class":105},[52,18547,957],{"class":58},[52,18549,18128],{"class":105},[52,18551,18456],{"class":58},[52,18553,18459],{"class":105},[52,18555,957],{"class":58},[52,18557,18464],{"class":105},[52,18559,957],{"class":58},[52,18561,18562],{"class":418},"indexOf",[52,18564,932],{"class":62},[52,18566,376],{"class":58},[52,18568,18569],{"class":75},"\u002Fpublic",[52,18571,376],{"class":58},[52,18573,3098],{"class":62},[52,18575,18576],{"class":58},"!=",[52,18578,3113],{"class":58},[52,18580,31],{"class":1646},[52,18582,3098],{"class":62},[52,18584,364],{"class":58},[52,18586,18587,18589,18591,18593,18595,18597,18599],{"class":54,"line":142},[52,18588,18222],{"class":418},[52,18590,932],{"class":62},[52,18592,376],{"class":58},[52,18594,18229],{"class":75},[52,18596,376],{"class":58},[52,18598,938],{"class":62},[52,18600,1007],{"class":58},[52,18602,18603],{"class":54,"line":169},[52,18604,2110],{"class":58},[52,18606,18607],{"class":54,"line":302},[52,18608,536],{"class":58},[17,18610,18612],{"id":18611},"meリクエストを初期処理にいれる","meリクエストを初期処理にいれる。",[13,18614,18615,18616,18619],{},"storeとミドルェアの準備ができたので、APIサーバーに通信をしてユーザー情報を取得するメソッドを作成しておきます。ここではmeリクエストと呼ぶことにします。今回はaxiosで",[49,18617,18618],{},"https:\u002F\u002Fapi.example.com\u002Fauth\u002Fme","へトークンと一緒にリクエストするとユーザー情報のJSONがレスポンスとして得られるとします。期限切れやトークンがない場合や間違っている場合などは401がレスポンスとして戻ります。",[1518,18621,18623],{"id":18622},"spa","SPA",[13,18625,18626,18627,18632],{},"SPAの場合は",[1450,18628,18631],{"href":18629,"rel":18630},"https:\u002F\u002Fgithub.com\u002Fpotato4d\u002Fnuxt-client-init-module",[1454],"nuxt-client-init-moduleというライブラリ"," を使用するとスムーズです。SSRの場合はNuxtServiceInitという初期処理を実装できるフックがあるのですが、SPAの場合はそれがありあません。Pluginでできなくもないのですが、nuxt-client-init-moduleを使用するとうまくいきやすいです。",[13,18634,18635],{},"上記のライブラリをインストールしてstoreにてmeリクエストをします。",[42,18637,18640],{"className":1249,"code":18638,"filename":18639,"language":1251,"meta":47,"style":47},"export const actions = {\n  async nuxtClientInit({ commit }, context) {\n    await this.$axios.get('https:\u002F\u002Fapi.example.com\u002Fauth\u002Fme')\n    .then(async res=>{\n        commit('setUser', {user:res.data});\n    })\n    .catch(err=>{\n        console.error(err)\n    })\n  }\n}\n","store\u002Findex.js]",[49,18641,18642,18655,18678,18703,18722,18756,18762,18777,18791,18797,18801],{"__ignoreMap":47},[52,18643,18644,18646,18648,18651,18653],{"class":54,"line":55},[52,18645,357],{"class":360},[52,18647,18062],{"class":65},[52,18649,18650],{"class":105}," actions ",[52,18652,69],{"class":58},[52,18654,6100],{"class":58},[52,18656,18657,18660,18663,18665,18668,18671,18674,18676],{"class":54,"line":83},[52,18658,18659],{"class":65},"  async",[52,18661,18662],{"class":62}," nuxtClientInit",[52,18664,10511],{"class":58},[52,18666,18667],{"class":986}," commit",[52,18669,18670],{"class":58}," },",[52,18672,18673],{"class":986}," context",[52,18675,938],{"class":58},[52,18677,6100],{"class":58},[52,18679,18680,18683,18685,18688,18690,18693,18695,18697,18699,18701],{"class":54,"line":115},[52,18681,18682],{"class":360},"    await",[52,18684,6370],{"class":58},[52,18686,18687],{"class":105},"$axios",[52,18689,957],{"class":58},[52,18691,18692],{"class":418},"get",[52,18694,932],{"class":62},[52,18696,376],{"class":58},[52,18698,18618],{"class":75},[52,18700,376],{"class":58},[52,18702,1015],{"class":62},[52,18704,18705,18708,18710,18712,18715,18718,18720],{"class":54,"line":142},[52,18706,18707],{"class":58},"    .",[52,18709,13163],{"class":418},[52,18711,932],{"class":62},[52,18713,18714],{"class":65},"async",[52,18716,18717],{"class":986}," res",[52,18719,990],{"class":65},[52,18721,364],{"class":58},[52,18723,18724,18727,18729,18731,18734,18736,18738,18740,18742,18744,18746,18748,18750,18752,18754],{"class":54,"line":169},[52,18725,18726],{"class":418},"        commit",[52,18728,932],{"class":62},[52,18730,376],{"class":58},[52,18732,18733],{"class":75},"setUser",[52,18735,376],{"class":58},[52,18737,408],{"class":58},[52,18739,10885],{"class":58},[52,18741,18128],{"class":62},[52,18743,373],{"class":58},[52,18745,6168],{"class":105},[52,18747,957],{"class":58},[52,18749,7112],{"class":105},[52,18751,1311],{"class":58},[52,18753,938],{"class":62},[52,18755,1007],{"class":58},[52,18757,18758,18760],{"class":54,"line":302},[52,18759,9134],{"class":58},[52,18761,1015],{"class":62},[52,18763,18764,18766,18768,18770,18773,18775],{"class":54,"line":308},[52,18765,18707],{"class":58},[52,18767,13230],{"class":418},[52,18769,932],{"class":62},[52,18771,18772],{"class":986},"err",[52,18774,990],{"class":65},[52,18776,364],{"class":58},[52,18778,18779,18781,18783,18785,18787,18789],{"class":54,"line":318},[52,18780,13247],{"class":105},[52,18782,957],{"class":58},[52,18784,6180],{"class":418},[52,18786,932],{"class":62},[52,18788,18772],{"class":105},[52,18790,1015],{"class":62},[52,18792,18793,18795],{"class":54,"line":328},[52,18794,9134],{"class":58},[52,18796,1015],{"class":62},[52,18798,18799],{"class":54,"line":337},[52,18800,1076],{"class":58},[52,18802,18803],{"class":54,"line":344},[52,18804,536],{"class":58},[13,18806,18807],{},"nuxtClientInitを使用することでページ側の初期化処理より前にユーザー情報を取得できます。",[1518,18809,18811],{"id":18810},"ssr","SSR",[13,18813,18814,18815,18818],{},"SSRの場合は今度は",[49,18816,18817],{},"NuxtServiceInit","に変更するだけです。これは特にライブラリは必要なく、SSRであれば利用できます。",[42,18820,18822],{"className":1249,"code":18821,"filename":18639,"language":1251,"meta":47,"style":47},"export const actions = {\n  async NuxtServiceInit({ commit }, context) {\n    \u002F\u002F サーバーサイドでトークンを入れるなどが必要な場合は適宜入れください。\n     await this.$axios.get('https:\u002F\u002Fapi.example.com\u002Fauth\u002Fme')\n    .then(async res=>{\n        commit('setUser', {user:res.data});\n    })\n    .catch(err=>{\n        console.error(err)\n    })\n  }\n}\n",[49,18823,18824,18836,18855,18860,18883,18899,18931,18937,18951,18965,18971,18975],{"__ignoreMap":47},[52,18825,18826,18828,18830,18832,18834],{"class":54,"line":55},[52,18827,357],{"class":360},[52,18829,18062],{"class":65},[52,18831,18650],{"class":105},[52,18833,69],{"class":58},[52,18835,6100],{"class":58},[52,18837,18838,18840,18843,18845,18847,18849,18851,18853],{"class":54,"line":83},[52,18839,18659],{"class":65},[52,18841,18842],{"class":62}," NuxtServiceInit",[52,18844,10511],{"class":58},[52,18846,18667],{"class":986},[52,18848,18670],{"class":58},[52,18850,18673],{"class":986},[52,18852,938],{"class":58},[52,18854,6100],{"class":58},[52,18856,18857],{"class":54,"line":115},[52,18858,18859],{"class":411},"    \u002F\u002F サーバーサイドでトークンを入れるなどが必要な場合は適宜入れください。\n",[52,18861,18862,18865,18867,18869,18871,18873,18875,18877,18879,18881],{"class":54,"line":142},[52,18863,18864],{"class":360},"     await",[52,18866,6370],{"class":58},[52,18868,18687],{"class":105},[52,18870,957],{"class":58},[52,18872,18692],{"class":418},[52,18874,932],{"class":62},[52,18876,376],{"class":58},[52,18878,18618],{"class":75},[52,18880,376],{"class":58},[52,18882,1015],{"class":62},[52,18884,18885,18887,18889,18891,18893,18895,18897],{"class":54,"line":169},[52,18886,18707],{"class":58},[52,18888,13163],{"class":418},[52,18890,932],{"class":62},[52,18892,18714],{"class":65},[52,18894,18717],{"class":986},[52,18896,990],{"class":65},[52,18898,364],{"class":58},[52,18900,18901,18903,18905,18907,18909,18911,18913,18915,18917,18919,18921,18923,18925,18927,18929],{"class":54,"line":302},[52,18902,18726],{"class":418},[52,18904,932],{"class":62},[52,18906,376],{"class":58},[52,18908,18733],{"class":75},[52,18910,376],{"class":58},[52,18912,408],{"class":58},[52,18914,10885],{"class":58},[52,18916,18128],{"class":62},[52,18918,373],{"class":58},[52,18920,6168],{"class":105},[52,18922,957],{"class":58},[52,18924,7112],{"class":105},[52,18926,1311],{"class":58},[52,18928,938],{"class":62},[52,18930,1007],{"class":58},[52,18932,18933,18935],{"class":54,"line":308},[52,18934,9134],{"class":58},[52,18936,1015],{"class":62},[52,18938,18939,18941,18943,18945,18947,18949],{"class":54,"line":318},[52,18940,18707],{"class":58},[52,18942,13230],{"class":418},[52,18944,932],{"class":62},[52,18946,18772],{"class":986},[52,18948,990],{"class":65},[52,18950,364],{"class":58},[52,18952,18953,18955,18957,18959,18961,18963],{"class":54,"line":328},[52,18954,13247],{"class":105},[52,18956,957],{"class":58},[52,18958,6180],{"class":418},[52,18960,932],{"class":62},[52,18962,18772],{"class":105},[52,18964,1015],{"class":62},[52,18966,18967,18969],{"class":54,"line":337},[52,18968,9134],{"class":58},[52,18970,1015],{"class":62},[52,18972,18973],{"class":54,"line":344},[52,18974,1076],{"class":58},[52,18976,18977],{"class":54,"line":354},[52,18978,536],{"class":58},[13,18980,18981],{},"NuxtServiceInitというサーバーサイドで上記のリクエストを行って、storeにユーザー情報をいれてくれます。401が来てもcatchしてくれ、ユーザー情報はnullのままになります。",[17,18983,18984],{"id":18984},"完了",[13,18986,18987],{},"エッセンスは以下の通りです。",[2489,18989,18990,18993,18996],{},[1467,18991,18992],{},"初期処理にユーザー情報を取得するAPIをリクエスト",[1467,18994,18995],{},"認証済みであればstoreのuserにユーザー情報のオブジェクトが入る",[1467,18997,18998],{},"ミドルウェアやstoreの情報を使用して認証による分岐を行う",[1413,19000,19001],{},"html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}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 pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}",{"title":47,"searchDepth":115,"depth":115,"links":19003},[19004,19005,19006,19009,19013],{"id":18019,"depth":83,"text":18019},{"id":18045,"depth":83,"text":18046},{"id":18157,"depth":83,"text":18157,"children":19007},[19008],{"id":18392,"depth":115,"text":18393},{"id":18611,"depth":83,"text":18612,"children":19010},[19011,19012],{"id":18622,"depth":115,"text":18623},{"id":18810,"depth":115,"text":18811},{"id":18984,"depth":83,"text":18984},[1423],"2022-05-19","フロント側の認証機能を実装します",{},"\u002Farticles\u002Fnuxt-auth-middleware",{"title":17988,"description":19016},"articles\u002Fnuxt-auth-middleware",[1433,19022],"nuxt","_common\u002Fnuxt.jpg","MWiO6wSm6JBqm1_bIaWiFdYFIqDsWKtpAvMLHvfsPGg",{"id":19026,"title":19027,"body":19028,"category":21315,"createdAt":21316,"description":21317,"extension":1426,"index":55,"meta":21318,"navigation":340,"path":21319,"publish":340,"seo":21320,"series":21321,"seriesTitle":21322,"stem":21323,"tag":21324,"thumbnail":21325,"updatedAt":1427,"__hash__":21326},"series\u002Fseries\u002Fvue-laravel-app-1.md","Vue SPA x Laravelでつくる実務パチモンアプリその１：アプリの概要と環境構築",{"type":10,"value":19029,"toc":21291},[19030,19033,19050,19061,19064,19084,19087,19090,19093,19096,19099,19102,19133,19136,19173,19176,19187,19190,19193,19196,19199,19202,19211,19225,19231,19293,19307,19312,19316,19319,19372,19375,19378,19381,19399,19456,19463,19467,19470,19792,19809,19818,19876,19879,19885,19899,19908,19914,19928,19931,19934,19938,19945,19951,19954,19958,19961,19965,19971,19992,19998,20128,20139,20142,20164,20169,20175,20179,20186,20192,20195,20248,20251,20377,20384,20387,20499,20582,20585,20910,20917,20924,20927,20946,21117,21126,21138,21175,21178,21217,21223,21236,21247,21250,21263,21266,21275,21278,21281,21285,21288],[13,19031,19032],{},"こんにちはjunです。仕事で中規模プロジェクトのディレクターとしてアサインされて、半年ほどブログを書けませんでした。ようやく最近時間が出てきたので新しいシリーズ記事を作成しようと思いました。とにかく最近１年はシステムばっかり作っていました。それぞのれプロジェクトでは",[1464,19034,19035,19038,19041,19044,19047],{},[1467,19036,19037],{},"CRUDなAPI",[1467,19039,19040],{},"管理画面の構築",[1467,19042,19043],{},"ユーザー権限によるアクセス制限",[1467,19045,19046],{},"ログイン・ログアウト",[1467,19048,19049],{},"ジョブ・キューを用いた長い処理",[13,19051,19052,19053,19056,19057,19060],{},"など使用するシステムの構築を多く行い、いろいろ学ぶことできました。その中で「CRUDなAPI」を考えた「管理画面」の構築をすることが多くありました。バックエンドは",[49,19054,19055],{},"Laravel","を、フロントエンドは",[49,19058,19059],{},"Vue.js","を使用していました。Vue.jsのおかげで高速にリッチなUIを構築し、システムを開発することができました。",[13,19062,19063],{},"この知見を共有し、まとめるためにも今回は",[1464,19065,19066,19069,19072,19075,19078,19081],{},[1467,19067,19068],{},"管理画面はVue.js SPA",[1467,19070,19071],{},"web APIによるCRUD操作",[1467,19073,19074],{},"ルーティング設計",[1467,19076,19077],{},"バリデーション",[1467,19079,19080],{},"MVCによるアプリケーション設計",[1467,19082,19083],{},"laravel\u002Fpermissionを用いたユーザー権限",[13,19085,19086],{},"以上の様な機能を持ったデモアプリを作ろうと思います。Laravelを用いたシステム開発と、Vue.jsによる管理画面フロントエンド開発を解説していきます。",[17,19088,19089],{"id":19089},"作るアプリの概要",[13,19091,19092],{},"今回のアプリはn○teみたいなブログサービスを作ります。CRUDや管理画面の構築をメインに行いたいのでシンプルにいきます。",[1518,19094,19095],{"id":19095},"機能概要",[13,19097,19098],{},"ざっくりとした機能概要は以下のとおりです。",[2409,19100,19101],{"id":19101},"ユーザー系の特徴",[1464,19103,19104,19107,19110,19113,19116,19119],{},[1467,19105,19106],{},"利用する際にはユーザーとしてアカウントを作る（今回は全てゲストとする）。",[1467,19108,19109],{},"ログインはアドレスとパスワードを使用する。",[1467,19111,19112],{},"アプリを利用する一般ユーザーと管理を行う管理ユーザーが存在する。",[1467,19114,19115],{},"管理ユーザーは特定の管理用ルートから入る。",[1467,19117,19118],{},"退会可能。退会時は関連するデータは削除される。",[1467,19120,19121,19122],{},"プロフィールでは以下の項目を持つ\n",[1464,19123,19124,19127,19130],{},[1467,19125,19126],{},"ユーザー名（仮名）",[1467,19128,19129],{},"プロフィール文",[1467,19131,19132],{},"アバター写真",[2409,19134,19135],{"id":19135},"ブログ機能",[1464,19137,19138,19141,19158,19161,19164,19167,19170],{},[1467,19139,19140],{},"ブログを作成することができ、公開することができる。",[1467,19142,19143,19144],{},"ブログは以下の項目を持つ\n",[1464,19145,19146,19149,19152,19155],{},[1467,19147,19148],{},"タイトル",[1467,19150,19151],{},"内容（リッチテキスト）",[1467,19153,19154],{},"タグ（任意）",[1467,19156,19157],{},"公開日時",[1467,19159,19160],{},"下書きとして保存することができる。更新時も下書き可能。",[1467,19162,19163],{},"任意数のタグを添付できる。タグはユーザーが新しく作成できる。",[1467,19165,19166],{},"削除可能",[1467,19168,19169],{},"画像の添付が可能。",[1467,19171,19172],{},"アップロードした画像は管理できる。",[2409,19174,19175],{"id":19175},"一覧検索機能",[1464,19177,19178,19181,19184],{},[1467,19179,19180],{},"サイトトップは投稿された記事が新着順に並ぶ。",[1467,19182,19183],{},"任意のタグを選んで一覧表示することが可能。",[1467,19185,19186],{},"部分一致検索も一応可能とする。",[1518,19188,19189],{"id":19189},"アプリケーションアーキテクチャー",[13,19191,19192],{},"バックエンドにはLaravel9、管理画面はVue.js(2系)を使用して構築します。なおVue.jsはSPA構成とします。記事の一覧や詳細画面などの公開側はLaravelのレンダリングで表示します。",[729,19194],{":src":19195,":center":1322},"'vue_laravel_app\u002Farchitecture.jpg'",[13,19197,19198],{},"デザインに関してはセンスが皆無なので管理画面・公開側ともにBootstrapを使用します。",[17,19200,19201],{"id":19201},"環境構築",[13,19203,19204,19205,19210],{},"ではまずは環境構築を行っていきましょう。環境構築にはDockerを使用してLAMP環境を作ります。",[1450,19206,19209],{"href":19207,"rel":19208},"https:\u002F\u002Fwww.twilio.com\u002Fblog\u002Fget-started-docker-laravel-jp",[1454],"こちらの記事","を参考にし、",[1464,19212,19213,19216,19219,19222],{},[1467,19214,19215],{},"mysql5.7",[1467,19217,19218],{},"phpmyadmin",[1467,19220,19221],{},"apache2.4",[1467,19223,19224],{},"php8.1",[13,19226,19227,19228,18164],{},"以上の構成を作成したいと思います。ディレクトリを作成して",[49,19229,19230],{},"docker-compose.yaml",[42,19232,19236],{"className":19233,"code":19234,"language":19235,"meta":47,"style":47},"language-bash shiki shiki-themes material-theme-ocean","mkdir ~\u002Flaravel_vue\ncd  ~\u002Flaravel_vue\n\ntouch docker-compose.yaml\nmkdir html\nmkdir web_1\ncd web_1\ntouch Dockerfile\n","bash",[49,19237,19238,19246,19254,19258,19266,19273,19280,19286],{"__ignoreMap":47},[52,19239,19240,19243],{"class":54,"line":55},[52,19241,19242],{"class":370},"mkdir",[52,19244,19245],{"class":75}," ~\u002Flaravel_vue\n",[52,19247,19248,19251],{"class":54,"line":83},[52,19249,19250],{"class":418},"cd",[52,19252,19253],{"class":75},"  ~\u002Flaravel_vue\n",[52,19255,19256],{"class":54,"line":115},[52,19257,341],{"emptyLinePlaceholder":340},[52,19259,19260,19263],{"class":54,"line":142},[52,19261,19262],{"class":370},"touch",[52,19264,19265],{"class":75}," docker-compose.yaml\n",[52,19267,19268,19270],{"class":54,"line":169},[52,19269,19242],{"class":370},[52,19271,19272],{"class":75}," html\n",[52,19274,19275,19277],{"class":54,"line":302},[52,19276,19242],{"class":370},[52,19278,19279],{"class":75}," web_1\n",[52,19281,19282,19284],{"class":54,"line":308},[52,19283,19250],{"class":418},[52,19285,19279],{"class":75},[52,19287,19288,19290],{"class":54,"line":318},[52,19289,19262],{"class":370},[52,19291,19292],{"class":75}," Dockerfile\n",[13,19294,19295,19298,19299,19302,19303,19306],{},[49,19296,19297],{},"html\u002F","という名前で作成したディレクトリにLaravelのソースが作成され、Dockerコンテナにマウントされる様にします。",[49,19300,19301],{},"web_1\u002F","ディレクトリにはphpとapacheが構築できる",[49,19304,19305],{},"Dockerfile","を作成してビルドします。",[13,19308,19309,19311],{},[49,19310,19230],{},"でDBとapache+phpと連結したいと思います。",[1518,19313,19315],{"id":19314},"dockerfile設定","Dockerfile設定",[13,19317,19318],{},"まずはapacheとphpのイメージをDockerfileを作成します。",[42,19320,19325],{"className":19321,"code":19322,"filename":19323,"language":19324,"meta":47,"style":47},"language-dockerfie shiki shiki-themes material-theme-ocean","FROM php:8.1-apache\nRUN apt update \\\n        && apt install -y g++ libicu-dev libpq-dev libzip-dev zip zlib1g-dev \\\n        && mv \u002Fetc\u002Fapache2\u002Fmods-available\u002Frewrite.load \u002Fetc\u002Fapache2\u002Fmods-enabled\nRUN docker-php-ext-install pdo pdo_mysql\nWORKDIR \u002Fvar\u002Fwww\u002Fhtml\nRUN curl -sS https:\u002F\u002Fgetcomposer.org\u002Finstaller | php -- --install-dir=\u002Fusr\u002Flocal\u002Fbin --filename=composer\nRUN curl -fsSL https:\u002F\u002Fdeb.nodesource.com\u002Fsetup_lts.x | bash - \\\n    && apt-get install -y nodejs\n","web_1\u002FDockerfile","dockerfie",[49,19326,19327,19332,19337,19342,19347,19352,19357,19362,19367],{"__ignoreMap":47},[52,19328,19329],{"class":54,"line":55},[52,19330,19331],{},"FROM php:8.1-apache\n",[52,19333,19334],{"class":54,"line":83},[52,19335,19336],{},"RUN apt update \\\n",[52,19338,19339],{"class":54,"line":115},[52,19340,19341],{},"        && apt install -y g++ libicu-dev libpq-dev libzip-dev zip zlib1g-dev \\\n",[52,19343,19344],{"class":54,"line":142},[52,19345,19346],{},"        && mv \u002Fetc\u002Fapache2\u002Fmods-available\u002Frewrite.load \u002Fetc\u002Fapache2\u002Fmods-enabled\n",[52,19348,19349],{"class":54,"line":169},[52,19350,19351],{},"RUN docker-php-ext-install pdo pdo_mysql\n",[52,19353,19354],{"class":54,"line":302},[52,19355,19356],{},"WORKDIR \u002Fvar\u002Fwww\u002Fhtml\n",[52,19358,19359],{"class":54,"line":308},[52,19360,19361],{},"RUN curl -sS https:\u002F\u002Fgetcomposer.org\u002Finstaller | php -- --install-dir=\u002Fusr\u002Flocal\u002Fbin --filename=composer\n",[52,19363,19364],{"class":54,"line":318},[52,19365,19366],{},"RUN curl -fsSL https:\u002F\u002Fdeb.nodesource.com\u002Fsetup_lts.x | bash - \\\n",[52,19368,19369],{"class":54,"line":328},[52,19370,19371],{},"    && apt-get install -y nodejs\n",[13,19373,19374],{},"このファイルではphpとapacheが入った環境にてLaravelの依存PHPモジュールのインストールとcomposerをインストールする内容を記述しています。そしてLaravelのアセットビルドにはnode.jsを使用するのでそのインストールも記述しています。",[1518,19376,19377],{"id":19377},"apacheの設定ファイルを作成",[13,19379,19380],{},"Laravelのドキュメントルートを設定するためにapacheの設定ファイル作成して、マウントできる様にします。",[42,19382,19384],{"className":19233,"code":19383,"language":19235,"meta":47,"style":47},"cd web_1\ntouch default.conf\n",[49,19385,19386,19392],{"__ignoreMap":47},[52,19387,19388,19390],{"class":54,"line":55},[52,19389,19250],{"class":418},[52,19391,19279],{"class":75},[52,19393,19394,19396],{"class":54,"line":83},[52,19395,19262],{"class":370},[52,19397,19398],{"class":75}," default.conf\n",[42,19400,19405],{"className":19401,"code":19402,"filename":19403,"language":19404,"meta":47,"style":47},"language-conf shiki shiki-themes material-theme-ocean","\u003CVirtualHost *:80>\n    ServerName laravel_docker\n    DocumentRoot \u002Fvar\u002Fwww\u002Fhtml\u002Fpublic\n\n    \u003CDirectory \"\u002Fvar\u002Fwww\u002Fhtml\u002Fpublic\">\n        AllowOverride All\n    \u003C\u002FDirectory>\n    ErrorLog ${APACHE_LOG_DIR}\u002Ferror.log\n    CustomLog ${APACHE_LOG_DIR}\u002Faccess.log combined\n\u003C\u002FVirtualHost>\n","default.conf","conf",[49,19406,19407,19412,19417,19422,19426,19431,19436,19441,19446,19451],{"__ignoreMap":47},[52,19408,19409],{"class":54,"line":55},[52,19410,19411],{},"\u003CVirtualHost *:80>\n",[52,19413,19414],{"class":54,"line":83},[52,19415,19416],{},"    ServerName laravel_docker\n",[52,19418,19419],{"class":54,"line":115},[52,19420,19421],{},"    DocumentRoot \u002Fvar\u002Fwww\u002Fhtml\u002Fpublic\n",[52,19423,19424],{"class":54,"line":142},[52,19425,341],{"emptyLinePlaceholder":340},[52,19427,19428],{"class":54,"line":169},[52,19429,19430],{},"    \u003CDirectory \"\u002Fvar\u002Fwww\u002Fhtml\u002Fpublic\">\n",[52,19432,19433],{"class":54,"line":302},[52,19434,19435],{},"        AllowOverride All\n",[52,19437,19438],{"class":54,"line":308},[52,19439,19440],{},"    \u003C\u002FDirectory>\n",[52,19442,19443],{"class":54,"line":318},[52,19444,19445],{},"    ErrorLog ${APACHE_LOG_DIR}\u002Ferror.log\n",[52,19447,19448],{"class":54,"line":328},[52,19449,19450],{},"    CustomLog ${APACHE_LOG_DIR}\u002Faccess.log combined\n",[52,19452,19453],{"class":54,"line":337},[52,19454,19455],{},"\u003C\u002FVirtualHost>\n",[13,19457,19458,19459,19462],{},"このファイルはコンテナの",[49,19460,19461],{},"\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F000-default.conf","にマウントされます。",[1518,19464,19466],{"id":19465},"docker-composeyamlを作成","docker-compose.yamlを作成",[13,19468,19469],{},"次にdocker-comose.yamlを作成してDBとphpmyadmin",[42,19471,19476],{"className":19472,"code":19473,"filename":19474,"language":19475,"meta":47,"style":47},"language-yaml shiki shiki-themes material-theme-ocean","version: '3'\nservices: \n  web_1:\n    build: .\u002Fweb_1\n    depends_on: \n      - db\n    volumes: \n      - .\u002Fhtml\u002F:\u002Fvar\u002Fwww\u002Fhtml\u002F\n      - .\u002Fweb_1\u002Fdefault.conf:\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F000-default.conf\n    ports: \n      - \"8005:80\"\n  phpmyadmin:\n    image: phpmyadmin\n    restart: always\n    ports:\n      - \"8080:80\"\n    environment:\n     - PMA_ARBITRARY=1\n     - PMA_HOST=db:3306\n     - PMA_USER=root\n     - PMA_PASSWORD=rootroot\n  db:\n    image: mysql:5.7\n    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci\n    environment:\n      MYSQL_DATABASE: preform\n      MYSQL_USER: test\n      MYSQL_PASSWORD: testtest\n      MYSQL_ROOT_PASSWORD: rootroot\n      TZ: Asia\u002FTokyo\n    ports: \n      - \"3306:3306\"\n    volumes: \n      - vue_laravel:\u002Fvar\u002Flib\u002Fmysql\nvolumes: \n  vue_laravel: {}\n","docker-comose.yaml","yaml",[49,19477,19478,19491,19500,19508,19518,19527,19535,19544,19551,19558,19567,19578,19585,19594,19604,19610,19621,19628,19636,19643,19650,19657,19664,19673,19683,19689,19699,19709,19719,19729,19739,19747,19758,19766,19773,19782],{"__ignoreMap":47},[52,19479,19480,19483,19485,19487,19489],{"class":54,"line":55},[52,19481,19482],{"class":62},"version",[52,19484,373],{"class":58},[52,19486,6067],{"class":58},[52,19488,39],{"class":75},[52,19490,10489],{"class":58},[52,19492,19493,19496,19498],{"class":54,"line":83},[52,19494,19495],{"class":62},"services",[52,19497,373],{"class":58},[52,19499,283],{"class":105},[52,19501,19502,19505],{"class":54,"line":115},[52,19503,19504],{"class":62},"  web_1",[52,19506,19507],{"class":58},":\n",[52,19509,19510,19513,19515],{"class":54,"line":142},[52,19511,19512],{"class":62},"    build",[52,19514,373],{"class":58},[52,19516,19517],{"class":75}," .\u002Fweb_1\n",[52,19519,19520,19523,19525],{"class":54,"line":169},[52,19521,19522],{"class":62},"    depends_on",[52,19524,373],{"class":58},[52,19526,283],{"class":105},[52,19528,19529,19532],{"class":54,"line":302},[52,19530,19531],{"class":58},"      -",[52,19533,19534],{"class":75}," db\n",[52,19536,19537,19540,19542],{"class":54,"line":308},[52,19538,19539],{"class":62},"    volumes",[52,19541,373],{"class":58},[52,19543,283],{"class":105},[52,19545,19546,19548],{"class":54,"line":318},[52,19547,19531],{"class":58},[52,19549,19550],{"class":75}," .\u002Fhtml\u002F:\u002Fvar\u002Fwww\u002Fhtml\u002F\n",[52,19552,19553,19555],{"class":54,"line":328},[52,19554,19531],{"class":58},[52,19556,19557],{"class":75}," .\u002Fweb_1\u002Fdefault.conf:\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F000-default.conf\n",[52,19559,19560,19563,19565],{"class":54,"line":337},[52,19561,19562],{"class":62},"    ports",[52,19564,373],{"class":58},[52,19566,283],{"class":105},[52,19568,19569,19571,19573,19576],{"class":54,"line":344},[52,19570,19531],{"class":58},[52,19572,3013],{"class":58},[52,19574,19575],{"class":75},"8005:80",[52,19577,266],{"class":58},[52,19579,19580,19583],{"class":54,"line":354},[52,19581,19582],{"class":62},"  phpmyadmin",[52,19584,19507],{"class":58},[52,19586,19587,19589,19591],{"class":54,"line":367},[52,19588,1948],{"class":62},[52,19590,373],{"class":58},[52,19592,19593],{"class":75}," phpmyadmin\n",[52,19595,19596,19599,19601],{"class":54,"line":387},[52,19597,19598],{"class":62},"    restart",[52,19600,373],{"class":58},[52,19602,19603],{"class":75}," always\n",[52,19605,19606,19608],{"class":54,"line":415},[52,19607,19562],{"class":62},[52,19609,19507],{"class":58},[52,19611,19612,19614,19616,19619],{"class":54,"line":427},[52,19613,19531],{"class":58},[52,19615,3013],{"class":58},[52,19617,19618],{"class":75},"8080:80",[52,19620,266],{"class":58},[52,19622,19623,19626],{"class":54,"line":435},[52,19624,19625],{"class":62},"    environment",[52,19627,19507],{"class":58},[52,19629,19630,19633],{"class":54,"line":446},[52,19631,19632],{"class":58},"     -",[52,19634,19635],{"class":75}," PMA_ARBITRARY=1\n",[52,19637,19638,19640],{"class":54,"line":480},[52,19639,19632],{"class":58},[52,19641,19642],{"class":75}," PMA_HOST=db:3306\n",[52,19644,19645,19647],{"class":54,"line":509},[52,19646,19632],{"class":58},[52,19648,19649],{"class":75}," PMA_USER=root\n",[52,19651,19652,19654],{"class":54,"line":539},[52,19653,19632],{"class":58},[52,19655,19656],{"class":75}," PMA_PASSWORD=rootroot\n",[52,19658,19659,19662],{"class":54,"line":547},[52,19660,19661],{"class":62},"  db",[52,19663,19507],{"class":58},[52,19665,19666,19668,19670],{"class":54,"line":553},[52,19667,1948],{"class":62},[52,19669,373],{"class":58},[52,19671,19672],{"class":75}," mysql:5.7\n",[52,19674,19675,19678,19680],{"class":54,"line":559},[52,19676,19677],{"class":62},"    command",[52,19679,373],{"class":58},[52,19681,19682],{"class":75}," mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci\n",[52,19684,19685,19687],{"class":54,"line":564},[52,19686,19625],{"class":62},[52,19688,19507],{"class":58},[52,19690,19691,19694,19696],{"class":54,"line":569},[52,19692,19693],{"class":62},"      MYSQL_DATABASE",[52,19695,373],{"class":58},[52,19697,19698],{"class":75}," preform\n",[52,19700,19701,19704,19706],{"class":54,"line":1106},[52,19702,19703],{"class":62},"      MYSQL_USER",[52,19705,373],{"class":58},[52,19707,19708],{"class":75}," test\n",[52,19710,19711,19714,19716],{"class":54,"line":1135},[52,19712,19713],{"class":62},"      MYSQL_PASSWORD",[52,19715,373],{"class":58},[52,19717,19718],{"class":75}," testtest\n",[52,19720,19721,19724,19726],{"class":54,"line":1164},[52,19722,19723],{"class":62},"      MYSQL_ROOT_PASSWORD",[52,19725,373],{"class":58},[52,19727,19728],{"class":75}," rootroot\n",[52,19730,19731,19734,19736],{"class":54,"line":4},[52,19732,19733],{"class":62},"      TZ",[52,19735,373],{"class":58},[52,19737,19738],{"class":75}," Asia\u002FTokyo\n",[52,19740,19741,19743,19745],{"class":54,"line":1199},[52,19742,19562],{"class":62},[52,19744,373],{"class":58},[52,19746,283],{"class":105},[52,19748,19749,19751,19753,19756],{"class":54,"line":1204},[52,19750,19531],{"class":58},[52,19752,3013],{"class":58},[52,19754,19755],{"class":75},"3306:3306",[52,19757,266],{"class":58},[52,19759,19760,19762,19764],{"class":54,"line":1209},[52,19761,19539],{"class":62},[52,19763,373],{"class":58},[52,19765,283],{"class":105},[52,19767,19768,19770],{"class":54,"line":1214},[52,19769,19531],{"class":58},[52,19771,19772],{"class":75}," vue_laravel:\u002Fvar\u002Flib\u002Fmysql\n",[52,19774,19775,19778,19780],{"class":54,"line":1219},[52,19776,19777],{"class":62},"volumes",[52,19779,373],{"class":58},[52,19781,283],{"class":105},[52,19783,19784,19787,19789],{"class":54,"line":3216},[52,19785,19786],{"class":62},"  vue_laravel",[52,19788,373],{"class":58},[52,19790,19791],{"class":58}," {}\n",[13,19793,19794,19797,19798,19800,19801,19804,19805,19808],{},[49,19795,19796],{},"web_1","サービスでapacheとphpの環境をビルドします。そして",[49,19799,19297],{},"をコンテナ内の",[49,19802,19803],{},"\u002Fvar\u002Fwww\u002Fhtml\u002F","にマウントされます。ubuntuのapacheは",[49,19806,19807],{},"\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F","配下にconfファイルを設置することで設定を追加したりオーバーライドできます。",[13,19810,19811,19813,19814,19817],{},[49,19812,19218],{},"サービスでphpmyadminを起動してDBの内容を編集しやすくします。",[49,19815,19816],{},"db","サービスではmysqlの環境を作成します。ボリュームの設定をして永続化を行います。以上の設定を行ったらコンテナを立ち上げます。",[42,19819,19821],{"className":19233,"code":19820,"language":19235,"meta":47,"style":47},"docker-compose up -d\n\nCreating laravel_vue_phpmyadmin_1 ... done\nCreating laravel_vue_db_1         ... done\nCreating laravel_vue_web_1_1      ... done\n",[49,19822,19823,19834,19838,19852,19864],{"__ignoreMap":47},[52,19824,19825,19828,19831],{"class":54,"line":55},[52,19826,19827],{"class":370},"docker-compose",[52,19829,19830],{"class":75}," up",[52,19832,19833],{"class":75}," -d\n",[52,19835,19836],{"class":54,"line":83},[52,19837,341],{"emptyLinePlaceholder":340},[52,19839,19840,19843,19846,19849],{"class":54,"line":115},[52,19841,19842],{"class":370},"Creating",[52,19844,19845],{"class":75}," laravel_vue_phpmyadmin_1",[52,19847,19848],{"class":75}," ...",[52,19850,19851],{"class":75}," done\n",[52,19853,19854,19856,19859,19862],{"class":54,"line":142},[52,19855,19842],{"class":370},[52,19857,19858],{"class":75}," laravel_vue_db_1",[52,19860,19861],{"class":75},"         ...",[52,19863,19851],{"class":75},[52,19865,19866,19868,19871,19874],{"class":54,"line":169},[52,19867,19842],{"class":370},[52,19869,19870],{"class":75}," laravel_vue_web_1_1",[52,19872,19873],{"class":75},"      ...",[52,19875,19851],{"class":75},[13,19877,19878],{},"そしてコンテナ内に入ってLaravelをインストールしていきます。",[42,19880,19883],{"className":19881,"code":19882,"language":452},[5819],"docker exec -it laravel_vue_web_1_1 \u002Fbin\u002Fbash\n\nroot@a790844c74d6:\u002Fvar\u002Fwww\u002Fhtml# composer \nroot@a790844c74d6:\u002Fvar\u002Fwww\u002Fhtml# composer create-project laravel\u002Flaravel .\u002F\n",[49,19884,19882],{"__ignoreMap":47},[13,19886,19887,19888,19891,19892,19895,19896,19898],{},"これで",[49,19889,19890],{},"\u002Fvar\u002Fwww\u002Fhtml","配下にLaravelプロジェクトが入ります。コンテナの",[49,19893,19894],{}," \u002Fvar\u002Fwww\u002Fhtml","はホストマシンの",[49,19897,19297],{},"に同期されます。",[13,19900,19901,19903,19904,19907],{},[49,19902,19297],{},"配下にソースが生成されたのを確認しましたら、",[49,19905,19906],{},".env","ファイルにDBの接続設定を記述します。",[42,19909,19912],{"className":19910,"code":19911,"filename":19906,"language":452,"meta":47},[5819],"DB_CONNECTION=mysql\nDB_HOST=db\nDB_PORT=3306\nDB_DATABASE=laravel\nDB_USERNAME=root\nDB_PASSWORD=rootroot\n",[49,19913,19911],{"__ignoreMap":47},[13,19915,19916,19917,19919,19920,19923,19924,19927],{},"phpmyadminとかで",[49,19918,17983],{},"データベースは作成しておいてください。",[49,19921,19922],{},"DB_HOST","はdockerのDBのサービス名を入力します。設定したらブラウザで",[49,19925,19926],{},"localhost:8005","にアクセスします。以下の様なウェルカムページが表示されれば成功です。",[729,19929],{":src":19930,":center":1322},"'vue_laravel_app\u002Flaravel_welcome.png'",[13,19932,19933],{},"（特に断りがない限り、以降ではDockerコンテナ内の操作とします。）",[17,19935,19937],{"id":19936},"laravelとvueセットアップ","LaravelとVueセットアップ",[13,19939,19940,19941,19944],{},"それではアセットと認証機能のセットアップをしましょう。今回は",[49,19942,19943],{},"laravel\u002Fbreeze","をスターターキットとして利用します。",[42,19946,19949],{"className":19947,"code":19948,"language":452},[5819],"composer require laravel\u002Fbreeze --dev\n\nphp artisan breeze:install\nBreeze scaffolding installed successfully.\nPlease execute the \"npm install && npm run dev\" command to build your assets.\n\nnpm install\nnpm run dev\nphp artisan migrate\nMigration table created successfully.\nMigrating: 2014_10_12_000000_create_users_table\nMigrated:  2014_10_12_000000_create_users_table (23.69ms)\nMigrating: 2014_10_12_100000_create_password_resets_table\nMigrated:  2014_10_12_100000_create_password_resets_table (10.45ms)\nMigrating: 2019_08_19_000000_create_failed_jobs_table\nMigrated:  2019_08_19_000000_create_failed_jobs_table (10.60ms)\nMigrating: 2019_12_14_000001_create_personal_access_tokens_table\nMigrated:  2019_12_14_000001_create_personal_access_tokens_table (14.54ms)\n",[49,19950,19948],{"__ignoreMap":47},[13,19952,19953],{},"マイグレーションが完了したので登録などができる様になっているはずです。ひとまずLaravelのセットアップは以上となります。",[17,19955,19957],{"id":19956},"とりあえずspa構成ができるかチェック","とりあえずSPA構成ができるかチェック",[13,19959,19960],{},"次は管理画面のフロントエンドのセットアップをしていきます。管理画面はvue.jsで作成し、公開側コンテンツはLaravelのサーバーサイドレンダリングをします。ここではSPA構成のセットアップをします。",[1518,19962,19964],{"id":19963},"spaのビューとルートの設定","SPAのビューとルートの設定",[13,19966,19967,19970],{},[49,19968,19969],{},"\u002Fsystem","配下を管理画面として構築していきます。以下の様なビューテンプレートとルートを作成します。",[42,19972,19975],{"className":16378,"code":19973,"filename":19974,"language":16381,"meta":47,"style":47},"Route::get('\u002Fsystem\u002F{path?}', function () {\n    return view('admin');\n})->where('path', '.*')->name('admin');\n","routes\u002Fweb.php",[49,19976,19977,19982,19987],{"__ignoreMap":47},[52,19978,19979],{"class":54,"line":55},[52,19980,19981],{},"Route::get('\u002Fsystem\u002F{path?}', function () {\n",[52,19983,19984],{"class":54,"line":83},[52,19985,19986],{},"    return view('admin');\n",[52,19988,19989],{"class":54,"line":115},[52,19990,19991],{},"})->where('path', '.*')->name('admin');\n",[13,19993,19994,19995,19997],{},"このルートは",[49,19996,19969],{},"配下すべてのルートに対してadminビューを返すことを示しています。",[42,19999,20002],{"className":16378,"code":20000,"filename":20001,"language":16381,"meta":47,"style":47},"\u003C!DOCTYPE html>\n\u003Chtml lang=\"{{ str_replace('_', '-', app()->getLocale()) }}\">\n    \u003Chead>\n        \u003Cmeta charset=\"utf-8\">\n        \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n        \u003Cmeta name=\"csrf-token\" content=\"{{ csrf_token() }}\">\n\n        \u003Ctitle>{{ config('app.name', 'Laravel') }}\u003C\u002Ftitle>\n\n        \u003C!-- Fonts -->\n        \u003Clink rel=\"stylesheet\" href=\"https:\u002F\u002Ffonts.googleapis.com\u002Fcss2?family=Nunito:wght@400;600;700&display=swap\">\n\n        \u003C!-- Styles -->\n        \u003Clink rel=\"stylesheet\" href=\"{{ asset('css\u002Fapp.css') }}\">\n\n    \u003C\u002Fhead>\n    \u003Cbody>\n        \u003Cdiv class=\"font-sans text-gray-900 antialiased\">\n            \u003Cdiv id=\"app\">\n                \u003Crouter-view\u002F>\n            \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n    \u003C\u002Fbody>\n    \u003C!-- Scripts -->\n    \u003Cscript src=\"{{ asset('js\u002Fadmin\u002Findex.js') }}\" defer>\u003C\u002Fscript>\n\u003C\u002Fhtml>\n","resources\u002Fview\u002Fadmin.blade.php",[49,20003,20004,20009,20014,20019,20024,20029,20034,20038,20043,20047,20052,20057,20061,20066,20071,20075,20080,20085,20090,20095,20100,20104,20108,20113,20118,20123],{"__ignoreMap":47},[52,20005,20006],{"class":54,"line":55},[52,20007,20008],{},"\u003C!DOCTYPE html>\n",[52,20010,20011],{"class":54,"line":83},[52,20012,20013],{},"\u003Chtml lang=\"{{ str_replace('_', '-', app()->getLocale()) }}\">\n",[52,20015,20016],{"class":54,"line":115},[52,20017,20018],{},"    \u003Chead>\n",[52,20020,20021],{"class":54,"line":142},[52,20022,20023],{},"        \u003Cmeta charset=\"utf-8\">\n",[52,20025,20026],{"class":54,"line":169},[52,20027,20028],{},"        \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n",[52,20030,20031],{"class":54,"line":302},[52,20032,20033],{},"        \u003Cmeta name=\"csrf-token\" content=\"{{ csrf_token() }}\">\n",[52,20035,20036],{"class":54,"line":308},[52,20037,341],{"emptyLinePlaceholder":340},[52,20039,20040],{"class":54,"line":318},[52,20041,20042],{},"        \u003Ctitle>{{ config('app.name', 'Laravel') }}\u003C\u002Ftitle>\n",[52,20044,20045],{"class":54,"line":328},[52,20046,341],{"emptyLinePlaceholder":340},[52,20048,20049],{"class":54,"line":337},[52,20050,20051],{},"        \u003C!-- Fonts -->\n",[52,20053,20054],{"class":54,"line":344},[52,20055,20056],{},"        \u003Clink rel=\"stylesheet\" href=\"https:\u002F\u002Ffonts.googleapis.com\u002Fcss2?family=Nunito:wght@400;600;700&display=swap\">\n",[52,20058,20059],{"class":54,"line":354},[52,20060,341],{"emptyLinePlaceholder":340},[52,20062,20063],{"class":54,"line":367},[52,20064,20065],{},"        \u003C!-- Styles -->\n",[52,20067,20068],{"class":54,"line":387},[52,20069,20070],{},"        \u003Clink rel=\"stylesheet\" href=\"{{ asset('css\u002Fapp.css') }}\">\n",[52,20072,20073],{"class":54,"line":415},[52,20074,341],{"emptyLinePlaceholder":340},[52,20076,20077],{"class":54,"line":427},[52,20078,20079],{},"    \u003C\u002Fhead>\n",[52,20081,20082],{"class":54,"line":435},[52,20083,20084],{},"    \u003Cbody>\n",[52,20086,20087],{"class":54,"line":446},[52,20088,20089],{},"        \u003Cdiv class=\"font-sans text-gray-900 antialiased\">\n",[52,20091,20092],{"class":54,"line":480},[52,20093,20094],{},"            \u003Cdiv id=\"app\">\n",[52,20096,20097],{"class":54,"line":509},[52,20098,20099],{},"                \u003Crouter-view\u002F>\n",[52,20101,20102],{"class":54,"line":539},[52,20103,12205],{},[52,20105,20106],{"class":54,"line":547},[52,20107,12210],{},[52,20109,20110],{"class":54,"line":553},[52,20111,20112],{},"    \u003C\u002Fbody>\n",[52,20114,20115],{"class":54,"line":559},[52,20116,20117],{},"    \u003C!-- Scripts -->\n",[52,20119,20120],{"class":54,"line":564},[52,20121,20122],{},"    \u003Cscript src=\"{{ asset('js\u002Fadmin\u002Findex.js') }}\" defer>\u003C\u002Fscript>\n",[52,20124,20125],{"class":54,"line":569},[52,20126,20127],{},"\u003C\u002Fhtml>\n",[13,20129,20130,20131,20134,20135,20138],{},"テンプレートは上記の様にして",[49,20132,20133],{},"\u003Cdiv id=\"app\">\u003Crouter-view\u002F>\u003C\u002Fdiv>","を設置して、vueのエントリーポイントと、vueのビルドファイルを読み込む様にします。実際はヘッダーなどを分けたほうがいいですが、今はとりあえずこんな感じでOKです。\n次に",[49,20136,20137],{},"'js\u002Fadmin\u002Findex.js","で読み込ませるjsファイルを作成していきます。",[1518,20140,20141],{"id":20141},"必要なライブラリをインストール",[13,20143,20144,20145,20148,20149,4209,20151,20154,20155,6451,20157,4209,20160,20163],{},"一昔前の",[49,20146,20147],{},"laravel\u002Fui","の時は",[49,20150,204],{},[49,20152,20153],{},"bootstrap","の設定がインストール時に自動設定されますが、今回の",[49,20156,19943],{},[49,20158,20159],{},"alpinejs",[49,20161,20162],{},"tailwindcss","が入っているので、vue一式を自分でインストールする必要があります。まずはVue・Vuex・Vue-Routerをインストールしましょう。",[5995,20165,20168],{"className":20166},[16418,20167],"alert-danger","\nこの記事を作成した2021年3月現在、Vue3がリリースされておりバージョンを指定しないと、それぞれ最新版がインストールされる可能性があります。この記事はVue2を用いて解説しますのでバージョンを指定してインストールしています。\n",[42,20170,20173],{"className":20171,"code":20172,"language":452},[5819],"npm install vue@2 vue-loader@15 vue-template-compiler@2 --save-dev\nnpm install vuex@3 vue-router@3\n",[49,20174,20172],{"__ignoreMap":47},[1518,20176,20178],{"id":20177},"spa用のjsファイルを作成する","SPA用のJSファイルを作成する",[13,20180,20181,20182,20185],{},"それでは",[49,20183,20184],{},"resources\u002Fjs","配下にSPAのファイルを作成を行います。以下の様にファイルとディレクトリを作成してください。",[42,20187,20190],{"className":20188,"code":20189,"language":452},[5819],"js\n├── admin\n│   ├── index.js\n│   ├── router.js\n│   └── store.js\n└── vue\n    └── page\n        ├── Home.vue\n        └── Profile.vue\n",[49,20191,20189],{"__ignoreMap":47},[13,20193,20194],{},"store.jsとrouter.jsは以下の様にします。",[42,20196,20199],{"className":6471,"code":20197,"filename":20198,"language":1433,"meta":47,"style":47},"export default {\n    state:{},\n    getters: {},\n    mutations: {},\n    actions: {}\n}\n","store.js",[49,20200,20201,20209,20217,20226,20235,20244],{"__ignoreMap":47},[52,20202,20203,20205,20207],{"class":54,"line":55},[52,20204,357],{"class":360},[52,20206,361],{"class":360},[52,20208,6100],{"class":58},[52,20210,20211,20214],{"class":54,"line":83},[52,20212,20213],{"class":62},"    state",[52,20215,20216],{"class":58},":{},\n",[52,20218,20219,20222,20224],{"class":54,"line":115},[52,20220,20221],{"class":62},"    getters",[52,20223,373],{"class":58},[52,20225,7119],{"class":58},[52,20227,20228,20231,20233],{"class":54,"line":142},[52,20229,20230],{"class":62},"    mutations",[52,20232,373],{"class":58},[52,20234,7119],{"class":58},[52,20236,20237,20240,20242],{"class":54,"line":169},[52,20238,20239],{"class":62},"    actions",[52,20241,373],{"class":58},[52,20243,19791],{"class":58},[52,20245,20246],{"class":54,"line":302},[52,20247,536],{"class":58},[13,20249,20250],{},"storeはひとまず必要な型だけ準備します。",[42,20252,20255],{"className":6471,"code":20253,"filename":20254,"language":1433,"meta":47,"style":47},"import Home from '~js\u002Fvue\u002Fpage\u002FHome.vue';\nimport Profile from '~js\u002Fvue\u002Fpage\u002FProfile.vue';\n\nconst routes = [\n    { path: '\u002Fsystem', component: Home },\n    { path: '\u002Fsystem\u002Fprofile', component: Profile },\n]\n\nexport default routes;\n","router.js",[49,20256,20257,20275,20293,20297,20308,20333,20358,20362,20366],{"__ignoreMap":47},[52,20258,20259,20261,20264,20266,20268,20271,20273],{"class":54,"line":55},[52,20260,5939],{"class":360},[52,20262,20263],{"class":105}," Home ",[52,20265,6064],{"class":360},[52,20267,6067],{"class":58},[52,20269,20270],{"class":75},"~js\u002Fvue\u002Fpage\u002FHome.vue",[52,20272,376],{"class":58},[52,20274,1007],{"class":58},[52,20276,20277,20279,20282,20284,20286,20289,20291],{"class":54,"line":83},[52,20278,5939],{"class":360},[52,20280,20281],{"class":105}," Profile ",[52,20283,6064],{"class":360},[52,20285,6067],{"class":58},[52,20287,20288],{"class":75},"~js\u002Fvue\u002Fpage\u002FProfile.vue",[52,20290,376],{"class":58},[52,20292,1007],{"class":58},[52,20294,20295],{"class":54,"line":115},[52,20296,341],{"emptyLinePlaceholder":340},[52,20298,20299,20301,20304,20306],{"class":54,"line":142},[52,20300,6867],{"class":65},[52,20302,20303],{"class":105}," routes ",[52,20305,69],{"class":58},[52,20307,9337],{"class":105},[52,20309,20310,20312,20314,20316,20318,20320,20322,20324,20327,20329,20331],{"class":54,"line":169},[52,20311,14910],{"class":58},[52,20313,14288],{"class":62},[52,20315,373],{"class":58},[52,20317,6067],{"class":58},[52,20319,19969],{"class":75},[52,20321,376],{"class":58},[52,20323,408],{"class":58},[52,20325,20326],{"class":62}," component",[52,20328,373],{"class":58},[52,20330,20263],{"class":105},[52,20332,477],{"class":58},[52,20334,20335,20337,20339,20341,20343,20346,20348,20350,20352,20354,20356],{"class":54,"line":302},[52,20336,14910],{"class":58},[52,20338,14288],{"class":62},[52,20340,373],{"class":58},[52,20342,6067],{"class":58},[52,20344,20345],{"class":75},"\u002Fsystem\u002Fprofile",[52,20347,376],{"class":58},[52,20349,408],{"class":58},[52,20351,20326],{"class":62},[52,20353,373],{"class":58},[52,20355,20281],{"class":105},[52,20357,477],{"class":58},[52,20359,20360],{"class":54,"line":308},[52,20361,15104],{"class":105},[52,20363,20364],{"class":54,"line":318},[52,20365,341],{"emptyLinePlaceholder":340},[52,20367,20368,20370,20372,20375],{"class":54,"line":328},[52,20369,357],{"class":360},[52,20371,361],{"class":360},[52,20373,20374],{"class":105}," routes",[52,20376,1007],{"class":58},[13,20378,20379,20380,20383],{},"router.jsでは指定のパスに対してどのコンポーネントを出すかを定義します。",[49,20381,20382],{},"~js","という記述は後で解説しますので、ひとまず記述してください。",[13,20385,20386],{},"router.jsで参照しているコンポーネントは以下の様にしておきます。",[42,20388,20391],{"className":202,"code":20389,"filename":20390,"language":204,"meta":47,"style":47},"\u003Ctemplate>\n    \u003Cdiv>\n        home\n        \u003Crouter-link to=\"\u002Fsystem\u002Fprofile\">profile\u003C\u002Frouter-link>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\u003Cscript>\nexport default {\n    name:'home'\n}\n\u003C\u002Fscript>\n","Home.vue",[49,20392,20393,20401,20409,20414,20443,20451,20459,20467,20475,20487,20491],{"__ignoreMap":47},[52,20394,20395,20397,20399],{"class":54,"line":55},[52,20396,59],{"class":58},[52,20398,213],{"class":62},[52,20400,80],{"class":58},[52,20402,20403,20405,20407],{"class":54,"line":83},[52,20404,1575],{"class":58},[52,20406,5995],{"class":62},[52,20408,80],{"class":58},[52,20410,20411],{"class":54,"line":115},[52,20412,20413],{"class":105},"        home\n",[52,20415,20416,20418,20421,20424,20426,20428,20430,20432,20434,20437,20439,20441],{"class":54,"line":142},[52,20417,1585],{"class":58},[52,20419,20420],{"class":62},"router-link",[52,20422,20423],{"class":65}," to",[52,20425,69],{"class":58},[52,20427,72],{"class":58},[52,20429,20345],{"class":75},[52,20431,72],{"class":58},[52,20433,102],{"class":58},[52,20435,20436],{"class":105},"profile",[52,20438,108],{"class":58},[52,20440,20420],{"class":62},[52,20442,80],{"class":58},[52,20444,20445,20447,20449],{"class":54,"line":169},[52,20446,1717],{"class":58},[52,20448,5995],{"class":62},[52,20450,80],{"class":58},[52,20452,20453,20455,20457],{"class":54,"line":302},[52,20454,108],{"class":58},[52,20456,213],{"class":62},[52,20458,80],{"class":58},[52,20460,20461,20463,20465],{"class":54,"line":308},[52,20462,59],{"class":58},[52,20464,349],{"class":62},[52,20466,80],{"class":58},[52,20468,20469,20471,20473],{"class":54,"line":318},[52,20470,357],{"class":360},[52,20472,361],{"class":360},[52,20474,6100],{"class":58},[52,20476,20477,20479,20481,20483,20485],{"class":54,"line":328},[52,20478,6105],{"class":62},[52,20480,373],{"class":58},[52,20482,376],{"class":58},[52,20484,18319],{"class":75},[52,20486,10489],{"class":58},[52,20488,20489],{"class":54,"line":337},[52,20490,536],{"class":58},[52,20492,20493,20495,20497],{"class":54,"line":344},[52,20494,108],{"class":58},[52,20496,349],{"class":62},[52,20498,80],{"class":58},[42,20500,20503],{"className":202,"code":20501,"filename":20502,"language":204,"meta":47,"style":47},"\u003Ctemplate>\n    \u003Cdiv>\n        profile\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\u003Cscript>\nexport default {\n    name:'profile'\n}\n\u003C\u002Fscript>\n","Profile.vue",[49,20504,20505,20513,20521,20526,20534,20542,20550,20558,20570,20574],{"__ignoreMap":47},[52,20506,20507,20509,20511],{"class":54,"line":55},[52,20508,59],{"class":58},[52,20510,213],{"class":62},[52,20512,80],{"class":58},[52,20514,20515,20517,20519],{"class":54,"line":83},[52,20516,1575],{"class":58},[52,20518,5995],{"class":62},[52,20520,80],{"class":58},[52,20522,20523],{"class":54,"line":115},[52,20524,20525],{"class":105},"        profile\n",[52,20527,20528,20530,20532],{"class":54,"line":142},[52,20529,1717],{"class":58},[52,20531,5995],{"class":62},[52,20533,80],{"class":58},[52,20535,20536,20538,20540],{"class":54,"line":169},[52,20537,108],{"class":58},[52,20539,213],{"class":62},[52,20541,80],{"class":58},[52,20543,20544,20546,20548],{"class":54,"line":302},[52,20545,59],{"class":58},[52,20547,349],{"class":62},[52,20549,80],{"class":58},[52,20551,20552,20554,20556],{"class":54,"line":308},[52,20553,357],{"class":360},[52,20555,361],{"class":360},[52,20557,6100],{"class":58},[52,20559,20560,20562,20564,20566,20568],{"class":54,"line":318},[52,20561,6105],{"class":62},[52,20563,373],{"class":58},[52,20565,376],{"class":58},[52,20567,20436],{"class":75},[52,20569,10489],{"class":58},[52,20571,20572],{"class":54,"line":328},[52,20573,536],{"class":58},[52,20575,20576,20578,20580],{"class":54,"line":337},[52,20577,108],{"class":58},[52,20579,349],{"class":62},[52,20581,80],{"class":58},[13,20583,20584],{},"index.jsは以下の様に記述します。",[42,20586,20589],{"className":6471,"code":20587,"filename":20588,"language":1433,"meta":47,"style":47},"window.axios = require('axios');\nwindow.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';\n\nimport Vue from 'vue';\n\nimport Vuex from 'vuex';\nimport index from '.\u002Fstore';\nVue.use(Vuex);\nconst store = new Vuex.Store(index);\n\nimport VueRouter from 'vue-router'\nimport routes from '.\u002Frouter';\nVue.use(VueRouter)\nconst router =  new VueRouter({mode: 'history',routes})\n\nconst app = new Vue({\n    store,\n    router,\n}).$mount(\"#app\");\n","index.js",[49,20590,20591,20618,20662,20666,20683,20687,20705,20723,20737,20761,20765,20781,20798,20809,20850,20854,20872,20879,20886],{"__ignoreMap":47},[52,20592,20593,20595,20597,20600,20602,20605,20607,20609,20612,20614,20616],{"class":54,"line":55},[52,20594,1983],{"class":105},[52,20596,957],{"class":58},[52,20598,20599],{"class":105},"axios ",[52,20601,69],{"class":58},[52,20603,20604],{"class":418}," require",[52,20606,932],{"class":105},[52,20608,376],{"class":58},[52,20610,20611],{"class":75},"axios",[52,20613,376],{"class":58},[52,20615,938],{"class":105},[52,20617,1007],{"class":58},[52,20619,20620,20622,20624,20626,20628,20631,20633,20636,20638,20641,20643,20646,20648,20651,20653,20655,20658,20660],{"class":54,"line":83},[52,20621,1983],{"class":105},[52,20623,957],{"class":58},[52,20625,20611],{"class":105},[52,20627,957],{"class":58},[52,20629,20630],{"class":105},"defaults",[52,20632,957],{"class":58},[52,20634,20635],{"class":105},"headers",[52,20637,957],{"class":58},[52,20639,20640],{"class":105},"common[",[52,20642,376],{"class":58},[52,20644,20645],{"class":75},"X-Requested-With",[52,20647,376],{"class":58},[52,20649,20650],{"class":105},"] ",[52,20652,69],{"class":58},[52,20654,6067],{"class":58},[52,20656,20657],{"class":75},"XMLHttpRequest",[52,20659,376],{"class":58},[52,20661,1007],{"class":58},[52,20663,20664],{"class":54,"line":115},[52,20665,341],{"emptyLinePlaceholder":340},[52,20667,20668,20670,20673,20675,20677,20679,20681],{"class":54,"line":142},[52,20669,5939],{"class":360},[52,20671,20672],{"class":105}," Vue ",[52,20674,6064],{"class":360},[52,20676,6067],{"class":58},[52,20678,204],{"class":75},[52,20680,376],{"class":58},[52,20682,1007],{"class":58},[52,20684,20685],{"class":54,"line":169},[52,20686,341],{"emptyLinePlaceholder":340},[52,20688,20689,20691,20694,20696,20698,20701,20703],{"class":54,"line":302},[52,20690,5939],{"class":360},[52,20692,20693],{"class":105}," Vuex ",[52,20695,6064],{"class":360},[52,20697,6067],{"class":58},[52,20699,20700],{"class":75},"vuex",[52,20702,376],{"class":58},[52,20704,1007],{"class":58},[52,20706,20707,20709,20712,20714,20716,20719,20721],{"class":54,"line":308},[52,20708,5939],{"class":360},[52,20710,20711],{"class":105}," index ",[52,20713,6064],{"class":360},[52,20715,6067],{"class":58},[52,20717,20718],{"class":75},".\u002Fstore",[52,20720,376],{"class":58},[52,20722,1007],{"class":58},[52,20724,20725,20727,20729,20732,20735],{"class":54,"line":318},[52,20726,6904],{"class":105},[52,20728,957],{"class":58},[52,20730,20731],{"class":418},"use",[52,20733,20734],{"class":105},"(Vuex)",[52,20736,1007],{"class":58},[52,20738,20739,20741,20744,20746,20748,20751,20753,20756,20759],{"class":54,"line":328},[52,20740,6867],{"class":65},[52,20742,20743],{"class":105}," store ",[52,20745,69],{"class":58},[52,20747,1936],{"class":58},[52,20749,20750],{"class":105}," Vuex",[52,20752,957],{"class":58},[52,20754,20755],{"class":418},"Store",[52,20757,20758],{"class":105},"(index)",[52,20760,1007],{"class":58},[52,20762,20763],{"class":54,"line":337},[52,20764,341],{"emptyLinePlaceholder":340},[52,20766,20767,20769,20772,20774,20776,20779],{"class":54,"line":344},[52,20768,5939],{"class":360},[52,20770,20771],{"class":105}," VueRouter ",[52,20773,6064],{"class":360},[52,20775,6067],{"class":58},[52,20777,20778],{"class":75},"vue-router",[52,20780,10489],{"class":58},[52,20782,20783,20785,20787,20789,20791,20794,20796],{"class":54,"line":354},[52,20784,5939],{"class":360},[52,20786,20303],{"class":105},[52,20788,6064],{"class":360},[52,20790,6067],{"class":58},[52,20792,20793],{"class":75},".\u002Frouter",[52,20795,376],{"class":58},[52,20797,1007],{"class":58},[52,20799,20800,20802,20804,20806],{"class":54,"line":367},[52,20801,6904],{"class":105},[52,20803,957],{"class":58},[52,20805,20731],{"class":418},[52,20807,20808],{"class":105},"(VueRouter)\n",[52,20810,20811,20813,20816,20818,20821,20824,20826,20829,20832,20834,20836,20839,20841,20843,20846,20848],{"class":54,"line":387},[52,20812,6867],{"class":65},[52,20814,20815],{"class":105}," router ",[52,20817,69],{"class":58},[52,20819,20820],{"class":58},"  new",[52,20822,20823],{"class":418}," VueRouter",[52,20825,932],{"class":105},[52,20827,20828],{"class":58},"{",[52,20830,20831],{"class":62},"mode",[52,20833,373],{"class":58},[52,20835,6067],{"class":58},[52,20837,20838],{"class":75},"history",[52,20840,376],{"class":58},[52,20842,408],{"class":58},[52,20844,20845],{"class":105},"routes",[52,20847,1311],{"class":58},[52,20849,1015],{"class":105},[52,20851,20852],{"class":54,"line":415},[52,20853,341],{"emptyLinePlaceholder":340},[52,20855,20856,20858,20861,20863,20865,20868,20870],{"class":54,"line":427},[52,20857,6867],{"class":65},[52,20859,20860],{"class":105}," app ",[52,20862,69],{"class":58},[52,20864,1936],{"class":58},[52,20866,20867],{"class":418}," Vue",[52,20869,932],{"class":105},[52,20871,364],{"class":58},[52,20873,20874,20877],{"class":54,"line":435},[52,20875,20876],{"class":105},"    store",[52,20878,384],{"class":58},[52,20880,20881,20884],{"class":54,"line":446},[52,20882,20883],{"class":105},"    router",[52,20885,384],{"class":58},[52,20887,20888,20890,20892,20894,20897,20899,20901,20904,20906,20908],{"class":54,"line":480},[52,20889,1311],{"class":58},[52,20891,938],{"class":105},[52,20893,957],{"class":58},[52,20895,20896],{"class":418},"$mount",[52,20898,932],{"class":105},[52,20900,72],{"class":58},[52,20902,20903],{"class":75},"#app",[52,20905,72],{"class":58},[52,20907,938],{"class":105},[52,20909,1007],{"class":58},[13,20911,20912,20913,20916],{},"axiosはLaravelインストール時に入っているのでそのまま利用しています。このファイルではVuexとVue-routerの連携を行い、最終的に",[49,20914,20915],{},"\u003Cdiv id=\"app\">\u003C\u002Fdiv>","にレンダリングされる様にします。",[13,20918,20919,20920,20923],{},"上記のファイルを作成した後、",[49,20921,20922],{},"webpac.mix.js","を修正します。",[1518,20925,20926],{"id":20926},"mixファイルの作成",[13,20928,20929,20930,20933,20934,20937,20938,20941,20942,20945],{},"このようなVueのプロジェクトwebpackを使用しますがLaravelは簡単な記述で",[49,20931,20932],{},"resources","配下を簡単に",[49,20935,20936],{},"public\u002F","配下に出力してくれます。またvueやsassのコンパイル設定を",[49,20939,20940],{},"webpack.config.js","の様に書く必要がありません。どのファイルをコンパイル対象にするかなどは",[49,20943,20944],{},"webpack.mix.js","に記載します。以下の様に修正します。",[42,20947,20949],{"className":6471,"code":20948,"filename":20940,"language":1433,"meta":47,"style":47},"const mix = require('laravel-mix');\nconst path = require('path');\n\nmix.webpackConfig({\n    resolve: {\n      alias: {\n        '~js': path.resolve('.\u002Fresources\u002Fjs'),\n      }\n    }\n});\nmix.js('resources\u002Fjs\u002Fadmin\u002Findex.js', 'public\u002Fjs\u002Fadmin').vue();\n",[49,20950,20951,20975,20998,21002,21016,21025,21034,21065,21069,21073,21081],{"__ignoreMap":47},[52,20952,20953,20955,20958,20960,20962,20964,20966,20969,20971,20973],{"class":54,"line":55},[52,20954,6867],{"class":65},[52,20956,20957],{"class":105}," mix ",[52,20959,69],{"class":58},[52,20961,20604],{"class":418},[52,20963,932],{"class":105},[52,20965,376],{"class":58},[52,20967,20968],{"class":75},"laravel-mix",[52,20970,376],{"class":58},[52,20972,938],{"class":105},[52,20974,1007],{"class":58},[52,20976,20977,20979,20981,20983,20985,20987,20989,20992,20994,20996],{"class":54,"line":83},[52,20978,6867],{"class":65},[52,20980,14215],{"class":105},[52,20982,69],{"class":58},[52,20984,20604],{"class":418},[52,20986,932],{"class":105},[52,20988,376],{"class":58},[52,20990,20991],{"class":75},"path",[52,20993,376],{"class":58},[52,20995,938],{"class":105},[52,20997,1007],{"class":58},[52,20999,21000],{"class":54,"line":115},[52,21001,341],{"emptyLinePlaceholder":340},[52,21003,21004,21007,21009,21012,21014],{"class":54,"line":142},[52,21005,21006],{"class":105},"mix",[52,21008,957],{"class":58},[52,21010,21011],{"class":418},"webpackConfig",[52,21013,932],{"class":105},[52,21015,364],{"class":58},[52,21017,21018,21021,21023],{"class":54,"line":169},[52,21019,21020],{"class":62},"    resolve",[52,21022,373],{"class":58},[52,21024,6100],{"class":58},[52,21026,21027,21030,21032],{"class":54,"line":302},[52,21028,21029],{"class":62},"      alias",[52,21031,373],{"class":58},[52,21033,6100],{"class":58},[52,21035,21036,21039,21041,21043,21045,21047,21049,21052,21054,21056,21059,21061,21063],{"class":54,"line":308},[52,21037,21038],{"class":58},"        '",[52,21040,20382],{"class":62},[52,21042,376],{"class":58},[52,21044,373],{"class":58},[52,21046,14288],{"class":105},[52,21048,957],{"class":58},[52,21050,21051],{"class":418},"resolve",[52,21053,932],{"class":105},[52,21055,376],{"class":58},[52,21057,21058],{"class":75},".\u002Fresources\u002Fjs",[52,21060,376],{"class":58},[52,21062,938],{"class":105},[52,21064,384],{"class":58},[52,21066,21067],{"class":54,"line":318},[52,21068,3665],{"class":58},[52,21070,21071],{"class":54,"line":328},[52,21072,2110],{"class":58},[52,21074,21075,21077,21079],{"class":54,"line":337},[52,21076,1311],{"class":58},[52,21078,938],{"class":105},[52,21080,1007],{"class":58},[52,21082,21083,21085,21087,21089,21091,21093,21096,21098,21100,21102,21105,21107,21109,21111,21113,21115],{"class":54,"line":344},[52,21084,21006],{"class":105},[52,21086,957],{"class":58},[52,21088,1433],{"class":418},[52,21090,932],{"class":105},[52,21092,376],{"class":58},[52,21094,21095],{"class":75},"resources\u002Fjs\u002Fadmin\u002Findex.js",[52,21097,376],{"class":58},[52,21099,408],{"class":58},[52,21101,6067],{"class":58},[52,21103,21104],{"class":75},"public\u002Fjs\u002Fadmin",[52,21106,376],{"class":58},[52,21108,938],{"class":105},[52,21110,957],{"class":58},[52,21112,204],{"class":418},[52,21114,422],{"class":105},[52,21116,1007],{"class":58},[13,21118,21119,21122,21123,21125],{},[49,21120,21121],{},"mix.webpackConfig","はのように",[49,21124,20940],{},"に書く様な他の細かいwebpackの設定を定義でき、ここではエイリアスを定義しています。",[13,21127,21128,21131,21132,21134,21135,21137],{},[49,21129,21130],{},"'~js': path.resolve('.\u002Fresources\u002Fjs')","という記述は",[49,21133,20382],{},"というパスの記載があった場合は「",[49,21136,20184],{},"までの絶対ルート」であるとwebpackに支持しています。この様にエイリアスを定義することで相対パスによるファイルの参照がなくなります。router.jsで以下の様に定義していましたが、",[42,21139,21141],{"className":6471,"code":21140,"filename":20254,"language":1433,"meta":47,"style":47},"import Home from '~js\u002Fvue\u002Fpage\u002FHome.vue';\nimport Profile from '~js\u002Fvue\u002Fpage\u002FProfile.vue';\n",[49,21142,21143,21159],{"__ignoreMap":47},[52,21144,21145,21147,21149,21151,21153,21155,21157],{"class":54,"line":55},[52,21146,5939],{"class":360},[52,21148,20263],{"class":105},[52,21150,6064],{"class":360},[52,21152,6067],{"class":58},[52,21154,20270],{"class":75},[52,21156,376],{"class":58},[52,21158,1007],{"class":58},[52,21160,21161,21163,21165,21167,21169,21171,21173],{"class":54,"line":83},[52,21162,5939],{"class":360},[52,21164,20281],{"class":105},[52,21166,6064],{"class":360},[52,21168,6067],{"class":58},[52,21170,20288],{"class":75},[52,21172,376],{"class":58},[52,21174,1007],{"class":58},[13,21176,21177],{},"もしエイリアスが使えない場合は",[42,21179,21181],{"className":6471,"code":21180,"filename":20254,"language":1433,"meta":47,"style":47},"import Home from '..\u002Fvue\u002Fpage\u002FHome.vue';\nimport Profile from '..\u002Fvue\u002Fpage\u002FProfile.vue';\n",[49,21182,21183,21200],{"__ignoreMap":47},[52,21184,21185,21187,21189,21191,21193,21196,21198],{"class":54,"line":55},[52,21186,5939],{"class":360},[52,21188,20263],{"class":105},[52,21190,6064],{"class":360},[52,21192,6067],{"class":58},[52,21194,21195],{"class":75},"..\u002Fvue\u002Fpage\u002FHome.vue",[52,21197,376],{"class":58},[52,21199,1007],{"class":58},[52,21201,21202,21204,21206,21208,21210,21213,21215],{"class":54,"line":83},[52,21203,5939],{"class":360},[52,21205,20281],{"class":105},[52,21207,6064],{"class":360},[52,21209,6067],{"class":58},[52,21211,21212],{"class":75},"..\u002Fvue\u002Fpage\u002FProfile.vue",[52,21214,376],{"class":58},[52,21216,1007],{"class":58},[13,21218,21219,21220,21222],{},"このように相対パスになってしまい、もし構成を変えようとした時に相対関係の修正が必要となります。その対策としてエイリアスを定めておくと今後の管理がしやすいです。",[49,21221,21130],{},"はjsディレクトリを参照する様にしています。",[13,21224,21225,21228,21229,21231,21232,21235],{},[49,21226,21227],{},"mix.js('resources\u002Fjs\u002Fadmin\u002Findex.js', 'public\u002Fjs\u002Fadmin').vue();"," にて",[49,21230,20588],{},"をソースとして",[49,21233,21234],{},"'public\u002Fjs\u002Fadmin'","配下に出力し、vueコンパイルを行う様に定義してます。",[13,21237,21238,21239,21242,21243,21246],{},"問題なければ",[49,21240,21241],{},"npm run prod","をプロジェクト ルートでコマンドを叩いてコンパイルを行います。完了後に",[49,21244,21245],{},"public\u002Fjs\u002Fadmin\u002Findex.js","というものが出力されていることを確認してください。",[1518,21248,21249],{"id":21249},"反映の確認",[13,21251,21252,21253,21256,21257,21259,21260,21262],{},"jsファイルが出力されましたら",[49,21254,21255],{},"http:\u002F\u002Flocalhost:8005\u002Fsystem","にアクセスします。以下の様に表示され、",[49,21258,20436],{},"というリンクをクリックした際にURLが変わり、",[49,21261,20502],{},"に書かれた内容が表示されればSPAはできています。",[729,21264],{":src":21265,":center":1322},"'vue_laravel_app\u002Fhomevue.png'",[13,21267,21268,21270,21271,21274],{},[49,21269,20436],{},"というリンクをクリック後 or ",[49,21272,21273],{},"http:\u002F\u002Flocalhost:8005\u002Fsystem\u002Fprofile","にアクセスしますと以下の様になります。",[729,21276],{":src":21277,":center":1322},"'vue_laravel_app\u002Fprofilevue.png'",[13,21279,21280],{},"これでSPAが設定できたことを確認できました。",[17,21282,21284],{"id":21283},"次回は","次回は..",[13,21286,21287],{},"今回は環境構築とLaravel、Vue SPAのセットアップまでとなります。来週はweb apiを通じたSPAでの認証と簡単なプロフィールの更新機能を作成してみます。",[1413,21289,21290],{},"html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}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 pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}",{"title":47,"searchDepth":115,"depth":115,"links":21292},[21293,21301,21306,21307,21314],{"id":19089,"depth":83,"text":19089,"children":21294},[21295,21300],{"id":19095,"depth":115,"text":19095,"children":21296},[21297,21298,21299],{"id":19101,"depth":142,"text":19101},{"id":19135,"depth":142,"text":19135},{"id":19175,"depth":142,"text":19175},{"id":19189,"depth":115,"text":19189},{"id":19201,"depth":83,"text":19201,"children":21302},[21303,21304,21305],{"id":19314,"depth":115,"text":19315},{"id":19377,"depth":115,"text":19377},{"id":19465,"depth":115,"text":19466},{"id":19936,"depth":83,"text":19937},{"id":19956,"depth":83,"text":19957,"children":21308},[21309,21310,21311,21312,21313],{"id":19963,"depth":115,"text":19964},{"id":20141,"depth":115,"text":20141},{"id":20177,"depth":115,"text":20178},{"id":20926,"depth":115,"text":20926},{"id":21249,"depth":115,"text":21249},{"id":21283,"depth":83,"text":21284},[1423],"2022-03-02","アプリの解説とシリーズの概要",{},"\u002Fseries\u002Fvue-laravel-app-1",{"title":19027,"description":21317},"vue_laravel_app","Vue SPA x Laravelでつくる実務パチモンアプリ","series\u002Fvue-laravel-app-1",[17983,16381,1433,204],"vue_laravel_app\u002Fseries.png","QS_7_9yqTbYmh1Gt9XAVtEnkXBXd1wzlct80a3xdm_U",1780987133065]