[{"data":1,"prerenderedAt":10071},["ShallowReactive",2],{"en-category-devstack-1":3},{"count":4,"content":5},4,[6,2809,3536,5986],{"id":7,"title":8,"body":9,"category":2795,"createdAt":2797,"description":8,"extension":2798,"index":2799,"meta":2800,"navigation":692,"path":2801,"publish":692,"seo":2802,"series":2799,"seriesTitle":2799,"stem":2803,"tag":2804,"thumbnail":2807,"updatedAt":2799,"__hash__":2808},"en_articles\u002Fen\u002Farticles\u002Fzoom-api-laravel.md","Zoom APIとLaravelを使って自動ミーティング作成フォームを構築する。",{"type":10,"value":11,"toc":2749},"minimark",[12,24,27,35,38,43,46,49,55,59,62,67,70,73,76,82,86,89,92,95,98,104,107,110,115,118,134,137,141,144,147,150,153,156,159,162,165,168,171,174,177,185,194,227,231,235,244,247,256,259,262,267,270,273,276,285,288,305,314,333,344,347,350,353,357,360,363,366,369,372,375,379,382,386,404,414,419,425,441,445,448,565,568,582,589,592,598,601,609,615,619,622,632,703,783,798,801,814,844,851,865,872,903,906,912,915,918,922,925,928,937,1118,1130,1133,1191,1202,1208,1218,1221,1255,1274,1277,1283,1290,1404,1407,1410,1413,1416,1419,1494,1509,1522,1528,1531,1534,1537,1540,1662,1665,1668,1672,1675,1678,1682,1910,1916,1922,1932,1935,1937,1941,1944,2317,2320,2323,2326,2387,2404,2408,2465,2474,2477,2480,2491,2494,2498,2655,2658,2661,2664,2667,2670,2673,2676,2679,2682,2685,2688,2691,2694,2697,2700,2703,2706,2721,2724,2727,2730,2738,2742,2745],[13,14,15,16,23],"p",{},"Hello everyone Corona started to get serious early in the new year, and the days when I couldn't go out continued. However, engineers can make various things with the program even at home, which is a good way to kill time. So I have heard that zoom that is favorite now has its ",[17,18,22],"a",{"href":19,"rel":20},"https:\u002F\u002Fmarketplace.zoom.us\u002Fdocs\u002Fapi-reference\u002Fintroduction",[21],"nofollow","API"," and have tested it.",[13,25,26],{},"Zoom API itself may has been since around 2018. The version2 has been released at present.¥",[13,28,29,30,34],{},"zoom API自体は2018年ごろから出ていたらしく、現在はv2がリリースされています。",[17,31,33],{"href":19,"rel":32},[21],"ドキュメント","を一通り読んだ所、zoomで行えることは一通りAPIを通じて行えるそうです。zoom APIについてわかった箇所を詳細に説明したいですが、この記事では実際の活用例を説明したいので部分省略します。",[13,36,37],{},"しかし、zoom APIを使用する認証フローや仕組みについては簡単に解説します。その説明から始めますので、「webアプリのはよ動き見せろや！」「そんなの知っとんじゃ！」という方は「Laravelを立ち上げる」から読み始めてください。",[39,40,42],"h2",{"id":41},"zoom-apiの概要","Zoom APIの概要",[13,44,45],{},"詳しくはZoom API レファランスを見ればわかりますが、zoomのGUIでできることは基本的に可能です。ミーティングを作ったり、ウェビナーを開催したり、ユーザー情報を取ったり、開催中のzoomに対してチャットを送るなどなんでもできます。他にもwebhookや独自のコードでブラウザ上に映像・音声を出力できるSDKやエンドポイントもあるみたいです。",[13,47,48],{},"これらのAPIはRestAPIであり、特定のルートに対してGET\u002FPOST\u002FPUT\u002FDELTEでリクエストし、付属の情報はGETパラメータやPOSTパラメータで送信します。",[13,50,51,52],{},"参考：",[17,53,19],{"href":19,"rel":54},[21],[56,57,58],"h3",{"id":58},"認証方法",[13,60,61],{},"zoom APIにアクセスするには、JWTとOAuth2.0が使用できます。二者の違いはアクセスできる機能の量とマーケットプレイスに公開できるかが主になります。",[63,64,66],"h4",{"id":65},"jwt","JWT",[13,68,69],{},"ます。JSONで認証情報をやりとりします。",[13,71,72],{},"JWTによる認証はOAuthで使用できる権限範囲より狭く、自分自身で簡単にライトに使用したい場合に使うらしいです。また開発したzoom アプリはマーケットプレイスを通じて公開することができますが、JWT認証の場合はその公開ができません。",[13,74,75],{},"独自のwebアプリとZoomを連携させたい場合は次のOAuth2.0認証をお勧めします。",[13,77,51,78],{},[17,79,80],{"href":80,"rel":81},"https:\u002F\u002Fmarketplace.zoom.us\u002Fdocs\u002Fapi-reference\u002Fusing-zoom-apis#using-jwt",[21],[63,83,85],{"id":84},"oauth-20","OAuth 2.0",[13,87,88],{},"OAuthは自身のwebサービスの資格情報を第三者のサービスへ提供する際に使用される認証フローです。ここでいう「webサービス」は「zoom」で「第三者のサービス」は「私のLaravelアプリ」です。",[13,90,91],{},"つまりOAuth認証を用いることで「私のLaravelアプリ」は連携させた人の「zoom」の資格情報を利用できる様になります。よって「私のLaravelアプリ」は連携させた人のzoomのミーティング情報を読み取ったり、作成したり、ユーザー情報を取得することができます。",[13,93,94],{},"資格情報を与え、操作権限も付与するのでかなり厳重な認証システムが必要となりそこで、秘密鍵や特定のプロトコルなどを用いたOAuthが使用されます。上記のJWTよりセキュアであり、また様々なAPIを利用できる様になります。",[13,96,97],{},"OAuthで実装したzoom APIはマーケットプレイスに出して公開することができます。今回の説明ではこのOAuth認証をLaravelを用いて実装していきます。まだ認証・連携のイメージがつかないと思いますが、読んでいくうちにわかると思います（多分）。",[13,99,51,100],{},[17,101,102],{"href":102,"rel":103},"https:\u002F\u002Fmarketplace.zoom.us\u002Fdocs\u002Fapi-reference\u002Fusing-zoom-apis#using-oauth",[21],[56,105,106],{"id":106},"連携の流れ",[13,108,109],{},"OAuthでの認証は以下の様に行われます。",[111,112],"image-render",{":src":113,":width":114},"'_mix\u002F1570826762485.png'","'100%'",[13,116,117],{},"もう少し具体的に解説すると",[119,120,121,125,128,131],"ol",{},[122,123,124],"li",{},"ユーザー（zoomにログイン済み）を認証画面へリダイレクトさせる",[122,126,127],{},"確認後、ユーザーが承認したという証であるcodeを取得。",[122,129,130],{},"OAuthプロトコルに従い作成した認証キーとリクエストをzoomに送る",[122,132,133],{},"アクセストークンを得る。そのアクセストークン でzoom APIにアクセスする。アクセストークン などはアプリのDBに保存しておく。",[13,135,136],{},"こちらも後で実際の画面のスクショ付きで解説しますので、今は「ヘェ〜」程度の理解で大丈夫です。",[56,138,140],{"id":139},"apiへのアクセス方法","APIへのアクセス方法",[13,142,143],{},"OAuthでアクセストークン を取得したら、そのトークンをリクエストヘッダーに仕込んでAPIにアクセスします。",[39,145,146],{"id":146},"今回作るアプリ",[13,148,149],{},"まずアプリの機能と概要を説明しておきます。今回作るアプリは「匿名ユーザーが入力したフォームの内容に応じてzoomミーティングが作成され、それを管理できるwebアプリ」です。見た目は以下の感じです。",[111,151],{":src":152,":width":114},"'_mix\u002Fsch-2021-01-10-20.51.27-768x445.png'",[13,154,155],{},"アプリを管理し連携させるzoomアカウントを持っている「管理者」と、フォームに入力する「匿名ユーザー（お客さん）」が存在するとします。",[56,157,158],{"id":158},"機能の概要",[13,160,161],{},"場面としては「zoomでのご相談はこちら」的な会社用のフォームであると思ってください。最初にお客さんはフォームにて名前・アドレス、zoomの希望開始時間を入力します。",[13,163,164],{},"フォーム入力内容が正しければ、zoom APIを通じて指定の時間で始まるzoom ミーティングを連携先のアカウントで作成。",[13,166,167],{},"APIからのレスポンスよりミーティングURLを取得し、DBに保存すると共にお客さんへミーティング情報をメールで飛ばす。（メールはローカルの環境でできなかったので、実装は割愛。ソースはあります。）",[13,169,170],{},"管理者は管理画面にて誰が・いつミーティングを開くのかを一覧で確認できる。またお客さんは時間変更用・削除用URLにアクセスして時間の変更・ミーティングのキャンセルが可能。",[13,172,173],{},"以上の様な機能を持たせたいと思います。とりあえずこれらの機能を実装する程度なので、厳密なバリデーションや細かい機能は割愛します。",[39,175,176],{"id":176},"開発環境",[13,178,179,180,184],{},"私が慣れているLaravel 6を用いて作成します。Laravelのインストール方法は省略します。一応",[17,181,183],{"href":182},"\u002Farticles\u002Fbuild-lamp-with-docker","こちら","で作ったDocker開発環境を用いています。またzoomへのHTTPアクセスをよく行うので、PHPのHTTPライブラリであるguzzlehttpをインストールしてください。",[13,186,187,188,193],{},"また、以下詳細な開発環境情報です。",[17,189,192],{"href":190,"rel":191},"https:\u002F\u002Fgithub.com\u002FjunjiIshii\u002Flaravel_zoom",[21],"開発したリポジトリも開放してます","のでぜひどうぞ。",[195,196,197,200,203,206,209,212,215,218,221,224],"ul",{},[122,198,199],{},"MacOS Catalina 10.15.5",[122,201,202],{},"Docker 20.10.0",[122,204,205],{},"Docker-compose 1.27.4",[122,207,208],{},"（コンテナ内）composer 1.10.19",[122,210,211],{},"（コンテナ内）Laravel 6.20",[122,213,214],{},"（コンテナ内）guzzlehttp 7.2",[122,216,217],{},"（コンテナ内）centod 8",[122,219,220],{},"（コンテナ内）httpd 2.4.37",[122,222,223],{},"（コンテナ内）php 7.4.7",[122,225,226],{},"（コンテナ内・公式イメージ）mysql:5.7",[39,228,230],{"id":229},"zoom-apiキーを手に入れる","Zoom APIキーを手に入れる",[56,232,234],{"id":233},"zoom-アプリの作成","zoom アプリの作成",[13,236,237,238,243],{},"それでは初めていきましょう。Laravelを実装する前に自身のZoomアカウントにて、zoomアプリを作成していきましょう。",[17,239,242],{"href":240,"rel":241},"https:\u002F\u002Fmarketplace.zoom.us\u002F",[21],"zoom マーケットプレイス","へ移動します。そして画面上部の「Develop」をクリックし「Build App」をクリックします。",[111,245],{":src":246,":width":114},"'_mix\u002Fzoom_marget-768x330.jpeg'",[13,248,249,250,255],{},"すると",[17,251,254],{"href":252,"rel":253},"https:\u002F\u002Fmarketplace.zoom.us\u002Fdevelop\u002Fcreate",[21],"zoomアプリを選択する以下の様な画面","が表示されますので、「OAuth」の「Create」をクリック",[111,257],{":src":258,":width":114},"'_mix\u002Fzoom_app_create-768x385.jpeg'",[13,260,261],{},"「Create」を押すとアプリの名前などを入力するモーダルが出現します。任意の名前を入力し、アプリのタイプ、マーケットプレイスに公表するかを決定します。とりあえず私は以下の様にしました。",[111,263],{":src":264,":width":265,":center":266},"'_mix\u002Fzoom_mordal-768x637.jpeg'","'500px'","true",[13,268,269],{},"名前とマーケットプレイスへの公開は後から変更できます。ひとまず入力したら「Create」を押します。",[56,271,272],{"id":272},"諸所の設定を入力",[63,274,275],{"id":275},"アプリの認証情報",[13,277,278,279,284],{},"作成後には",[17,280,283],{"href":281,"rel":282},"https:\u002F\u002Fmarketplace.zoom.us\u002Fuser\u002Fbuild",[21],"アプリの管理画面","に飛ばされると思います。そこから作成したアプリを選択して「App Credentials」を選択",[111,286],{":src":287,":width":114},"'_mix\u002Fzoom_app_info-768x637.jpeg'",[13,289,290,291,295,296,300,301,304],{},"Larvel側には ",[292,293,294],"strong",{},"「Client ID」「Client Secret」"," が必要となります。後で",[297,298,299],"code",{},".env","ファイルに記載します。ちなみに ",[292,302,303],{},"「Client Secret」"," は絶対に外に漏れてはいけません。",[13,306,307,310,311,313],{},[292,308,309],{},"「Redirect URL for OAuth」"," にて承認画面からのリダイレクト先を指定しておきます。承認画面でアプリ連携を許可した際にはトークンなどが ",[292,312,309],{}," あてへ送信され、ユーザーもリダイレクトされます。ここの値が異なっているとエラーで認証が進みません。",[13,315,316,317,320,321,324,325,328,329,332],{},"今は開発環境なのでドメインに",[297,318,319],{},"localhost","を指定しています。（私の環境では",[297,322,323],{},"localhost:9000","でLaravelの画面が表示される様に設定しています。",[297,326,327],{},"php artisan serve","などの場合は",[297,330,331],{},"localhost:8000","になると思いますので、ポートの指定に気をつけてください。）",[13,334,335,336,339,340,343],{},"「whitelist URL」はOAuthのリダイレクト先として許可するURLを指定できます。OAuthリダイレクトのURLに完全一致させるか、前方一致させる必要があります。設定したリダイレクト先は",[297,337,338],{},"http:\u002F\u002Flocalhost:9000\u002Fzoomauth\u002Fcheck","としていたので、その前方を含める様に",[297,341,342],{},"http:\u002F\u002Flocalhost:9000\u002F","に設定しておきます。",[63,345,346],{"id":346},"アプリの公開情報",[111,348],{":src":349,":width":114},"'_mix\u002Fzoom_information-572x1024.jpeg'",[13,351,352],{},"「Information」にて「Optional」と書かれている箇所以外を入力し記述します。",[63,354,356],{"id":355},"アプリのスコープアクセス範囲","アプリのスコープ（アクセス範囲）",[13,358,359],{},"ここが結構重要です。「Scopes」という箇所ではアプリの操作権限、アクセス範囲を設定できます。ユーザー情報の取得やミーティングの作成にもそれぞれスコープが用意されて、スコープ外の操作へのアクセスは401と認証エラーとなります。「Add Scopes」でスコープを追加します。",[13,361,362],{},"よくわからなければ全部追加してもいいですが、「Meeting」「User」のスコープを全て追加しておけば今回のアプリの実装は可能です。",[111,364],{":src":365,":width":114},"'_mix\u002Fzoom_scopes-768x431.jpeg'",[63,367,368],{"id":368},"アプリのアクティベート",[13,370,371],{},"最後に「Activation」にて確認します。不足箇所は以下の様にオレンジ文字で指摘されるので直しましょう。全てが入力できていれば後は問題ありません。「Install」などは押さなくても問題ありません。",[111,373],{":src":374,":width":114},"'_mix\u002Fzoom_activation-768x412.jpeg'",[39,376,378],{"id":377},"lravelを立ち上げるdocker","Lravelを立ち上げる(Docker）",[13,380,381],{},"それではLaravelを立ち上げましょう。インストールはされており、ユーザーテーブルのマイグレーションを行う前だと仮定します。",[56,383,385],{"id":384},"clien-id-と-client-secretを設定","Clien ID と Client Secretを設定",[13,387,388,389,391,392,395,396,399,400,403],{},"Larvelのプロジェクトルートに",[297,390,299],{},"という環境変数を記述するファイルがありますので、そちらに ",[292,393,394],{},"「App Credentials」"," で取得できる",[297,397,398],{},"Client ID","と",[297,401,402],{},"Client Secret","を設定します。",[405,406,412],"pre",{"className":407,"code":409,"filename":299,"language":410,"meta":411},[408],"language-text","APP_NAME=Laravel\nAPP_ENV=local\n...\nMIX_PUSHER_APP_CLUSTER=\"${PUSHER_APP_CLUSTER}\"\n\nZOOM_CLIENT_ID=clientid\nZOOM_CLIENT_SECRET=clientsecret\n","text","",[297,413,409],{"__ignoreMap":411},[13,415,416,418],{},[297,417,299],{},"を更新したらキャッシュをクリアして反映させます。",[405,420,423],{"className":421,"code":422,"language":410},[408],"php artisan config:clear\nphp artisan cache:clear\n",[297,424,422],{"__ignoreMap":411},[13,426,427,429,430,433,434,436,437,440],{},[297,428,299],{},"に記述することで他のソースコード内で",[297,431,432],{},"env('ZOOM_CLIENT_ID')","と行った形で出力できます。また",[297,435,299],{},"は基本的に",[297,438,439],{},".gitignore","に登録されているので公開リポジトリに秘密鍵が載ってしまうという様な事故を防げます。",[56,442,444],{"id":443},"user-tableをちょっと改造","User tableをちょっと改造",[13,446,447],{},"今回のアプリの管理者は一人ですが、もし複数人に使用してもらいたい時に「ユーザーごとにトークンを分けたいな」と思ったのでその改造をします。初期で用意されているユーザーテーブルを以下の様に書き換えます。",[405,449,454],{"className":450,"code":451,"filename":452,"language":453,"meta":411,"style":411},"language-php shiki shiki-themes material-theme-ocean","public function up()\n{\n    Schema::create('users', function (Blueprint $table) {\n        $table->bigIncrements('id');\n        $table->string('name');\n        $table->string('email')->unique();\n        $table->timestamp('email_verified_at')->nullable();\n        $table->string('password');\n    \n        \u002F\u002Fここから追加\n        $table->longText('zoom_code')->nullable()->default(null);\n        $table->longText('access_token')->nullable()->default(null);\n        $table->longText('refresh_token')->nullable()->default(null);\n        $table->timestamp('zoom_expires_in', 0)->nullable()->default(null);\n        $table->rememberToken();\n        $table->timestamps();\n    });\n}\n","database\u002Fmigrations\u002F2014_10_12_000000_create_users_table.php","php",[297,455,456,464,470,476,481,487,493,499,505,511,517,523,529,535,541,547,553,559],{"__ignoreMap":411},[457,458,461],"span",{"class":459,"line":460},"line",1,[457,462,463],{},"public function up()\n",[457,465,467],{"class":459,"line":466},2,[457,468,469],{},"{\n",[457,471,473],{"class":459,"line":472},3,[457,474,475],{},"    Schema::create('users', function (Blueprint $table) {\n",[457,477,478],{"class":459,"line":4},[457,479,480],{},"        $table->bigIncrements('id');\n",[457,482,484],{"class":459,"line":483},5,[457,485,486],{},"        $table->string('name');\n",[457,488,490],{"class":459,"line":489},6,[457,491,492],{},"        $table->string('email')->unique();\n",[457,494,496],{"class":459,"line":495},7,[457,497,498],{},"        $table->timestamp('email_verified_at')->nullable();\n",[457,500,502],{"class":459,"line":501},8,[457,503,504],{},"        $table->string('password');\n",[457,506,508],{"class":459,"line":507},9,[457,509,510],{},"    \n",[457,512,514],{"class":459,"line":513},10,[457,515,516],{},"        \u002F\u002Fここから追加\n",[457,518,520],{"class":459,"line":519},11,[457,521,522],{},"        $table->longText('zoom_code')->nullable()->default(null);\n",[457,524,526],{"class":459,"line":525},12,[457,527,528],{},"        $table->longText('access_token')->nullable()->default(null);\n",[457,530,532],{"class":459,"line":531},13,[457,533,534],{},"        $table->longText('refresh_token')->nullable()->default(null);\n",[457,536,538],{"class":459,"line":537},14,[457,539,540],{},"        $table->timestamp('zoom_expires_in', 0)->nullable()->default(null);\n",[457,542,544],{"class":459,"line":543},15,[457,545,546],{},"        $table->rememberToken();\n",[457,548,550],{"class":459,"line":549},16,[457,551,552],{},"        $table->timestamps();\n",[457,554,556],{"class":459,"line":555},17,[457,557,558],{},"    });\n",[457,560,562],{"class":459,"line":561},18,[457,563,564],{},"}\n",[13,566,567],{},"それぞれのカラムの説明はこの通り。",[195,569,570,573,576,579],{},[122,571,572],{},"zoom_code：連携許可の際に得られる許可コード。",[122,574,575],{},"access_token：APIにアクセスするためのアクセストークン これを手にしたら勝ち。",[122,577,578],{},"refresh_token：access_tokenを更新するためのトークン。",[122,580,581],{},"zoom_expires_in：access_tokenの期限を記録しておく。APIにアクセスする前にこれでチェックする。",[13,583,584,585,588],{},"ユーザーごとのトークンが管理できる様になり、",[297,586,587],{},"$user->auth()->access_token","みたいな感じでトークンを使用できます。",[13,590,591],{},"それではマイグレーションをしましょう。（仮ユーザーのseedも忘れずに）",[405,593,596],{"className":594,"code":595,"language":410},[408],"php artisan migrate --seed\nMigrating: 2014_10_12_000000_create_users_table\nMigrated:  2014_10_12_000000_create_users_table (0.02 seconds)\nMigrating: 2014_10_12_100000_create_password_resets_table\nMigrated:  2014_10_12_100000_create_password_resets_table (0.01 seconds)\nMigrating: 2019_08_19_000000_create_failed_jobs_table\nMigrated:  2019_08_19_000000_create_failed_jobs_table (0.01 seconds)\nSeeding: UsersTableSeeder\nSeeded:  UsersTableSeeder (0.06 seconds)\nDatabase seeding completed successfully.\n",[297,597,595],{"__ignoreMap":411},[56,599,600],{"id":600},"フォームと管理画面を適当に作る",[13,602,603,604,608],{},"詳しくは",[17,605,607],{"href":190,"rel":606},[21],"アップしたリポジトリ","を見てください。スタイルはbootstrapで調整しています。ルート情報だけ載せておきます。",[405,610,613],{"className":611,"code":612,"language":410},[408],"\u002F                   フォームを表示  \n\u002Fconfirm            フォームの受付完了確認画面\n\u002Fadmin              管理者用ページ\n\u002Flogin              ログインページ\n\u002Flogout             ログアウトルート\n\u002Fzoomoatuh\u002Fcheck    zoom OAuthリダイレクト先\n\u002Fform\u002Falter         ミーティングの時間変更画面\n\u002Fform\u002Fdelete        ミーティングのキャンセル画面\n",[297,614,612],{"__ignoreMap":411},[39,616,618],{"id":617},"lravel-と-zoomのoatuh-2-認証連携","Lravel と ZoomのOatuh 2 認証・連携",[56,620,621],{"id":621},"管理画面から連携確認画面へ誘導とユーザー認証",[13,623,624,625,628,629,631],{},"それではLaravelとZoomの連携処理を実装していきます。連携処理はログインした管理者のアクセス配下で行います。管理画面は",[297,626,627],{},"\u002Fadmin","です。",[297,630,627],{},"のコントローラーとビューは以下の通りです。",[405,633,636],{"className":450,"code":634,"filename":635,"language":453,"meta":411,"style":411},"public function index(Request $request){\n    $user = auth()->user();\n    $noZoomCode = $user->zoom_code == null; \u002F\u002F連携を行っているか\n    $zoomOuthLink = 'https:\u002F\u002Fzoom.us\u002Foauth\u002Fauthorize?'.http_build_query([\n        'response_type'=>'code',\n        'redirect_uri'=>env('APP_URL').'\u002Fzoomoatuh\u002Fcheck',\n        'client_id'=>env('ZOOM_CLIENT_ID'),\n    ]);\n    $oauthSuccess=false;\n    $meetings = Meeting::all();\n\n    return view('admin',compact('noZoomCode','zoomOuthLink','oauthSuccess','meetings'));\n}\n","app\u002FHttp\u002FControllers\u002FAdminController.php",[297,637,638,643,648,653,658,663,668,673,678,683,688,694,699],{"__ignoreMap":411},[457,639,640],{"class":459,"line":460},[457,641,642],{},"public function index(Request $request){\n",[457,644,645],{"class":459,"line":466},[457,646,647],{},"    $user = auth()->user();\n",[457,649,650],{"class":459,"line":472},[457,651,652],{},"    $noZoomCode = $user->zoom_code == null; \u002F\u002F連携を行っているか\n",[457,654,655],{"class":459,"line":4},[457,656,657],{},"    $zoomOuthLink = 'https:\u002F\u002Fzoom.us\u002Foauth\u002Fauthorize?'.http_build_query([\n",[457,659,660],{"class":459,"line":483},[457,661,662],{},"        'response_type'=>'code',\n",[457,664,665],{"class":459,"line":489},[457,666,667],{},"        'redirect_uri'=>env('APP_URL').'\u002Fzoomoatuh\u002Fcheck',\n",[457,669,670],{"class":459,"line":495},[457,671,672],{},"        'client_id'=>env('ZOOM_CLIENT_ID'),\n",[457,674,675],{"class":459,"line":501},[457,676,677],{},"    ]);\n",[457,679,680],{"class":459,"line":507},[457,681,682],{},"    $oauthSuccess=false;\n",[457,684,685],{"class":459,"line":513},[457,686,687],{},"    $meetings = Meeting::all();\n",[457,689,690],{"class":459,"line":519},[457,691,693],{"emptyLinePlaceholder":692},true,"\n",[457,695,696],{"class":459,"line":525},[457,697,698],{},"    return view('admin',compact('noZoomCode','zoomOuthLink','oauthSuccess','meetings'));\n",[457,700,701],{"class":459,"line":531},[457,702,564],{},[405,704,707],{"className":450,"code":705,"filename":706,"language":453,"meta":411,"style":411},"@extends('layouts.layout')\n\n@section('main-content')\n    \u003Cdiv class=\"main-content\">\n        @if($noZoomCode)\n        \u003Cdiv class=\"alert alert-danger mb-3\" role=\"alert\">\n            \u003Ch4 class=\"alert-heading\">Zoomとの連携が行われていません。\u003C\u002Fh4>\n            \u003Cp>このシステムをご利用する場合、Zoomとの連携を行ってください。\u003C\u002Fp>\n            \u003Ca href=\"{{$zoomOuthLink}}\" class=\"btn btn-danger\">Zoomと連携\u003C\u002Fa>\n        \u003C\u002Fdiv>\n        @else\n        \u003Ch1>予約一覧\u003C\u002Fh1>\n        @endif\n    \u003C\u002Fdiv>\n@endsection\n","resources\u002Fviews\u002Fadmin.blade.php",[297,708,709,714,718,723,728,733,738,743,748,753,758,763,768,773,778],{"__ignoreMap":411},[457,710,711],{"class":459,"line":460},[457,712,713],{},"@extends('layouts.layout')\n",[457,715,716],{"class":459,"line":466},[457,717,693],{"emptyLinePlaceholder":692},[457,719,720],{"class":459,"line":472},[457,721,722],{},"@section('main-content')\n",[457,724,725],{"class":459,"line":4},[457,726,727],{},"    \u003Cdiv class=\"main-content\">\n",[457,729,730],{"class":459,"line":483},[457,731,732],{},"        @if($noZoomCode)\n",[457,734,735],{"class":459,"line":489},[457,736,737],{},"        \u003Cdiv class=\"alert alert-danger mb-3\" role=\"alert\">\n",[457,739,740],{"class":459,"line":495},[457,741,742],{},"            \u003Ch4 class=\"alert-heading\">Zoomとの連携が行われていません。\u003C\u002Fh4>\n",[457,744,745],{"class":459,"line":501},[457,746,747],{},"            \u003Cp>このシステムをご利用する場合、Zoomとの連携を行ってください。\u003C\u002Fp>\n",[457,749,750],{"class":459,"line":507},[457,751,752],{},"            \u003Ca href=\"{{$zoomOuthLink}}\" class=\"btn btn-danger\">Zoomと連携\u003C\u002Fa>\n",[457,754,755],{"class":459,"line":513},[457,756,757],{},"        \u003C\u002Fdiv>\n",[457,759,760],{"class":459,"line":519},[457,761,762],{},"        @else\n",[457,764,765],{"class":459,"line":525},[457,766,767],{},"        \u003Ch1>予約一覧\u003C\u002Fh1>\n",[457,769,770],{"class":459,"line":531},[457,771,772],{},"        @endif\n",[457,774,775],{"class":459,"line":537},[457,776,777],{},"    \u003C\u002Fdiv>\n",[457,779,780],{"class":459,"line":543},[457,781,782],{},"@endsection\n",[13,784,785,786,789,790,793,794,797],{},"管理画面では管理者がzoomと連携しているかで表示を変更しています。連携しているかは",[297,787,788],{},"user","テーブルの",[297,791,792],{},"zoom_code","が",[297,795,796],{},"null","かで確認しています。",[13,799,800],{},"連携が済んでいない場合はzoomの連携確認画面へ飛ばすリンクボタンを表示させています。",[13,802,803,804,807,808,813],{},"この",[297,805,806],{},"$zoomOuthLink","の作成は",[17,809,812],{"href":810,"rel":811},"https:\u002F\u002Fmarketplace.zoom.us\u002Fdocs\u002Fguides\u002Fauth\u002Foauth#getting-access-token",[21],"こちらのドキュメント","にある通り、ルールがあります。",[405,815,817],{"className":450,"code":816,"filename":635,"language":453,"meta":411,"style":411},"$zoomOuthLink = 'https:\u002F\u002Fzoom.us\u002Foauth\u002Fauthorize?'.http_build_query([\n    'response_type'=>'code',\n    'redirect_uri'=>env('APP_URL').'\u002Fzoomoatuh\u002Fcheck',\n    'client_id'=>env('ZOOM_CLIENT_ID'),\n]);\n",[297,818,819,824,829,834,839],{"__ignoreMap":411},[457,820,821],{"class":459,"line":460},[457,822,823],{},"$zoomOuthLink = 'https:\u002F\u002Fzoom.us\u002Foauth\u002Fauthorize?'.http_build_query([\n",[457,825,826],{"class":459,"line":466},[457,827,828],{},"    'response_type'=>'code',\n",[457,830,831],{"class":459,"line":472},[457,832,833],{},"    'redirect_uri'=>env('APP_URL').'\u002Fzoomoatuh\u002Fcheck',\n",[457,835,836],{"class":459,"line":4},[457,837,838],{},"    'client_id'=>env('ZOOM_CLIENT_ID'),\n",[457,840,841],{"class":459,"line":483},[457,842,843],{},"]);\n",[13,845,846,847,850],{},"今はOAuthのステップの中で「ユーザー認証」というユーザーへ「このアプリ（Laravel）とzoomを連携してもいい？」とzoomが聞いている段階です。そのユーザー認証にはまず",[297,848,849],{},"https:\u002F\u002Fzoom.us\u002Foauth\u002Fauthorize","へGETで管理者本人がアクセスします。",[13,852,853,854,857,858,857,861,864],{},"その際にGETパラメータに",[297,855,856],{},"response_type","、",[297,859,860],{},"redirect_uri",[297,862,863],{},"client_id","を入力します。",[866,867,871],"div",{"className":868},[869,870],"alert","alert-info","\nresponse_typeAccess response type being requested. The supported authorization workflow requires the value `code`.\n",[13,873,874,875,877,878,880,881,883,884,889,890,892,893,895,896,898,899,902],{},"とある様に",[297,876,856],{},"には",[297,879,297],{},"という文字を設定します。そして",[297,882,860],{},"はzoom ",[17,885,888],{"href":886,"rel":887},"https:\u002F\u002Fjun-app.com\u002Fzoom-api-laravel\u002F#zoom-redirect-url",[21],"アプリ作成時にも設定した通りのURL","を入力しますので、",[297,891,338],{},"を設定。",[297,894,863],{},"は",[297,897,299],{},"で設定値を",[297,900,901],{},"env()","で呼び出します。",[13,904,905],{},"それらをGETパラメータとして一つのURLにまとめます。以下の様な感じです。",[405,907,910],{"className":908,"code":909,"language":410},[408],"https:\u002F\u002Fzoom.us\u002Foauth\u002Fauthorize?response_type=code&redirect_uri=http:\u002F\u002Flocalhost:9000\u002Fzoomauth\u002Fcheck&client_id=clientid\n",[297,911,909],{"__ignoreMap":411},[13,913,914],{},"予めサーバーサイドで作っておき、ボタンのリンクにはめ込んでおきます。画面では以下の様に表示されます。",[111,916],{":src":917,":width":114},"'_mix\u002Fsch-2021-01-10-18.08.06.png'",[56,919,921],{"id":920},"認証画面からのリダイレクトurlでの処理","認証画面からのリダイレクトURLでの処理",[13,923,924],{},"ボタンをクリックすると以下の画面が表示されます。（正確には承認画面のGETを叩く）",[111,926],{":src":927,":width":265,":center":266},"'_mix\u002Fzoom_approve.jpeg'",[13,929,930,931,933,934,936],{},"管理者に対してこのアプリが自身のzoomアカウントに対して、何をするのかが書かれています。管理者はこの「認可」を押すと、",[297,932,860],{},"のリダイレクト先に飛ばされます。OAuthではこのリダイレクト先の処理が大切です！",[297,935,338],{},"のコントローラーは以下の通りです。（ビューはなし）",[405,938,940],{"className":450,"code":939,"filename":635,"language":453,"meta":411,"style":411},"public function zoomOauth(Request $request){\n    $user = auth()->user();\n\n    if($user->zoom_code==null){\n        $code = $request['code'];\n\n        $user->zoom_code = $code;\n        $user->save();\n\n        $basic = base64_encode(env('ZOOM_CLIENT_ID').':'.env('ZOOM_CLIENT_SECRET'));\n        $client = new \\GuzzleHttp\\Client([\n            'headers' => ['Authorization' => 'Basic '.$basic]\n        ]);\n        $res = $client->request('POST','https:\u002F\u002Fzoom.us\u002Foauth\u002Ftoken',[\n            'query' => [\n                'grant_type'=>'authorization_code',\n                'code'=>$code,\n                'redirect_uri'=>'http:\u002F\u002Flocalhost:9000\u002Fzoomoatuh\u002Fcheck'\n            ]\n        ]);\n        $result = json_decode($res->getBody()->getContents());\n\n        $user->access_token= $result->access_token;\n        $user->refresh_token= $result->refresh_token;\n        $unixTime = time();\n        $user->zoom_expires_in= date(\"Y-m-d H:i:s\",$unixTime+$result->expires_in);\n        $user->save();\n\n        return redirect()->route('amdin')->with([\n            'noZoomCode'=>false,\n            'oauthSuccess'=>true\n        ]);\n    }\n}\n",[297,941,942,947,951,955,960,965,969,974,979,983,988,993,998,1003,1008,1013,1018,1023,1028,1034,1039,1045,1050,1056,1062,1068,1074,1079,1084,1090,1096,1102,1107,1113],{"__ignoreMap":411},[457,943,944],{"class":459,"line":460},[457,945,946],{},"public function zoomOauth(Request $request){\n",[457,948,949],{"class":459,"line":466},[457,950,647],{},[457,952,953],{"class":459,"line":472},[457,954,693],{"emptyLinePlaceholder":692},[457,956,957],{"class":459,"line":4},[457,958,959],{},"    if($user->zoom_code==null){\n",[457,961,962],{"class":459,"line":483},[457,963,964],{},"        $code = $request['code'];\n",[457,966,967],{"class":459,"line":489},[457,968,693],{"emptyLinePlaceholder":692},[457,970,971],{"class":459,"line":495},[457,972,973],{},"        $user->zoom_code = $code;\n",[457,975,976],{"class":459,"line":501},[457,977,978],{},"        $user->save();\n",[457,980,981],{"class":459,"line":507},[457,982,693],{"emptyLinePlaceholder":692},[457,984,985],{"class":459,"line":513},[457,986,987],{},"        $basic = base64_encode(env('ZOOM_CLIENT_ID').':'.env('ZOOM_CLIENT_SECRET'));\n",[457,989,990],{"class":459,"line":519},[457,991,992],{},"        $client = new \\GuzzleHttp\\Client([\n",[457,994,995],{"class":459,"line":525},[457,996,997],{},"            'headers' => ['Authorization' => 'Basic '.$basic]\n",[457,999,1000],{"class":459,"line":531},[457,1001,1002],{},"        ]);\n",[457,1004,1005],{"class":459,"line":537},[457,1006,1007],{},"        $res = $client->request('POST','https:\u002F\u002Fzoom.us\u002Foauth\u002Ftoken',[\n",[457,1009,1010],{"class":459,"line":543},[457,1011,1012],{},"            'query' => [\n",[457,1014,1015],{"class":459,"line":549},[457,1016,1017],{},"                'grant_type'=>'authorization_code',\n",[457,1019,1020],{"class":459,"line":555},[457,1021,1022],{},"                'code'=>$code,\n",[457,1024,1025],{"class":459,"line":561},[457,1026,1027],{},"                'redirect_uri'=>'http:\u002F\u002Flocalhost:9000\u002Fzoomoatuh\u002Fcheck'\n",[457,1029,1031],{"class":459,"line":1030},19,[457,1032,1033],{},"            ]\n",[457,1035,1037],{"class":459,"line":1036},20,[457,1038,1002],{},[457,1040,1042],{"class":459,"line":1041},21,[457,1043,1044],{},"        $result = json_decode($res->getBody()->getContents());\n",[457,1046,1048],{"class":459,"line":1047},22,[457,1049,693],{"emptyLinePlaceholder":692},[457,1051,1053],{"class":459,"line":1052},23,[457,1054,1055],{},"        $user->access_token= $result->access_token;\n",[457,1057,1059],{"class":459,"line":1058},24,[457,1060,1061],{},"        $user->refresh_token= $result->refresh_token;\n",[457,1063,1065],{"class":459,"line":1064},25,[457,1066,1067],{},"        $unixTime = time();\n",[457,1069,1071],{"class":459,"line":1070},26,[457,1072,1073],{},"        $user->zoom_expires_in= date(\"Y-m-d H:i:s\",$unixTime+$result->expires_in);\n",[457,1075,1077],{"class":459,"line":1076},27,[457,1078,978],{},[457,1080,1082],{"class":459,"line":1081},28,[457,1083,693],{"emptyLinePlaceholder":692},[457,1085,1087],{"class":459,"line":1086},29,[457,1088,1089],{},"        return redirect()->route('amdin')->with([\n",[457,1091,1093],{"class":459,"line":1092},30,[457,1094,1095],{},"            'noZoomCode'=>false,\n",[457,1097,1099],{"class":459,"line":1098},31,[457,1100,1101],{},"            'oauthSuccess'=>true\n",[457,1103,1105],{"class":459,"line":1104},32,[457,1106,1002],{},[457,1108,1110],{"class":459,"line":1109},33,[457,1111,1112],{},"    }\n",[457,1114,1116],{"class":459,"line":1115},34,[457,1117,564],{},[13,1119,1120,1122,1123,1125,1126,1129],{},[297,1121,849],{}," から ",[297,1124,338],{}," へリダイレクトされると自動的にGETパラメータに",[297,1127,1128],{},"?code=~~~~","というものが付与されています。このcodeは後の認証に必要になります。",[13,1131,1132],{},"リダイレクトURLからcodeの値を取得します。いったんDBに保存してから、実際にAPIへリクエストするのに必要なアクセストークンの取得処理を行います。そこで以下の様なリクエストを行います。",[405,1134,1136],{"className":450,"code":1135,"filename":635,"language":453,"meta":411,"style":411},"$basic = base64_encode(env('ZOOM_CLIENT_ID').':'.env('ZOOM_CLIENT_SECRET'));\n$client = new \\GuzzleHttp\\Client([\n    'headers' => ['Authorization' => 'Basic '.$basic]\n]);\n$res = $client->request('POST','https:\u002F\u002Fzoom.us\u002Foauth\u002Ftoken',[\n    'query' => [\n        'grant_type'=>'authorization_code',\n        'code'=>$code,\n        'redirect_uri'=>'http:\u002F\u002Flocalhost:9000\u002Fzoomoatuh\u002Fcheck'\n    ]\n]);\n",[297,1137,1138,1143,1148,1153,1157,1162,1167,1172,1177,1182,1187],{"__ignoreMap":411},[457,1139,1140],{"class":459,"line":460},[457,1141,1142],{},"$basic = base64_encode(env('ZOOM_CLIENT_ID').':'.env('ZOOM_CLIENT_SECRET'));\n",[457,1144,1145],{"class":459,"line":466},[457,1146,1147],{},"$client = new \\GuzzleHttp\\Client([\n",[457,1149,1150],{"class":459,"line":472},[457,1151,1152],{},"    'headers' => ['Authorization' => 'Basic '.$basic]\n",[457,1154,1155],{"class":459,"line":4},[457,1156,843],{},[457,1158,1159],{"class":459,"line":483},[457,1160,1161],{},"$res = $client->request('POST','https:\u002F\u002Fzoom.us\u002Foauth\u002Ftoken',[\n",[457,1163,1164],{"class":459,"line":489},[457,1165,1166],{},"    'query' => [\n",[457,1168,1169],{"class":459,"line":495},[457,1170,1171],{},"        'grant_type'=>'authorization_code',\n",[457,1173,1174],{"class":459,"line":501},[457,1175,1176],{},"        'code'=>$code,\n",[457,1178,1179],{"class":459,"line":507},[457,1180,1181],{},"        'redirect_uri'=>'http:\u002F\u002Flocalhost:9000\u002Fzoomoatuh\u002Fcheck'\n",[457,1183,1184],{"class":459,"line":513},[457,1185,1186],{},"    ]\n",[457,1188,1189],{"class":459,"line":519},[457,1190,843],{},[13,1192,1193,1194,1197,1198,1201],{},"Zoomにも書いてある通りの処理ですが、アクセストークンを得る ",[297,1195,1196],{},"https:\u002F\u002Fzoom.us\u002Foauth\u002Ftoken"," というルートにアクセスするときは、まずリクエストヘッダーを付与します。リクエストヘッダーは ",[297,1199,1200],{},"'headers' => ['Authorization' => 'Basic '.$basic]"," です。ここに client IDとclient secretをコロンで付けて一つの文字列にし、それをbase64エンコードをします。つまり明示的に処理を表示すると以下の様な感じです。",[405,1203,1206],{"className":1204,"code":1205,"language":410},[408],"client_id:cilent_secret \u002F\u002Fこれで一行の文字列\n↓\nこの値を64base encode\n↓\nsi84nf7435934jdfsdfi... \u002F\u002Fエンコード化された文字。これを送る\n",[297,1207,1205],{"__ignoreMap":411},[13,1209,1210,1211,1213,1214,1217],{},"そしてそれをリクエストヘッダーに付与します。",[297,1212,1200],{}," これを文字列として表示すると、",[297,1215,1216],{},"Authorization: Basic si84nf7435934jdfsdfi…"," みたいな感じです。ちなみに Basicとエンコード文字の間は半角が空いていますので注意。",[13,1219,1220],{},"リクエストヘッダーを付けたら先ほどと似た感じでGETパラメータを以下の様に設定します。",[405,1222,1224],{"className":450,"code":1223,"language":453,"meta":411,"style":411},"$res = $client->request('POST','https:\u002F\u002Fzoom.us\u002Foauth\u002Ftoken',[\n    'query' => [\n        'grant_type'=>'authorization_code',\n        'code'=>$code,\n        'redirect_uri'=>'http:\u002F\u002Flocalhost:9000\u002Fzoomoatuh\u002Fcheck'\n    ]\n])\n",[297,1225,1226,1230,1234,1238,1242,1246,1250],{"__ignoreMap":411},[457,1227,1228],{"class":459,"line":460},[457,1229,1161],{},[457,1231,1232],{"class":459,"line":466},[457,1233,1166],{},[457,1235,1236],{"class":459,"line":472},[457,1237,1171],{},[457,1239,1240],{"class":459,"line":4},[457,1241,1176],{},[457,1243,1244],{"class":459,"line":483},[457,1245,1181],{},[457,1247,1248],{"class":459,"line":489},[457,1249,1186],{},[457,1251,1252],{"class":459,"line":495},[457,1253,1254],{},"])\n",[13,1256,1257,793,1260,1263,1264,1267,1268,1270,1271,1273],{},[297,1258,1259],{},"grant_type",[297,1261,1262],{},"authorization_code","という文字とし、codeにはリダイレクト時についてきた値である",[297,1265,1266],{},"$request['code']","を用います。",[297,1269,860],{},"は先ほどと同じです。（",[297,1272,860],{},"を別にすると認証が通りません！）",[13,1275,1276],{},"これでセットアップが完了です。実際のURLとしては以下の感じです。",[405,1278,1281],{"className":1279,"code":1280,"language":410},[408],"https:\u002F\u002Fzoom.us\u002Foauth\u002Ftoken?grant_type=authorization_code&code=~~~~~&redirect_uri=http:\u002F\u002Flocalhost:9000\u002Fzoomoatuh\u002Fcheck\n（そして直接は見えないですが、リクエストヘッダーには 「Authorization: Basic si84nf7435934jdfsdfi…」 という値がついています！\n",[297,1282,1280],{"__ignoreMap":411},[13,1284,1285,1286,1289],{},"リクエストが成功するとアクセストークン を含んだレスポンスがJSONで戻ってきます。それを展開してDBへ保存します。",[297,1287,1288],{},"zoom_expires_in","は現在時刻と足し合わせて、期限切れ時刻を計算してから格納しています。",[405,1291,1293],{"className":450,"code":1292,"language":453,"meta":411,"style":411},"\u002F*\n$resultの中身の例\n{\n    \"access_token\": \"eyJhbGciOiJIUz...\",\n    \"token_type\": \"bearer\",\n    \"refresh_token\": \"eyJhbGciOiJI..\",\n    \"expires_in\": 3599,\n    \"scope\": \"user:read\"\n}\n*\u002F\n\n$result = json_decode($res->getBody()->getContents());\n\n$user->access_token= $result->access_token;\n$user->refresh_token= $result->refresh_token;\n$unixTime = time();\n$user->zoom_expires_in= date(\"Y-m-d H:i:s\",$unixTime+$result->expires_in);\n$user->save();\n\nreturn redirect()->route('amdin')->with([\n         'noZoomCode'=>false,\n         'oauthSuccess'=>true\n]);\n",[297,1294,1295,1300,1305,1309,1314,1319,1324,1329,1334,1338,1343,1347,1352,1356,1361,1366,1371,1376,1381,1385,1390,1395,1400],{"__ignoreMap":411},[457,1296,1297],{"class":459,"line":460},[457,1298,1299],{},"\u002F*\n",[457,1301,1302],{"class":459,"line":466},[457,1303,1304],{},"$resultの中身の例\n",[457,1306,1307],{"class":459,"line":472},[457,1308,469],{},[457,1310,1311],{"class":459,"line":4},[457,1312,1313],{},"    \"access_token\": \"eyJhbGciOiJIUz...\",\n",[457,1315,1316],{"class":459,"line":483},[457,1317,1318],{},"    \"token_type\": \"bearer\",\n",[457,1320,1321],{"class":459,"line":489},[457,1322,1323],{},"    \"refresh_token\": \"eyJhbGciOiJI..\",\n",[457,1325,1326],{"class":459,"line":495},[457,1327,1328],{},"    \"expires_in\": 3599,\n",[457,1330,1331],{"class":459,"line":501},[457,1332,1333],{},"    \"scope\": \"user:read\"\n",[457,1335,1336],{"class":459,"line":507},[457,1337,564],{},[457,1339,1340],{"class":459,"line":513},[457,1341,1342],{},"*\u002F\n",[457,1344,1345],{"class":459,"line":519},[457,1346,693],{"emptyLinePlaceholder":692},[457,1348,1349],{"class":459,"line":525},[457,1350,1351],{},"$result = json_decode($res->getBody()->getContents());\n",[457,1353,1354],{"class":459,"line":531},[457,1355,693],{"emptyLinePlaceholder":692},[457,1357,1358],{"class":459,"line":537},[457,1359,1360],{},"$user->access_token= $result->access_token;\n",[457,1362,1363],{"class":459,"line":543},[457,1364,1365],{},"$user->refresh_token= $result->refresh_token;\n",[457,1367,1368],{"class":459,"line":549},[457,1369,1370],{},"$unixTime = time();\n",[457,1372,1373],{"class":459,"line":555},[457,1374,1375],{},"$user->zoom_expires_in= date(\"Y-m-d H:i:s\",$unixTime+$result->expires_in);\n",[457,1377,1378],{"class":459,"line":561},[457,1379,1380],{},"$user->save();\n",[457,1382,1383],{"class":459,"line":1030},[457,1384,693],{"emptyLinePlaceholder":692},[457,1386,1387],{"class":459,"line":1036},[457,1388,1389],{},"return redirect()->route('amdin')->with([\n",[457,1391,1392],{"class":459,"line":1041},[457,1393,1394],{},"         'noZoomCode'=>false,\n",[457,1396,1397],{"class":459,"line":1047},[457,1398,1399],{},"         'oauthSuccess'=>true\n",[457,1401,1402],{"class":459,"line":1052},[457,1403,843],{},[13,1405,1406],{},"そして最後は管理画面へリダイレクトしてあげます。管理者からしてみるとzoomの画面で「許可」を押すと元のサイトに戻って、グルグルローディングしてるなーと思ったら管理画面に戻ってきた感覚となります。実際の画面ではzoom連携の警告がなくなり以下の様な感じになります。",[111,1408],{":src":1409,":width":114},"'_mix\u002Fsch-2021-01-10-20.30.08-768x155.png'",[13,1411,1412],{},"これでOAuthは完了です。意外と簡単ですね。access_tokenは1時間で切れてしまうので、APIアクセスの際は期限切れでないかをチェック、そしてダメならtokenをリフレッシュする機能が必要となります。次はaccess_tokenのチェック方ら連携した人のユーザー情報を取得するとともに、リフレッシュ機能を実装します。",[39,1414,1415],{"id":1415},"ユーザー情報を取得",[13,1417,1418],{},"ミーティングを作成したりなどはユーザーIDが必要となります。他のAPIで使用するのでコントローラー内の共通メソッドとして分離しておきましょう。以下の様にします。",[405,1420,1423],{"className":450,"code":1421,"filename":1422,"language":453,"meta":411,"style":411},"class ZoomApiController extends Controller\n{\n    \u002F\u002F\n    protected function me(){\n        $user = auth()->user();\n        $client = new \\GuzzleHttp\\Client([\n            'headers' => ['Authorization' => 'Bearer '.$user->access_token]\n        ]);\n        $res = $client->request('GET','https:\u002F\u002Fapi.zoom.us\u002Fv2\u002Fusers\u002Fme');\n        $result = json_decode($res->getBody()->getContents());\n        \u002F\u002F dd($result);\n        return $result;\n    }\n...\n}\n","app\u002FHttp\u002FControllers\u002FZoomApiController.php",[297,1424,1425,1430,1434,1439,1444,1449,1453,1458,1462,1467,1471,1476,1481,1485,1490],{"__ignoreMap":411},[457,1426,1427],{"class":459,"line":460},[457,1428,1429],{},"class ZoomApiController extends Controller\n",[457,1431,1432],{"class":459,"line":466},[457,1433,469],{},[457,1435,1436],{"class":459,"line":472},[457,1437,1438],{},"    \u002F\u002F\n",[457,1440,1441],{"class":459,"line":4},[457,1442,1443],{},"    protected function me(){\n",[457,1445,1446],{"class":459,"line":483},[457,1447,1448],{},"        $user = auth()->user();\n",[457,1450,1451],{"class":459,"line":489},[457,1452,992],{},[457,1454,1455],{"class":459,"line":495},[457,1456,1457],{},"            'headers' => ['Authorization' => 'Bearer '.$user->access_token]\n",[457,1459,1460],{"class":459,"line":501},[457,1461,1002],{},[457,1463,1464],{"class":459,"line":507},[457,1465,1466],{},"        $res = $client->request('GET','https:\u002F\u002Fapi.zoom.us\u002Fv2\u002Fusers\u002Fme');\n",[457,1468,1469],{"class":459,"line":513},[457,1470,1044],{},[457,1472,1473],{"class":459,"line":519},[457,1474,1475],{},"        \u002F\u002F dd($result);\n",[457,1477,1478],{"class":459,"line":525},[457,1479,1480],{},"        return $result;\n",[457,1482,1483],{"class":459,"line":531},[457,1484,1112],{},[457,1486,1487],{"class":459,"line":537},[457,1488,1489],{},"...\n",[457,1491,1492],{"class":459,"line":543},[457,1493,564],{},[13,1495,1496,1497,1500,1501,1504,1505,1508],{},"ユーザーテーブルに",[297,1498,1499],{},"access_token","があるので",[297,1502,1503],{},"$user = auth()->user();","で現在のログインユーザーを取り出して、",[297,1506,1507],{},"$user->access_token","にて出力します。",[13,1510,1511,1513,1514,1517,1518,1521],{},[297,1512,1499],{},"があればAPIへのアクセスはリクエストヘッダーにトークンを入れるだけでアクセスできます。リクエストヘッダーは",[297,1515,1516],{},"'headers' => ['Authorization' => 'Bearer '.$user->access_token]","です。さっきはBasicだったのが、",[297,1519,1520],{},"Bearer（ベアラー）","になっていますのでタイポに注意。",[13,1523,1524,1527],{},[297,1525,1526],{},"dd($result)","を有効にして出力してみると",[111,1529],{":src":1530,":width":265,":center":266},"'_mix\u002Fzoom_me.jpeg'",[13,1532,1533],{},"こんな感じのJSONが返ってきますので、適宜IDなどを使用します。",[39,1535,1536],{"id":1536},"リフレッシュ機能を実装",[13,1538,1539],{},"access tokenは1時間しか持たないのでもし期限切れになった際にはリフレッシュトークンを使用してトークンを更新します。ちなみにリフレッシュトークンの有効期限は15年です笑私の場合は以下の様に実装しました。",[405,1541,1543],{"className":450,"code":1542,"filename":1422,"language":453,"meta":411,"style":411},"protected function checkRefresh(){\n    $user = auth()->user();\n    $token_expires =  new \\DateTime($user->zoom_expires_in);\n    $now = new \\DateTime();\n\n    if($now >= $token_expires){\n        $basic = base64_encode(env('ZOOM_CLIENT_ID').':'.env('ZOOM_CLIENT_SECRET'));\n        $client = new \\GuzzleHttp\\Client([\n            'headers' => ['Authorization' => 'Basic '.$basic]\n        ]);\n        $res = $client->request('POST','https:\u002F\u002Fzoom.us\u002Foauth\u002Ftoken',[\n            'query' => [\n                'grant_type'=>'refresh_token',\n                'refresh_token'=>$user->refresh_token\n            ]\n        ]);\n        $result = json_decode($res->getBody()->getContents());\n\n        $user->access_token= $result->access_token;\n        $user->refresh_token= $result->access_token;\n        $unixTime = time();\n        $user->zoom_expires_in= date(\"Y-m-d H:i:s\",$unixTime+$result->expires_in);\n        $user->save();\n        return $user;\n    }\n    return $user;\n}\n",[297,1544,1545,1550,1554,1559,1564,1568,1573,1577,1581,1585,1589,1593,1597,1602,1607,1611,1615,1619,1623,1627,1632,1636,1640,1644,1649,1653,1658],{"__ignoreMap":411},[457,1546,1547],{"class":459,"line":460},[457,1548,1549],{},"protected function checkRefresh(){\n",[457,1551,1552],{"class":459,"line":466},[457,1553,647],{},[457,1555,1556],{"class":459,"line":472},[457,1557,1558],{},"    $token_expires =  new \\DateTime($user->zoom_expires_in);\n",[457,1560,1561],{"class":459,"line":4},[457,1562,1563],{},"    $now = new \\DateTime();\n",[457,1565,1566],{"class":459,"line":483},[457,1567,693],{"emptyLinePlaceholder":692},[457,1569,1570],{"class":459,"line":489},[457,1571,1572],{},"    if($now >= $token_expires){\n",[457,1574,1575],{"class":459,"line":495},[457,1576,987],{},[457,1578,1579],{"class":459,"line":501},[457,1580,992],{},[457,1582,1583],{"class":459,"line":507},[457,1584,997],{},[457,1586,1587],{"class":459,"line":513},[457,1588,1002],{},[457,1590,1591],{"class":459,"line":519},[457,1592,1007],{},[457,1594,1595],{"class":459,"line":525},[457,1596,1012],{},[457,1598,1599],{"class":459,"line":531},[457,1600,1601],{},"                'grant_type'=>'refresh_token',\n",[457,1603,1604],{"class":459,"line":537},[457,1605,1606],{},"                'refresh_token'=>$user->refresh_token\n",[457,1608,1609],{"class":459,"line":543},[457,1610,1033],{},[457,1612,1613],{"class":459,"line":549},[457,1614,1002],{},[457,1616,1617],{"class":459,"line":555},[457,1618,1044],{},[457,1620,1621],{"class":459,"line":561},[457,1622,693],{"emptyLinePlaceholder":692},[457,1624,1625],{"class":459,"line":1030},[457,1626,1055],{},[457,1628,1629],{"class":459,"line":1036},[457,1630,1631],{},"        $user->refresh_token= $result->access_token;\n",[457,1633,1634],{"class":459,"line":1041},[457,1635,1067],{},[457,1637,1638],{"class":459,"line":1047},[457,1639,1073],{},[457,1641,1642],{"class":459,"line":1052},[457,1643,978],{},[457,1645,1646],{"class":459,"line":1058},[457,1647,1648],{},"        return $user;\n",[457,1650,1651],{"class":459,"line":1064},[457,1652,1112],{},[457,1654,1655],{"class":459,"line":1070},[457,1656,1657],{},"    return $user;\n",[457,1659,1660],{"class":459,"line":1076},[457,1661,564],{},[13,1663,1664],{},"APIリクエストごとにトークンをチェックできる様にしています。有効期限をテーブルに保存してあるのでそれを比較して、現在時刻が有効期限を過ぎていたらリフレッシュ処理を行う様します。そして戻ってきたトークンをテーブルで更新させ、ユーザーモデルをreturnします。",[13,1666,1667],{},"有効期限ないであればそのままユーザーモデルを返却するという感じです。",[39,1669,1671],{"id":1670},"apiからミーティングを作成","APIからミーティングを作成",[13,1673,1674],{},"それではフォームから入力された値を元にミーティングを作れる様にしましょう。メール通知は機能してはいませんが、実装したコードはコメントアウトさせてますので、頑張れる人はメールも実装してみてください。",[13,1676,1677],{},"まずフォームから取得したミーティング情報を格納するテーブルを以下の様に定義して、マイグレーションを実施します。",[56,1679,1681],{"id":1680},"フォームミーティング管理テーブルを作成","フォーム＆ミーティング管理テーブルを作成",[405,1683,1686],{"className":450,"code":1684,"filename":1685,"language":453,"meta":411,"style":411},"\u003C?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\Facades\\DB;\n\nclass CreateMeeting extends Migration\n{\n    \u002F**\n     * Run the migrations.\n     *\n     * @return void\n     *\u002F\n    public function up()\n    {\n        Schema::create('meeting', function (Blueprint $table) {\n            $table->bigIncrements('id');\n            $table->string('name');\n            $table->string('company_name');\n            $table->string('email');\n            $table->longText('content');\n\n            $table->timestamp('start_at', 0)->default(DB::raw('CURRENT_TIMESTAMP'));\n            $table->longText('hash');\n            $table->boolean('is_canceled');\n\n            $table->longText('zoom_meeting_id');\n            $table->longText('zoom_join_url');\n            $table->longText('zoom_start_url');\n            $table->longText('zoom_password');\n            $table->timestamps();\n        });\n    }\n\n    \u002F**\n     * Reverse the migrations.\n     *\n     * @return void\n     *\u002F\n    public function down()\n    {\n        Schema::dropIfExists('meeting');\n    }\n}\n","database\u002Fmigrations\u002F2021_01_10_005145_create_meeting.php",[297,1687,1688,1693,1697,1702,1707,1712,1717,1721,1726,1730,1735,1740,1745,1750,1755,1760,1765,1770,1775,1780,1785,1790,1795,1799,1804,1809,1814,1818,1823,1828,1833,1838,1843,1848,1852,1857,1862,1868,1873,1878,1883,1889,1894,1900,1905],{"__ignoreMap":411},[457,1689,1690],{"class":459,"line":460},[457,1691,1692],{},"\u003C?php\n",[457,1694,1695],{"class":459,"line":466},[457,1696,693],{"emptyLinePlaceholder":692},[457,1698,1699],{"class":459,"line":472},[457,1700,1701],{},"use Illuminate\\Database\\Migrations\\Migration;\n",[457,1703,1704],{"class":459,"line":4},[457,1705,1706],{},"use Illuminate\\Database\\Schema\\Blueprint;\n",[457,1708,1709],{"class":459,"line":483},[457,1710,1711],{},"use Illuminate\\Support\\Facades\\Schema;\n",[457,1713,1714],{"class":459,"line":489},[457,1715,1716],{},"use Illuminate\\Support\\Facades\\DB;\n",[457,1718,1719],{"class":459,"line":495},[457,1720,693],{"emptyLinePlaceholder":692},[457,1722,1723],{"class":459,"line":501},[457,1724,1725],{},"class CreateMeeting extends Migration\n",[457,1727,1728],{"class":459,"line":507},[457,1729,469],{},[457,1731,1732],{"class":459,"line":513},[457,1733,1734],{},"    \u002F**\n",[457,1736,1737],{"class":459,"line":519},[457,1738,1739],{},"     * Run the migrations.\n",[457,1741,1742],{"class":459,"line":525},[457,1743,1744],{},"     *\n",[457,1746,1747],{"class":459,"line":531},[457,1748,1749],{},"     * @return void\n",[457,1751,1752],{"class":459,"line":537},[457,1753,1754],{},"     *\u002F\n",[457,1756,1757],{"class":459,"line":543},[457,1758,1759],{},"    public function up()\n",[457,1761,1762],{"class":459,"line":549},[457,1763,1764],{},"    {\n",[457,1766,1767],{"class":459,"line":555},[457,1768,1769],{},"        Schema::create('meeting', function (Blueprint $table) {\n",[457,1771,1772],{"class":459,"line":561},[457,1773,1774],{},"            $table->bigIncrements('id');\n",[457,1776,1777],{"class":459,"line":1030},[457,1778,1779],{},"            $table->string('name');\n",[457,1781,1782],{"class":459,"line":1036},[457,1783,1784],{},"            $table->string('company_name');\n",[457,1786,1787],{"class":459,"line":1041},[457,1788,1789],{},"            $table->string('email');\n",[457,1791,1792],{"class":459,"line":1047},[457,1793,1794],{},"            $table->longText('content');\n",[457,1796,1797],{"class":459,"line":1052},[457,1798,693],{"emptyLinePlaceholder":692},[457,1800,1801],{"class":459,"line":1058},[457,1802,1803],{},"            $table->timestamp('start_at', 0)->default(DB::raw('CURRENT_TIMESTAMP'));\n",[457,1805,1806],{"class":459,"line":1064},[457,1807,1808],{},"            $table->longText('hash');\n",[457,1810,1811],{"class":459,"line":1070},[457,1812,1813],{},"            $table->boolean('is_canceled');\n",[457,1815,1816],{"class":459,"line":1076},[457,1817,693],{"emptyLinePlaceholder":692},[457,1819,1820],{"class":459,"line":1081},[457,1821,1822],{},"            $table->longText('zoom_meeting_id');\n",[457,1824,1825],{"class":459,"line":1086},[457,1826,1827],{},"            $table->longText('zoom_join_url');\n",[457,1829,1830],{"class":459,"line":1092},[457,1831,1832],{},"            $table->longText('zoom_start_url');\n",[457,1834,1835],{"class":459,"line":1098},[457,1836,1837],{},"            $table->longText('zoom_password');\n",[457,1839,1840],{"class":459,"line":1104},[457,1841,1842],{},"            $table->timestamps();\n",[457,1844,1845],{"class":459,"line":1109},[457,1846,1847],{},"        });\n",[457,1849,1850],{"class":459,"line":1115},[457,1851,1112],{},[457,1853,1855],{"class":459,"line":1854},35,[457,1856,693],{"emptyLinePlaceholder":692},[457,1858,1860],{"class":459,"line":1859},36,[457,1861,1734],{},[457,1863,1865],{"class":459,"line":1864},37,[457,1866,1867],{},"     * Reverse the migrations.\n",[457,1869,1871],{"class":459,"line":1870},38,[457,1872,1744],{},[457,1874,1876],{"class":459,"line":1875},39,[457,1877,1749],{},[457,1879,1881],{"class":459,"line":1880},40,[457,1882,1754],{},[457,1884,1886],{"class":459,"line":1885},41,[457,1887,1888],{},"    public function down()\n",[457,1890,1892],{"class":459,"line":1891},42,[457,1893,1764],{},[457,1895,1897],{"class":459,"line":1896},43,[457,1898,1899],{},"        Schema::dropIfExists('meeting');\n",[457,1901,1903],{"class":459,"line":1902},44,[457,1904,1112],{},[457,1906,1908],{"class":459,"line":1907},45,[457,1909,564],{},[13,1911,1912,1915],{},[297,1913,1914],{},"start_at","はお客さんが入力した希望zoom開催時間です。本来は管理側の都合を合わせた機能にすべきですが今回はプロトタイプなので割愛。フロントからはdatetime形式で来たものを受け取ります。",[13,1917,1918,1921],{},[297,1919,1920],{},"hash","は後でお客さんがミーティングをキャンセルしたり、時間を変更するときにアクセスするURLに付けるランダムな文字列です。一種のパスワードみたいなものです。後ほど使い方を解説します。",[13,1923,1924,1925,399,1928,1931],{},"そしてzoomへCreate Meeting API を送信すると、ミーティングURLなどが返ってきますのでそれを",[297,1926,1927],{},"zoom_join_url",[297,1929,1930],{},"zoom_start_url","に入れておきます 。他にフォームの内容を格納する箇所を定義してマイグレーションします。",[13,1933,1934],{},"フォームの画面以下の様に実装しました。",[111,1936],{":src":152,":width":114},[56,1938,1940],{"id":1939},"バリデーションとmeeting-apiをリクエスト","バリデーションとmeeting apiをリクエスト",[13,1942,1943],{},"フォームのビューがPOSTリクエストを受けたら以下のコントローラーが実行されます。",[405,1945,1947],{"className":450,"code":1946,"filename":1422,"language":453,"meta":411,"style":411},"public function createMeeting(Request $request){\n    $validator = Validator::make($request->all(),[\n        'email'=>'required|email:rfc',\n        'yourname'=>'required',\n        'companyname'=>'required',\n        'startAt'=>'date|required',\n        'content'=>'required|max:1000',\n    ]);\n\n    $error = $validator->getMessageBag()->toArray();\n    \n    \u002F\u002Fバリデーションエラーがあれば元の画面へ\n    if ($validator->fails()) {\n        return view('form',compact('error'));\n    }\n    \n    $user = $this->checkRefresh();\n    $user = auth()->user();\n\n    $zoom_user = $this->me();\n\n    $url = 'https:\u002F\u002Fapi.zoom.us\u002Fv2\u002Fusers\u002F'.$zoom_user->id.'\u002Fmeetings';\n    $client = new \\GuzzleHttp\\Client([\n        'headers' => [\n            'Authorization' => 'Bearer '.$user->access_token,\n            'Content-Type'=>'application\u002Fjson'\n        ],\n    ]);\n\n    $topic = $request->companyname.' '.$request->yourname.'様 ご相談';\n    $meeting_password = substr(base_convert(bin2hex(openssl_random_pseudo_bytes(9)),16,36),0,9);\n    $res = $client->request('POST',$url,[\n        \\GuzzleHttp\\RequestOptions::JSON => [\n            'topic'=>$topic,\n            'type'=>2,\n            'start_time'=>$request->startAt,\n            'password'=>$meeting_password\n        ]\n    ]);\n    $result = json_decode($res->getBody()->getContents());\n\n    $meeting = new Meeting();\n    $meeting->name=$request->yourname;\n    $meeting->company_name=$request->companyname;\n    $meeting->email=$request->email;\n    $meeting->content=$request->content;\n\n    $start = new \\DateTime($result->start_time);\n    $meeting->start_at=$start;\n    $meeting->hash=substr(base_convert(bin2hex(openssl_random_pseudo_bytes(64)),16,36),0,64);\n    $meeting->is_canceled=false;\n\n    $meeting->zoom_meeting_id=$result->id;\n    $meeting->zoom_join_url=$result->join_url;\n    $meeting->zoom_start_url=$result->start_url;\n    $meeting->zoom_password=$result->password;\n    $meeting->save();\n\n    $format = $start->format('Y年m月d日 H時i分');\n    \u002F\u002F $meeting->start_at = $format;\n    \u002F\u002F $mail = new ContactMail($meeting);\n    \u002F\u002F Mail::to($request->email)->send($mail);\n\n\n    return redirect('\u002Fconfirm')->with([\n        'form_id'=>$meeting->id,\n        'name'=>$request->yourname,\n        'companyname'=>$request->companyname,\n        'content'=>$request->content,\n        'start_time'=>$format\n    ]);\n}\n",[297,1948,1949,1954,1959,1964,1969,1974,1979,1984,1988,1992,1997,2001,2006,2011,2016,2020,2024,2029,2033,2037,2042,2046,2051,2056,2061,2066,2071,2076,2080,2084,2089,2094,2099,2104,2109,2114,2119,2124,2129,2133,2138,2142,2147,2152,2157,2162,2168,2173,2179,2185,2191,2197,2202,2208,2214,2220,2226,2232,2237,2243,2249,2255,2261,2266,2271,2277,2283,2289,2295,2301,2307,2312],{"__ignoreMap":411},[457,1950,1951],{"class":459,"line":460},[457,1952,1953],{},"public function createMeeting(Request $request){\n",[457,1955,1956],{"class":459,"line":466},[457,1957,1958],{},"    $validator = Validator::make($request->all(),[\n",[457,1960,1961],{"class":459,"line":472},[457,1962,1963],{},"        'email'=>'required|email:rfc',\n",[457,1965,1966],{"class":459,"line":4},[457,1967,1968],{},"        'yourname'=>'required',\n",[457,1970,1971],{"class":459,"line":483},[457,1972,1973],{},"        'companyname'=>'required',\n",[457,1975,1976],{"class":459,"line":489},[457,1977,1978],{},"        'startAt'=>'date|required',\n",[457,1980,1981],{"class":459,"line":495},[457,1982,1983],{},"        'content'=>'required|max:1000',\n",[457,1985,1986],{"class":459,"line":501},[457,1987,677],{},[457,1989,1990],{"class":459,"line":507},[457,1991,693],{"emptyLinePlaceholder":692},[457,1993,1994],{"class":459,"line":513},[457,1995,1996],{},"    $error = $validator->getMessageBag()->toArray();\n",[457,1998,1999],{"class":459,"line":519},[457,2000,510],{},[457,2002,2003],{"class":459,"line":525},[457,2004,2005],{},"    \u002F\u002Fバリデーションエラーがあれば元の画面へ\n",[457,2007,2008],{"class":459,"line":531},[457,2009,2010],{},"    if ($validator->fails()) {\n",[457,2012,2013],{"class":459,"line":537},[457,2014,2015],{},"        return view('form',compact('error'));\n",[457,2017,2018],{"class":459,"line":543},[457,2019,1112],{},[457,2021,2022],{"class":459,"line":549},[457,2023,510],{},[457,2025,2026],{"class":459,"line":555},[457,2027,2028],{},"    $user = $this->checkRefresh();\n",[457,2030,2031],{"class":459,"line":561},[457,2032,647],{},[457,2034,2035],{"class":459,"line":1030},[457,2036,693],{"emptyLinePlaceholder":692},[457,2038,2039],{"class":459,"line":1036},[457,2040,2041],{},"    $zoom_user = $this->me();\n",[457,2043,2044],{"class":459,"line":1041},[457,2045,693],{"emptyLinePlaceholder":692},[457,2047,2048],{"class":459,"line":1047},[457,2049,2050],{},"    $url = 'https:\u002F\u002Fapi.zoom.us\u002Fv2\u002Fusers\u002F'.$zoom_user->id.'\u002Fmeetings';\n",[457,2052,2053],{"class":459,"line":1052},[457,2054,2055],{},"    $client = new \\GuzzleHttp\\Client([\n",[457,2057,2058],{"class":459,"line":1058},[457,2059,2060],{},"        'headers' => [\n",[457,2062,2063],{"class":459,"line":1064},[457,2064,2065],{},"            'Authorization' => 'Bearer '.$user->access_token,\n",[457,2067,2068],{"class":459,"line":1070},[457,2069,2070],{},"            'Content-Type'=>'application\u002Fjson'\n",[457,2072,2073],{"class":459,"line":1076},[457,2074,2075],{},"        ],\n",[457,2077,2078],{"class":459,"line":1081},[457,2079,677],{},[457,2081,2082],{"class":459,"line":1086},[457,2083,693],{"emptyLinePlaceholder":692},[457,2085,2086],{"class":459,"line":1092},[457,2087,2088],{},"    $topic = $request->companyname.' '.$request->yourname.'様 ご相談';\n",[457,2090,2091],{"class":459,"line":1098},[457,2092,2093],{},"    $meeting_password = substr(base_convert(bin2hex(openssl_random_pseudo_bytes(9)),16,36),0,9);\n",[457,2095,2096],{"class":459,"line":1104},[457,2097,2098],{},"    $res = $client->request('POST',$url,[\n",[457,2100,2101],{"class":459,"line":1109},[457,2102,2103],{},"        \\GuzzleHttp\\RequestOptions::JSON => [\n",[457,2105,2106],{"class":459,"line":1115},[457,2107,2108],{},"            'topic'=>$topic,\n",[457,2110,2111],{"class":459,"line":1854},[457,2112,2113],{},"            'type'=>2,\n",[457,2115,2116],{"class":459,"line":1859},[457,2117,2118],{},"            'start_time'=>$request->startAt,\n",[457,2120,2121],{"class":459,"line":1864},[457,2122,2123],{},"            'password'=>$meeting_password\n",[457,2125,2126],{"class":459,"line":1870},[457,2127,2128],{},"        ]\n",[457,2130,2131],{"class":459,"line":1875},[457,2132,677],{},[457,2134,2135],{"class":459,"line":1880},[457,2136,2137],{},"    $result = json_decode($res->getBody()->getContents());\n",[457,2139,2140],{"class":459,"line":1885},[457,2141,693],{"emptyLinePlaceholder":692},[457,2143,2144],{"class":459,"line":1891},[457,2145,2146],{},"    $meeting = new Meeting();\n",[457,2148,2149],{"class":459,"line":1896},[457,2150,2151],{},"    $meeting->name=$request->yourname;\n",[457,2153,2154],{"class":459,"line":1902},[457,2155,2156],{},"    $meeting->company_name=$request->companyname;\n",[457,2158,2159],{"class":459,"line":1907},[457,2160,2161],{},"    $meeting->email=$request->email;\n",[457,2163,2165],{"class":459,"line":2164},46,[457,2166,2167],{},"    $meeting->content=$request->content;\n",[457,2169,2171],{"class":459,"line":2170},47,[457,2172,693],{"emptyLinePlaceholder":692},[457,2174,2176],{"class":459,"line":2175},48,[457,2177,2178],{},"    $start = new \\DateTime($result->start_time);\n",[457,2180,2182],{"class":459,"line":2181},49,[457,2183,2184],{},"    $meeting->start_at=$start;\n",[457,2186,2188],{"class":459,"line":2187},50,[457,2189,2190],{},"    $meeting->hash=substr(base_convert(bin2hex(openssl_random_pseudo_bytes(64)),16,36),0,64);\n",[457,2192,2194],{"class":459,"line":2193},51,[457,2195,2196],{},"    $meeting->is_canceled=false;\n",[457,2198,2200],{"class":459,"line":2199},52,[457,2201,693],{"emptyLinePlaceholder":692},[457,2203,2205],{"class":459,"line":2204},53,[457,2206,2207],{},"    $meeting->zoom_meeting_id=$result->id;\n",[457,2209,2211],{"class":459,"line":2210},54,[457,2212,2213],{},"    $meeting->zoom_join_url=$result->join_url;\n",[457,2215,2217],{"class":459,"line":2216},55,[457,2218,2219],{},"    $meeting->zoom_start_url=$result->start_url;\n",[457,2221,2223],{"class":459,"line":2222},56,[457,2224,2225],{},"    $meeting->zoom_password=$result->password;\n",[457,2227,2229],{"class":459,"line":2228},57,[457,2230,2231],{},"    $meeting->save();\n",[457,2233,2235],{"class":459,"line":2234},58,[457,2236,693],{"emptyLinePlaceholder":692},[457,2238,2240],{"class":459,"line":2239},59,[457,2241,2242],{},"    $format = $start->format('Y年m月d日 H時i分');\n",[457,2244,2246],{"class":459,"line":2245},60,[457,2247,2248],{},"    \u002F\u002F $meeting->start_at = $format;\n",[457,2250,2252],{"class":459,"line":2251},61,[457,2253,2254],{},"    \u002F\u002F $mail = new ContactMail($meeting);\n",[457,2256,2258],{"class":459,"line":2257},62,[457,2259,2260],{},"    \u002F\u002F Mail::to($request->email)->send($mail);\n",[457,2262,2264],{"class":459,"line":2263},63,[457,2265,693],{"emptyLinePlaceholder":692},[457,2267,2269],{"class":459,"line":2268},64,[457,2270,693],{"emptyLinePlaceholder":692},[457,2272,2274],{"class":459,"line":2273},65,[457,2275,2276],{},"    return redirect('\u002Fconfirm')->with([\n",[457,2278,2280],{"class":459,"line":2279},66,[457,2281,2282],{},"        'form_id'=>$meeting->id,\n",[457,2284,2286],{"class":459,"line":2285},67,[457,2287,2288],{},"        'name'=>$request->yourname,\n",[457,2290,2292],{"class":459,"line":2291},68,[457,2293,2294],{},"        'companyname'=>$request->companyname,\n",[457,2296,2298],{"class":459,"line":2297},69,[457,2299,2300],{},"        'content'=>$request->content,\n",[457,2302,2304],{"class":459,"line":2303},70,[457,2305,2306],{},"        'start_time'=>$format\n",[457,2308,2310],{"class":459,"line":2309},71,[457,2311,677],{},[457,2313,2315],{"class":459,"line":2314},72,[457,2316,564],{},[13,2318,2319],{},"長いですが、バリデーションからAPIのアクセスまで一通り行われています。",[63,2321,2322],{"id":2322},"有効期限チェックとエンドポイントリクエストの作成",[13,2324,2325],{},"まずは最初の方では有効期限のチェックを行い、そしてミーティングを作成するユーザー情報を取得しています。",[405,2327,2329],{"className":450,"code":2328,"filename":1422,"language":453,"meta":411,"style":411},"$user = $this->checkRefresh();\n$user = auth()->user();\n\n$zoom_user = $this->me();\n\n$url = 'https:\u002F\u002Fapi.zoom.us\u002Fv2\u002Fusers\u002F'.$zoom_user->id.'\u002Fmeetings';\n$client = new \\GuzzleHttp\\Client([\n    'headers' => [\n        'Authorization' => 'Bearer '.$user->access_token,\n        'Content-Type'=>'application\u002Fjson'\n    ],\n]);\n",[297,2330,2331,2336,2341,2345,2350,2354,2359,2363,2368,2373,2378,2383],{"__ignoreMap":411},[457,2332,2333],{"class":459,"line":460},[457,2334,2335],{},"$user = $this->checkRefresh();\n",[457,2337,2338],{"class":459,"line":466},[457,2339,2340],{},"$user = auth()->user();\n",[457,2342,2343],{"class":459,"line":472},[457,2344,693],{"emptyLinePlaceholder":692},[457,2346,2347],{"class":459,"line":4},[457,2348,2349],{},"$zoom_user = $this->me();\n",[457,2351,2352],{"class":459,"line":483},[457,2353,693],{"emptyLinePlaceholder":692},[457,2355,2356],{"class":459,"line":489},[457,2357,2358],{},"$url = 'https:\u002F\u002Fapi.zoom.us\u002Fv2\u002Fusers\u002F'.$zoom_user->id.'\u002Fmeetings';\n",[457,2360,2361],{"class":459,"line":495},[457,2362,1147],{},[457,2364,2365],{"class":459,"line":501},[457,2366,2367],{},"    'headers' => [\n",[457,2369,2370],{"class":459,"line":507},[457,2371,2372],{},"        'Authorization' => 'Bearer '.$user->access_token,\n",[457,2374,2375],{"class":459,"line":513},[457,2376,2377],{},"        'Content-Type'=>'application\u002Fjson'\n",[457,2379,2380],{"class":459,"line":519},[457,2381,2382],{},"    ],\n",[457,2384,2385],{"class":459,"line":525},[457,2386,843],{},[13,2388,2389,2390,628,2393,877,2396,2399,2400,2403],{},"ミーティングの作成を行うエンドポイントは ",[297,2391,2392],{},"https:\u002F\u002Fapi.zoom.us\u002Fv2\u002Fusers\u002F{zoom_user_id}\u002Fmeetings",[297,2394,2395],{},"{zoom_user_id}",[297,2397,2398],{},"me","で取得した",[297,2401,2402],{},"user_id","（連携したzoomアカウントのuser id）を挿入します。そしてリクエストヘッダーを付けてひとまず、GuzzleHttpのインスタンスを作成します。",[63,2405,2407],{"id":2406},"ミーティングパスワードを設定してapiへリクエスト","ミーティングパスワードを設定してAPIへリクエスト",[405,2409,2411],{"className":450,"code":2410,"filename":1422,"language":453,"meta":411,"style":411},"$topic = $request->companyname.' '.$request->yourname.'様 ご相談';        \n$meeting_password = substr(base_convert(bin2hex(openssl_random_pseudo_bytes(9)),16,36),0,9);\n$res = $client->request('POST',$url,[\n    \\GuzzleHttp\\RequestOptions::JSON => [\n        'topic'=>$topic,\n        'type'=>2,\n        'start_time'=>$request->startAt,\n        'password'=>$meeting_password\n    ]\n]);\n$result = json_decode($res->getBody()->getContents());\n",[297,2412,2413,2418,2423,2428,2433,2438,2443,2448,2453,2457,2461],{"__ignoreMap":411},[457,2414,2415],{"class":459,"line":460},[457,2416,2417],{},"$topic = $request->companyname.' '.$request->yourname.'様 ご相談';        \n",[457,2419,2420],{"class":459,"line":466},[457,2421,2422],{},"$meeting_password = substr(base_convert(bin2hex(openssl_random_pseudo_bytes(9)),16,36),0,9);\n",[457,2424,2425],{"class":459,"line":472},[457,2426,2427],{},"$res = $client->request('POST',$url,[\n",[457,2429,2430],{"class":459,"line":4},[457,2431,2432],{},"    \\GuzzleHttp\\RequestOptions::JSON => [\n",[457,2434,2435],{"class":459,"line":483},[457,2436,2437],{},"        'topic'=>$topic,\n",[457,2439,2440],{"class":459,"line":489},[457,2441,2442],{},"        'type'=>2,\n",[457,2444,2445],{"class":459,"line":495},[457,2446,2447],{},"        'start_time'=>$request->startAt,\n",[457,2449,2450],{"class":459,"line":501},[457,2451,2452],{},"        'password'=>$meeting_password\n",[457,2454,2455],{"class":459,"line":507},[457,2456,1186],{},[457,2458,2459],{"class":459,"line":513},[457,2460,843],{},[457,2462,2463],{"class":459,"line":519},[457,2464,1351],{},[13,2466,2467,2468,2473],{},"お客さんに入力してもらう様のパスワードを生成し、そして指定したエンドポイントへPOSTします。ここでPOSTパラメーター内にミーティング設定情報をJSONで記入します。どんな値が設定できるかは",[17,2469,2472],{"href":2470,"rel":2471},"https:\u002F\u002Fmarketplace.zoom.us\u002Fdocs\u002Fapi-reference\u002Fzoom-api\u002Fmeetings\u002Fmeetingcreate",[21],"ここで確認","できます。",[13,2475,2476],{},"上手く想像できない方は以下のGUIで行うzoom画面を参考にするといいです",[111,2478],{":src":2479,":width":265,":center":266},"'_mix\u002Fsch-2021-01-10-21.02.47-768x480.png'",[13,2481,2482,2483,2486,2487,2490],{},"ここで入力できる値は全て、APIでも入力できますのでリファランスでフォーマットなど確認しながら自分なりの設定をしましょう。私の場合、まずトピックを",[297,2484,2485],{},"「{会社名}　{客名様}　ご相談」","として必ず定義しており、そこは",[297,2488,2489],{},"'topic'=>$topic,","と定義してます。",[13,2492,2493],{},"start_timeは開催日時であり、フォームで入力された値を入れています。passwordは念のため付与しています。そしてAPIをリクエストします。",[63,2495,2497],{"id":2496},"api処理終了後","API処理終了後",[405,2499,2501],{"className":450,"code":2500,"filename":1422,"language":453,"meta":411,"style":411},"$result = json_decode($res->getBody()->getContents());\n\n$meeting = new Meeting();\n$meeting->name=$request->yourname;\n$meeting->company_name=$request->companyname;\n$meeting->email=$request->email;\n$meeting->content=$request->content;\n\n$start = new \\DateTime($result->start_time);\n$meeting->start_at=$start;\n$meeting->hash=substr(base_convert(bin2hex(openssl_random_pseudo_bytes(64)),16,36),0,64);\n$meeting->is_canceled=false;\n\n$meeting->zoom_meeting_id=$result->id;\n$meeting->zoom_join_url=$result->join_url;\n$meeting->zoom_start_url=$result->start_url;\n$meeting->zoom_password=$result->password;\n$meeting->save();\n\n$format = $start->format('Y年m月d日 H時i分');\n\u002F\u002F $meeting->start_at = $format;\n\u002F\u002F $mail = new ContactMail($meeting);\n\u002F\u002F Mail::to($request->email)->send($mail);\n\n\nreturn redirect('\u002Fconfirm')->with([\n    'form_id'=>$meeting->id,\n    'name'=>$request->yourname,\n    'companyname'=>$request->companyname,\n    'content'=>$request->content,\n    'start_time'=>$format\n]);\n",[297,2502,2503,2507,2511,2516,2521,2526,2531,2536,2540,2545,2550,2555,2560,2564,2569,2574,2579,2584,2589,2593,2598,2603,2608,2613,2617,2621,2626,2631,2636,2641,2646,2651],{"__ignoreMap":411},[457,2504,2505],{"class":459,"line":460},[457,2506,1351],{},[457,2508,2509],{"class":459,"line":466},[457,2510,693],{"emptyLinePlaceholder":692},[457,2512,2513],{"class":459,"line":472},[457,2514,2515],{},"$meeting = new Meeting();\n",[457,2517,2518],{"class":459,"line":4},[457,2519,2520],{},"$meeting->name=$request->yourname;\n",[457,2522,2523],{"class":459,"line":483},[457,2524,2525],{},"$meeting->company_name=$request->companyname;\n",[457,2527,2528],{"class":459,"line":489},[457,2529,2530],{},"$meeting->email=$request->email;\n",[457,2532,2533],{"class":459,"line":495},[457,2534,2535],{},"$meeting->content=$request->content;\n",[457,2537,2538],{"class":459,"line":501},[457,2539,693],{"emptyLinePlaceholder":692},[457,2541,2542],{"class":459,"line":507},[457,2543,2544],{},"$start = new \\DateTime($result->start_time);\n",[457,2546,2547],{"class":459,"line":513},[457,2548,2549],{},"$meeting->start_at=$start;\n",[457,2551,2552],{"class":459,"line":519},[457,2553,2554],{},"$meeting->hash=substr(base_convert(bin2hex(openssl_random_pseudo_bytes(64)),16,36),0,64);\n",[457,2556,2557],{"class":459,"line":525},[457,2558,2559],{},"$meeting->is_canceled=false;\n",[457,2561,2562],{"class":459,"line":531},[457,2563,693],{"emptyLinePlaceholder":692},[457,2565,2566],{"class":459,"line":537},[457,2567,2568],{},"$meeting->zoom_meeting_id=$result->id;\n",[457,2570,2571],{"class":459,"line":543},[457,2572,2573],{},"$meeting->zoom_join_url=$result->join_url;\n",[457,2575,2576],{"class":459,"line":549},[457,2577,2578],{},"$meeting->zoom_start_url=$result->start_url;\n",[457,2580,2581],{"class":459,"line":555},[457,2582,2583],{},"$meeting->zoom_password=$result->password;\n",[457,2585,2586],{"class":459,"line":561},[457,2587,2588],{},"$meeting->save();\n",[457,2590,2591],{"class":459,"line":1030},[457,2592,693],{"emptyLinePlaceholder":692},[457,2594,2595],{"class":459,"line":1036},[457,2596,2597],{},"$format = $start->format('Y年m月d日 H時i分');\n",[457,2599,2600],{"class":459,"line":1041},[457,2601,2602],{},"\u002F\u002F $meeting->start_at = $format;\n",[457,2604,2605],{"class":459,"line":1047},[457,2606,2607],{},"\u002F\u002F $mail = new ContactMail($meeting);\n",[457,2609,2610],{"class":459,"line":1052},[457,2611,2612],{},"\u002F\u002F Mail::to($request->email)->send($mail);\n",[457,2614,2615],{"class":459,"line":1058},[457,2616,693],{"emptyLinePlaceholder":692},[457,2618,2619],{"class":459,"line":1064},[457,2620,693],{"emptyLinePlaceholder":692},[457,2622,2623],{"class":459,"line":1070},[457,2624,2625],{},"return redirect('\u002Fconfirm')->with([\n",[457,2627,2628],{"class":459,"line":1076},[457,2629,2630],{},"    'form_id'=>$meeting->id,\n",[457,2632,2633],{"class":459,"line":1081},[457,2634,2635],{},"    'name'=>$request->yourname,\n",[457,2637,2638],{"class":459,"line":1086},[457,2639,2640],{},"    'companyname'=>$request->companyname,\n",[457,2642,2643],{"class":459,"line":1092},[457,2644,2645],{},"    'content'=>$request->content,\n",[457,2647,2648],{"class":459,"line":1098},[457,2649,2650],{},"    'start_time'=>$format\n",[457,2652,2653],{"class":459,"line":1104},[457,2654,843],{},[13,2656,2657],{},"$resultにzoomからのレスポンスがあるので適宜Meetingモデルやメールに格納します。そして最後にユーザーは確認画面が表示されます。",[39,2659,2660],{"id":2660},"自分のアカウントで実験",[13,2662,2663],{},"では実験してみます。連携した管理者のzoomアカウントでミーティング一覧をみています。リクエスト前はこの様に何もありません。",[111,2665],{":src":2666,":width":114},"'_mix\u002Fzoom_meeting_index-768x322.jpeg'",[13,2668,2669],{},"そこでフォームにこの様に入力していきます。（今回は管理者自身が入力）",[111,2671],{":src":2672,":width":114},"'_mix\u002Fsch-2021-01-10-21.10.26-768x699.png'",[13,2674,2675],{},"そして送信を押すとちょっとロードして、こちらの画面にリダイレクトされます。",[111,2677],{":src":2678,":width":114},"'_mix\u002Fzoom_confirm-768x902.jpeg'",[13,2680,2681],{},"そして先ほどのzoom一覧を見てみると、",[111,2683],{":src":2684,":width":114},"'_mix\u002Fzoom_meeting_create-768x412.jpeg'",[13,2686,2687],{},"JUNE様ですね。きちんとミーティングが作られています。（時間がずれているのはコンテナ側のタイムゾーンの設定をすっかり忘れていたからです。)そしてテーブルを見てみると",[111,2689],{":src":2690,":width":114},"'_mix\u002Fsch-2021-01-10-21.20.37-768x75.png'",[13,2692,2693],{},"きちんと作られていました。zoom_join_urlの値にアクセスすると",[111,2695],{":src":2696,":width":114},"'_mix\u002Fzoom_url_test-768x467.jpeg'",[13,2698,2699],{},"管理者が直接言っているのでミーティングの開始となっていますが、きちんと有効なミーティングURLを取得し保存できています。本来であればメールでお客様にこのURLとパスワードをお知らせします。",[13,2701,2702],{},"また管理画面では",[111,2704],{":src":2705,":width":114},"'_mix\u002Fsch-2021-01-10-21.24.07-768x143.png'",[13,2707,2708,2709,2712,2713,2716,2717,2720],{},"この様にして一覧で確認ができます。ちなみに「ミーティングを削除」などのボタンは",[297,2710,2711],{},"http:\u002F\u002Flocalhost:9000\u002Fform\u002Fdelete?hash=cgckkwc040okko..","へリンクされています。",[297,2714,2715],{},"form\u002Fdelete","でミーティングを削除する確認画面へ飛べます。そして照合のために",[297,2718,2719],{},"hash=cgckkwc040okko..","の値を用いています。（途中にあった$hashの値です。）",[13,2722,2723],{},"お客様が匿名であり、ユーザーセッションによる識別ができない時は、予測困難なハッシュ付きURLをお客様だけのメールに渡してミーティングの制御が可能です。",[39,2725,2726],{"id":2726},"まとめ",[13,2728,2729],{},"以上がアプリの実装の流れです。今回作成したOAuth認証zoom アプリはプライベートなので作った本人しか今は利用できませんが、しっかり実装して審査を受けることでマーケットプレイスに出店して自由に使用してもらうことが可能になります。",[13,2731,2732,2733,2737],{},"OAuthも意外と簡単でしたが、公式のAPIリクエストライブラリが出ているわけでないので本格的な開発の際には独自のzoom APIライブラリを作っておくといいかもしれません。zoomでできることはこれだけでないので、もっと色々調べてみようと思います。ひとまず今回のzoomアプリはここまでとします。",[17,2734,2736],{"href":190,"rel":2735},[21],"一応リポジトリに公開してある","のでぜひクローンして遊んでみてください。",[56,2739,2741],{"id":2740},"追記-2021-3-31","追記 2021 3 31",[13,2743,2744],{},"なんか、これぐらいの規模で特定のアプリでの利用であればJWTでも十分でした汗。JWT編もそのうちやろうと思います。",[2746,2747,2748],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":411,"searchDepth":472,"depth":472,"links":2750},[2751,2759,2762,2763,2772,2777,2781,2782,2783,2791,2792],{"id":41,"depth":466,"text":42,"children":2752},[2753,2757,2758],{"id":58,"depth":472,"text":58,"children":2754},[2755,2756],{"id":65,"depth":4,"text":66},{"id":84,"depth":4,"text":85},{"id":106,"depth":472,"text":106},{"id":139,"depth":472,"text":140},{"id":146,"depth":466,"text":146,"children":2760},[2761],{"id":158,"depth":472,"text":158},{"id":176,"depth":466,"text":176},{"id":229,"depth":466,"text":230,"children":2764},[2765,2766],{"id":233,"depth":472,"text":234},{"id":272,"depth":472,"text":272,"children":2767},[2768,2769,2770,2771],{"id":275,"depth":4,"text":275},{"id":346,"depth":4,"text":346},{"id":355,"depth":4,"text":356},{"id":368,"depth":4,"text":368},{"id":377,"depth":466,"text":378,"children":2773},[2774,2775,2776],{"id":384,"depth":472,"text":385},{"id":443,"depth":472,"text":444},{"id":600,"depth":472,"text":600},{"id":617,"depth":466,"text":618,"children":2778},[2779,2780],{"id":621,"depth":472,"text":621},{"id":920,"depth":472,"text":921},{"id":1415,"depth":466,"text":1415},{"id":1536,"depth":466,"text":1536},{"id":1670,"depth":466,"text":1671,"children":2784},[2785,2786],{"id":1680,"depth":472,"text":1681},{"id":1939,"depth":472,"text":1940,"children":2787},[2788,2789,2790],{"id":2322,"depth":4,"text":2322},{"id":2406,"depth":4,"text":2407},{"id":2496,"depth":4,"text":2497},{"id":2660,"depth":466,"text":2660},{"id":2726,"depth":466,"text":2726,"children":2793},[2794],{"id":2740,"depth":472,"text":2741},[2796],"devstack","2021-01-10","md",null,{},"\u002Fen\u002Farticles\u002Fzoom-api-laravel",{"title":8,"description":8},"en\u002Farticles\u002Fzoom-api-laravel",[2805,2806],"laravel","zoom","_mix\u002Ftop-768x463.jpeg","f5QUhprj0p3-EJlQVJWyjkcIytNdaDu3FJENo9K2mB4",{"id":2810,"title":2811,"body":2812,"category":3523,"createdAt":3524,"description":3525,"extension":2798,"index":460,"meta":3526,"navigation":692,"path":3527,"publish":692,"seo":3528,"series":3529,"seriesTitle":3525,"stem":3530,"tag":3531,"thumbnail":3534,"updatedAt":2799,"__hash__":3535},"en_series\u002Fen\u002Fseries\u002Fconcrete5vue-1.md","Concrete5 + Vue CLI3： Making Rich UI in package. 1st： Set up vue project on Concrete5 package.",{"type":10,"value":2813,"toc":3510},[2814,2825,2828,2832,2835,2897,2900,2903,2906,2909,2920,2923,2927,2930,2934,2938,2941,2947,2951,2954,2960,2963,2969,2975,2982,2985,2989,3003,3007,3013,3020,3163,3169,3179,3185,3344,3350,3353,3360,3428,3442,3448,3459,3464,3491,3494,3497,3500,3504,3507],[13,2815,2816,2817,2820,2821,2824],{},"Hello everyone. In this post, I explain how to use ",[292,2818,2819],{},"Vue.js"," to make rich UI in ",[292,2822,2823],{},"Concrete5 package",". At there, I write the process that create vue project in C5 package directory and use compiled js file.",[13,2826,2827],{},"I assumed Readers know how to customize C5 packages and basic C5 knowledge.The C5’s version is 8.4+ in this post.",[39,2829,2831],{"id":2830},"why-use-vuejs","Why use Vue.js?",[13,2833,2834],{},"Reason is simple. Because making rich UI with PHP(C5 system) and jquery is so hard.\nC5 prepares helper to print form as below.",[405,2836,2838],{"className":450,"code":2837,"language":453,"meta":411,"style":411},"\u002F\u002F Read Helper\n$form = Core::make('helper\u002Fform');\n\n\u002F\u002Finput type of text\necho $form->text($name, $default_value);\n\n\u002F\u002Ftext area\necho $form-> textarea($name, $default_value);\n\n\u002F\u002F File manager\n$file_selector = Core::make('helper\u002Fconcrete\u002Ffile_manager');\necho $file_selector->file('label', 'name_attr', 'Select Photo', $default_fileObj);\n",[297,2839,2840,2845,2850,2854,2859,2864,2868,2873,2878,2882,2887,2892],{"__ignoreMap":411},[457,2841,2842],{"class":459,"line":460},[457,2843,2844],{},"\u002F\u002F Read Helper\n",[457,2846,2847],{"class":459,"line":466},[457,2848,2849],{},"$form = Core::make('helper\u002Fform');\n",[457,2851,2852],{"class":459,"line":472},[457,2853,693],{"emptyLinePlaceholder":692},[457,2855,2856],{"class":459,"line":4},[457,2857,2858],{},"\u002F\u002Finput type of text\n",[457,2860,2861],{"class":459,"line":483},[457,2862,2863],{},"echo $form->text($name, $default_value);\n",[457,2865,2866],{"class":459,"line":489},[457,2867,693],{"emptyLinePlaceholder":692},[457,2869,2870],{"class":459,"line":495},[457,2871,2872],{},"\u002F\u002Ftext area\n",[457,2874,2875],{"class":459,"line":501},[457,2876,2877],{},"echo $form-> textarea($name, $default_value);\n",[457,2879,2880],{"class":459,"line":507},[457,2881,693],{"emptyLinePlaceholder":692},[457,2883,2884],{"class":459,"line":513},[457,2885,2886],{},"\u002F\u002F File manager\n",[457,2888,2889],{"class":459,"line":519},[457,2890,2891],{},"$file_selector = Core::make('helper\u002Fconcrete\u002Ffile_manager');\n",[457,2893,2894],{"class":459,"line":525},[457,2895,2896],{},"echo $file_selector->file('label', 'name_attr', 'Select Photo', $default_fileObj);\n",[13,2898,2899],{},"When I write them on page view.php, forms are shown as below.",[111,2901],{":src":2902,":width":114},"'_mix\u002Fformhelpertest-768x272.png'",[13,2904,2905],{},"Input text, textarea and C5’s file manager appeared. These has name property and can post data.",[13,2907,2908],{},"Simply forms are enough to use them. But,",[195,2910,2911,2914,2917],{},[122,2912,2913],{},"Implication of front-end validation",[122,2915,2916],{},"Repeatable form",[122,2918,2919],{},"Dependency Forms (There are some patterns correspond to inputed value)",[13,2921,2922],{},"These implication is hard with only jquery and PHP.",[56,2924,2926],{"id":2925},"lets-use-vuejs-power","Let’s use vue.js power",[13,2928,2929],{},"Many Rich Form’s UI are developed easily with Vue.js, React.js and backbone.js( which manage data state and HTML template by javascript.) Using those libraries, we can develop rich UI while reducing the number of development days.",[39,2931,2933],{"id":2932},"preparation-of-creating-package","Preparation of creating package",[56,2935,2937],{"id":2936},"create-a-dedicated-directory-for-package","Create a dedicated directory for package",[13,2939,2940],{},"Let’s make package has UI built by vue. Create custom package directory named ‘vuetest’ under \u002Fpackage . Directory structure as below.",[405,2942,2945],{"className":2943,"code":2944,"language":410},[408],"documentroot $ cd .\u002Fpackage\npackage $ mkdir vuetest && touch ...\npackage $ tree vuetest\n.\u002F\n└── vuetest\n    ├── controller.php\n    ├── controllers\n    │   └── single_page\n    │       └── dashboard\n    │           └── vuetest.php\n    ├── db.xml\n    ├── icon.png\n    ├── js\n    │\n    └── single_pages\n        └── dashboard\n            └── vuetest\n                └── view.php\n",[297,2946,2944],{"__ignoreMap":411},[56,2948,2950],{"id":2949},"create-vue-project","Create Vue project",[13,2952,2953],{},"Under package\u002Fvuetest, there are package controller file, single page file that shows UI. Then let’s create js directory and make vue project there, named as ‘packageui’.",[405,2955,2958],{"className":2956,"code":2957,"language":410},[408],"package $ cd vuetest\u002Fjs\njs $ vue create packageui\n\nVue CLI v4.4.6\n? Please pick a preset: default (babel, eslint) #default\n\nVue CLI v4.4.6\n✨  Creating project in documentroot\u002Fvuetest\u002Fjs\u002Fpackageui.\n🗃  Initializing git repository...\n⚙️  Installing CLI plugins. This might take a while... \n\n🎉  Successfully created project packageui.\n\njs $ cd packageui && tree -L 1\n.\n├── README.md\n├── babel.config.js\n├── node_modules\n├── package-lock.json\n├── package.json\n├── public\n",[297,2959,2957],{"__ignoreMap":411},[13,2961,2962],{},"Vue project has been created successfully! Next, create vue.config.js and write webpack setting for C5’s asset system.",[405,2964,2967],{"className":2965,"code":2966,"language":410},[408],"packageui $ touch vue.config.js\n",[297,2968,2966],{"__ignoreMap":411},[405,2970,2973],{"className":2971,"code":2972,"language":410},[408],"module.exports = {\n    configureWebpack: {\n      output: {\n        filename: '[name].js',\n        chunkFilename: '[name].js'\n      }\n    },\n  }\n",[297,2974,2972],{"__ignoreMap":411},[13,2976,2977,2978,2981],{},"This file configs that the name of compiled js files from vue file when running npm run build will be same name under the ",[297,2979,2980],{},"\u002Fdist"," directory.",[13,2983,2984],{},"When we build vue file on initial webpack config, built js file has been had hashed letters such as app384#34a.js . The hash will change at every build. Compiled js file name must be always same to be read by C5’s asset system. So I config that file.",[63,2986,2988],{"id":2987},"if-you-want-use-vue-project-by-multiple-packages","If you want use vue project by multiple packages",[13,2990,2991,2992,2995,2996,2999,3000],{},"In this time, I create dedicated vue project under the package called vuetest and its directory. But if you use it by multi packages, create ",[297,2993,2994],{},"\u002Fjs"," directory under ",[297,2997,2998],{},"\u002Fapplication"," directory and make vue project under ",[297,3001,3002],{},"\u002Fapplication\u002Fjs",[56,3004,3006],{"id":3005},"combine-c5-and-compiled-js-file","Combine C5 and compiled js file",[13,3008,3009,3010,3012],{},"When running build with vue cli, compiled js file will be generated under ",[297,3011,2980],{}," directory. Let’s config as C5 system and page renders components can read that files!",[13,3014,3015,3016,3019],{},"By the way, build ",[297,3017,3018],{},"main.js"," . That file renders App component at page contains div element with id attribute named as ‘app’.",[405,3021,3026],{"className":3022,"code":3023,"filename":3024,"language":3025,"meta":411,"style":411},"language-javascript shiki shiki-themes material-theme-ocean","import Vue from 'vue'\nimport App from '.\u002FApp.vue'\n\nVue.config.productionTip = false\n\nnew Vue({\n  render: h => h(App),\n}).$mount('#app')\n","src\u002Fmain.js","javascript",[297,3027,3028,3052,3068,3072,3095,3099,3113,3137],{"__ignoreMap":411},[457,3029,3030,3034,3038,3041,3045,3049],{"class":459,"line":460},[457,3031,3033],{"class":3032},"s6cf3","import",[457,3035,3037],{"class":3036},"s0W1g"," Vue ",[457,3039,3040],{"class":3032},"from",[457,3042,3044],{"class":3043},"sAklC"," '",[457,3046,3048],{"class":3047},"sfyAc","vue",[457,3050,3051],{"class":3043},"'\n",[457,3053,3054,3056,3059,3061,3063,3066],{"class":459,"line":466},[457,3055,3033],{"class":3032},[457,3057,3058],{"class":3036}," App ",[457,3060,3040],{"class":3032},[457,3062,3044],{"class":3043},[457,3064,3065],{"class":3047},".\u002FApp.vue",[457,3067,3051],{"class":3043},[457,3069,3070],{"class":459,"line":472},[457,3071,693],{"emptyLinePlaceholder":692},[457,3073,3074,3077,3080,3083,3085,3088,3091],{"class":459,"line":4},[457,3075,3076],{"class":3036},"Vue",[457,3078,3079],{"class":3043},".",[457,3081,3082],{"class":3036},"config",[457,3084,3079],{"class":3043},[457,3086,3087],{"class":3036},"productionTip ",[457,3089,3090],{"class":3043},"=",[457,3092,3094],{"class":3093},"sbqyR"," false\n",[457,3096,3097],{"class":459,"line":483},[457,3098,693],{"emptyLinePlaceholder":692},[457,3100,3101,3104,3108,3111],{"class":459,"line":489},[457,3102,3103],{"class":3043},"new",[457,3105,3107],{"class":3106},"sdLwU"," Vue",[457,3109,3110],{"class":3036},"(",[457,3112,469],{"class":3043},[457,3114,3115,3118,3121,3125,3129,3131,3134],{"class":459,"line":495},[457,3116,3117],{"class":3106},"  render",[457,3119,3120],{"class":3043},":",[457,3122,3124],{"class":3123},"s7ZW3"," h",[457,3126,3128],{"class":3127},"sJ14y"," =>",[457,3130,3124],{"class":3106},[457,3132,3133],{"class":3036},"(App)",[457,3135,3136],{"class":3043},",\n",[457,3138,3139,3142,3145,3147,3150,3152,3155,3158,3160],{"class":459,"line":501},[457,3140,3141],{"class":3043},"}",[457,3143,3144],{"class":3036},")",[457,3146,3079],{"class":3043},[457,3148,3149],{"class":3106},"$mount",[457,3151,3110],{"class":3036},[457,3153,3154],{"class":3043},"'",[457,3156,3157],{"class":3047},"#app",[457,3159,3154],{"class":3043},[457,3161,3162],{"class":3036},")\n",[405,3164,3167],{"className":3165,"code":3166,"language":410},[408],"packageui ＄ npm run build\n.\n├── README.md\n├── babel.config.js\n├── dist\n│   ├── app.js\n│   ├── app.js.map\n│   ├── chunk-vendors.js\n│   ├── chunk-vendors.js.map\n│   ├── css\n│   ├── favicon.ico\n│   ├── img\n│   └── index.html\n├\n",[297,3168,3166],{"__ignoreMap":411},[13,3170,3171,3172,3175,3176],{},"Build succeeded. Some files are not needed, but it is just ok to config as reading ",[297,3173,3174],{},"app.js"," and ",[297,3177,3178],{},"chunk-vendor.js",[13,3180,3181,3182,3144],{},"To read those js files, write some PHP script in package’s install controller php file (",[297,3183,3184],{},"package\u002Fvuetest\u002Fcontroller.php",[405,3186,3189],{"className":450,"code":3187,"filename":3188,"language":453,"meta":411,"style":411},"\u003C?php\nnamespace Concrete\\Package\\Vuetest;\ndefined('C5_EXECUTE') or die('Access Denied.');\nuse \\Concrete\\Core\\Asset\\AssetList;\nuse \\Concrete\\Core\\Asset\\Asset;\n\nclass Controller extends \\Concrete\\Core\\Package\\Package {\n    protected $pkgHandle = 'vuetest';\n    protected $appVersionRequired = '5.7.4';\n    protected $pkgVersion = '1.0.0';\n\n    public function on_start()\n    {\n        $al = AssetList::getInstance();\n        $al->register(\n            'javascript', 'package-vue-build', 'js\u002Fpackageui\u002Fdist\u002Fapp.js',\n            array('version' => '1.0.0', 'position' => Asset::ASSET_POSITION_FOOTER, 'combine' => true),\n            $this->pkgHandle\n        );\n        \n        $al->register(\n            'javascript', 'package-vue-chunk', 'js\u002Fpackageui\u002Fdist\u002Fchunk-vendors.js',\n            array('version' => '1.0.0', 'position' => Asset::ASSET_POSITION_FOOTER, 'combine' => true),\n            $this->pkgHandle\n        );\n        \n        $al->registerGroup('package-vue-production', array(\n            array('javascript', 'package-vue-build'),\n            array('javascript', 'package-vue-chunk'),\n        )); \n    }\n...\n}\n","controller.php",[297,3190,3191,3195,3200,3205,3210,3215,3219,3224,3229,3234,3239,3243,3248,3252,3257,3262,3267,3272,3277,3282,3287,3291,3296,3300,3304,3308,3312,3317,3322,3327,3332,3336,3340],{"__ignoreMap":411},[457,3192,3193],{"class":459,"line":460},[457,3194,1692],{},[457,3196,3197],{"class":459,"line":466},[457,3198,3199],{},"namespace Concrete\\Package\\Vuetest;\n",[457,3201,3202],{"class":459,"line":472},[457,3203,3204],{},"defined('C5_EXECUTE') or die('Access Denied.');\n",[457,3206,3207],{"class":459,"line":4},[457,3208,3209],{},"use \\Concrete\\Core\\Asset\\AssetList;\n",[457,3211,3212],{"class":459,"line":483},[457,3213,3214],{},"use \\Concrete\\Core\\Asset\\Asset;\n",[457,3216,3217],{"class":459,"line":489},[457,3218,693],{"emptyLinePlaceholder":692},[457,3220,3221],{"class":459,"line":495},[457,3222,3223],{},"class Controller extends \\Concrete\\Core\\Package\\Package {\n",[457,3225,3226],{"class":459,"line":501},[457,3227,3228],{},"    protected $pkgHandle = 'vuetest';\n",[457,3230,3231],{"class":459,"line":507},[457,3232,3233],{},"    protected $appVersionRequired = '5.7.4';\n",[457,3235,3236],{"class":459,"line":513},[457,3237,3238],{},"    protected $pkgVersion = '1.0.0';\n",[457,3240,3241],{"class":459,"line":519},[457,3242,693],{"emptyLinePlaceholder":692},[457,3244,3245],{"class":459,"line":525},[457,3246,3247],{},"    public function on_start()\n",[457,3249,3250],{"class":459,"line":531},[457,3251,1764],{},[457,3253,3254],{"class":459,"line":537},[457,3255,3256],{},"        $al = AssetList::getInstance();\n",[457,3258,3259],{"class":459,"line":543},[457,3260,3261],{},"        $al->register(\n",[457,3263,3264],{"class":459,"line":549},[457,3265,3266],{},"            'javascript', 'package-vue-build', 'js\u002Fpackageui\u002Fdist\u002Fapp.js',\n",[457,3268,3269],{"class":459,"line":555},[457,3270,3271],{},"            array('version' => '1.0.0', 'position' => Asset::ASSET_POSITION_FOOTER, 'combine' => true),\n",[457,3273,3274],{"class":459,"line":561},[457,3275,3276],{},"            $this->pkgHandle\n",[457,3278,3279],{"class":459,"line":1030},[457,3280,3281],{},"        );\n",[457,3283,3284],{"class":459,"line":1036},[457,3285,3286],{},"        \n",[457,3288,3289],{"class":459,"line":1041},[457,3290,3261],{},[457,3292,3293],{"class":459,"line":1047},[457,3294,3295],{},"            'javascript', 'package-vue-chunk', 'js\u002Fpackageui\u002Fdist\u002Fchunk-vendors.js',\n",[457,3297,3298],{"class":459,"line":1052},[457,3299,3271],{},[457,3301,3302],{"class":459,"line":1058},[457,3303,3276],{},[457,3305,3306],{"class":459,"line":1064},[457,3307,3281],{},[457,3309,3310],{"class":459,"line":1070},[457,3311,3286],{},[457,3313,3314],{"class":459,"line":1076},[457,3315,3316],{},"        $al->registerGroup('package-vue-production', array(\n",[457,3318,3319],{"class":459,"line":1081},[457,3320,3321],{},"            array('javascript', 'package-vue-build'),\n",[457,3323,3324],{"class":459,"line":1086},[457,3325,3326],{},"            array('javascript', 'package-vue-chunk'),\n",[457,3328,3329],{"class":459,"line":1092},[457,3330,3331],{},"        )); \n",[457,3333,3334],{"class":459,"line":1098},[457,3335,1112],{},[457,3337,3338],{"class":459,"line":1104},[457,3339,1489],{},[457,3341,3342],{"class":459,"line":1109},[457,3343,564],{},[13,3345,3346,3349],{},[297,3347,3348],{},"$al->register"," registers those two js files referred by path at controller of vuetest package. For simply asset reading, let’s group two files as name of package-vue-production .",[13,3351,3352],{},"C5 has asset registering\u002Freading system like this. Using this system, we can use registered files anywhere pages without file path problem. Now, we have registered js files, so next we set reading them on the page.",[13,3354,3355,3356,3359],{},"Write PHP script in ",[297,3357,3358],{},"vuetest\u002Fsingle_pages\u002Fdashboard\u002Fvuetest\u002Fview.php"," as below",[405,3361,3363],{"className":450,"code":3362,"language":453,"meta":411,"style":411},"\u003C?php\nnamespace Concrete\\Package\\Vuetest\\Controller\\SinglePage\\Dashboard;\ndefined('C5_EXECUTE') or die('Access Denied.');\nuse \\Concrete\\Core\\Page\\Controller\\DashboardPageController;\n\nclass Vuetest extends DashboardPageController\n{\n    public $packageHandle = 'vuetest';\n\n    public function view() {\n        $this->requireAsset('package-vue-production');\n        $this->set('success', 'My success message');\n    }\n}\n",[297,3364,3365,3369,3374,3378,3383,3387,3392,3396,3401,3405,3410,3415,3420,3424],{"__ignoreMap":411},[457,3366,3367],{"class":459,"line":460},[457,3368,1692],{},[457,3370,3371],{"class":459,"line":466},[457,3372,3373],{},"namespace Concrete\\Package\\Vuetest\\Controller\\SinglePage\\Dashboard;\n",[457,3375,3376],{"class":459,"line":472},[457,3377,3204],{},[457,3379,3380],{"class":459,"line":4},[457,3381,3382],{},"use \\Concrete\\Core\\Page\\Controller\\DashboardPageController;\n",[457,3384,3385],{"class":459,"line":483},[457,3386,693],{"emptyLinePlaceholder":692},[457,3388,3389],{"class":459,"line":489},[457,3390,3391],{},"class Vuetest extends DashboardPageController\n",[457,3393,3394],{"class":459,"line":495},[457,3395,469],{},[457,3397,3398],{"class":459,"line":501},[457,3399,3400],{},"    public $packageHandle = 'vuetest';\n",[457,3402,3403],{"class":459,"line":507},[457,3404,693],{"emptyLinePlaceholder":692},[457,3406,3407],{"class":459,"line":513},[457,3408,3409],{},"    public function view() {\n",[457,3411,3412],{"class":459,"line":519},[457,3413,3414],{},"        $this->requireAsset('package-vue-production');\n",[457,3416,3417],{"class":459,"line":525},[457,3418,3419],{},"        $this->set('success', 'My success message');\n",[457,3421,3422],{"class":459,"line":531},[457,3423,1112],{},[457,3425,3426],{"class":459,"line":537},[457,3427,564],{},[13,3429,3430,3433,3434,3437,3438,3441],{},[297,3431,3432],{},"$this->requireAsset('package-vue-production');"," orders page controller that ‘Read registered asset name as ",[297,3435,3436],{},"“package-vue-production”"," on this page(",[297,3439,3440],{},"view.php",")!‘",[13,3443,3444,3445],{},"After setting, let’s search if the js files are read. Opening Chrome developer tool…\n",[111,3446],{":src":3447,":width":114},"'_mix\u002Fsc-2020-08-01-19.52.09-768x133.png'",[13,3449,3450,3451,3454,3455,3458],{},"Path ",[297,3452,3453],{},"\u002Fpackages\u002Fvuetest\u002Fjs\u002Fdist\u002F**"," are found! Next, create div element has ",[297,3456,3457],{},"id=\"app\""," and see the page again.",[13,3460,3461],{},[297,3462,3463],{},"Invuetest\u002Fsingle_pages\u002Fdashboard\u002Fvuetest\u002Fview.php",[405,3465,3467],{"className":450,"code":3466,"filename":3440,"language":453,"meta":411,"style":411},"\u003C?php\ndefined('C5_EXECUTE') or die('Access Denied.');\n?>\n\n\u003Cdiv id=\"app\">\u003C\u002Fdiv>\n",[297,3468,3469,3473,3477,3482,3486],{"__ignoreMap":411},[457,3470,3471],{"class":459,"line":460},[457,3472,1692],{},[457,3474,3475],{"class":459,"line":466},[457,3476,3204],{},[457,3478,3479],{"class":459,"line":472},[457,3480,3481],{},"?>\n",[457,3483,3484],{"class":459,"line":4},[457,3485,693],{"emptyLinePlaceholder":692},[457,3487,3488],{"class":459,"line":483},[457,3489,3490],{},"\u003Cdiv id=\"app\">\u003C\u002Fdiv>\n",[13,3492,3493],{},"Reload page…",[111,3495],{":src":3496,":width":265,":center":266},"'_mix\u002Fsh-2020-08-01-19.56.01-768x812.png'",[13,3498,3499],{},"Vue component (App) are rendered successfully!! Some image and layout dose not work because the paths for img and css are incorrect. But created vue component itself with vue CLI are read and appeared on target page.",[39,3501,3503],{"id":3502},"next-step","Next Step",[13,3505,3506],{},"The above is the method to create a vue project on the concrete5 package and combine them. Setting to use vue has been readied, so next step, we create form to input data ,flow to register data in DB and registered data editing page .",[2746,3508,3509],{},"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 .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}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 .sbqyR, html code.shiki .sbqyR{--shiki-default:#FF9CAC}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 pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}",{"title":411,"searchDepth":472,"depth":472,"links":3511},[3512,3515,3522],{"id":2830,"depth":466,"text":2831,"children":3513},[3514],{"id":2925,"depth":472,"text":2926},{"id":2932,"depth":466,"text":2933,"children":3516},[3517,3518,3521],{"id":2936,"depth":472,"text":2937},{"id":2949,"depth":472,"text":2950,"children":3519},[3520],{"id":2987,"depth":4,"text":2988},{"id":3005,"depth":472,"text":3006},{"id":3502,"depth":466,"text":3503},[2796],"2020-08-25","Making Rich UI in Concrete5 with VueCLI.",{},"\u002Fen\u002Fseries\u002Fconcrete5vue-1",{"title":2811,"description":3525},"enconcrete5vue","en\u002Fseries\u002Fconcrete5vue-1",[3532,3533,3048],"concrete5","js","_mix\u002Fvuewithconcrete.png","uCGiRmlnXldeIG8DikjaUPsIaa4G61Yrz5b1kwmPD2w",{"id":3537,"title":3538,"body":3539,"category":5977,"createdAt":2799,"description":5978,"extension":2798,"index":2799,"meta":5979,"navigation":692,"path":5980,"publish":692,"seo":5981,"series":2799,"seriesTitle":2799,"stem":5982,"tag":5983,"thumbnail":5984,"updatedAt":2799,"__hash__":5985},"en_articles\u002Fen\u002Farticles\u002Fgoogle-map-vuejs.md","How to draw routes on Google Map with Google Maps API and Vue.js",{"type":10,"value":3540,"toc":5964},[3541,3544,3548,3557,3566,3569,3572,3576,3585,3591,3595,3605,3935,3942,3945,3949,3955,3995,3998,4054,4064,4202,4205,4209,4215,4510,4519,4522,4530,4543,4549,4552,4558,4663,4667,4670,5076,5082,5085,5150,5153,5156,5159,5163,5170,5173,5518,5525,5528,5531,5535,5538,5542,5545,5548,5562,5568,5923,5941,5944,5947,5951,5954,5958,5961],[13,3542,3543],{},"Hello developers! Thank you for reading my article. When I developed my own application, I searched how to draw lines on Google Map with Vue. And I have manages to develop it. This article, explains about developing function to dispaly map on your web browser with Google Map API and Vue.js(or vanilla.js). I omit partially how to install vue.js and obtain Google Map API key.",[39,3545,3547],{"id":3546},"obtain-google-map-api-key","Obtain Google Map API key",[13,3549,3550,3551,3556],{},"I omit detail to get but explain minimum requirements. At first, obtain Google Map API at ",[17,3552,3555],{"href":3553,"rel":3554},"https:\u002F\u002Fcloud.google.com\u002Fmaps-platform?hl=ja",[21],"Google Cloud Platform"," with your Google Account.",[13,3558,3559,3560,3565],{},"Requests for $ 200 per month are free. So you can test within the free limit. And you need ",[17,3561,3564],{"href":3562,"rel":3563},"https:\u002F\u002Fconsole.cloud.google.com\u002Fapis\u002Flibrary\u002Fmaps-backend.googleapis.com?",[21],"Maps JavaScript API"," to render Google Map on the web browser.",[111,3567],{":src":3568,":width":265,":center":266},"'_mix\u002Fsch-2021-03-07-17.15.12.png'",[13,3570,3571],{},"Enable this API key and you will obtain the key code. That's all to set up for API.",[39,3573,3575],{"id":3574},"prepear-vuejs","Prepear Vue.js",[13,3577,3578,3579,3584],{},"In this time, we develop with ",[17,3580,3583],{"href":3581,"rel":3582},"https:\u002F\u002Fcli.vuejs.org\u002F",[21],"Vue CLI",". Create a project with Vue CLI.",[405,3586,3589],{"className":3587,"code":3588,"language":410},[408],"$ vue create vue_map\n",[297,3590,3588],{"__ignoreMap":411},[56,3592,3594],{"id":3593},"load-for-api-sourrce-file","Load for api sourrce file",[13,3596,3597,3598,3601,3602,2981],{},"Insert the below codes to ",[297,3599,3600],{},"index.html"," under the ",[297,3603,3604],{},"\u002Fpublic",[405,3606,3611],{"className":3607,"code":3608,"filename":3609,"language":3610,"meta":411,"style":411},"language-html shiki shiki-themes material-theme-ocean","\u003C!DOCTYPE html>\n\u003Chtml lang=\"\">\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 rel=\"icon\" href=\"\u003C%= BASE_URL %>favicon.ico\">\n    \n　　\u003C!-- Insert this script -->\n    \u003Cscript src=\"http:\u002F\u002Fmaps.google.com\u002Fmaps\u002Fapi\u002Fjs?key=YOUR_API_KEY&language=ja\">\u003C\u002Fscript>\n   \n　　 \u003Ctitle>\u003C%= htmlWebpackPlugin.options.title %>\u003C\u002Ftitle>\n  \u003C\u002Fhead>\n  \u003Cbody>\n    \u003Cnoscript>\n      \u003Cstrong>We're sorry but \u003C%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.\u003C\u002Fstrong>\n    \u003C\u002Fnoscript>\n    \u003Cdiv id=\"app\">\u003C\u002Fdiv>\n    \u003C!-- built files will be auto injected -->\n  \u003C\u002Fbody>\n\u003C\u002Fhtml>\n","public\u002Findex.html","html",[297,3612,3613,3628,3645,3655,3678,3710,3741,3774,3778,3784,3810,3815,3836,3845,3854,3863,3881,3890,3914,3919,3927],{"__ignoreMap":411},[457,3614,3615,3618,3622,3625],{"class":459,"line":460},[457,3616,3617],{"class":3043},"\u003C!",[457,3619,3621],{"class":3620},"s-wAU","DOCTYPE",[457,3623,3624],{"class":3127}," html",[457,3626,3627],{"class":3043},">\n",[457,3629,3630,3633,3635,3638,3640,3643],{"class":459,"line":466},[457,3631,3632],{"class":3043},"\u003C",[457,3634,3610],{"class":3620},[457,3636,3637],{"class":3127}," lang",[457,3639,3090],{"class":3043},[457,3641,3642],{"class":3043},"\"\"",[457,3644,3627],{"class":3043},[457,3646,3647,3650,3653],{"class":459,"line":472},[457,3648,3649],{"class":3043},"  \u003C",[457,3651,3652],{"class":3620},"head",[457,3654,3627],{"class":3043},[457,3656,3657,3660,3663,3666,3668,3671,3674,3676],{"class":459,"line":4},[457,3658,3659],{"class":3043},"    \u003C",[457,3661,3662],{"class":3620},"meta",[457,3664,3665],{"class":3127}," charset",[457,3667,3090],{"class":3043},[457,3669,3670],{"class":3043},"\"",[457,3672,3673],{"class":3047},"utf-8",[457,3675,3670],{"class":3043},[457,3677,3627],{"class":3043},[457,3679,3680,3682,3684,3687,3689,3691,3694,3696,3699,3701,3703,3706,3708],{"class":459,"line":483},[457,3681,3659],{"class":3043},[457,3683,3662],{"class":3620},[457,3685,3686],{"class":3127}," http-equiv",[457,3688,3090],{"class":3043},[457,3690,3670],{"class":3043},[457,3692,3693],{"class":3047},"X-UA-Compatible",[457,3695,3670],{"class":3043},[457,3697,3698],{"class":3127}," content",[457,3700,3090],{"class":3043},[457,3702,3670],{"class":3043},[457,3704,3705],{"class":3047},"IE=edge",[457,3707,3670],{"class":3043},[457,3709,3627],{"class":3043},[457,3711,3712,3714,3716,3719,3721,3723,3726,3728,3730,3732,3734,3737,3739],{"class":459,"line":489},[457,3713,3659],{"class":3043},[457,3715,3662],{"class":3620},[457,3717,3718],{"class":3127}," name",[457,3720,3090],{"class":3043},[457,3722,3670],{"class":3043},[457,3724,3725],{"class":3047},"viewport",[457,3727,3670],{"class":3043},[457,3729,3698],{"class":3127},[457,3731,3090],{"class":3043},[457,3733,3670],{"class":3043},[457,3735,3736],{"class":3047},"width=device-width,initial-scale=1.0",[457,3738,3670],{"class":3043},[457,3740,3627],{"class":3043},[457,3742,3743,3745,3748,3751,3753,3755,3758,3760,3763,3765,3767,3770,3772],{"class":459,"line":495},[457,3744,3659],{"class":3043},[457,3746,3747],{"class":3620},"link",[457,3749,3750],{"class":3127}," rel",[457,3752,3090],{"class":3043},[457,3754,3670],{"class":3043},[457,3756,3757],{"class":3047},"icon",[457,3759,3670],{"class":3043},[457,3761,3762],{"class":3127}," href",[457,3764,3090],{"class":3043},[457,3766,3670],{"class":3043},[457,3768,3769],{"class":3047},"\u003C%= BASE_URL %>favicon.ico",[457,3771,3670],{"class":3043},[457,3773,3627],{"class":3043},[457,3775,3776],{"class":459,"line":501},[457,3777,510],{"class":3036},[457,3779,3780],{"class":459,"line":507},[457,3781,3783],{"class":3782},"sC9rS","　　\u003C!-- Insert this script -->\n",[457,3785,3786,3788,3791,3794,3796,3798,3801,3803,3806,3808],{"class":459,"line":513},[457,3787,3659],{"class":3043},[457,3789,3790],{"class":3620},"script",[457,3792,3793],{"class":3127}," src",[457,3795,3090],{"class":3043},[457,3797,3670],{"class":3043},[457,3799,3800],{"class":3047},"http:\u002F\u002Fmaps.google.com\u002Fmaps\u002Fapi\u002Fjs?key=YOUR_API_KEY&language=ja",[457,3802,3670],{"class":3043},[457,3804,3805],{"class":3043},">\u003C\u002F",[457,3807,3790],{"class":3620},[457,3809,3627],{"class":3043},[457,3811,3812],{"class":459,"line":519},[457,3813,3814],{"class":3036},"   \n",[457,3816,3817,3820,3823,3826,3829,3832,3834],{"class":459,"line":525},[457,3818,3819],{"class":3043},"　　 \u003C",[457,3821,3822],{"class":3620},"title",[457,3824,3825],{"class":3043},">",[457,3827,3828],{"class":3036},"\u003C%= htmlWebpackPlugin.options.title %>",[457,3830,3831],{"class":3043},"\u003C\u002F",[457,3833,3822],{"class":3620},[457,3835,3627],{"class":3043},[457,3837,3838,3841,3843],{"class":459,"line":531},[457,3839,3840],{"class":3043},"  \u003C\u002F",[457,3842,3652],{"class":3620},[457,3844,3627],{"class":3043},[457,3846,3847,3849,3852],{"class":459,"line":537},[457,3848,3649],{"class":3043},[457,3850,3851],{"class":3620},"body",[457,3853,3627],{"class":3043},[457,3855,3856,3858,3861],{"class":459,"line":543},[457,3857,3659],{"class":3043},[457,3859,3860],{"class":3620},"noscript",[457,3862,3627],{"class":3043},[457,3864,3865,3868,3870,3872,3875,3877,3879],{"class":459,"line":549},[457,3866,3867],{"class":3043},"      \u003C",[457,3869,292],{"class":3620},[457,3871,3825],{"class":3043},[457,3873,3874],{"class":3036},"We're sorry but \u003C%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.",[457,3876,3831],{"class":3043},[457,3878,292],{"class":3620},[457,3880,3627],{"class":3043},[457,3882,3883,3886,3888],{"class":459,"line":555},[457,3884,3885],{"class":3043},"    \u003C\u002F",[457,3887,3860],{"class":3620},[457,3889,3627],{"class":3043},[457,3891,3892,3894,3896,3899,3901,3903,3906,3908,3910,3912],{"class":459,"line":561},[457,3893,3659],{"class":3043},[457,3895,866],{"class":3620},[457,3897,3898],{"class":3127}," id",[457,3900,3090],{"class":3043},[457,3902,3670],{"class":3043},[457,3904,3905],{"class":3047},"app",[457,3907,3670],{"class":3043},[457,3909,3805],{"class":3043},[457,3911,866],{"class":3620},[457,3913,3627],{"class":3043},[457,3915,3916],{"class":459,"line":1030},[457,3917,3918],{"class":3782},"    \u003C!-- built files will be auto injected -->\n",[457,3920,3921,3923,3925],{"class":459,"line":1036},[457,3922,3840],{"class":3043},[457,3924,3851],{"class":3620},[457,3926,3627],{"class":3043},[457,3928,3929,3931,3933],{"class":459,"line":1041},[457,3930,3831],{"class":3043},[457,3932,3610],{"class":3620},[457,3934,3627],{"class":3043},[13,3936,3937,3938,3941],{},"I had tried to install API source with npm but many libraries are for node.js. So I used the outer script file from ",[297,3939,3940],{},"http:\u002F\u002Fmaps.google.com\u002Fmaps\u002Fapi\u002Fjs",". If you use it, you should load it before vue.js loaded.",[13,3943,3944],{},"The script needs the API key you get. When you use in the production, set the API configure on Google Cloud Platform as used only within your site (specific origin). If you don't that, anybody can use the key and billing just comes to YOUR credit card!",[56,3946,3948],{"id":3947},"register-google-map-api-as-vue-plugin","Register Google Map API as Vue plugin",[13,3950,3951,3952],{},"After the script loaded, you can use the classes and method that operate Google Map in ",[297,3953,3954],{},"window.google",[405,3956,3958],{"className":3022,"code":3957,"language":3025,"meta":411,"style":411},"console.lgo(window.google);\n\u002F**\n*{maps: {…}}\n*\u002F\n",[297,3959,3960,3981,3986,3991],{"__ignoreMap":411},[457,3961,3962,3965,3967,3970,3973,3975,3978],{"class":459,"line":460},[457,3963,3964],{"class":3036},"console",[457,3966,3079],{"class":3043},[457,3968,3969],{"class":3106},"lgo",[457,3971,3972],{"class":3036},"(window",[457,3974,3079],{"class":3043},[457,3976,3977],{"class":3036},"google)",[457,3979,3980],{"class":3043},";\n",[457,3982,3983],{"class":459,"line":466},[457,3984,3985],{"class":3782},"\u002F**\n",[457,3987,3988],{"class":459,"line":472},[457,3989,3990],{"class":3782},"*{maps: {…}}\n",[457,3992,3993],{"class":459,"line":4},[457,3994,1342],{"class":3782},[13,3996,3997],{},"For example, calling the api to mount a map as below.",[405,3999,4001],{"className":3022,"code":4000,"language":3025,"meta":411,"style":411},"window.google.maps.Map(document.getElementById('map'),{option});\n",[297,4002,4003],{"__ignoreMap":411},[457,4004,4005,4008,4010,4013,4015,4018,4020,4023,4026,4028,4031,4033,4035,4038,4040,4042,4045,4048,4050,4052],{"class":459,"line":460},[457,4006,4007],{"class":3036},"window",[457,4009,3079],{"class":3043},[457,4011,4012],{"class":3036},"google",[457,4014,3079],{"class":3043},[457,4016,4017],{"class":3036},"maps",[457,4019,3079],{"class":3043},[457,4021,4022],{"class":3106},"Map",[457,4024,4025],{"class":3036},"(document",[457,4027,3079],{"class":3043},[457,4029,4030],{"class":3106},"getElementById",[457,4032,3110],{"class":3036},[457,4034,3154],{"class":3043},[457,4036,4037],{"class":3047},"map",[457,4039,3154],{"class":3043},[457,4041,3144],{"class":3036},[457,4043,4044],{"class":3043},",{",[457,4046,4047],{"class":3036},"option",[457,4049,3141],{"class":3043},[457,4051,3144],{"class":3036},[457,4053,3980],{"class":3043},[13,4055,4056,4057,4060,4061,3079],{},"But calling ",[297,4058,4059],{},"window.google.maps"," in every time is bothersome so let's make a plugin to call it as ",[297,4062,4063],{},"this.$gm",[405,4065,4067],{"className":3022,"code":4066,"filename":3024,"language":3025,"meta":411,"style":411},"import Vue from 'vue'\nimport App from '.\u002FApp.vue'\n\n\u002F\u002F THIS\nVue.prototype.$gm = window.google.maps;\n\nVue.config.productionTip = false\nnew Vue({\n  render: h => h(App),\n}).$mount('#app')\n",[297,4068,4069,4083,4097,4101,4106,4136,4140,4156,4166,4182],{"__ignoreMap":411},[457,4070,4071,4073,4075,4077,4079,4081],{"class":459,"line":460},[457,4072,3033],{"class":3032},[457,4074,3037],{"class":3036},[457,4076,3040],{"class":3032},[457,4078,3044],{"class":3043},[457,4080,3048],{"class":3047},[457,4082,3051],{"class":3043},[457,4084,4085,4087,4089,4091,4093,4095],{"class":459,"line":466},[457,4086,3033],{"class":3032},[457,4088,3058],{"class":3036},[457,4090,3040],{"class":3032},[457,4092,3044],{"class":3043},[457,4094,3065],{"class":3047},[457,4096,3051],{"class":3043},[457,4098,4099],{"class":459,"line":472},[457,4100,693],{"emptyLinePlaceholder":692},[457,4102,4103],{"class":459,"line":4},[457,4104,4105],{"class":3782},"\u002F\u002F THIS\n",[457,4107,4108,4111,4113,4116,4118,4121,4123,4126,4128,4130,4132,4134],{"class":459,"line":483},[457,4109,3076],{"class":4110},"s5Dmg",[457,4112,3079],{"class":3043},[457,4114,4115],{"class":3036},"prototype",[457,4117,3079],{"class":3043},[457,4119,4120],{"class":3036},"$gm ",[457,4122,3090],{"class":3043},[457,4124,4125],{"class":3036}," window",[457,4127,3079],{"class":3043},[457,4129,4012],{"class":3036},[457,4131,3079],{"class":3043},[457,4133,4017],{"class":3036},[457,4135,3980],{"class":3043},[457,4137,4138],{"class":459,"line":489},[457,4139,693],{"emptyLinePlaceholder":692},[457,4141,4142,4144,4146,4148,4150,4152,4154],{"class":459,"line":495},[457,4143,3076],{"class":3036},[457,4145,3079],{"class":3043},[457,4147,3082],{"class":3036},[457,4149,3079],{"class":3043},[457,4151,3087],{"class":3036},[457,4153,3090],{"class":3043},[457,4155,3094],{"class":3093},[457,4157,4158,4160,4162,4164],{"class":459,"line":501},[457,4159,3103],{"class":3043},[457,4161,3107],{"class":3106},[457,4163,3110],{"class":3036},[457,4165,469],{"class":3043},[457,4167,4168,4170,4172,4174,4176,4178,4180],{"class":459,"line":507},[457,4169,3117],{"class":3106},[457,4171,3120],{"class":3043},[457,4173,3124],{"class":3123},[457,4175,3128],{"class":3127},[457,4177,3124],{"class":3106},[457,4179,3133],{"class":3036},[457,4181,3136],{"class":3043},[457,4183,4184,4186,4188,4190,4192,4194,4196,4198,4200],{"class":459,"line":513},[457,4185,3141],{"class":3043},[457,4187,3144],{"class":3036},[457,4189,3079],{"class":3043},[457,4191,3149],{"class":3106},[457,4193,3110],{"class":3036},[457,4195,3154],{"class":3043},[457,4197,3157],{"class":3047},[457,4199,3154],{"class":3043},[457,4201,3162],{"class":3036},[13,4203,4204],{},"Preparation is completed.",[39,4206,4208],{"id":4207},"anyway-lets-mount-the-map","Anyway lets' mount the map.",[13,4210,4211,4212,3079],{},"Let's mount the Google Map with vue. Write codes in ",[297,4213,4214],{},"App.vue",[405,4216,4220],{"className":4217,"code":4218,"filename":4219,"language":3048,"meta":411,"style":411},"language-vue shiki shiki-themes material-theme-ocean","\u003Ctemplate>\n  \u003Cdiv id=\"app\" class=\"mt-4\">\n      \u003Cdiv id=\"map\">\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  name: 'App',\n  data(){\n    return{\n      map:undefined,\n    }\n  },\n  mounted(){\n   const map = new this.$gm.Map(document.getElementById('map'),{\n      center: { lat: 34.855273888888888, lng: 135.30649 }, \u002F\u002F自由な緯度・経度を入力\n      zoom: 10,\n    });\n　　this.map = map;\n  }\n}\n\u003C\u002Fscript>\n","src\u002FApp.vue",[297,4221,4222,4231,4261,4283,4291,4299,4303,4311,4322,4338,4346,4353,4361,4365,4370,4377,4423,4459,4471,4480,4493,4498,4502],{"__ignoreMap":411},[457,4223,4224,4226,4229],{"class":459,"line":460},[457,4225,3632],{"class":3043},[457,4227,4228],{"class":3620},"template",[457,4230,3627],{"class":3043},[457,4232,4233,4235,4237,4239,4241,4243,4245,4247,4250,4252,4254,4257,4259],{"class":459,"line":466},[457,4234,3649],{"class":3043},[457,4236,866],{"class":3620},[457,4238,3898],{"class":3127},[457,4240,3090],{"class":3043},[457,4242,3670],{"class":3043},[457,4244,3905],{"class":3047},[457,4246,3670],{"class":3043},[457,4248,4249],{"class":3127}," class",[457,4251,3090],{"class":3043},[457,4253,3670],{"class":3043},[457,4255,4256],{"class":3047},"mt-4",[457,4258,3670],{"class":3043},[457,4260,3627],{"class":3043},[457,4262,4263,4265,4267,4269,4271,4273,4275,4277,4279,4281],{"class":459,"line":472},[457,4264,3867],{"class":3043},[457,4266,866],{"class":3620},[457,4268,3898],{"class":3127},[457,4270,3090],{"class":3043},[457,4272,3670],{"class":3043},[457,4274,4037],{"class":3047},[457,4276,3670],{"class":3043},[457,4278,3805],{"class":3043},[457,4280,866],{"class":3620},[457,4282,3627],{"class":3043},[457,4284,4285,4287,4289],{"class":459,"line":4},[457,4286,3840],{"class":3043},[457,4288,866],{"class":3620},[457,4290,3627],{"class":3043},[457,4292,4293,4295,4297],{"class":459,"line":483},[457,4294,3831],{"class":3043},[457,4296,4228],{"class":3620},[457,4298,3627],{"class":3043},[457,4300,4301],{"class":459,"line":489},[457,4302,693],{"emptyLinePlaceholder":692},[457,4304,4305,4307,4309],{"class":459,"line":495},[457,4306,3632],{"class":3043},[457,4308,3790],{"class":3620},[457,4310,3627],{"class":3043},[457,4312,4313,4316,4319],{"class":459,"line":501},[457,4314,4315],{"class":3032},"export",[457,4317,4318],{"class":3032}," default",[457,4320,4321],{"class":3043}," {\n",[457,4323,4324,4327,4329,4331,4334,4336],{"class":459,"line":507},[457,4325,4326],{"class":3620},"  name",[457,4328,3120],{"class":3043},[457,4330,3044],{"class":3043},[457,4332,4333],{"class":3047},"App",[457,4335,3154],{"class":3043},[457,4337,3136],{"class":3043},[457,4339,4340,4343],{"class":459,"line":513},[457,4341,4342],{"class":3620},"  data",[457,4344,4345],{"class":3043},"(){\n",[457,4347,4348,4351],{"class":459,"line":519},[457,4349,4350],{"class":3032},"    return",[457,4352,469],{"class":3043},[457,4354,4355,4358],{"class":459,"line":525},[457,4356,4357],{"class":3620},"      map",[457,4359,4360],{"class":3043},":undefined,\n",[457,4362,4363],{"class":459,"line":531},[457,4364,1112],{"class":3043},[457,4366,4367],{"class":459,"line":537},[457,4368,4369],{"class":3043},"  },\n",[457,4371,4372,4375],{"class":459,"line":543},[457,4373,4374],{"class":3620},"  mounted",[457,4376,4345],{"class":3043},[457,4378,4379,4382,4385,4388,4391,4394,4397,4399,4401,4403,4406,4408,4410,4412,4414,4416,4418,4420],{"class":459,"line":549},[457,4380,4381],{"class":3127},"   const",[457,4383,4384],{"class":3036}," map",[457,4386,4387],{"class":3043}," =",[457,4389,4390],{"class":3043}," new",[457,4392,4393],{"class":3043}," this.",[457,4395,4396],{"class":3036},"$gm",[457,4398,3079],{"class":3043},[457,4400,4022],{"class":3106},[457,4402,3110],{"class":3620},[457,4404,4405],{"class":3036},"document",[457,4407,3079],{"class":3043},[457,4409,4030],{"class":3106},[457,4411,3110],{"class":3620},[457,4413,3154],{"class":3043},[457,4415,4037],{"class":3047},[457,4417,3154],{"class":3043},[457,4419,3144],{"class":3620},[457,4421,4422],{"class":3043},",{\n",[457,4424,4425,4428,4430,4433,4436,4438,4442,4445,4448,4450,4453,4456],{"class":459,"line":555},[457,4426,4427],{"class":3620},"      center",[457,4429,3120],{"class":3043},[457,4431,4432],{"class":3043}," {",[457,4434,4435],{"class":3620}," lat",[457,4437,3120],{"class":3043},[457,4439,4441],{"class":4440},"sx098"," 34.855273888888888",[457,4443,4444],{"class":3043},",",[457,4446,4447],{"class":3620}," lng",[457,4449,3120],{"class":3043},[457,4451,4452],{"class":4440}," 135.30649",[457,4454,4455],{"class":3043}," },",[457,4457,4458],{"class":3782}," \u002F\u002F自由な緯度・経度を入力\n",[457,4460,4461,4464,4466,4469],{"class":459,"line":561},[457,4462,4463],{"class":3620},"      zoom",[457,4465,3120],{"class":3043},[457,4467,4468],{"class":4440}," 10",[457,4470,3136],{"class":3043},[457,4472,4473,4476,4478],{"class":459,"line":1030},[457,4474,4475],{"class":3043},"    }",[457,4477,3144],{"class":3620},[457,4479,3980],{"class":3043},[457,4481,4482,4485,4487,4489,4491],{"class":459,"line":1036},[457,4483,4484],{"class":3043},"　　this.",[457,4486,4037],{"class":3036},[457,4488,4387],{"class":3043},[457,4490,4384],{"class":3036},[457,4492,3980],{"class":3043},[457,4494,4495],{"class":459,"line":1041},[457,4496,4497],{"class":3043},"  }\n",[457,4499,4500],{"class":459,"line":1047},[457,4501,564],{"class":3043},[457,4503,4504,4506,4508],{"class":459,"line":1052},[457,4505,3831],{"class":3043},[457,4507,3790],{"class":3620},[457,4509,3627],{"class":3043},[13,4511,4512,4513,4518],{},"As the reference about ",[17,4514,4517],{"href":4515,"rel":4516},"https:\u002F\u002Fdevelopers.google.com\u002Fmaps\u002Fdocumentation\u002Fjavascript\u002Freference\u002Fmap?hl=ja",[21],"Maps Class"," says, specify the mounted target DOM in the first argument and write the options in the second argument.",[13,4520,4521],{},"Option cantains",[195,4523,4524,4527],{},[122,4525,4526],{},"center：the central coordinate when displayed the map.",[122,4528,4529],{},"zoom：the scale of zooming. 10 is just right.",[13,4531,4532,4535,4536,4539,4540,3079],{},[297,4533,4534],{},"lat"," is the latitude and ",[297,4537,4538],{},"lng"," is the longitude. This map instance will be used to edit map so substitute to vue like ",[297,4541,4542],{},"this.map = map",[405,4544,4547],{"className":4545,"code":4546,"language":410},[408],"$ npm run serve\n",[297,4548,4546],{"__ignoreMap":411},[111,4550],{":src":4551,":width":114},"'_mix\u002Fsch-2021-03-07-17.39.26-768x352.png'",[13,4553,4554,4555,4557],{},"If displayed like this on the ",[297,4556,319],{},", there are no problems. If you favor not to display Street View or other buttons, set the option like below.",[405,4559,4561],{"className":3022,"code":4560,"filename":3024,"language":3025,"meta":411,"style":411},"new this.$gm.Map(document.getElementById('map'),{\n      center: { lat: 34.855273888888888, lng: 135.30649 },\n      zoom: 10,\n      streetViewControl:false, \u002F\u002F hide street view\n      fullscreenControl:false  \u002F\u002F hide full screen\n});\n",[297,4562,4563,4593,4618,4628,4643,4655],{"__ignoreMap":411},[457,4564,4565,4567,4569,4571,4573,4575,4577,4579,4581,4583,4585,4587,4589,4591],{"class":459,"line":460},[457,4566,3103],{"class":3043},[457,4568,4393],{"class":3043},[457,4570,4396],{"class":3036},[457,4572,3079],{"class":3043},[457,4574,4022],{"class":3106},[457,4576,4025],{"class":3036},[457,4578,3079],{"class":3043},[457,4580,4030],{"class":3106},[457,4582,3110],{"class":3036},[457,4584,3154],{"class":3043},[457,4586,4037],{"class":3047},[457,4588,3154],{"class":3043},[457,4590,3144],{"class":3036},[457,4592,4422],{"class":3043},[457,4594,4595,4597,4599,4601,4603,4605,4607,4609,4611,4613,4615],{"class":459,"line":466},[457,4596,4427],{"class":3620},[457,4598,3120],{"class":3043},[457,4600,4432],{"class":3043},[457,4602,4435],{"class":3620},[457,4604,3120],{"class":3043},[457,4606,4441],{"class":4440},[457,4608,4444],{"class":3043},[457,4610,4447],{"class":3620},[457,4612,3120],{"class":3043},[457,4614,4452],{"class":4440},[457,4616,4617],{"class":3043}," },\n",[457,4619,4620,4622,4624,4626],{"class":459,"line":472},[457,4621,4463],{"class":3620},[457,4623,3120],{"class":3043},[457,4625,4468],{"class":4440},[457,4627,3136],{"class":3043},[457,4629,4630,4633,4635,4638,4640],{"class":459,"line":4},[457,4631,4632],{"class":3620},"      streetViewControl",[457,4634,3120],{"class":3043},[457,4636,4637],{"class":3093},"false",[457,4639,4444],{"class":3043},[457,4641,4642],{"class":3782}," \u002F\u002F hide street view\n",[457,4644,4645,4648,4650,4652],{"class":459,"line":483},[457,4646,4647],{"class":3620},"      fullscreenControl",[457,4649,3120],{"class":3043},[457,4651,4637],{"class":3093},[457,4653,4654],{"class":3782},"  \u002F\u002F hide full screen\n",[457,4656,4657,4659,4661],{"class":459,"line":489},[457,4658,3141],{"class":3043},[457,4660,3144],{"class":3036},[457,4662,3980],{"class":3043},[39,4664,4666],{"id":4665},"add-click-event-to-the-map","Add click event to the map",[13,4668,4669],{},"You can add event listener for dragging and clicking to the Google Map. It is able to add any functions fired when user clicks the specific position or a maker. As an example, let's create a function to get the latitude and longitude of clicked position on the map.",[405,4671,4673],{"className":3022,"code":4672,"filename":3024,"language":3025,"meta":411,"style":411},"\u003Ctemplate>\n  \u003Cdiv id=\"app\" class=\"mt-4\">\n      \u003Cdiv id=\"map\">\u003C\u002Fdiv>\n      {{position}}\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n\nexport default {\n  name: 'App',\n  data(){\n      return{\n        map:undefined,\n        position:undefined\n      }\n  },\n  methods:{\n   clickOnMap(mapEvent){\n    console.log(mapEvent.latLng.toString())\n    this.position = mapEvent.latLng.toString();\n   }\n  },\n  mounted(){\n   const map = new this.$gm.Map(document.getElementById('map'),{\n      center: { lat: 34.855273888888888, lng: 135.30649 }, \u002F\u002F自由な緯度・経度を入力\n      zoom: 10,\n    });\n\n    \u002F\u002F register event listener\n    map.addListener('click',(mapsMouseEvent)=>{\n      return this.clickOnMap(mapsMouseEvent);\n    });\n\n　　this.map = map;\n  }\n}\n",[297,4674,4675,4683,4711,4733,4744,4752,4760,4764,4772,4776,4783,4796,4805,4812,4819,4827,4832,4836,4843,4856,4883,4907,4912,4916,4924,4959,4985,4995,5002,5006,5011,5030,5048,5054,5058,5068,5072],{"__ignoreMap":411},[457,4676,4677,4679,4681],{"class":459,"line":460},[457,4678,3632],{"class":3043},[457,4680,4228],{"class":3620},[457,4682,3627],{"class":3043},[457,4684,4685,4687,4689,4691,4693,4695,4697,4699,4701,4703,4705,4707,4709],{"class":459,"line":466},[457,4686,3649],{"class":3043},[457,4688,866],{"class":3620},[457,4690,3898],{"class":3127},[457,4692,3090],{"class":3043},[457,4694,3670],{"class":3043},[457,4696,3905],{"class":3047},[457,4698,3670],{"class":3043},[457,4700,4249],{"class":3127},[457,4702,3090],{"class":3043},[457,4704,3670],{"class":3043},[457,4706,4256],{"class":3047},[457,4708,3670],{"class":3043},[457,4710,3627],{"class":3043},[457,4712,4713,4715,4717,4719,4721,4723,4725,4727,4729,4731],{"class":459,"line":472},[457,4714,3867],{"class":3043},[457,4716,866],{"class":3620},[457,4718,3898],{"class":3127},[457,4720,3090],{"class":3043},[457,4722,3670],{"class":3043},[457,4724,4037],{"class":3047},[457,4726,3670],{"class":3043},[457,4728,3805],{"class":3043},[457,4730,866],{"class":3620},[457,4732,3627],{"class":3043},[457,4734,4735,4738,4741],{"class":459,"line":4},[457,4736,4737],{"class":3043},"      {{",[457,4739,4740],{"class":3036},"position",[457,4742,4743],{"class":3043},"}}\n",[457,4745,4746,4748,4750],{"class":459,"line":483},[457,4747,3840],{"class":3043},[457,4749,866],{"class":3620},[457,4751,3627],{"class":3043},[457,4753,4754,4756,4758],{"class":459,"line":489},[457,4755,3831],{"class":3043},[457,4757,4228],{"class":3620},[457,4759,3627],{"class":3043},[457,4761,4762],{"class":459,"line":495},[457,4763,693],{"emptyLinePlaceholder":692},[457,4765,4766,4768,4770],{"class":459,"line":501},[457,4767,3632],{"class":3043},[457,4769,3790],{"class":3620},[457,4771,3627],{"class":3043},[457,4773,4774],{"class":459,"line":507},[457,4775,693],{"emptyLinePlaceholder":692},[457,4777,4778,4781],{"class":459,"line":513},[457,4779,4780],{"class":3036},"export default ",[457,4782,469],{"class":3043},[457,4784,4785,4788,4790,4792,4794],{"class":459,"line":519},[457,4786,4787],{"class":3036},"  name: ",[457,4789,3154],{"class":3043},[457,4791,4333],{"class":3047},[457,4793,3154],{"class":3043},[457,4795,3136],{"class":3043},[457,4797,4798,4800,4803],{"class":459,"line":525},[457,4799,4342],{"class":3106},[457,4801,4802],{"class":3036},"()",[457,4804,469],{"class":3043},[457,4806,4807,4810],{"class":459,"line":531},[457,4808,4809],{"class":3036},"      return",[457,4811,469],{"class":3043},[457,4813,4814,4817],{"class":459,"line":537},[457,4815,4816],{"class":4110},"        map",[457,4818,4360],{"class":3043},[457,4820,4821,4824],{"class":459,"line":543},[457,4822,4823],{"class":4110},"        position",[457,4825,4826],{"class":3043},":undefined\n",[457,4828,4829],{"class":459,"line":549},[457,4830,4831],{"class":3043},"      }\n",[457,4833,4834],{"class":459,"line":555},[457,4835,4369],{"class":3043},[457,4837,4838,4841],{"class":459,"line":561},[457,4839,4840],{"class":3036},"  methods:",[457,4842,469],{"class":3043},[457,4844,4845,4848,4850,4853],{"class":459,"line":1030},[457,4846,4847],{"class":3620},"   clickOnMap",[457,4849,3110],{"class":3043},[457,4851,4852],{"class":3123},"mapEvent",[457,4854,4855],{"class":3043},"){\n",[457,4857,4858,4861,4863,4866,4868,4870,4872,4875,4877,4880],{"class":459,"line":1036},[457,4859,4860],{"class":3036},"    console",[457,4862,3079],{"class":3043},[457,4864,4865],{"class":3106},"log",[457,4867,3110],{"class":3620},[457,4869,4852],{"class":3036},[457,4871,3079],{"class":3043},[457,4873,4874],{"class":3036},"latLng",[457,4876,3079],{"class":3043},[457,4878,4879],{"class":3106},"toString",[457,4881,4882],{"class":3620},"())\n",[457,4884,4885,4888,4890,4892,4895,4897,4899,4901,4903,4905],{"class":459,"line":1041},[457,4886,4887],{"class":3043},"    this.",[457,4889,4740],{"class":3036},[457,4891,4387],{"class":3043},[457,4893,4894],{"class":3036}," mapEvent",[457,4896,3079],{"class":3043},[457,4898,4874],{"class":3036},[457,4900,3079],{"class":3043},[457,4902,4879],{"class":3106},[457,4904,4802],{"class":3620},[457,4906,3980],{"class":3043},[457,4908,4909],{"class":459,"line":1047},[457,4910,4911],{"class":3043},"   }\n",[457,4913,4914],{"class":459,"line":1052},[457,4915,4369],{"class":3043},[457,4917,4918,4920,4922],{"class":459,"line":1058},[457,4919,4374],{"class":3106},[457,4921,4802],{"class":3036},[457,4923,469],{"class":3043},[457,4925,4926,4929,4931,4933,4935,4937,4939,4941,4943,4945,4947,4949,4951,4953,4955,4957],{"class":459,"line":1064},[457,4927,4928],{"class":3036},"   const map ",[457,4930,3090],{"class":3043},[457,4932,4390],{"class":3043},[457,4934,4393],{"class":3043},[457,4936,4396],{"class":3036},[457,4938,3079],{"class":3043},[457,4940,4022],{"class":3106},[457,4942,4025],{"class":3036},[457,4944,3079],{"class":3043},[457,4946,4030],{"class":3106},[457,4948,3110],{"class":3036},[457,4950,3154],{"class":3043},[457,4952,4037],{"class":3047},[457,4954,3154],{"class":3043},[457,4956,3144],{"class":3036},[457,4958,4422],{"class":3043},[457,4960,4961,4963,4965,4967,4969,4971,4973,4975,4977,4979,4981,4983],{"class":459,"line":1070},[457,4962,4427],{"class":3620},[457,4964,3120],{"class":3043},[457,4966,4432],{"class":3043},[457,4968,4435],{"class":3620},[457,4970,3120],{"class":3043},[457,4972,4441],{"class":4440},[457,4974,4444],{"class":3043},[457,4976,4447],{"class":3620},[457,4978,3120],{"class":3043},[457,4980,4452],{"class":4440},[457,4982,4455],{"class":3043},[457,4984,4458],{"class":3782},[457,4986,4987,4989,4991,4993],{"class":459,"line":1076},[457,4988,4463],{"class":3620},[457,4990,3120],{"class":3043},[457,4992,4468],{"class":4440},[457,4994,3136],{"class":3043},[457,4996,4997,4999],{"class":459,"line":1081},[457,4998,4475],{"class":3043},[457,5000,5001],{"class":3036},");\n",[457,5003,5004],{"class":459,"line":1086},[457,5005,693],{"emptyLinePlaceholder":692},[457,5007,5008],{"class":459,"line":1092},[457,5009,5010],{"class":3782},"    \u002F\u002F register event listener\n",[457,5012,5013,5016,5018,5021,5023,5025,5028],{"class":459,"line":1098},[457,5014,5015],{"class":3036},"    map.addListener(",[457,5017,3154],{"class":3043},[457,5019,5020],{"class":3620},"click",[457,5022,3154],{"class":3043},[457,5024,4444],{"class":3043},[457,5026,5027],{"class":3036},"(mapsMouseEvent)=>",[457,5029,469],{"class":3043},[457,5031,5032,5034,5036,5039,5041,5044,5046],{"class":459,"line":1104},[457,5033,4809],{"class":3032},[457,5035,4393],{"class":3043},[457,5037,5038],{"class":3106},"clickOnMap",[457,5040,3110],{"class":3620},[457,5042,5043],{"class":3036},"mapsMouseEvent",[457,5045,3144],{"class":3620},[457,5047,3980],{"class":3043},[457,5049,5050,5052],{"class":459,"line":1109},[457,5051,4475],{"class":3043},[457,5053,5001],{"class":3036},[457,5055,5056],{"class":459,"line":1115},[457,5057,693],{"emptyLinePlaceholder":692},[457,5059,5060,5063,5065],{"class":459,"line":1854},[457,5061,5062],{"class":3036},"　　this.map ",[457,5064,3090],{"class":3043},[457,5066,5067],{"class":3036}," map;\n",[457,5069,5070],{"class":459,"line":1859},[457,5071,4497],{"class":3043},[457,5073,5074],{"class":459,"line":1864},[457,5075,564],{"class":3043},[13,5077,5078,5081],{},[297,5079,5080],{},"map.addListener('click',(mapsMouseEvent)=>{...})"," is similar to adding the event to DOM. Insert the callback function to the second argument. By the way, the argument of callback contains automatically the map data like clicked position coordinate.",[13,5083,5084],{},"I register the click event listener has callback executes the below method to get clicked position coordinate.",[405,5086,5088],{"className":3022,"code":5087,"filename":3024,"language":3025,"meta":411,"style":411},"clickOnMap(mapEvent){\n        console.log(mapEvent.latLng.toString())\n        this.position = mapEvent.latLng.toString();\n},\n",[297,5089,5090,5099,5122,5145],{"__ignoreMap":411},[457,5091,5092,5094,5097],{"class":459,"line":460},[457,5093,5038],{"class":3106},[457,5095,5096],{"class":3036},"(mapEvent)",[457,5098,469],{"class":3043},[457,5100,5101,5104,5106,5108,5110,5112,5114,5116,5118,5120],{"class":459,"line":466},[457,5102,5103],{"class":3036},"        console",[457,5105,3079],{"class":3043},[457,5107,4865],{"class":3106},[457,5109,3110],{"class":3620},[457,5111,4852],{"class":3036},[457,5113,3079],{"class":3043},[457,5115,4874],{"class":3036},[457,5117,3079],{"class":3043},[457,5119,4879],{"class":3106},[457,5121,4882],{"class":3620},[457,5123,5124,5127,5129,5131,5133,5135,5137,5139,5141,5143],{"class":459,"line":472},[457,5125,5126],{"class":3043},"        this.",[457,5128,4740],{"class":3036},[457,5130,4387],{"class":3043},[457,5132,4894],{"class":3036},[457,5134,3079],{"class":3043},[457,5136,4874],{"class":3036},[457,5138,3079],{"class":3043},[457,5140,4879],{"class":3106},[457,5142,4802],{"class":3620},[457,5144,3980],{"class":3043},[457,5146,5147],{"class":459,"line":4},[457,5148,5149],{"class":3043},"},\n",[13,5151,5152],{},"When clicking the map, the coordinate displayed. (ignore red point on the center..)",[111,5154],{":src":5155,":width":114},"'_mix\u002Fsch-2021-03-07-18.02.03-768x326.png'",[13,5157,5158],{},"The coordinate is displayed at the under left. Anyway, it is how register an event listener and obtain data of the map.",[39,5160,5162],{"id":5161},"drawing-the-line","Drawing the line.",[13,5164,5165,5166,5169],{},"Next, let's draw the line when clicking the map. To draw lines, you create a instance of ",[297,5167,5168],{},"google.maps.Polyline"," class and set the instance to the map instance.",[13,5171,5172],{},"You can obtain map data as above so let's draw the line when map rendered.",[405,5174,5176],{"className":3022,"code":5175,"filename":3024,"language":3025,"meta":411,"style":411},"...  \nmounted(){\n  const map = new this.$gm.Map(document.getElementById('map'),{\n    center: { lat: 34.855273888888888, lng: 135.30649 },\n    zoom: 10,\n  });\n  map.addListener('click',(mapsMouseEvent)=>{\n    return this.clickOnMap(mapsMouseEvent);\n  });\n  const LINE = new this.$gm.Polyline({\n      path:[\n        { lat: 34.855273888888888, lng: 135.30649 },\n        { lat: 34.854465, lng: 135.8 },\n      ],\n      geodesic: true,\n      strokeColor: \"#FF0000\",\n      strokeOpacity: 1.0,\n      strokeWeight: 2,\n  })\n  LINE.setMap(map);\n  this.map = map;\n} \n...\n",[297,5177,5178,5186,5195,5234,5259,5270,5279,5309,5325,5333,5357,5367,5388,5410,5417,5429,5446,5458,5470,5476,5494,5507,5514],{"__ignoreMap":411},[457,5179,5180,5183],{"class":459,"line":460},[457,5181,5182],{"class":3043},"...",[457,5184,5185],{"class":3036},"  \n",[457,5187,5188,5191,5193],{"class":459,"line":466},[457,5189,5190],{"class":3106},"mounted",[457,5192,4802],{"class":3036},[457,5194,469],{"class":3043},[457,5196,5197,5200,5202,5204,5206,5208,5210,5212,5214,5216,5218,5220,5222,5224,5226,5228,5230,5232],{"class":459,"line":472},[457,5198,5199],{"class":3127},"  const",[457,5201,4384],{"class":3036},[457,5203,4387],{"class":3043},[457,5205,4390],{"class":3043},[457,5207,4393],{"class":3043},[457,5209,4396],{"class":3036},[457,5211,3079],{"class":3043},[457,5213,4022],{"class":3106},[457,5215,3110],{"class":3620},[457,5217,4405],{"class":3036},[457,5219,3079],{"class":3043},[457,5221,4030],{"class":3106},[457,5223,3110],{"class":3620},[457,5225,3154],{"class":3043},[457,5227,4037],{"class":3047},[457,5229,3154],{"class":3043},[457,5231,3144],{"class":3620},[457,5233,4422],{"class":3043},[457,5235,5236,5239,5241,5243,5245,5247,5249,5251,5253,5255,5257],{"class":459,"line":4},[457,5237,5238],{"class":3620},"    center",[457,5240,3120],{"class":3043},[457,5242,4432],{"class":3043},[457,5244,4435],{"class":3620},[457,5246,3120],{"class":3043},[457,5248,4441],{"class":4440},[457,5250,4444],{"class":3043},[457,5252,4447],{"class":3620},[457,5254,3120],{"class":3043},[457,5256,4452],{"class":4440},[457,5258,4617],{"class":3043},[457,5260,5261,5264,5266,5268],{"class":459,"line":483},[457,5262,5263],{"class":3620},"    zoom",[457,5265,3120],{"class":3043},[457,5267,4468],{"class":4440},[457,5269,3136],{"class":3043},[457,5271,5272,5275,5277],{"class":459,"line":489},[457,5273,5274],{"class":3043},"  }",[457,5276,3144],{"class":3620},[457,5278,3980],{"class":3043},[457,5280,5281,5284,5286,5289,5291,5293,5295,5297,5300,5302,5304,5307],{"class":459,"line":495},[457,5282,5283],{"class":3036},"  map",[457,5285,3079],{"class":3043},[457,5287,5288],{"class":3106},"addListener",[457,5290,3110],{"class":3620},[457,5292,3154],{"class":3043},[457,5294,5020],{"class":3047},[457,5296,3154],{"class":3043},[457,5298,5299],{"class":3043},",(",[457,5301,5043],{"class":3123},[457,5303,3144],{"class":3043},[457,5305,5306],{"class":3127},"=>",[457,5308,469],{"class":3043},[457,5310,5311,5313,5315,5317,5319,5321,5323],{"class":459,"line":501},[457,5312,4350],{"class":3032},[457,5314,4393],{"class":3043},[457,5316,5038],{"class":3106},[457,5318,3110],{"class":3620},[457,5320,5043],{"class":3036},[457,5322,3144],{"class":3620},[457,5324,3980],{"class":3043},[457,5326,5327,5329,5331],{"class":459,"line":507},[457,5328,5274],{"class":3043},[457,5330,3144],{"class":3620},[457,5332,3980],{"class":3043},[457,5334,5335,5337,5340,5342,5344,5346,5348,5350,5353,5355],{"class":459,"line":513},[457,5336,5199],{"class":3127},[457,5338,5339],{"class":3036}," LINE",[457,5341,4387],{"class":3043},[457,5343,4390],{"class":3043},[457,5345,4393],{"class":3043},[457,5347,4396],{"class":3036},[457,5349,3079],{"class":3043},[457,5351,5352],{"class":3106},"Polyline",[457,5354,3110],{"class":3620},[457,5356,469],{"class":3043},[457,5358,5359,5362,5364],{"class":459,"line":519},[457,5360,5361],{"class":3620},"      path",[457,5363,3120],{"class":3043},[457,5365,5366],{"class":3620},"[\n",[457,5368,5369,5372,5374,5376,5378,5380,5382,5384,5386],{"class":459,"line":525},[457,5370,5371],{"class":3043},"        {",[457,5373,4435],{"class":3620},[457,5375,3120],{"class":3043},[457,5377,4441],{"class":4440},[457,5379,4444],{"class":3043},[457,5381,4447],{"class":3620},[457,5383,3120],{"class":3043},[457,5385,4452],{"class":4440},[457,5387,4617],{"class":3043},[457,5389,5390,5392,5394,5396,5399,5401,5403,5405,5408],{"class":459,"line":531},[457,5391,5371],{"class":3043},[457,5393,4435],{"class":3620},[457,5395,3120],{"class":3043},[457,5397,5398],{"class":4440}," 34.854465",[457,5400,4444],{"class":3043},[457,5402,4447],{"class":3620},[457,5404,3120],{"class":3043},[457,5406,5407],{"class":4440}," 135.8",[457,5409,4617],{"class":3043},[457,5411,5412,5415],{"class":459,"line":537},[457,5413,5414],{"class":3620},"      ]",[457,5416,3136],{"class":3043},[457,5418,5419,5422,5424,5427],{"class":459,"line":543},[457,5420,5421],{"class":3620},"      geodesic",[457,5423,3120],{"class":3043},[457,5425,5426],{"class":3093}," true",[457,5428,3136],{"class":3043},[457,5430,5431,5434,5436,5439,5442,5444],{"class":459,"line":549},[457,5432,5433],{"class":3620},"      strokeColor",[457,5435,3120],{"class":3043},[457,5437,5438],{"class":3043}," \"",[457,5440,5441],{"class":3047},"#FF0000",[457,5443,3670],{"class":3043},[457,5445,3136],{"class":3043},[457,5447,5448,5451,5453,5456],{"class":459,"line":555},[457,5449,5450],{"class":3620},"      strokeOpacity",[457,5452,3120],{"class":3043},[457,5454,5455],{"class":4440}," 1.0",[457,5457,3136],{"class":3043},[457,5459,5460,5463,5465,5468],{"class":459,"line":561},[457,5461,5462],{"class":3620},"      strokeWeight",[457,5464,3120],{"class":3043},[457,5466,5467],{"class":4440}," 2",[457,5469,3136],{"class":3043},[457,5471,5472,5474],{"class":459,"line":1030},[457,5473,5274],{"class":3043},[457,5475,3162],{"class":3620},[457,5477,5478,5481,5483,5486,5488,5490,5492],{"class":459,"line":1036},[457,5479,5480],{"class":3036},"  LINE",[457,5482,3079],{"class":3043},[457,5484,5485],{"class":3106},"setMap",[457,5487,3110],{"class":3620},[457,5489,4037],{"class":3036},[457,5491,3144],{"class":3620},[457,5493,3980],{"class":3043},[457,5495,5496,5499,5501,5503,5505],{"class":459,"line":1041},[457,5497,5498],{"class":3043},"  this.",[457,5500,4037],{"class":3036},[457,5502,4387],{"class":3043},[457,5504,4384],{"class":3036},[457,5506,3980],{"class":3043},[457,5508,5509,5511],{"class":459,"line":1047},[457,5510,3141],{"class":3043},[457,5512,5513],{"class":3036}," \n",[457,5515,5516],{"class":459,"line":1052},[457,5517,1489],{"class":3043},[13,5519,5520,5521,5524],{},"At the first, create a line instance with ",[297,5522,5523],{},"this.$gm.Polyline",". You can set line width, path (2 points needed), color, and so on with object.",[111,5526],{":src":5527,":width":114},"'_mix\u002Fsch-2021-03-10-21.17.39-768x346.png'",[13,5529,5530],{},"The red line is displayed. The above example is straight, but if you set detailed paths, you can draw as below.",[111,5532],{":src":5533,":width":5534,":center":266},"'_mix\u002Fsch-2021-03-10-21.19.41.png'","'300px'",[13,5536,5537],{},"I downloaded these coordinate data from my friend's phone GPS and that has detailed data coordinate as an array.",[39,5539,5541],{"id":5540},"drawing-lines-with-any-clikced-positions","Drawing lines with any clikced positions",[13,5543,5544],{},"Rendering the line at the initial dispaly is successful. Next, let's draw the line from point to point when the user clicked any position on the map.",[13,5546,5547],{},"You remember obtaining from click event, don't you? Applay it.",[119,5549,5550,5553,5556,5559],{},[122,5551,5552],{},"Obtain latitude and longitude from clicked position.",[122,5554,5555],{},"Convert them to Google Map Coordinate instance.",[122,5557,5558],{},"Add the coordinate (path) to the line instance.",[122,5560,5561],{},"Execute it every clicking.",[13,5563,5564,5567],{},[297,5565,5566],{},"drawLine"," method I add to click event is below.",[405,5569,5571],{"className":3022,"code":5570,"filename":3024,"language":3025,"meta":411,"style":411},"data(){\n  return{\n    map:undefined,\n    bounds:undefined,\n    path:[],\n    line:undefined\n  }\n},\nmethods:{\n  drawLine(mapEvent){\n    let latLng = mapEvent.latLng;\n\n    \u002F\u002F click 1st point\n    if(this.line === undefined){\n      const newLine = new this.$gm.Polyline({\n        path:[latLng],\n        geodesic: true,\n        strokeColor: \"#FF0000\",\n        strokeOpacity: 1.0,\n        strokeWeight: 2,\n      })\n      newLine.setMap(this.map);\n      this.path.push(latLng);\n      this.line = newLine;\n      return;\n    }\n\n    \u002F\u002F click second and later point\n    this.path.push(latLng);\n    this.line.setPath([...this.path]);\n    return;\n  },\n},\n",[297,5572,5573,5582,5589,5596,5603,5615,5622,5626,5630,5638,5651,5669,5673,5678,5700,5724,5741,5752,5767,5778,5789,5796,5815,5836,5848,5854,5858,5862,5867,5885,5909,5915,5919],{"__ignoreMap":411},[457,5574,5575,5578,5580],{"class":459,"line":460},[457,5576,5577],{"class":3106},"data",[457,5579,4802],{"class":3036},[457,5581,469],{"class":3043},[457,5583,5584,5587],{"class":459,"line":466},[457,5585,5586],{"class":3032},"  return",[457,5588,469],{"class":3043},[457,5590,5591,5594],{"class":459,"line":472},[457,5592,5593],{"class":3620},"    map",[457,5595,4360],{"class":3043},[457,5597,5598,5601],{"class":459,"line":4},[457,5599,5600],{"class":3620},"    bounds",[457,5602,4360],{"class":3043},[457,5604,5605,5608,5610,5613],{"class":459,"line":483},[457,5606,5607],{"class":3620},"    path",[457,5609,3120],{"class":3043},[457,5611,5612],{"class":3620},"[]",[457,5614,3136],{"class":3043},[457,5616,5617,5620],{"class":459,"line":489},[457,5618,5619],{"class":3620},"    line",[457,5621,4826],{"class":3043},[457,5623,5624],{"class":459,"line":495},[457,5625,4497],{"class":3043},[457,5627,5628],{"class":459,"line":501},[457,5629,5149],{"class":3043},[457,5631,5632,5635],{"class":459,"line":507},[457,5633,5634],{"class":4110},"methods",[457,5636,5637],{"class":3043},":{\n",[457,5639,5640,5643,5645,5647,5649],{"class":459,"line":513},[457,5641,5642],{"class":3106},"  drawLine",[457,5644,3110],{"class":3620},[457,5646,4852],{"class":3036},[457,5648,3144],{"class":3620},[457,5650,469],{"class":3043},[457,5652,5653,5656,5659,5661,5663,5665,5667],{"class":459,"line":519},[457,5654,5655],{"class":3127},"    let",[457,5657,5658],{"class":3036}," latLng",[457,5660,4387],{"class":3043},[457,5662,4894],{"class":3036},[457,5664,3079],{"class":3043},[457,5666,4874],{"class":3036},[457,5668,3980],{"class":3043},[457,5670,5671],{"class":459,"line":525},[457,5672,693],{"emptyLinePlaceholder":692},[457,5674,5675],{"class":459,"line":531},[457,5676,5677],{"class":3782},"    \u002F\u002F click 1st point\n",[457,5679,5680,5683,5685,5688,5690,5693,5696,5698],{"class":459,"line":537},[457,5681,5682],{"class":3032},"    if",[457,5684,3110],{"class":3620},[457,5686,5687],{"class":3043},"this.",[457,5689,459],{"class":3036},[457,5691,5692],{"class":3043}," ===",[457,5694,5695],{"class":3043}," undefined",[457,5697,3144],{"class":3620},[457,5699,469],{"class":3043},[457,5701,5702,5705,5708,5710,5712,5714,5716,5718,5720,5722],{"class":459,"line":543},[457,5703,5704],{"class":3127},"      const",[457,5706,5707],{"class":3036}," newLine",[457,5709,4387],{"class":3043},[457,5711,4390],{"class":3043},[457,5713,4393],{"class":3043},[457,5715,4396],{"class":3036},[457,5717,3079],{"class":3043},[457,5719,5352],{"class":3106},[457,5721,3110],{"class":3620},[457,5723,469],{"class":3043},[457,5725,5726,5729,5731,5734,5736,5739],{"class":459,"line":549},[457,5727,5728],{"class":3620},"        path",[457,5730,3120],{"class":3043},[457,5732,5733],{"class":3620},"[",[457,5735,4874],{"class":3036},[457,5737,5738],{"class":3620},"]",[457,5740,3136],{"class":3043},[457,5742,5743,5746,5748,5750],{"class":459,"line":555},[457,5744,5745],{"class":3620},"        geodesic",[457,5747,3120],{"class":3043},[457,5749,5426],{"class":3093},[457,5751,3136],{"class":3043},[457,5753,5754,5757,5759,5761,5763,5765],{"class":459,"line":561},[457,5755,5756],{"class":3620},"        strokeColor",[457,5758,3120],{"class":3043},[457,5760,5438],{"class":3043},[457,5762,5441],{"class":3047},[457,5764,3670],{"class":3043},[457,5766,3136],{"class":3043},[457,5768,5769,5772,5774,5776],{"class":459,"line":1030},[457,5770,5771],{"class":3620},"        strokeOpacity",[457,5773,3120],{"class":3043},[457,5775,5455],{"class":4440},[457,5777,3136],{"class":3043},[457,5779,5780,5783,5785,5787],{"class":459,"line":1036},[457,5781,5782],{"class":3620},"        strokeWeight",[457,5784,3120],{"class":3043},[457,5786,5467],{"class":4440},[457,5788,3136],{"class":3043},[457,5790,5791,5794],{"class":459,"line":1041},[457,5792,5793],{"class":3043},"      }",[457,5795,3162],{"class":3620},[457,5797,5798,5801,5803,5805,5807,5809,5811,5813],{"class":459,"line":1047},[457,5799,5800],{"class":3036},"      newLine",[457,5802,3079],{"class":3043},[457,5804,5485],{"class":3106},[457,5806,3110],{"class":3620},[457,5808,5687],{"class":3043},[457,5810,4037],{"class":3036},[457,5812,3144],{"class":3620},[457,5814,3980],{"class":3043},[457,5816,5817,5820,5823,5825,5828,5830,5832,5834],{"class":459,"line":1052},[457,5818,5819],{"class":3043},"      this.",[457,5821,5822],{"class":3036},"path",[457,5824,3079],{"class":3043},[457,5826,5827],{"class":3106},"push",[457,5829,3110],{"class":3620},[457,5831,4874],{"class":3036},[457,5833,3144],{"class":3620},[457,5835,3980],{"class":3043},[457,5837,5838,5840,5842,5844,5846],{"class":459,"line":1058},[457,5839,5819],{"class":3043},[457,5841,459],{"class":3036},[457,5843,4387],{"class":3043},[457,5845,5707],{"class":3036},[457,5847,3980],{"class":3043},[457,5849,5850,5852],{"class":459,"line":1064},[457,5851,4809],{"class":3032},[457,5853,3980],{"class":3043},[457,5855,5856],{"class":459,"line":1070},[457,5857,1112],{"class":3043},[457,5859,5860],{"class":459,"line":1076},[457,5861,693],{"emptyLinePlaceholder":692},[457,5863,5864],{"class":459,"line":1081},[457,5865,5866],{"class":3782},"    \u002F\u002F click second and later point\n",[457,5868,5869,5871,5873,5875,5877,5879,5881,5883],{"class":459,"line":1086},[457,5870,4887],{"class":3043},[457,5872,5822],{"class":3036},[457,5874,3079],{"class":3043},[457,5876,5827],{"class":3106},[457,5878,3110],{"class":3620},[457,5880,4874],{"class":3036},[457,5882,3144],{"class":3620},[457,5884,3980],{"class":3043},[457,5886,5887,5889,5891,5893,5896,5899,5902,5904,5907],{"class":459,"line":1092},[457,5888,4887],{"class":3043},[457,5890,459],{"class":3036},[457,5892,3079],{"class":3043},[457,5894,5895],{"class":3106},"setPath",[457,5897,5898],{"class":3620},"([",[457,5900,5901],{"class":3043},"...this.",[457,5903,5822],{"class":3036},[457,5905,5906],{"class":3620},"])",[457,5908,3980],{"class":3043},[457,5910,5911,5913],{"class":459,"line":1098},[457,5912,4350],{"class":3032},[457,5914,3980],{"class":3043},[457,5916,5917],{"class":459,"line":1104},[457,5918,4369],{"class":3043},[457,5920,5921],{"class":459,"line":1109},[457,5922,5149],{"class":3043},[13,5924,5925,5926,5929,5930,5932,5933,5936,5937,5940],{},"When first clicking, create the instance and store it into ",[297,5927,5928],{},"data()",". Insert coordinate data to ",[297,5931,5822],{}," every clicking. And expand the path array with ",[297,5934,5935],{},"this.line.setPath([...this.path])"," and update instance's path. So it will draw the line from point to point with every click. I have written the character ",[292,5938,5939],{},"VALUE"," on the map with one line (^^).",[111,5942],{":src":5943,":width":114},"'_mix\u002Fscp-2021-03-10-21.42.12-768x408.png'",[13,5945,5946],{},"If you reset the last path, that will be an undo function. If you change any path, also will be line editing function. Google Map API has many other functions to make the rich map.",[39,5948,5950],{"id":5949},"is-not-vue-needed","Is not Vue needed??",[13,5952,5953],{},"Yes. You can make it without vue.js. Most functions are developed with Object and methods of Google map javascript api. So you can make it only with Vanilla.js. But because I have developed app with vue.js, I just used it.",[39,5955,5957],{"id":5956},"lets-try-othe-functions","Let's try othe functions!",[13,5959,5960],{},"Google Map API has any other functions. Examples, adding the maker or custom design maker and also inserting DOM element to the map. If saving these rendering data (path or maker position) as JSON and store in DB, we can create sharing map application as an example. I will try and research Google Map API. Thank you for your reading!",[2746,5962,5963],{},"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 .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);}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}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 .sbqyR, html code.shiki .sbqyR{--shiki-default:#FF9CAC}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}",{"title":411,"searchDepth":472,"depth":472,"links":5965},[5966,5967,5971,5972,5973,5974,5975,5976],{"id":3546,"depth":466,"text":3547},{"id":3574,"depth":466,"text":3575,"children":5968},[5969,5970],{"id":3593,"depth":472,"text":3594},{"id":3947,"depth":472,"text":3948},{"id":4207,"depth":466,"text":4208},{"id":4665,"depth":466,"text":4666},{"id":5161,"depth":466,"text":5162},{"id":5540,"depth":466,"text":5541},{"id":5949,"depth":466,"text":5950},{"id":5956,"depth":466,"text":5957},[2796],"Drawing routes on Google Map with Google Maps API and Vue.js",{},"\u002Fen\u002Farticles\u002Fgoogle-map-vuejs",{"title":3538,"description":5978},"en\u002Farticles\u002Fgoogle-map-vuejs",[3533,3048],"_mix\u002Fscp-2021-03-10-21.42.12-768x408.png","e3OhGlnl1sX9IYuWMsptHFjFwBdJSkLcFZlc_d3i3s4",{"id":5987,"title":5988,"body":5989,"category":10063,"createdAt":2799,"description":5988,"extension":2798,"index":2799,"meta":10064,"navigation":692,"path":10065,"publish":692,"seo":10066,"series":2799,"seriesTitle":2799,"stem":10067,"tag":10068,"thumbnail":10069,"updatedAt":2799,"__hash__":10070},"en_articles\u002Fen\u002Farticles\u002Fjs-canvas-wave.md","How to make wave animation at bottom of the image with canvas",{"type":10,"value":5990,"toc":10040},[5991,5994,5997,6005,6009,6012,6023,6027,6035,6038,6041,6044,6047,6064,6067,6071,6074,6078,6081,6090,6093,6347,6350,6354,6357,6645,6648,6708,6711,6906,6916,6932,6935,6938,6942,6952,7013,7016,7019,7045,7048,7129,7135,7142,7148,7151,7155,7161,7835,7838,7849,7852,7856,7967,7973,7977,7983,8298,8301,8321,8330,8333,8337,8405,8416,8421,8423,8433,8436,8440,8641,8644,8651,8677,8680,8704,8708,8711,8749,8762,8765,8767,8771,8780,8889,8899,8908,8948,8952,8955,9939,9943,9946,10015,10030,10034,10037],[13,5992,5993],{},"Hello developers! Thank you for reading my article. Today, I explain how to make animation with canvas as below.\nResponsive is not supported , but you can make it with only 70 lines of javascript.",[111,5995],{":src":5996,":width":114},"'_mix\u002Fex01-768x299.png'",[13,5998,5999,6004],{},[17,6000,6003],{"href":6001,"rel":6002},"https:\u002F\u002Fapps.jun-app.com\u002Fwave\u002F",[21],"Here,"," you can check it.",[39,6006,6008],{"id":6007},"required-knowledge","Required knowledge",[13,6010,6011],{},"Before explaining detail, let's check the required knowledge and principle about web canvas animation. If you well know, skip this section.",[195,6013,6014,6017,6020],{},[122,6015,6016],{},"how to set canvas with js",[122,6018,6019],{},"how to render image on canvas with js",[122,6021,6022],{},"Trigonometric function（very basic）",[39,6024,6026],{"id":6025},"principle-of-canvas-animation","Principle of canvas animation",[13,6028,6029,6030,6034],{},"You can check the sample ",[17,6031,6033],{"href":6001,"rel":6032},[21],"here",", the animation is running correctly and I don't use movie. This animation is played on canvas element with javascript.",[13,6036,6037],{},"Waving images is difficult for using only div tag or img tag. The animation needs canvas element.",[13,6039,6040],{},"Animation is doing \"Erase the image, redraw, erase, redraw ...\" at an invisible speed. Also, js draws after erasing the bottom of the image with a transparent wavy line. And to make the wave look like it is flowing, I use a trigonometric function to calculate the part to be erased in a wavy shape.",[13,6042,6043],{},"It is hard to understand, so let's make figures.",[111,6045],{":src":6046,":width":114},"'_mix\u002Fwave01.jpeg'",[195,6048,6049,6052,6055,6058,6061],{},[122,6050,6051],{},"Specify the drawing point as indicated by the orange dot in coordinates（１）",[122,6053,6054],{},"Specify the drawing other points with trigonometric function.（２）",[122,6056,6057],{},"When the point arrive at edge of image, back it to first place. （３、４）",[122,6059,6060],{},"You can fill srrounded area with any colors, so fill transparently.（５）",[122,6062,6063],{},"Rerender image as soon and do 1~5 again.",[13,6065,6066],{},"If you increase θ of sin(θ) each 1~6 process is repeated, it looks like a different wave swells each time. Implement the wave animation in this way.",[56,6068,6070],{"id":6069},"what-is-canvas","What is canvas?",[13,6072,6073],{},"Canvas is one of the HTML5 elements. It can draw 2D shapes, graphics and animation with javaScript. It makes complex animation and shapes.",[39,6075,6077],{"id":6076},"render-image-on-canvas","Render image on canvas",[13,6079,6080],{},"At first, let's display image on canvas element. This time, put the below files at the same level.",[195,6082,6083,6085,6087],{},[122,6084,3600],{},[122,6086,3174],{},[122,6088,6089],{},"sample.jpg",[13,6091,6092],{},"sample.jpg is trimed with 1:2 (verical:horizon).Yeah, let's develop! First step, create HTML as below.",[405,6094,6096],{"className":3607,"code":6095,"language":3610,"meta":411,"style":411},"\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",[297,6097,6098,6108,6116,6124,6143,6160,6168,6175,6188,6199,6204,6211,6238,6242,6251,6259,6267,6276,6300,6308,6331,6339],{"__ignoreMap":411},[457,6099,6100,6102,6104,6106],{"class":459,"line":460},[457,6101,3617],{"class":3043},[457,6103,3621],{"class":3620},[457,6105,3624],{"class":3127},[457,6107,3627],{"class":3043},[457,6109,6110,6112,6114],{"class":459,"line":466},[457,6111,3632],{"class":3043},[457,6113,3610],{"class":3620},[457,6115,3627],{"class":3043},[457,6117,6118,6120,6122],{"class":459,"line":472},[457,6119,3659],{"class":3043},[457,6121,3652],{"class":3620},[457,6123,3627],{"class":3043},[457,6125,6126,6129,6131,6133,6135,6137,6139,6141],{"class":459,"line":4},[457,6127,6128],{"class":3043},"        \u003C",[457,6130,3662],{"class":3620},[457,6132,3665],{"class":3127},[457,6134,3090],{"class":3043},[457,6136,3670],{"class":3043},[457,6138,3673],{"class":3047},[457,6140,3670],{"class":3043},[457,6142,3627],{"class":3043},[457,6144,6145,6147,6149,6151,6154,6156,6158],{"class":459,"line":483},[457,6146,6128],{"class":3043},[457,6148,3822],{"class":3620},[457,6150,3825],{"class":3043},[457,6152,6153],{"class":3036},"waving image",[457,6155,3831],{"class":3043},[457,6157,3822],{"class":3620},[457,6159,3627],{"class":3043},[457,6161,6162,6164,6166],{"class":459,"line":489},[457,6163,6128],{"class":3043},[457,6165,2746],{"class":3620},[457,6167,3627],{"class":3043},[457,6169,6170,6173],{"class":459,"line":495},[457,6171,6172],{"class":4110},"            *",[457,6174,469],{"class":3043},[457,6176,6177,6181,6183,6186],{"class":459,"line":501},[457,6178,6180],{"class":6179},"s6YsC","                margin",[457,6182,3120],{"class":3043},[457,6184,6185],{"class":4440},"0",[457,6187,3980],{"class":3043},[457,6189,6190,6193,6195,6197],{"class":459,"line":507},[457,6191,6192],{"class":6179},"                padding",[457,6194,3120],{"class":3043},[457,6196,6185],{"class":4440},[457,6198,3980],{"class":3043},[457,6200,6201],{"class":459,"line":513},[457,6202,6203],{"class":3043},"            }\n",[457,6205,6206,6209],{"class":459,"line":519},[457,6207,6208],{"class":4110},"            main",[457,6210,469],{"class":3043},[457,6212,6213,6216,6218,6221,6223,6226,6228,6231,6233,6235],{"class":459,"line":525},[457,6214,6215],{"class":6179},"                background",[457,6217,3120],{"class":3043},[457,6219,6220],{"class":3106},"rgb",[457,6222,3110],{"class":3043},[457,6224,6225],{"class":4440},"240",[457,6227,4444],{"class":3043},[457,6229,6230],{"class":4440}," 255",[457,6232,4444],{"class":3043},[457,6234,6230],{"class":4440},[457,6236,6237],{"class":3043},");;\n",[457,6239,6240],{"class":459,"line":531},[457,6241,6203],{"class":3043},[457,6243,6244,6247,6249],{"class":459,"line":537},[457,6245,6246],{"class":3043},"        \u003C\u002F",[457,6248,2746],{"class":3620},[457,6250,3627],{"class":3043},[457,6252,6253,6255,6257],{"class":459,"line":543},[457,6254,3885],{"class":3043},[457,6256,3652],{"class":3620},[457,6258,3627],{"class":3043},[457,6260,6261,6263,6265],{"class":459,"line":549},[457,6262,3659],{"class":3043},[457,6264,3851],{"class":3620},[457,6266,3627],{"class":3043},[457,6268,6269,6271,6274],{"class":459,"line":555},[457,6270,6128],{"class":3043},[457,6272,6273],{"class":3620},"main",[457,6275,3627],{"class":3043},[457,6277,6278,6281,6284,6286,6288,6290,6292,6294,6296,6298],{"class":459,"line":561},[457,6279,6280],{"class":3043},"            \u003C",[457,6282,6283],{"class":3620},"canvas",[457,6285,3898],{"class":3127},[457,6287,3090],{"class":3043},[457,6289,3670],{"class":3043},[457,6291,6283],{"class":3047},[457,6293,3670],{"class":3043},[457,6295,3805],{"class":3043},[457,6297,6283],{"class":3620},[457,6299,3627],{"class":3043},[457,6301,6302,6304,6306],{"class":459,"line":1030},[457,6303,6246],{"class":3043},[457,6305,6273],{"class":3620},[457,6307,3627],{"class":3043},[457,6309,6310,6312,6314,6316,6318,6320,6323,6325,6327,6329],{"class":459,"line":1036},[457,6311,6128],{"class":3043},[457,6313,3790],{"class":3620},[457,6315,3793],{"class":3127},[457,6317,3090],{"class":3043},[457,6319,3670],{"class":3043},[457,6321,6322],{"class":3047},".\u002Fapp.js",[457,6324,3670],{"class":3043},[457,6326,3805],{"class":3043},[457,6328,3790],{"class":3620},[457,6330,3627],{"class":3043},[457,6332,6333,6335,6337],{"class":459,"line":1041},[457,6334,3885],{"class":3043},[457,6336,3851],{"class":3620},[457,6338,3627],{"class":3043},[457,6340,6341,6343,6345],{"class":459,"line":1047},[457,6342,3831],{"class":3043},[457,6344,3610],{"class":3620},[457,6346,3627],{"class":3043},[13,6348,6349],{},"Prepare canvas element which has id to detect with javascript.　Next step, write js file.",[56,6351,6353],{"id":6352},"get-target-canvas-element-by-javascript","Get target canvas element by Javascript",[13,6355,6356],{},"Write app.js code as below.",[405,6358,6360],{"className":3022,"code":6359,"language":3025,"meta":411,"style":411},"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",[297,6361,6362,6372,6401,6430,6434,6457,6475,6491,6495,6523,6554,6570,6637,6641],{"__ignoreMap":411},[457,6363,6364,6367,6370],{"class":459,"line":460},[457,6365,6366],{"class":3127},"function",[457,6368,6369],{"class":3106}," initAnimation",[457,6371,4345],{"class":3043},[457,6373,6374,6377,6380,6382,6385,6387,6389,6391,6393,6395,6397,6399],{"class":459,"line":466},[457,6375,6376],{"class":3127},"    var",[457,6378,6379],{"class":3036}," canvas",[457,6381,4387],{"class":3043},[457,6383,6384],{"class":3036}," document",[457,6386,3079],{"class":3043},[457,6388,4030],{"class":3106},[457,6390,3110],{"class":3620},[457,6392,3154],{"class":3043},[457,6394,6283],{"class":3047},[457,6396,3154],{"class":3043},[457,6398,3144],{"class":3620},[457,6400,3980],{"class":3043},[457,6402,6403,6405,6408,6410,6412,6414,6417,6419,6421,6424,6426,6428],{"class":459,"line":472},[457,6404,6376],{"class":3127},[457,6406,6407],{"class":3036}," ctx",[457,6409,4387],{"class":3043},[457,6411,6379],{"class":3036},[457,6413,3079],{"class":3043},[457,6415,6416],{"class":3106},"getContext",[457,6418,3110],{"class":3620},[457,6420,3154],{"class":3043},[457,6422,6423],{"class":3047},"2d",[457,6425,3154],{"class":3043},[457,6427,3144],{"class":3620},[457,6429,3980],{"class":3043},[457,6431,6432],{"class":459,"line":4},[457,6433,693],{"emptyLinePlaceholder":692},[457,6435,6436,6438,6441,6443,6446,6448,6451,6453,6455],{"class":459,"line":483},[457,6437,6376],{"class":3127},[457,6439,6440],{"class":3036}," imagePath",[457,6442,4387],{"class":3043},[457,6444,6445],{"class":3620}," (",[457,6447,3154],{"class":3043},[457,6449,6450],{"class":3047},".\u002Fsample.jpg",[457,6452,3154],{"class":3043},[457,6454,3144],{"class":3620},[457,6456,3980],{"class":3043},[457,6458,6459,6461,6464,6466,6468,6471,6473],{"class":459,"line":489},[457,6460,6376],{"class":3127},[457,6462,6463],{"class":3036}," image",[457,6465,4387],{"class":3043},[457,6467,4390],{"class":3043},[457,6469,6470],{"class":3106}," Image",[457,6472,4802],{"class":3620},[457,6474,3980],{"class":3043},[457,6476,6477,6480,6482,6485,6487,6489],{"class":459,"line":495},[457,6478,6479],{"class":3036},"    image",[457,6481,3079],{"class":3043},[457,6483,6484],{"class":3036},"src",[457,6486,4387],{"class":3043},[457,6488,6440],{"class":3036},[457,6490,3980],{"class":3043},[457,6492,6493],{"class":459,"line":501},[457,6494,693],{"emptyLinePlaceholder":692},[457,6496,6497,6500,6502,6505,6507,6510,6512,6514,6516,6519,6521],{"class":459,"line":507},[457,6498,6499],{"class":3036},"    canvas",[457,6501,3079],{"class":3043},[457,6503,6504],{"class":3036},"width",[457,6506,4387],{"class":3043},[457,6508,6509],{"class":3106}," Number",[457,6511,3110],{"class":3620},[457,6513,4007],{"class":3036},[457,6515,3079],{"class":3043},[457,6517,6518],{"class":3036},"innerWidth",[457,6520,3144],{"class":3620},[457,6522,3980],{"class":3043},[457,6524,6525,6527,6529,6532,6534,6536,6538,6540,6542,6544,6547,6550,6552],{"class":459,"line":513},[457,6526,6499],{"class":3036},[457,6528,3079],{"class":3043},[457,6530,6531],{"class":3036},"height",[457,6533,4387],{"class":3043},[457,6535,6509],{"class":3106},[457,6537,3110],{"class":3620},[457,6539,6283],{"class":3036},[457,6541,3079],{"class":3043},[457,6543,6504],{"class":3036},[457,6545,6546],{"class":3043},"\u002F",[457,6548,6549],{"class":4440},"2",[457,6551,3144],{"class":3620},[457,6553,3980],{"class":3043},[457,6555,6556,6558,6560,6563,6565,6568],{"class":459,"line":519},[457,6557,6479],{"class":3036},[457,6559,3079],{"class":3043},[457,6561,6562],{"class":3106},"onload",[457,6564,4387],{"class":3043},[457,6566,6567],{"class":3127}," function",[457,6569,4345],{"class":3043},[457,6571,6572,6575,6577,6580,6582,6585,6587,6589,6591,6593,6595,6597,6599,6601,6603,6605,6607,6609,6611,6613,6615,6617,6619,6621,6623,6625,6627,6629,6631,6633,6635],{"class":459,"line":525},[457,6573,6574],{"class":3036},"          ctx",[457,6576,3079],{"class":3043},[457,6578,6579],{"class":3106},"drawImage",[457,6581,3110],{"class":3620},[457,6583,6584],{"class":3036},"image",[457,6586,4444],{"class":3043},[457,6588,6185],{"class":4440},[457,6590,4444],{"class":3043},[457,6592,6185],{"class":4440},[457,6594,4444],{"class":3043},[457,6596,6584],{"class":3036},[457,6598,3079],{"class":3043},[457,6600,6504],{"class":3036},[457,6602,4444],{"class":3043},[457,6604,6584],{"class":3036},[457,6606,3079],{"class":3043},[457,6608,6531],{"class":3036},[457,6610,4444],{"class":3043},[457,6612,6185],{"class":4440},[457,6614,4444],{"class":3043},[457,6616,6185],{"class":4440},[457,6618,4444],{"class":3043},[457,6620,6283],{"class":3036},[457,6622,3079],{"class":3043},[457,6624,6504],{"class":3036},[457,6626,4444],{"class":3043},[457,6628,6283],{"class":3036},[457,6630,3079],{"class":3043},[457,6632,6531],{"class":3036},[457,6634,3144],{"class":3620},[457,6636,3980],{"class":3043},[457,6638,6639],{"class":459,"line":531},[457,6640,1112],{"class":3043},[457,6642,6643],{"class":459,"line":537},[457,6644,564],{"class":3043},[13,6646,6647],{},"This function get canvas element from HTML and context instance to operate with javascript.\nAfter that, various process are made for this ctx (drawing context instance).",[405,6649,6651],{"className":3022,"code":6650,"language":3025,"meta":411,"style":411},"var canvas = document.getElementById('canvas');\nvar ctx = canvas.getContext('2d');\n",[297,6652,6653,6681],{"__ignoreMap":411},[457,6654,6655,6658,6661,6663,6665,6667,6669,6671,6673,6675,6677,6679],{"class":459,"line":460},[457,6656,6657],{"class":3127},"var",[457,6659,6660],{"class":3036}," canvas ",[457,6662,3090],{"class":3043},[457,6664,6384],{"class":3036},[457,6666,3079],{"class":3043},[457,6668,4030],{"class":3106},[457,6670,3110],{"class":3036},[457,6672,3154],{"class":3043},[457,6674,6283],{"class":3047},[457,6676,3154],{"class":3043},[457,6678,3144],{"class":3036},[457,6680,3980],{"class":3043},[457,6682,6683,6685,6688,6690,6692,6694,6696,6698,6700,6702,6704,6706],{"class":459,"line":466},[457,6684,6657],{"class":3127},[457,6686,6687],{"class":3036}," ctx ",[457,6689,3090],{"class":3043},[457,6691,6379],{"class":3036},[457,6693,3079],{"class":3043},[457,6695,6416],{"class":3106},[457,6697,3110],{"class":3036},[457,6699,3154],{"class":3043},[457,6701,6423],{"class":3047},[457,6703,3154],{"class":3043},[457,6705,3144],{"class":3036},[457,6707,3980],{"class":3043},[13,6709,6710],{},"and",[405,6712,6714],{"className":3022,"code":6713,"language":3025,"meta":411,"style":411},"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",[297,6715,6716,6737,6754,6769,6773,6795,6823,6837,6902],{"__ignoreMap":411},[457,6717,6718,6720,6723,6725,6727,6729,6731,6733,6735],{"class":459,"line":460},[457,6719,6657],{"class":3127},[457,6721,6722],{"class":3036}," imagePath ",[457,6724,3090],{"class":3043},[457,6726,6445],{"class":3036},[457,6728,3154],{"class":3043},[457,6730,6450],{"class":3047},[457,6732,3154],{"class":3043},[457,6734,3144],{"class":3036},[457,6736,3980],{"class":3043},[457,6738,6739,6741,6744,6746,6748,6750,6752],{"class":459,"line":466},[457,6740,6657],{"class":3127},[457,6742,6743],{"class":3036}," image ",[457,6745,3090],{"class":3043},[457,6747,4390],{"class":3043},[457,6749,6470],{"class":3106},[457,6751,4802],{"class":3036},[457,6753,3980],{"class":3043},[457,6755,6756,6758,6760,6763,6765,6767],{"class":459,"line":472},[457,6757,6584],{"class":3036},[457,6759,3079],{"class":3043},[457,6761,6762],{"class":3036},"src ",[457,6764,3090],{"class":3043},[457,6766,6440],{"class":3036},[457,6768,3980],{"class":3043},[457,6770,6771],{"class":459,"line":4},[457,6772,693],{"emptyLinePlaceholder":692},[457,6774,6775,6777,6779,6782,6784,6786,6788,6790,6793],{"class":459,"line":483},[457,6776,6283],{"class":3036},[457,6778,3079],{"class":3043},[457,6780,6781],{"class":3036},"width ",[457,6783,3090],{"class":3043},[457,6785,6509],{"class":3106},[457,6787,3972],{"class":3036},[457,6789,3079],{"class":3043},[457,6791,6792],{"class":3036},"innerWidth)",[457,6794,3980],{"class":3043},[457,6796,6797,6799,6801,6804,6806,6808,6811,6813,6815,6817,6819,6821],{"class":459,"line":489},[457,6798,6283],{"class":3036},[457,6800,3079],{"class":3043},[457,6802,6803],{"class":3036},"height ",[457,6805,3090],{"class":3043},[457,6807,6509],{"class":3106},[457,6809,6810],{"class":3036},"(canvas",[457,6812,3079],{"class":3043},[457,6814,6504],{"class":3036},[457,6816,6546],{"class":3043},[457,6818,6549],{"class":4440},[457,6820,3144],{"class":3036},[457,6822,3980],{"class":3043},[457,6824,6825,6827,6829,6831,6833,6835],{"class":459,"line":495},[457,6826,6584],{"class":3036},[457,6828,3079],{"class":3043},[457,6830,6562],{"class":3106},[457,6832,4387],{"class":3043},[457,6834,6567],{"class":3127},[457,6836,4345],{"class":3043},[457,6838,6839,6842,6844,6846,6848,6850,6852,6854,6856,6858,6860,6862,6864,6866,6868,6870,6872,6874,6876,6878,6880,6882,6884,6886,6888,6890,6892,6894,6896,6898,6900],{"class":459,"line":501},[457,6840,6841],{"class":3036},"        ctx",[457,6843,3079],{"class":3043},[457,6845,6579],{"class":3106},[457,6847,3110],{"class":3620},[457,6849,6584],{"class":3036},[457,6851,4444],{"class":3043},[457,6853,6185],{"class":4440},[457,6855,4444],{"class":3043},[457,6857,6185],{"class":4440},[457,6859,4444],{"class":3043},[457,6861,6584],{"class":3036},[457,6863,3079],{"class":3043},[457,6865,6504],{"class":3036},[457,6867,4444],{"class":3043},[457,6869,6584],{"class":3036},[457,6871,3079],{"class":3043},[457,6873,6531],{"class":3036},[457,6875,4444],{"class":3043},[457,6877,6185],{"class":4440},[457,6879,4444],{"class":3043},[457,6881,6185],{"class":4440},[457,6883,4444],{"class":3043},[457,6885,6283],{"class":3036},[457,6887,3079],{"class":3043},[457,6889,6504],{"class":3036},[457,6891,4444],{"class":3043},[457,6893,6283],{"class":3036},[457,6895,3079],{"class":3043},[457,6897,6531],{"class":3036},[457,6899,3144],{"class":3620},[457,6901,3980],{"class":3043},[457,6903,6904],{"class":459,"line":507},[457,6905,564],{"class":3043},[13,6907,6908,6909,3175,6912,6915],{},"Set image's path to src property using Image Object. Set canvas size with ",[297,6910,6911],{},"canvas.width",[297,6913,6914],{},"canvas.height",". So image ratio is 1:2, make canvas's ratio same.",[13,6917,6918,6921,6922,6925,6926,6928,6929,6931],{},[297,6919,6920],{},"image.onload"," run callback after image loaded.\n",[297,6923,6924],{},"drawImage()"," method renders image on canvas。\n",[297,6927,6924],{}," must be run after ",[297,6930,6920],{}," to set image.",[13,6933,6934],{},"Anyway, run script. Image will be rendered even you don't use img tag.",[111,6936],{":src":6937,":width":265,":center":266},"'_mix\u002Fwave02.png'",[63,6939,6941],{"id":6940},"how-to-use-drawimage","How to use drawImage()",[13,6943,6944,6946,6947],{},[297,6945,6924],{}," draws image on canvas specify range. If you render image at whole canvas, you should enter all arguments.",[17,6948,6951],{"href":6949,"rel":6950},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FCanvasRenderingContext2D\u002FdrawImage",[21],"（MDN）",[405,6953,6955],{"className":3022,"code":6954,"language":3025,"meta":411,"style":411},"void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);\n",[297,6956,6957],{"__ignoreMap":411},[457,6958,6959,6962,6964,6966,6968,6971,6973,6976,6978,6981,6983,6986,6988,6991,6993,6996,6998,7001,7003,7006,7008,7011],{"class":459,"line":460},[457,6960,6961],{"class":3043},"void",[457,6963,6407],{"class":3036},[457,6965,3079],{"class":3043},[457,6967,6579],{"class":3106},[457,6969,6970],{"class":3036},"(image",[457,6972,4444],{"class":3043},[457,6974,6975],{"class":3036}," sx",[457,6977,4444],{"class":3043},[457,6979,6980],{"class":3036}," sy",[457,6982,4444],{"class":3043},[457,6984,6985],{"class":3036}," sWidth",[457,6987,4444],{"class":3043},[457,6989,6990],{"class":3036}," sHeight",[457,6992,4444],{"class":3043},[457,6994,6995],{"class":3036}," dx",[457,6997,4444],{"class":3043},[457,6999,7000],{"class":3036}," dy",[457,7002,4444],{"class":3043},[457,7004,7005],{"class":3036}," dWidth",[457,7007,4444],{"class":3043},[457,7009,7010],{"class":3036}," dHeight)",[457,7012,3980],{"class":3043},[13,7014,7015],{},"Let me explain about arguments.",[13,7017,7018],{},"Image instance as well target image.",[119,7020,7021,7024,7027,7030,7033,7036,7039,7042],{},[122,7022,7023],{},"X coordinate of the image cropping start point",[122,7025,7026],{},"Y coordinate of the image cropping start point",[122,7028,7029],{},"Distance in the X direction to cut out from the X coordinate specified by the second argument（width）",[122,7031,7032],{},"Distance in the Y direction to cut out from the Y coordinate specified by the third argument（height）",[122,7034,7035],{},"X coordinate of the starting point to draw on canvas.",[122,7037,7038],{},"Y coordinate of the starting point to draw on canvas",[122,7040,7041],{},"Distance in the X direction to draw from the X coordinate specified by the 6th argument（width）",[122,7043,7044],{},"Distance (height) in the Y direction to draw from the Y coordinate specified by the 7th argument（height）",[13,7046,7047],{},"Like this.\nIn my example this time,",[405,7049,7051],{"className":3022,"code":7050,"language":3025,"meta":411,"style":411},"ctx.drawImage(\n    image,\n    0,0,image.width,image.height,\n    0,0,canvas.width,canvas.height\n);\n",[297,7052,7053,7065,7071,7098,7123],{"__ignoreMap":411},[457,7054,7055,7058,7060,7062],{"class":459,"line":460},[457,7056,7057],{"class":3036},"ctx",[457,7059,3079],{"class":3043},[457,7061,6579],{"class":3106},[457,7063,7064],{"class":3036},"(\n",[457,7066,7067,7069],{"class":459,"line":466},[457,7068,6479],{"class":3036},[457,7070,3136],{"class":3043},[457,7072,7073,7076,7078,7080,7082,7084,7086,7088,7090,7092,7094,7096],{"class":459,"line":472},[457,7074,7075],{"class":4440},"    0",[457,7077,4444],{"class":3043},[457,7079,6185],{"class":4440},[457,7081,4444],{"class":3043},[457,7083,6584],{"class":3036},[457,7085,3079],{"class":3043},[457,7087,6504],{"class":3036},[457,7089,4444],{"class":3043},[457,7091,6584],{"class":3036},[457,7093,3079],{"class":3043},[457,7095,6531],{"class":3036},[457,7097,3136],{"class":3043},[457,7099,7100,7102,7104,7106,7108,7110,7112,7114,7116,7118,7120],{"class":459,"line":4},[457,7101,7075],{"class":4440},[457,7103,4444],{"class":3043},[457,7105,6185],{"class":4440},[457,7107,4444],{"class":3043},[457,7109,6283],{"class":3036},[457,7111,3079],{"class":3043},[457,7113,6504],{"class":3036},[457,7115,4444],{"class":3043},[457,7117,6283],{"class":3036},[457,7119,3079],{"class":3043},[457,7121,7122],{"class":3036},"height\n",[457,7124,7125,7127],{"class":459,"line":483},[457,7126,3144],{"class":3036},[457,7128,3980],{"class":3043},[13,7130,7131,7134],{},[297,7132,7133],{},"0,0,image.width,image.height"," cut image from (0,0) coordinate point of the image by the image's width and height. That same as to cut whole image.",[13,7136,7137,7138,7141],{},"If you set ",[297,7139,7140],{},"0,0,image.width\u002F2,image.height\u002F2",", canvas displays 1\u002F4 size of original image.",[13,7143,7144,7147],{},[297,7145,7146],{},"0,0,canvas.width,canvas.height"," paste the image on canvas from (0,0) coordinate point by the canvas's width and height.",[13,7149,7150],{},"When you change ratio on code, you will well understand what I write above.",[39,7152,7154],{"id":7153},"cut-image-by-wave-shape","Cut image by wave shape",[13,7156,7157,7158,7160],{},"Let's cut image by wave shape. As I explained previwe of cutting above, draw square image which bottom line is wave shape and fill there transparent. Add below codes to ",[297,7159,6920],{}," function.",[405,7162,7164],{"className":3022,"code":7163,"language":3025,"meta":411,"style":411},"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",[297,7165,7166,7180,7189,7193,7197,7214,7231,7251,7255,7269,7283,7297,7301,7310,7333,7370,7374,7378,7400,7418,7435,7486,7490,7494,7530,7544,7564,7577,7598,7602,7644,7703,7729,7733,7737,7757,7777,7790,7794,7818,7831],{"__ignoreMap":411},[457,7167,7168,7170,7172,7174,7176,7178],{"class":459,"line":460},[457,7169,6584],{"class":3036},[457,7171,3079],{"class":3043},[457,7173,6562],{"class":3106},[457,7175,4387],{"class":3043},[457,7177,6567],{"class":3127},[457,7179,4345],{"class":3043},[457,7181,7182,7185,7187],{"class":459,"line":466},[457,7183,7184],{"class":3106},"    initDraw",[457,7186,4802],{"class":3620},[457,7188,3980],{"class":3043},[457,7190,7191],{"class":459,"line":472},[457,7192,564],{"class":3043},[457,7194,7195],{"class":459,"line":4},[457,7196,510],{"class":3036},[457,7198,7199,7201,7204,7206,7208,7210,7212],{"class":459,"line":483},[457,7200,6657],{"class":3127},[457,7202,7203],{"class":3036}," canvasEndX ",[457,7205,3090],{"class":3043},[457,7207,6379],{"class":3036},[457,7209,3079],{"class":3043},[457,7211,6504],{"class":3036},[457,7213,3980],{"class":3043},[457,7215,7216,7218,7221,7223,7225,7227,7229],{"class":459,"line":489},[457,7217,6657],{"class":3127},[457,7219,7220],{"class":3036}," canvasEndY ",[457,7222,3090],{"class":3043},[457,7224,6379],{"class":3036},[457,7226,3079],{"class":3043},[457,7228,6531],{"class":3036},[457,7230,3980],{"class":3043},[457,7232,7233,7235,7238,7240,7243,7246,7249],{"class":459,"line":495},[457,7234,6657],{"class":3127},[457,7236,7237],{"class":3036}," waveStartPoint ",[457,7239,3090],{"class":3043},[457,7241,7242],{"class":3036}," canvasEndY",[457,7244,7245],{"class":3043},"-",[457,7247,7248],{"class":4440},"150",[457,7250,3980],{"class":3043},[457,7252,7253],{"class":459,"line":501},[457,7254,693],{"emptyLinePlaceholder":692},[457,7256,7257,7259,7262,7264,7267],{"class":459,"line":507},[457,7258,6657],{"class":3127},[457,7260,7261],{"class":3036}," amplitude ",[457,7263,3090],{"class":3043},[457,7265,7266],{"class":4440}," 30",[457,7268,3980],{"class":3043},[457,7270,7271,7273,7276,7278,7281],{"class":459,"line":513},[457,7272,6657],{"class":3127},[457,7274,7275],{"class":3036}," period ",[457,7277,3090],{"class":3043},[457,7279,7280],{"class":4440}," 1000",[457,7282,3980],{"class":3043},[457,7284,7285,7287,7290,7292,7295],{"class":459,"line":519},[457,7286,6657],{"class":3127},[457,7288,7289],{"class":3036}," degree ",[457,7291,3090],{"class":3043},[457,7293,7294],{"class":4440}," 0",[457,7296,3980],{"class":3043},[457,7298,7299],{"class":459,"line":525},[457,7300,693],{"emptyLinePlaceholder":692},[457,7302,7303,7305,7308],{"class":459,"line":531},[457,7304,6366],{"class":3127},[457,7306,7307],{"class":3106}," initDraw",[457,7309,4345],{"class":3043},[457,7311,7312,7315,7317,7319,7321,7324,7326,7329,7331],{"class":459,"line":537},[457,7313,7314],{"class":3106},"    imageSet",[457,7316,3110],{"class":3620},[457,7318,6584],{"class":3036},[457,7320,4444],{"class":3043},[457,7322,7323],{"class":3036},"canvasEndX",[457,7325,4444],{"class":3043},[457,7327,7328],{"class":3036},"canvasEndY",[457,7330,3144],{"class":3620},[457,7332,3980],{"class":3043},[457,7334,7335,7338,7340,7343,7345,7347,7349,7351,7353,7356,7358,7361,7363,7366,7368],{"class":459,"line":543},[457,7336,7337],{"class":3106},"    waveDrawing",[457,7339,3110],{"class":3620},[457,7341,7342],{"class":3036},"waveStartPoint",[457,7344,4444],{"class":3043},[457,7346,7323],{"class":3036},[457,7348,4444],{"class":3043},[457,7350,7328],{"class":3036},[457,7352,4444],{"class":3043},[457,7354,7355],{"class":3036},"degree",[457,7357,4444],{"class":3043},[457,7359,7360],{"class":3036},"amplitude",[457,7362,4444],{"class":3043},[457,7364,7365],{"class":3036},"period",[457,7367,3144],{"class":3620},[457,7369,3980],{"class":3043},[457,7371,7372],{"class":459,"line":549},[457,7373,564],{"class":3043},[457,7375,7376],{"class":459,"line":555},[457,7377,693],{"emptyLinePlaceholder":692},[457,7379,7380,7382,7385,7387,7390,7392,7394,7396,7398],{"class":459,"line":561},[457,7381,6366],{"class":3127},[457,7383,7384],{"class":3106}," imageSet",[457,7386,3110],{"class":3043},[457,7388,7389],{"class":3123},"imageObj",[457,7391,4444],{"class":3043},[457,7393,7323],{"class":3123},[457,7395,4444],{"class":3043},[457,7397,7328],{"class":3123},[457,7399,4855],{"class":3043},[457,7401,7402,7404,7407,7409,7412,7414,7416],{"class":459,"line":1030},[457,7403,6376],{"class":3127},[457,7405,7406],{"class":3036}," imgWidth",[457,7408,4387],{"class":3043},[457,7410,7411],{"class":3036}," imageObj",[457,7413,3079],{"class":3043},[457,7415,6504],{"class":3036},[457,7417,3980],{"class":3043},[457,7419,7420,7422,7425,7427,7429,7431,7433],{"class":459,"line":1036},[457,7421,6376],{"class":3127},[457,7423,7424],{"class":3036}," imgHeight",[457,7426,4387],{"class":3043},[457,7428,7411],{"class":3036},[457,7430,3079],{"class":3043},[457,7432,6531],{"class":3036},[457,7434,3980],{"class":3043},[457,7436,7437,7440,7442,7444,7446,7448,7450,7452,7454,7456,7458,7461,7463,7466,7468,7470,7472,7474,7476,7478,7480,7482,7484],{"class":459,"line":1041},[457,7438,7439],{"class":3036},"    ctx",[457,7441,3079],{"class":3043},[457,7443,6579],{"class":3106},[457,7445,3110],{"class":3620},[457,7447,6584],{"class":3036},[457,7449,4444],{"class":3043},[457,7451,6185],{"class":4440},[457,7453,4444],{"class":3043},[457,7455,6185],{"class":4440},[457,7457,4444],{"class":3043},[457,7459,7460],{"class":3036},"imgWidth",[457,7462,4444],{"class":3043},[457,7464,7465],{"class":3036},"imgHeight",[457,7467,4444],{"class":3043},[457,7469,6185],{"class":4440},[457,7471,4444],{"class":3043},[457,7473,6185],{"class":4440},[457,7475,4444],{"class":3043},[457,7477,7323],{"class":3036},[457,7479,4444],{"class":3043},[457,7481,7328],{"class":3036},[457,7483,3144],{"class":3620},[457,7485,3980],{"class":3043},[457,7487,7488],{"class":459,"line":1047},[457,7489,564],{"class":3043},[457,7491,7492],{"class":459,"line":1052},[457,7493,693],{"emptyLinePlaceholder":692},[457,7495,7496,7498,7501,7503,7505,7507,7509,7511,7513,7515,7518,7520,7523,7525,7528],{"class":459,"line":1058},[457,7497,6366],{"class":3127},[457,7499,7500],{"class":3106}," waveDrawing",[457,7502,3110],{"class":3043},[457,7504,7342],{"class":3123},[457,7506,4444],{"class":3043},[457,7508,7323],{"class":3123},[457,7510,4444],{"class":3043},[457,7512,7328],{"class":3123},[457,7514,4444],{"class":3043},[457,7516,7517],{"class":3123},"deg",[457,7519,4444],{"class":3043},[457,7521,7522],{"class":3123},"am",[457,7524,4444],{"class":3043},[457,7526,7527],{"class":3123},"tp",[457,7529,4855],{"class":3043},[457,7531,7532,7534,7537,7539,7542],{"class":459,"line":1064},[457,7533,6376],{"class":3127},[457,7535,7536],{"class":3036}," waveStartY",[457,7538,4387],{"class":3043},[457,7540,7541],{"class":3036}," waveStartPoint",[457,7543,3980],{"class":3043},[457,7545,7546,7548,7550,7553,7555,7557,7560,7562],{"class":459,"line":1070},[457,7547,7439],{"class":3036},[457,7549,3079],{"class":3043},[457,7551,7552],{"class":3036},"globalCompositeOperation",[457,7554,4387],{"class":3043},[457,7556,5438],{"class":3043},[457,7558,7559],{"class":3047},"destination-out",[457,7561,3670],{"class":3043},[457,7563,3980],{"class":3043},[457,7565,7566,7568,7570,7573,7575],{"class":459,"line":1076},[457,7567,7439],{"class":3036},[457,7569,3079],{"class":3043},[457,7571,7572],{"class":3106},"beginPath",[457,7574,4802],{"class":3620},[457,7576,3980],{"class":3043},[457,7578,7579,7581,7583,7586,7588,7590,7592,7594,7596],{"class":459,"line":1081},[457,7580,7439],{"class":3036},[457,7582,3079],{"class":3043},[457,7584,7585],{"class":3106},"moveTo",[457,7587,3110],{"class":3620},[457,7589,6185],{"class":4440},[457,7591,4444],{"class":3043},[457,7593,7536],{"class":3036},[457,7595,3144],{"class":3620},[457,7597,3980],{"class":3043},[457,7599,7600],{"class":459,"line":1086},[457,7601,693],{"emptyLinePlaceholder":692},[457,7603,7604,7607,7609,7611,7614,7616,7618,7621,7623,7626,7629,7631,7633,7636,7639,7642],{"class":459,"line":1092},[457,7605,7606],{"class":3032},"    for",[457,7608,6445],{"class":3620},[457,7610,6657],{"class":3127},[457,7612,7613],{"class":3036}," x",[457,7615,3090],{"class":3043},[457,7617,6185],{"class":4440},[457,7619,7620],{"class":3043},";",[457,7622,7613],{"class":3036},[457,7624,7625],{"class":3043}," \u003C=",[457,7627,7628],{"class":3036}," canvasEndX",[457,7630,7620],{"class":3043},[457,7632,7613],{"class":3036},[457,7634,7635],{"class":3043},"+=",[457,7637,7638],{"class":4440}," 1",[457,7640,7641],{"class":3620},") ",[457,7643,469],{"class":3043},[457,7645,7646,7649,7652,7654,7657,7659,7662,7665,7667,7670,7673,7675,7677,7680,7682,7684,7686,7688,7690,7692,7695,7698,7701],{"class":459,"line":1098},[457,7647,7648],{"class":3127},"        var",[457,7650,7651],{"class":3036}," y",[457,7653,4387],{"class":3043},[457,7655,7656],{"class":3043}," -",[457,7658,7522],{"class":3036},[457,7660,7661],{"class":3043},"*",[457,7663,7664],{"class":3036},"Math",[457,7666,3079],{"class":3043},[457,7668,7669],{"class":3106},"sin",[457,7671,7672],{"class":3620},"((",[457,7674,7664],{"class":3036},[457,7676,3079],{"class":3043},[457,7678,7679],{"class":3036},"PI",[457,7681,6546],{"class":3043},[457,7683,7527],{"class":3036},[457,7685,3144],{"class":3620},[457,7687,7661],{"class":3043},[457,7689,3110],{"class":3620},[457,7691,7517],{"class":3036},[457,7693,7694],{"class":3043},"+",[457,7696,7697],{"class":3036},"x",[457,7699,7700],{"class":3620},"))",[457,7702,3980],{"class":3043},[457,7704,7705,7707,7709,7712,7714,7716,7718,7720,7722,7725,7727],{"class":459,"line":1104},[457,7706,6841],{"class":3036},[457,7708,3079],{"class":3043},[457,7710,7711],{"class":3106},"lineTo",[457,7713,3110],{"class":3620},[457,7715,7697],{"class":3036},[457,7717,4444],{"class":3043},[457,7719,7651],{"class":3036},[457,7721,7694],{"class":3043},[457,7723,7724],{"class":3036},"waveStartY",[457,7726,3144],{"class":3620},[457,7728,3980],{"class":3043},[457,7730,7731],{"class":459,"line":1109},[457,7732,1112],{"class":3043},[457,7734,7735],{"class":459,"line":1115},[457,7736,693],{"emptyLinePlaceholder":692},[457,7738,7739,7741,7743,7745,7747,7749,7751,7753,7755],{"class":459,"line":1854},[457,7740,7439],{"class":3036},[457,7742,3079],{"class":3043},[457,7744,7711],{"class":3106},[457,7746,3110],{"class":3620},[457,7748,7323],{"class":3036},[457,7750,4444],{"class":3043},[457,7752,7328],{"class":3036},[457,7754,3144],{"class":3620},[457,7756,3980],{"class":3043},[457,7758,7759,7761,7763,7765,7767,7769,7771,7773,7775],{"class":459,"line":1859},[457,7760,7439],{"class":3036},[457,7762,3079],{"class":3043},[457,7764,7711],{"class":3106},[457,7766,3110],{"class":3620},[457,7768,6185],{"class":4440},[457,7770,4444],{"class":3043},[457,7772,7328],{"class":3036},[457,7774,3144],{"class":3620},[457,7776,3980],{"class":3043},[457,7778,7779,7781,7783,7786,7788],{"class":459,"line":1864},[457,7780,7439],{"class":3036},[457,7782,3079],{"class":3043},[457,7784,7785],{"class":3106},"closePath",[457,7787,4802],{"class":3620},[457,7789,3980],{"class":3043},[457,7791,7792],{"class":459,"line":1870},[457,7793,693],{"emptyLinePlaceholder":692},[457,7795,7796,7799,7801,7804,7806,7808,7811,7813,7815],{"class":459,"line":1875},[457,7797,7798],{"class":3036},"    　ctx",[457,7800,3079],{"class":3043},[457,7802,7803],{"class":3036},"fillStyle",[457,7805,4387],{"class":3043},[457,7807,5438],{"class":3043},[457,7809,7810],{"class":3047},"rgba(255,255,255,1)",[457,7812,3670],{"class":3043},[457,7814,7620],{"class":3043},[457,7816,7817],{"class":3782}," \u002F\u002Fopacity 1\n",[457,7819,7820,7822,7824,7827,7829],{"class":459,"line":1880},[457,7821,7439],{"class":3036},[457,7823,3079],{"class":3043},[457,7825,7826],{"class":3106},"fill",[457,7828,4802],{"class":3620},[457,7830,3980],{"class":3043},[457,7832,7833],{"class":459,"line":1885},[457,7834,564],{"class":3043},[13,7836,7837],{},"I make new three functions there.",[195,7839,7840,7843,7846],{},[122,7841,7842],{},"initDraw()：Call below two function.",[122,7844,7845],{},"imageSet()：Render image on canvas. If it is already rendered, clear it.",[122,7847,7848],{},"waveDrawing()：Cut image by wave shape",[13,7850,7851],{},"I explain these.",[56,7853,7855],{"id":7854},"imagesetrender-image-on-canvas","imageSet():Render image on canvas.",[405,7857,7859],{"className":3022,"code":7858,"language":3025,"meta":411,"style":411},"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",[297,7860,7861,7881,7898,7914,7963],{"__ignoreMap":411},[457,7862,7863,7865,7867,7869,7871,7873,7875,7877,7879],{"class":459,"line":460},[457,7864,6366],{"class":3127},[457,7866,7384],{"class":3106},[457,7868,3110],{"class":3043},[457,7870,7389],{"class":3123},[457,7872,4444],{"class":3043},[457,7874,7323],{"class":3123},[457,7876,4444],{"class":3043},[457,7878,7328],{"class":3123},[457,7880,4855],{"class":3043},[457,7882,7883,7886,7888,7890,7892,7894,7896],{"class":459,"line":466},[457,7884,7885],{"class":3127},"     var",[457,7887,7406],{"class":3036},[457,7889,4387],{"class":3043},[457,7891,7411],{"class":3036},[457,7893,3079],{"class":3043},[457,7895,6504],{"class":3036},[457,7897,3980],{"class":3043},[457,7899,7900,7902,7904,7906,7908,7910,7912],{"class":459,"line":472},[457,7901,7885],{"class":3127},[457,7903,7424],{"class":3036},[457,7905,4387],{"class":3043},[457,7907,7411],{"class":3036},[457,7909,3079],{"class":3043},[457,7911,6531],{"class":3036},[457,7913,3980],{"class":3043},[457,7915,7916,7919,7921,7923,7925,7927,7929,7931,7933,7935,7937,7939,7941,7943,7945,7947,7949,7951,7953,7955,7957,7959,7961],{"class":459,"line":4},[457,7917,7918],{"class":3036},"     ctx",[457,7920,3079],{"class":3043},[457,7922,6579],{"class":3106},[457,7924,3110],{"class":3620},[457,7926,6584],{"class":3036},[457,7928,4444],{"class":3043},[457,7930,6185],{"class":4440},[457,7932,4444],{"class":3043},[457,7934,6185],{"class":4440},[457,7936,4444],{"class":3043},[457,7938,7460],{"class":3036},[457,7940,4444],{"class":3043},[457,7942,7465],{"class":3036},[457,7944,4444],{"class":3043},[457,7946,6185],{"class":4440},[457,7948,4444],{"class":3043},[457,7950,6185],{"class":4440},[457,7952,4444],{"class":3043},[457,7954,7323],{"class":3036},[457,7956,4444],{"class":3043},[457,7958,7328],{"class":3036},[457,7960,3144],{"class":3620},[457,7962,3980],{"class":3043},[457,7964,7965],{"class":459,"line":483},[457,7966,564],{"class":3043},[13,7968,7969,7970,7972],{},"This function just render image on canvas. When you set image object, canvas height and width at artguments, function use ",[297,7971,6924],{}," to render image.",[56,7974,7976],{"id":7975},"wavedrawingcut-image-by-wave-shape","waveDrawing():Cut image by wave shape",[13,7978,7979,7982],{},[297,7980,7981],{},"waveDrawing()","has process cutting image by wave shape.",[405,7984,7986],{"className":3022,"code":7985,"language":3025,"meta":411,"style":411},"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",[297,7987,7988,8020,8033,8052,8064,8084,8088,8123,8173,8198,8202,8206,8226,8246,8258,8262,8282,8294],{"__ignoreMap":411},[457,7989,7990,7992,7994,7996,7998,8000,8002,8004,8006,8008,8010,8012,8014,8016,8018],{"class":459,"line":460},[457,7991,6366],{"class":3127},[457,7993,7500],{"class":3106},[457,7995,3110],{"class":3043},[457,7997,7342],{"class":3123},[457,7999,4444],{"class":3043},[457,8001,7323],{"class":3123},[457,8003,4444],{"class":3043},[457,8005,7328],{"class":3123},[457,8007,4444],{"class":3043},[457,8009,7517],{"class":3123},[457,8011,4444],{"class":3043},[457,8013,7522],{"class":3123},[457,8015,4444],{"class":3043},[457,8017,7527],{"class":3123},[457,8019,4855],{"class":3043},[457,8021,8022,8025,8027,8029,8031],{"class":459,"line":466},[457,8023,8024],{"class":3127},"      var",[457,8026,7536],{"class":3036},[457,8028,4387],{"class":3043},[457,8030,7541],{"class":3036},[457,8032,3980],{"class":3043},[457,8034,8035,8038,8040,8042,8044,8046,8048,8050],{"class":459,"line":472},[457,8036,8037],{"class":3036},"      ctx",[457,8039,3079],{"class":3043},[457,8041,7552],{"class":3036},[457,8043,4387],{"class":3043},[457,8045,5438],{"class":3043},[457,8047,7559],{"class":3047},[457,8049,3670],{"class":3043},[457,8051,3980],{"class":3043},[457,8053,8054,8056,8058,8060,8062],{"class":459,"line":4},[457,8055,8037],{"class":3036},[457,8057,3079],{"class":3043},[457,8059,7572],{"class":3106},[457,8061,4802],{"class":3620},[457,8063,3980],{"class":3043},[457,8065,8066,8068,8070,8072,8074,8076,8078,8080,8082],{"class":459,"line":483},[457,8067,8037],{"class":3036},[457,8069,3079],{"class":3043},[457,8071,7585],{"class":3106},[457,8073,3110],{"class":3620},[457,8075,6185],{"class":4440},[457,8077,4444],{"class":3043},[457,8079,7536],{"class":3036},[457,8081,3144],{"class":3620},[457,8083,3980],{"class":3043},[457,8085,8086],{"class":459,"line":489},[457,8087,693],{"emptyLinePlaceholder":692},[457,8089,8090,8093,8095,8097,8099,8101,8103,8105,8107,8109,8111,8113,8115,8117,8119,8121],{"class":459,"line":495},[457,8091,8092],{"class":3032},"      for",[457,8094,6445],{"class":3620},[457,8096,6657],{"class":3127},[457,8098,7613],{"class":3036},[457,8100,3090],{"class":3043},[457,8102,6185],{"class":4440},[457,8104,7620],{"class":3043},[457,8106,7613],{"class":3036},[457,8108,7625],{"class":3043},[457,8110,7628],{"class":3036},[457,8112,7620],{"class":3043},[457,8114,7613],{"class":3036},[457,8116,7635],{"class":3043},[457,8118,7638],{"class":4440},[457,8120,7641],{"class":3620},[457,8122,469],{"class":3043},[457,8124,8125,8128,8130,8132,8134,8136,8138,8140,8142,8144,8146,8148,8150,8152,8154,8156,8158,8160,8162,8164,8166,8168,8170],{"class":459,"line":501},[457,8126,8127],{"class":3127},"           var",[457,8129,7651],{"class":3036},[457,8131,4387],{"class":3043},[457,8133,7656],{"class":3043},[457,8135,7522],{"class":3036},[457,8137,7661],{"class":3043},[457,8139,7664],{"class":3036},[457,8141,3079],{"class":3043},[457,8143,7669],{"class":3106},[457,8145,7672],{"class":3620},[457,8147,7664],{"class":3036},[457,8149,3079],{"class":3043},[457,8151,7679],{"class":3036},[457,8153,6546],{"class":3043},[457,8155,7527],{"class":3036},[457,8157,3144],{"class":3620},[457,8159,7661],{"class":3043},[457,8161,3110],{"class":3620},[457,8163,7517],{"class":3036},[457,8165,7694],{"class":3043},[457,8167,7697],{"class":3036},[457,8169,7700],{"class":3620},[457,8171,8172],{"class":3043},";;\n",[457,8174,8175,8178,8180,8182,8184,8186,8188,8190,8192,8194,8196],{"class":459,"line":507},[457,8176,8177],{"class":3036},"           ctx",[457,8179,3079],{"class":3043},[457,8181,7711],{"class":3106},[457,8183,3110],{"class":3620},[457,8185,7697],{"class":3036},[457,8187,4444],{"class":3043},[457,8189,7651],{"class":3036},[457,8191,7694],{"class":3043},[457,8193,7724],{"class":3036},[457,8195,3144],{"class":3620},[457,8197,3980],{"class":3043},[457,8199,8200],{"class":459,"line":513},[457,8201,4831],{"class":3043},[457,8203,8204],{"class":459,"line":519},[457,8205,693],{"emptyLinePlaceholder":692},[457,8207,8208,8210,8212,8214,8216,8218,8220,8222,8224],{"class":459,"line":525},[457,8209,8037],{"class":3036},[457,8211,3079],{"class":3043},[457,8213,7711],{"class":3106},[457,8215,3110],{"class":3620},[457,8217,7323],{"class":3036},[457,8219,4444],{"class":3043},[457,8221,7328],{"class":3036},[457,8223,3144],{"class":3620},[457,8225,3980],{"class":3043},[457,8227,8228,8230,8232,8234,8236,8238,8240,8242,8244],{"class":459,"line":531},[457,8229,8037],{"class":3036},[457,8231,3079],{"class":3043},[457,8233,7711],{"class":3106},[457,8235,3110],{"class":3620},[457,8237,6185],{"class":4440},[457,8239,4444],{"class":3043},[457,8241,7328],{"class":3036},[457,8243,3144],{"class":3620},[457,8245,3980],{"class":3043},[457,8247,8248,8250,8252,8254,8256],{"class":459,"line":537},[457,8249,8037],{"class":3036},[457,8251,3079],{"class":3043},[457,8253,7785],{"class":3106},[457,8255,4802],{"class":3620},[457,8257,3980],{"class":3043},[457,8259,8260],{"class":459,"line":543},[457,8261,693],{"emptyLinePlaceholder":692},[457,8263,8264,8266,8268,8270,8272,8274,8276,8278,8280],{"class":459,"line":549},[457,8265,8037],{"class":3036},[457,8267,3079],{"class":3043},[457,8269,7803],{"class":3036},[457,8271,4387],{"class":3043},[457,8273,5438],{"class":3043},[457,8275,7810],{"class":3047},[457,8277,3670],{"class":3043},[457,8279,7620],{"class":3043},[457,8281,7817],{"class":3782},[457,8283,8284,8286,8288,8290,8292],{"class":459,"line":555},[457,8285,8037],{"class":3036},[457,8287,3079],{"class":3043},[457,8289,7826],{"class":3106},[457,8291,4802],{"class":3620},[457,8293,3980],{"class":3043},[457,8295,8296],{"class":459,"line":561},[457,8297,564],{"class":3043},[13,8299,8300],{},"Each parameters mean",[195,8302,8303,8306,8309,8312,8315,8318],{},[122,8304,8305],{},"waveStartPoint：Start Y coordinate to write the wave.",[122,8307,8308],{},"canvasEndX：X coordinate of the canvas's right edge.（max canvas's X coordinate）",[122,8310,8311],{},"canvasEndY：Y coordinate of the canvas's bottom edge.（max canvas's Y coordinate）",[122,8313,8314],{},"deg：Init degree.",[122,8316,8317],{},"am：amplitude（This changes the wave's maxed height）",[122,8319,8320],{},"tp：period（This changes a wave's maxed width）",[13,8322,8323,8324,8326,8327,8329],{},"You don't have to think hard. More incresing ",[297,8325,7522],{}," higher wave. More increasing ",[297,8328,7527],{},",  gentle wave.",[13,8331,8332],{},"And ctx is the canvas instance you define above. It is global vars at this time.",[63,8334,8336],{"id":8335},"define-the-position-to-draw-and-configure-superposition","Define the position to draw and Configure superposition",[405,8338,8340],{"className":3022,"code":8339,"language":3025,"meta":411,"style":411},"var waveStartY = waveStartPoint;\nctx.globalCompositeOperation = \"destination-out\";\nctx.beginPath();\nctx.moveTo(0, waveStartY);\n",[297,8341,8342,8355,8374,8386],{"__ignoreMap":411},[457,8343,8344,8346,8349,8351,8353],{"class":459,"line":460},[457,8345,6657],{"class":3127},[457,8347,8348],{"class":3036}," waveStartY ",[457,8350,3090],{"class":3043},[457,8352,7541],{"class":3036},[457,8354,3980],{"class":3043},[457,8356,8357,8359,8361,8364,8366,8368,8370,8372],{"class":459,"line":466},[457,8358,7057],{"class":3036},[457,8360,3079],{"class":3043},[457,8362,8363],{"class":3036},"globalCompositeOperation ",[457,8365,3090],{"class":3043},[457,8367,5438],{"class":3043},[457,8369,7559],{"class":3047},[457,8371,3670],{"class":3043},[457,8373,3980],{"class":3043},[457,8375,8376,8378,8380,8382,8384],{"class":459,"line":472},[457,8377,7057],{"class":3036},[457,8379,3079],{"class":3043},[457,8381,7572],{"class":3106},[457,8383,4802],{"class":3036},[457,8385,3980],{"class":3043},[457,8387,8388,8390,8392,8394,8396,8398,8400,8403],{"class":459,"line":4},[457,8389,7057],{"class":3036},[457,8391,3079],{"class":3043},[457,8393,7585],{"class":3106},[457,8395,3110],{"class":3036},[457,8397,6185],{"class":4440},[457,8399,4444],{"class":3043},[457,8401,8402],{"class":3036}," waveStartY)",[457,8404,3980],{"class":3043},[13,8406,8407,8410,8411],{},[297,8408,8409],{},"ctx.globalCompositeOperation = \"destination-out\"; "," is very important to make the image transparent. ",[17,8412,8415],{"href":8413,"rel":8414},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fja\u002Fdocs\u002FWeb\u002FAPI\u002FCanvasRenderingContext2D\u002FglobalCompositeOperation",[21],"MDN",[13,8417,8418,8420],{},[297,8419,7552],{}," configure how the browser renders superposed shapes on the canvas. Remenber the below image.",[111,8422],{":src":6046,":width":114},[13,8424,8425,8426,3175,8429,8432],{},"This process fills the image by transparent within the area surrounded by a red line. When that, ",[292,8427,8428],{},"the already rendered image",[292,8430,8431],{},"the wave shape filled transparent"," are superposed.",[13,8434,8435],{},"The configure renderers only parts whitch the image and the shape are not superposed. So particular area will be transparent like no.5 of the above figure",[63,8437,8439],{"id":8438},"specify-cut-area","Specify cut area",[405,8441,8443],{"className":3022,"code":8442,"language":3025,"meta":411,"style":411}," 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",[297,8444,8445,8457,8475,8512,8560,8584,8589,8593,8611,8629],{"__ignoreMap":411},[457,8446,8447,8449,8451,8453,8455],{"class":459,"line":460},[457,8448,6407],{"class":3036},[457,8450,3079],{"class":3043},[457,8452,7572],{"class":3106},[457,8454,4802],{"class":3036},[457,8456,3980],{"class":3043},[457,8458,8459,8461,8463,8465,8467,8469,8471,8473],{"class":459,"line":466},[457,8460,6407],{"class":3036},[457,8462,3079],{"class":3043},[457,8464,7585],{"class":3106},[457,8466,3110],{"class":3036},[457,8468,6185],{"class":4440},[457,8470,4444],{"class":3043},[457,8472,8402],{"class":3036},[457,8474,3980],{"class":3043},[457,8476,8477,8480,8482,8484,8486,8488,8490,8492,8495,8498,8500,8502,8504,8506,8508,8510],{"class":459,"line":472},[457,8478,8479],{"class":3032}," for",[457,8481,6445],{"class":3036},[457,8483,6657],{"class":3127},[457,8485,7613],{"class":3036},[457,8487,3090],{"class":3043},[457,8489,6185],{"class":4440},[457,8491,7620],{"class":3043},[457,8493,8494],{"class":3036}," x ",[457,8496,8497],{"class":3043},"\u003C=",[457,8499,7628],{"class":3036},[457,8501,7620],{"class":3043},[457,8503,7613],{"class":3036},[457,8505,7635],{"class":3043},[457,8507,7638],{"class":4440},[457,8509,7641],{"class":3036},[457,8511,469],{"class":3043},[457,8513,8514,8516,8518,8520,8522,8524,8526,8528,8530,8532,8534,8536,8538,8540,8542,8544,8546,8548,8550,8552,8554,8556,8558],{"class":459,"line":4},[457,8515,7885],{"class":3127},[457,8517,7651],{"class":3036},[457,8519,4387],{"class":3043},[457,8521,7656],{"class":3043},[457,8523,7522],{"class":3036},[457,8525,7661],{"class":3043},[457,8527,7664],{"class":3036},[457,8529,3079],{"class":3043},[457,8531,7669],{"class":3106},[457,8533,7672],{"class":3620},[457,8535,7664],{"class":3036},[457,8537,3079],{"class":3043},[457,8539,7679],{"class":3036},[457,8541,6546],{"class":3043},[457,8543,7527],{"class":3036},[457,8545,3144],{"class":3620},[457,8547,7661],{"class":3043},[457,8549,3110],{"class":3620},[457,8551,7517],{"class":3036},[457,8553,7694],{"class":3043},[457,8555,7697],{"class":3036},[457,8557,7700],{"class":3620},[457,8559,8172],{"class":3043},[457,8561,8562,8564,8566,8568,8570,8572,8574,8576,8578,8580,8582],{"class":459,"line":483},[457,8563,7918],{"class":3036},[457,8565,3079],{"class":3043},[457,8567,7711],{"class":3106},[457,8569,3110],{"class":3620},[457,8571,7697],{"class":3036},[457,8573,4444],{"class":3043},[457,8575,7651],{"class":3036},[457,8577,7694],{"class":3043},[457,8579,7724],{"class":3036},[457,8581,3144],{"class":3620},[457,8583,3980],{"class":3043},[457,8585,8586],{"class":459,"line":489},[457,8587,8588],{"class":3043}," }\n",[457,8590,8591],{"class":459,"line":495},[457,8592,693],{"emptyLinePlaceholder":692},[457,8594,8595,8597,8599,8601,8604,8606,8609],{"class":459,"line":501},[457,8596,6407],{"class":3036},[457,8598,3079],{"class":3043},[457,8600,7711],{"class":3106},[457,8602,8603],{"class":3036},"(canvasEndX",[457,8605,4444],{"class":3043},[457,8607,8608],{"class":3036},"canvasEndY)",[457,8610,3980],{"class":3043},[457,8612,8613,8615,8617,8619,8621,8623,8625,8627],{"class":459,"line":507},[457,8614,6407],{"class":3036},[457,8616,3079],{"class":3043},[457,8618,7711],{"class":3106},[457,8620,3110],{"class":3036},[457,8622,6185],{"class":4440},[457,8624,4444],{"class":3043},[457,8626,8608],{"class":3036},[457,8628,3980],{"class":3043},[457,8630,8631,8633,8635,8637,8639],{"class":459,"line":513},[457,8632,6407],{"class":3036},[457,8634,3079],{"class":3043},[457,8636,7785],{"class":3106},[457,8638,4802],{"class":3036},[457,8640,3980],{"class":3043},[13,8642,8643],{},"Next, I specify the cut range. That is 1~4 of the above figure. In this section, the word 'path' is often used. If you use Illustrator or Photoshop, you know it. Javascript also uses 'path' and specify the cut range.",[13,8645,8646,8647,8650],{},"Just in the case for who do not know 'path', I explain it. The path is the point that defines the shape. Drawing lines from point to point makes the shape. For example, the square has 4 points(edge) and, writing in one stroke from point to point makes the square. You define the ",[8648,8649,5822],"em",{}," of writing in one stroke and position on the code.",[119,8652,8653,8659,8665,8671],{},[122,8654,8655,8658],{},[297,8656,8657],{},"ctx.beginPath()"," starts setting paths.",[122,8660,8661,8664],{},[297,8662,8663],{},"ctx.moveTo(X,Y)"," moves the path to specify coordinates.",[122,8666,8667,8670],{},[297,8668,8669],{},"ctx.lineTo(nextX,nextY)"," draw a line and move next coordinates.",[122,8672,8673,8676],{},[297,8674,8675],{},"ctx.closePath()"," declear to end setting path.",[13,8678,8679],{},"In this article case,",[119,8681,8682,8691,8698,8701],{},[122,8683,8684,8685,8687,8688,8690],{},"Start setting paths from （0, waveStartY）. ",[297,8686,6185],{}," is the left edge of the image (X = 0) and ",[297,8689,7724],{}," is a start point for the wave.",[122,8692,8693,8694,8697],{},"Using ",[297,8695,8696],{},"for"," and write the trigonometric function has x coordinates as sin argument, set paths and the paths.",[122,8699,8700],{},"When the path arrives at the right edge of the image, back to the start point via the edge of the right bottom and left bottom.",[122,8702,8703],{},"Close paths.",[63,8705,8707],{"id":8706},"fill-the-selected-area-in-transparent","Fill the selected area in transparent",[13,8709,8710],{},"You just fill after you select the area by above method.",[405,8712,8714],{"className":3022,"code":8713,"language":3025,"meta":411,"style":411},"ctx.fillStyle = \"rgba(255,255,255,1)\"; \u002F\u002Fopacity 1\nctx.fill();\n",[297,8715,8716,8737],{"__ignoreMap":411},[457,8717,8718,8720,8722,8725,8727,8729,8731,8733,8735],{"class":459,"line":460},[457,8719,7057],{"class":3036},[457,8721,3079],{"class":3043},[457,8723,8724],{"class":3036},"fillStyle ",[457,8726,3090],{"class":3043},[457,8728,5438],{"class":3043},[457,8730,7810],{"class":3047},[457,8732,3670],{"class":3043},[457,8734,7620],{"class":3043},[457,8736,7817],{"class":3782},[457,8738,8739,8741,8743,8745,8747],{"class":459,"line":466},[457,8740,7057],{"class":3036},[457,8742,3079],{"class":3043},[457,8744,7826],{"class":3106},[457,8746,4802],{"class":3036},[457,8748,3980],{"class":3043},[13,8750,8751,8753,8754,8757,8758,8761],{},[297,8752,7803],{}," configures the color to fill.  If you set ",[297,8755,8756],{},"globalCompositeOperation = \"destination-out\";`, there no problem to use any colors. ","fill()",[297,8759,8760],{},"method fills the area surrounded paths. Because","globalCompositeOperation = \"destination-out\";`` is configured, the parts which the image and the shape are not superposed remains, but superposed area will be transparent.",[13,8763,8764],{},"If you come here, you get the image that is cut with the wave.",[111,8766],{":src":5996,":width":114},[39,8768,8770],{"id":8769},"implement-animation-by-the-loop","Implement animation by the loop.",[13,8772,8773,8774,3175,8777,8779],{},"Using defined functions ",[297,8775,8776],{},"imageSet()",[297,8778,7981],{},", set a loop fucntion.",[405,8781,8783],{"className":3022,"code":8782,"language":3025,"meta":411,"style":411},"function loop(){\n      setInterval(function(){\n      imageSet(image,canvasEndX,canvasEndY);\n      waveDrawing(waveStartPoint,canvasEndX,canvasEndY,degree,amplitude,period);\n      degree += 12; \u002F\u002F 12 is feeling.\n    },30)\n}\n",[297,8784,8785,8794,8805,8826,8859,8875,8885],{"__ignoreMap":411},[457,8786,8787,8789,8792],{"class":459,"line":460},[457,8788,6366],{"class":3127},[457,8790,8791],{"class":3106}," loop",[457,8793,4345],{"class":3043},[457,8795,8796,8799,8801,8803],{"class":459,"line":466},[457,8797,8798],{"class":3106},"      setInterval",[457,8800,3110],{"class":3620},[457,8802,6366],{"class":3127},[457,8804,4345],{"class":3043},[457,8806,8807,8810,8812,8814,8816,8818,8820,8822,8824],{"class":459,"line":472},[457,8808,8809],{"class":3106},"      imageSet",[457,8811,3110],{"class":3620},[457,8813,6584],{"class":3036},[457,8815,4444],{"class":3043},[457,8817,7323],{"class":3036},[457,8819,4444],{"class":3043},[457,8821,7328],{"class":3036},[457,8823,3144],{"class":3620},[457,8825,3980],{"class":3043},[457,8827,8828,8831,8833,8835,8837,8839,8841,8843,8845,8847,8849,8851,8853,8855,8857],{"class":459,"line":4},[457,8829,8830],{"class":3106},"      waveDrawing",[457,8832,3110],{"class":3620},[457,8834,7342],{"class":3036},[457,8836,4444],{"class":3043},[457,8838,7323],{"class":3036},[457,8840,4444],{"class":3043},[457,8842,7328],{"class":3036},[457,8844,4444],{"class":3043},[457,8846,7355],{"class":3036},[457,8848,4444],{"class":3043},[457,8850,7360],{"class":3036},[457,8852,4444],{"class":3043},[457,8854,7365],{"class":3036},[457,8856,3144],{"class":3620},[457,8858,3980],{"class":3043},[457,8860,8861,8864,8867,8870,8872],{"class":459,"line":483},[457,8862,8863],{"class":3036},"      degree",[457,8865,8866],{"class":3043}," +=",[457,8868,8869],{"class":4440}," 12",[457,8871,7620],{"class":3043},[457,8873,8874],{"class":3782}," \u002F\u002F 12 is feeling.\n",[457,8876,8877,8880,8883],{"class":459,"line":489},[457,8878,8879],{"class":3043},"    },",[457,8881,8882],{"class":4440},"30",[457,8884,3162],{"class":3620},[457,8886,8887],{"class":459,"line":495},[457,8888,564],{"class":3043},[13,8890,8891,8892,8894,8895,8898],{},"The most important point is adding the number of angle with ",[297,8893,7981],{}," like ",[297,8896,8897],{},"degree +=4",".　Thus, the wave is animated. More increase degrees, more fast the wave. If you set over 50, it will be rough waves.",[13,8900,8901,8902,8904,8905,7160],{},"And ",[297,8903,6920],{}," fires the  ",[297,8906,8907],{},"loop()",[405,8909,8911],{"className":3022,"code":8910,"language":3025,"meta":411,"style":411},"image.onload = function(){\n    initDraw();\n    loop();\n}\n",[297,8912,8913,8927,8935,8944],{"__ignoreMap":411},[457,8914,8915,8917,8919,8921,8923,8925],{"class":459,"line":460},[457,8916,6584],{"class":3036},[457,8918,3079],{"class":3043},[457,8920,6562],{"class":3106},[457,8922,4387],{"class":3043},[457,8924,6567],{"class":3127},[457,8926,4345],{"class":3043},[457,8928,8929,8931,8933],{"class":459,"line":466},[457,8930,7184],{"class":3106},[457,8932,4802],{"class":3620},[457,8934,3980],{"class":3043},[457,8936,8937,8940,8942],{"class":459,"line":472},[457,8938,8939],{"class":3106},"    loop",[457,8941,4802],{"class":3620},[457,8943,3980],{"class":3043},[457,8945,8946],{"class":459,"line":4},[457,8947,564],{"class":3043},[39,8949,8951],{"id":8950},"whole-code","Whole code",[13,8953,8954],{},"Whole code is theb below.",[405,8956,8958],{"className":3022,"code":8957,"language":3025,"meta":411,"style":411},"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",[297,8959,8960,8978,8986,8995,8999,9008,9034,9060,9064,9084,9100,9115,9119,9124,9149,9177,9191,9200,9209,9213,9218,9234,9250,9266,9270,9283,9297,9310,9314,9323,9344,9377,9382,9386,9394,9405,9426,9459,9470,9479,9483,9487,9507,9524,9540,9544,9564,9612,9616,9620,9652,9664,9682,9694,9714,9718,9753,9802,9827,9831,9835,9855,9875,9887,9891,9911,9923,9927,9931,9935],{"__ignoreMap":411},[457,8961,8962,8964,8966,8969,8971,8974,8976],{"class":459,"line":460},[457,8963,4007],{"class":3036},[457,8965,3079],{"class":3043},[457,8967,8968],{"class":3036},"onload ",[457,8970,3090],{"class":3043},[457,8972,8973],{"class":3106}," init",[457,8975,4802],{"class":3036},[457,8977,3980],{"class":3043},[457,8979,8980,8982,8984],{"class":459,"line":466},[457,8981,6366],{"class":3127},[457,8983,8973],{"class":3106},[457,8985,4345],{"class":3043},[457,8987,8988,8991,8993],{"class":459,"line":472},[457,8989,8990],{"class":3106},"    initAnimation",[457,8992,4802],{"class":3620},[457,8994,3980],{"class":3043},[457,8996,8997],{"class":459,"line":4},[457,8998,693],{"emptyLinePlaceholder":692},[457,9000,9001,9004,9006],{"class":459,"line":483},[457,9002,9003],{"class":3127},"    function",[457,9005,6369],{"class":3106},[457,9007,4345],{"class":3043},[457,9009,9010,9012,9014,9016,9018,9020,9022,9024,9026,9028,9030,9032],{"class":459,"line":489},[457,9011,7648],{"class":3127},[457,9013,6379],{"class":3036},[457,9015,4387],{"class":3043},[457,9017,6384],{"class":3036},[457,9019,3079],{"class":3043},[457,9021,4030],{"class":3106},[457,9023,3110],{"class":3620},[457,9025,3154],{"class":3043},[457,9027,6283],{"class":3047},[457,9029,3154],{"class":3043},[457,9031,3144],{"class":3620},[457,9033,3980],{"class":3043},[457,9035,9036,9038,9040,9042,9044,9046,9048,9050,9052,9054,9056,9058],{"class":459,"line":495},[457,9037,7648],{"class":3127},[457,9039,6407],{"class":3036},[457,9041,4387],{"class":3043},[457,9043,6379],{"class":3036},[457,9045,3079],{"class":3043},[457,9047,6416],{"class":3106},[457,9049,3110],{"class":3620},[457,9051,3154],{"class":3043},[457,9053,6423],{"class":3047},[457,9055,3154],{"class":3043},[457,9057,3144],{"class":3620},[457,9059,3980],{"class":3043},[457,9061,9062],{"class":459,"line":501},[457,9063,693],{"emptyLinePlaceholder":692},[457,9065,9066,9068,9070,9072,9074,9076,9078,9080,9082],{"class":459,"line":507},[457,9067,7648],{"class":3127},[457,9069,6440],{"class":3036},[457,9071,4387],{"class":3043},[457,9073,6445],{"class":3620},[457,9075,3154],{"class":3043},[457,9077,6450],{"class":3047},[457,9079,3154],{"class":3043},[457,9081,3144],{"class":3620},[457,9083,3980],{"class":3043},[457,9085,9086,9088,9090,9092,9094,9096,9098],{"class":459,"line":513},[457,9087,7648],{"class":3127},[457,9089,6463],{"class":3036},[457,9091,4387],{"class":3043},[457,9093,4390],{"class":3043},[457,9095,6470],{"class":3106},[457,9097,4802],{"class":3620},[457,9099,3980],{"class":3043},[457,9101,9102,9105,9107,9109,9111,9113],{"class":459,"line":519},[457,9103,9104],{"class":3036},"        image",[457,9106,3079],{"class":3043},[457,9108,6484],{"class":3036},[457,9110,4387],{"class":3043},[457,9112,6440],{"class":3036},[457,9114,3980],{"class":3043},[457,9116,9117],{"class":459,"line":525},[457,9118,693],{"emptyLinePlaceholder":692},[457,9120,9121],{"class":459,"line":531},[457,9122,9123],{"class":3782},"        \u002F\u002Fset canvas width and height\n",[457,9125,9126,9129,9131,9133,9135,9137,9139,9141,9143,9145,9147],{"class":459,"line":537},[457,9127,9128],{"class":3036},"        canvas",[457,9130,3079],{"class":3043},[457,9132,6504],{"class":3036},[457,9134,4387],{"class":3043},[457,9136,6509],{"class":3106},[457,9138,3110],{"class":3620},[457,9140,4007],{"class":3036},[457,9142,3079],{"class":3043},[457,9144,6518],{"class":3036},[457,9146,3144],{"class":3620},[457,9148,3980],{"class":3043},[457,9150,9151,9153,9155,9157,9159,9161,9163,9165,9167,9169,9171,9173,9175],{"class":459,"line":543},[457,9152,9128],{"class":3036},[457,9154,3079],{"class":3043},[457,9156,6531],{"class":3036},[457,9158,4387],{"class":3043},[457,9160,6509],{"class":3106},[457,9162,3110],{"class":3620},[457,9164,6283],{"class":3036},[457,9166,3079],{"class":3043},[457,9168,6504],{"class":3036},[457,9170,6546],{"class":3043},[457,9172,6549],{"class":4440},[457,9174,3144],{"class":3620},[457,9176,3980],{"class":3043},[457,9178,9179,9181,9183,9185,9187,9189],{"class":459,"line":549},[457,9180,9104],{"class":3036},[457,9182,3079],{"class":3043},[457,9184,6562],{"class":3106},[457,9186,4387],{"class":3043},[457,9188,6567],{"class":3127},[457,9190,4345],{"class":3043},[457,9192,9193,9196,9198],{"class":459,"line":555},[457,9194,9195],{"class":3106},"                initDraw",[457,9197,4802],{"class":3620},[457,9199,3980],{"class":3043},[457,9201,9202,9205,9207],{"class":459,"line":561},[457,9203,9204],{"class":3106},"                loop",[457,9206,4802],{"class":3620},[457,9208,3980],{"class":3043},[457,9210,9211],{"class":459,"line":1030},[457,9212,6203],{"class":3043},[457,9214,9215],{"class":459,"line":1036},[457,9216,9217],{"class":3620},"            \n",[457,9219,9220,9222,9224,9226,9228,9230,9232],{"class":459,"line":1041},[457,9221,7648],{"class":3127},[457,9223,7628],{"class":3036},[457,9225,4387],{"class":3043},[457,9227,6379],{"class":3036},[457,9229,3079],{"class":3043},[457,9231,6504],{"class":3036},[457,9233,3980],{"class":3043},[457,9235,9236,9238,9240,9242,9244,9246,9248],{"class":459,"line":1047},[457,9237,7648],{"class":3127},[457,9239,7242],{"class":3036},[457,9241,4387],{"class":3043},[457,9243,6379],{"class":3036},[457,9245,3079],{"class":3043},[457,9247,6531],{"class":3036},[457,9249,3980],{"class":3043},[457,9251,9252,9254,9256,9258,9260,9262,9264],{"class":459,"line":1052},[457,9253,7648],{"class":3127},[457,9255,7541],{"class":3036},[457,9257,4387],{"class":3043},[457,9259,7242],{"class":3036},[457,9261,7245],{"class":3043},[457,9263,7248],{"class":4440},[457,9265,3980],{"class":3043},[457,9267,9268],{"class":459,"line":1058},[457,9269,693],{"emptyLinePlaceholder":692},[457,9271,9272,9274,9277,9279,9281],{"class":459,"line":1064},[457,9273,7648],{"class":3127},[457,9275,9276],{"class":3036}," amplitude",[457,9278,4387],{"class":3043},[457,9280,7266],{"class":4440},[457,9282,3980],{"class":3043},[457,9284,9285,9287,9290,9292,9295],{"class":459,"line":1070},[457,9286,7648],{"class":3127},[457,9288,9289],{"class":3036}," period",[457,9291,4387],{"class":3043},[457,9293,9294],{"class":4440}," 600",[457,9296,3980],{"class":3043},[457,9298,9299,9301,9304,9306,9308],{"class":459,"line":1076},[457,9300,7648],{"class":3127},[457,9302,9303],{"class":3036}," degree",[457,9305,4387],{"class":3043},[457,9307,7294],{"class":4440},[457,9309,3980],{"class":3043},[457,9311,9312],{"class":459,"line":1081},[457,9313,693],{"emptyLinePlaceholder":692},[457,9315,9316,9319,9321],{"class":459,"line":1086},[457,9317,9318],{"class":3127},"        function",[457,9320,7307],{"class":3106},[457,9322,4345],{"class":3043},[457,9324,9325,9328,9330,9332,9334,9336,9338,9340,9342],{"class":459,"line":1092},[457,9326,9327],{"class":3106},"            imageSet",[457,9329,3110],{"class":3620},[457,9331,6584],{"class":3036},[457,9333,4444],{"class":3043},[457,9335,7323],{"class":3036},[457,9337,4444],{"class":3043},[457,9339,7328],{"class":3036},[457,9341,3144],{"class":3620},[457,9343,3980],{"class":3043},[457,9345,9346,9349,9351,9353,9355,9357,9359,9361,9363,9365,9367,9369,9371,9373,9375],{"class":459,"line":1098},[457,9347,9348],{"class":3106},"            waveDrawing",[457,9350,3110],{"class":3620},[457,9352,7342],{"class":3036},[457,9354,4444],{"class":3043},[457,9356,7323],{"class":3036},[457,9358,4444],{"class":3043},[457,9360,7328],{"class":3036},[457,9362,4444],{"class":3043},[457,9364,7355],{"class":3036},[457,9366,4444],{"class":3043},[457,9368,7360],{"class":3036},[457,9370,4444],{"class":3043},[457,9372,7365],{"class":3036},[457,9374,3144],{"class":3620},[457,9376,3980],{"class":3043},[457,9378,9379],{"class":459,"line":1104},[457,9380,9381],{"class":3043},"        }\n",[457,9383,9384],{"class":459,"line":1109},[457,9385,693],{"emptyLinePlaceholder":692},[457,9387,9388,9390,9392],{"class":459,"line":1115},[457,9389,9318],{"class":3127},[457,9391,8791],{"class":3106},[457,9393,4345],{"class":3043},[457,9395,9396,9399,9401,9403],{"class":459,"line":1854},[457,9397,9398],{"class":3106},"            setInterval",[457,9400,3110],{"class":3620},[457,9402,6366],{"class":3127},[457,9404,4345],{"class":3043},[457,9406,9407,9410,9412,9414,9416,9418,9420,9422,9424],{"class":459,"line":1859},[457,9408,9409],{"class":3106},"                imageSet",[457,9411,3110],{"class":3620},[457,9413,6584],{"class":3036},[457,9415,4444],{"class":3043},[457,9417,7323],{"class":3036},[457,9419,4444],{"class":3043},[457,9421,7328],{"class":3036},[457,9423,3144],{"class":3620},[457,9425,3980],{"class":3043},[457,9427,9428,9431,9433,9435,9437,9439,9441,9443,9445,9447,9449,9451,9453,9455,9457],{"class":459,"line":1864},[457,9429,9430],{"class":3106},"                waveDrawing",[457,9432,3110],{"class":3620},[457,9434,7342],{"class":3036},[457,9436,4444],{"class":3043},[457,9438,7323],{"class":3036},[457,9440,4444],{"class":3043},[457,9442,7328],{"class":3036},[457,9444,4444],{"class":3043},[457,9446,7355],{"class":3036},[457,9448,4444],{"class":3043},[457,9450,7360],{"class":3036},[457,9452,4444],{"class":3043},[457,9454,7365],{"class":3036},[457,9456,3144],{"class":3620},[457,9458,3980],{"class":3043},[457,9460,9461,9464,9466,9468],{"class":459,"line":1870},[457,9462,9463],{"class":3036},"                degree",[457,9465,8866],{"class":3043},[457,9467,8869],{"class":4440},[457,9469,3980],{"class":3043},[457,9471,9472,9475,9477],{"class":459,"line":1875},[457,9473,9474],{"class":3043},"            },",[457,9476,8882],{"class":4440},[457,9478,3162],{"class":3620},[457,9480,9481],{"class":459,"line":1880},[457,9482,9381],{"class":3043},[457,9484,9485],{"class":459,"line":1885},[457,9486,693],{"emptyLinePlaceholder":692},[457,9488,9489,9491,9493,9495,9497,9499,9501,9503,9505],{"class":459,"line":1891},[457,9490,9318],{"class":3127},[457,9492,7384],{"class":3106},[457,9494,3110],{"class":3043},[457,9496,7389],{"class":3123},[457,9498,4444],{"class":3043},[457,9500,7323],{"class":3123},[457,9502,4444],{"class":3043},[457,9504,7328],{"class":3123},[457,9506,4855],{"class":3043},[457,9508,9509,9512,9514,9516,9518,9520,9522],{"class":459,"line":1896},[457,9510,9511],{"class":3127},"            var",[457,9513,7406],{"class":3036},[457,9515,4387],{"class":3043},[457,9517,7411],{"class":3036},[457,9519,3079],{"class":3043},[457,9521,6504],{"class":3036},[457,9523,3980],{"class":3043},[457,9525,9526,9528,9530,9532,9534,9536,9538],{"class":459,"line":1902},[457,9527,9511],{"class":3127},[457,9529,7424],{"class":3036},[457,9531,4387],{"class":3043},[457,9533,7411],{"class":3036},[457,9535,3079],{"class":3043},[457,9537,6531],{"class":3036},[457,9539,3980],{"class":3043},[457,9541,9542],{"class":459,"line":1907},[457,9543,693],{"emptyLinePlaceholder":692},[457,9545,9546,9549,9551,9553,9555,9557,9560,9562],{"class":459,"line":2164},[457,9547,9548],{"class":3036},"            ctx",[457,9550,3079],{"class":3043},[457,9552,7552],{"class":3036},[457,9554,4387],{"class":3043},[457,9556,5438],{"class":3043},[457,9558,9559],{"class":3047},"destination-over",[457,9561,3670],{"class":3043},[457,9563,3980],{"class":3043},[457,9565,9566,9568,9570,9572,9574,9576,9578,9580,9582,9584,9586,9588,9590,9592,9594,9596,9598,9600,9602,9604,9606,9608,9610],{"class":459,"line":2170},[457,9567,9548],{"class":3036},[457,9569,3079],{"class":3043},[457,9571,6579],{"class":3106},[457,9573,3110],{"class":3620},[457,9575,6584],{"class":3036},[457,9577,4444],{"class":3043},[457,9579,6185],{"class":4440},[457,9581,4444],{"class":3043},[457,9583,6185],{"class":4440},[457,9585,4444],{"class":3043},[457,9587,7460],{"class":3036},[457,9589,4444],{"class":3043},[457,9591,7465],{"class":3036},[457,9593,4444],{"class":3043},[457,9595,6185],{"class":4440},[457,9597,4444],{"class":3043},[457,9599,6185],{"class":4440},[457,9601,4444],{"class":3043},[457,9603,7323],{"class":3036},[457,9605,4444],{"class":3043},[457,9607,7328],{"class":3036},[457,9609,3144],{"class":3620},[457,9611,3980],{"class":3043},[457,9613,9614],{"class":459,"line":2175},[457,9615,9381],{"class":3043},[457,9617,9618],{"class":459,"line":2181},[457,9619,693],{"emptyLinePlaceholder":692},[457,9621,9622,9624,9626,9628,9630,9632,9634,9636,9638,9640,9642,9644,9646,9648,9650],{"class":459,"line":2187},[457,9623,9318],{"class":3127},[457,9625,7500],{"class":3106},[457,9627,3110],{"class":3043},[457,9629,7342],{"class":3123},[457,9631,4444],{"class":3043},[457,9633,7323],{"class":3123},[457,9635,4444],{"class":3043},[457,9637,7328],{"class":3123},[457,9639,4444],{"class":3043},[457,9641,7517],{"class":3123},[457,9643,4444],{"class":3043},[457,9645,7522],{"class":3123},[457,9647,4444],{"class":3043},[457,9649,7527],{"class":3123},[457,9651,4855],{"class":3043},[457,9653,9654,9656,9658,9660,9662],{"class":459,"line":2193},[457,9655,9511],{"class":3127},[457,9657,7536],{"class":3036},[457,9659,4387],{"class":3043},[457,9661,7541],{"class":3036},[457,9663,3980],{"class":3043},[457,9665,9666,9668,9670,9672,9674,9676,9678,9680],{"class":459,"line":2199},[457,9667,9548],{"class":3036},[457,9669,3079],{"class":3043},[457,9671,7552],{"class":3036},[457,9673,4387],{"class":3043},[457,9675,5438],{"class":3043},[457,9677,7559],{"class":3047},[457,9679,3670],{"class":3043},[457,9681,3980],{"class":3043},[457,9683,9684,9686,9688,9690,9692],{"class":459,"line":2204},[457,9685,9548],{"class":3036},[457,9687,3079],{"class":3043},[457,9689,7572],{"class":3106},[457,9691,4802],{"class":3620},[457,9693,3980],{"class":3043},[457,9695,9696,9698,9700,9702,9704,9706,9708,9710,9712],{"class":459,"line":2210},[457,9697,9548],{"class":3036},[457,9699,3079],{"class":3043},[457,9701,7585],{"class":3106},[457,9703,3110],{"class":3620},[457,9705,6185],{"class":4440},[457,9707,4444],{"class":3043},[457,9709,7536],{"class":3036},[457,9711,3144],{"class":3620},[457,9713,3980],{"class":3043},[457,9715,9716],{"class":459,"line":2216},[457,9717,693],{"emptyLinePlaceholder":692},[457,9719,9720,9723,9725,9727,9729,9731,9733,9735,9737,9739,9741,9743,9745,9747,9749,9751],{"class":459,"line":2222},[457,9721,9722],{"class":3032},"            for",[457,9724,6445],{"class":3620},[457,9726,6657],{"class":3127},[457,9728,7613],{"class":3036},[457,9730,3090],{"class":3043},[457,9732,6185],{"class":4440},[457,9734,7620],{"class":3043},[457,9736,7613],{"class":3036},[457,9738,7625],{"class":3043},[457,9740,7628],{"class":3036},[457,9742,7620],{"class":3043},[457,9744,7613],{"class":3036},[457,9746,7635],{"class":3043},[457,9748,7638],{"class":4440},[457,9750,7641],{"class":3620},[457,9752,469],{"class":3043},[457,9754,9755,9758,9760,9762,9764,9766,9768,9770,9772,9774,9776,9778,9780,9782,9784,9786,9788,9790,9792,9794,9796,9798,9800],{"class":459,"line":2228},[457,9756,9757],{"class":3127},"                var",[457,9759,7651],{"class":3036},[457,9761,4387],{"class":3043},[457,9763,7656],{"class":3043},[457,9765,7522],{"class":3036},[457,9767,7661],{"class":3043},[457,9769,7664],{"class":3036},[457,9771,3079],{"class":3043},[457,9773,7669],{"class":3106},[457,9775,7672],{"class":3620},[457,9777,7664],{"class":3036},[457,9779,3079],{"class":3043},[457,9781,7679],{"class":3036},[457,9783,6546],{"class":3043},[457,9785,7527],{"class":3036},[457,9787,3144],{"class":3620},[457,9789,7661],{"class":3043},[457,9791,3110],{"class":3620},[457,9793,7517],{"class":3036},[457,9795,7694],{"class":3043},[457,9797,7697],{"class":3036},[457,9799,7700],{"class":3620},[457,9801,3980],{"class":3043},[457,9803,9804,9807,9809,9811,9813,9815,9817,9819,9821,9823,9825],{"class":459,"line":2234},[457,9805,9806],{"class":3036},"                ctx",[457,9808,3079],{"class":3043},[457,9810,7711],{"class":3106},[457,9812,3110],{"class":3620},[457,9814,7697],{"class":3036},[457,9816,4444],{"class":3043},[457,9818,7651],{"class":3036},[457,9820,7694],{"class":3043},[457,9822,7724],{"class":3036},[457,9824,3144],{"class":3620},[457,9826,3980],{"class":3043},[457,9828,9829],{"class":459,"line":2239},[457,9830,6203],{"class":3043},[457,9832,9833],{"class":459,"line":2245},[457,9834,693],{"emptyLinePlaceholder":692},[457,9836,9837,9839,9841,9843,9845,9847,9849,9851,9853],{"class":459,"line":2251},[457,9838,9548],{"class":3036},[457,9840,3079],{"class":3043},[457,9842,7711],{"class":3106},[457,9844,3110],{"class":3620},[457,9846,7323],{"class":3036},[457,9848,4444],{"class":3043},[457,9850,7328],{"class":3036},[457,9852,3144],{"class":3620},[457,9854,3980],{"class":3043},[457,9856,9857,9859,9861,9863,9865,9867,9869,9871,9873],{"class":459,"line":2257},[457,9858,9548],{"class":3036},[457,9860,3079],{"class":3043},[457,9862,7711],{"class":3106},[457,9864,3110],{"class":3620},[457,9866,6185],{"class":4440},[457,9868,4444],{"class":3043},[457,9870,7328],{"class":3036},[457,9872,3144],{"class":3620},[457,9874,3980],{"class":3043},[457,9876,9877,9879,9881,9883,9885],{"class":459,"line":2263},[457,9878,9548],{"class":3036},[457,9880,3079],{"class":3043},[457,9882,7785],{"class":3106},[457,9884,4802],{"class":3620},[457,9886,3980],{"class":3043},[457,9888,9889],{"class":459,"line":2268},[457,9890,693],{"emptyLinePlaceholder":692},[457,9892,9893,9895,9897,9899,9901,9903,9905,9907,9909],{"class":459,"line":2273},[457,9894,9548],{"class":3036},[457,9896,3079],{"class":3043},[457,9898,7803],{"class":3036},[457,9900,4387],{"class":3043},[457,9902,5438],{"class":3043},[457,9904,7810],{"class":3047},[457,9906,3670],{"class":3043},[457,9908,7620],{"class":3043},[457,9910,7817],{"class":3782},[457,9912,9913,9915,9917,9919,9921],{"class":459,"line":2279},[457,9914,9548],{"class":3036},[457,9916,3079],{"class":3043},[457,9918,7826],{"class":3106},[457,9920,4802],{"class":3620},[457,9922,3980],{"class":3043},[457,9924,9925],{"class":459,"line":2285},[457,9926,9381],{"class":3043},[457,9928,9929],{"class":459,"line":2291},[457,9930,693],{"emptyLinePlaceholder":692},[457,9932,9933],{"class":459,"line":2297},[457,9934,1112],{"class":3043},[457,9936,9937],{"class":459,"line":2303},[457,9938,564],{"class":3043},[39,9940,9942],{"id":9941},"if-you-change-the-wave","If you change the wave",[13,9944,9945],{},"In this case,",[405,9947,9949],{"className":3022,"code":9948,"language":3025,"meta":411,"style":411},"var amplitude = 30;\nvar period = 600;\n\nfunction loop(){\n      ...\n      degree += 12;\n      },30)\n}\n",[297,9950,9951,9963,9975,9979,9987,9992,10002,10011],{"__ignoreMap":411},[457,9952,9953,9955,9957,9959,9961],{"class":459,"line":460},[457,9954,6657],{"class":3127},[457,9956,7261],{"class":3036},[457,9958,3090],{"class":3043},[457,9960,7266],{"class":4440},[457,9962,3980],{"class":3043},[457,9964,9965,9967,9969,9971,9973],{"class":459,"line":466},[457,9966,6657],{"class":3127},[457,9968,7275],{"class":3036},[457,9970,3090],{"class":3043},[457,9972,9294],{"class":4440},[457,9974,3980],{"class":3043},[457,9976,9977],{"class":459,"line":472},[457,9978,693],{"emptyLinePlaceholder":692},[457,9980,9981,9983,9985],{"class":459,"line":4},[457,9982,6366],{"class":3127},[457,9984,8791],{"class":3106},[457,9986,4345],{"class":3043},[457,9988,9989],{"class":459,"line":483},[457,9990,9991],{"class":3043},"      ...\n",[457,9993,9994,9996,9998,10000],{"class":459,"line":489},[457,9995,8863],{"class":3036},[457,9997,8866],{"class":3043},[457,9999,8869],{"class":4440},[457,10001,3980],{"class":3043},[457,10003,10004,10007,10009],{"class":459,"line":495},[457,10005,10006],{"class":3043},"      },",[457,10008,8882],{"class":4440},[457,10010,3162],{"class":3036},[457,10012,10013],{"class":459,"line":501},[457,10014,564],{"class":3036},[13,10016,10017,10019,10020,10022,10023,10026,10027,10029],{},[297,10018,7360],{}," defines the wave's height and ",[297,10021,7365],{}," is the width of the wave and ",[297,10024,10025],{},"degree +="," is velocity. On the other, by changing the trigonometric function you configured in ",[297,10028,7981],{},", you can render",[39,10031,10033],{"id":10032},"at-last","At last",[13,10035,10036],{},"That is all how to make the image waved. Canvas element can render complex animation like this.",[2746,10038,10039],{},"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":411,"searchDepth":472,"depth":472,"links":10041},[10042,10043,10046,10051,10059,10060,10061,10062],{"id":6007,"depth":466,"text":6008},{"id":6025,"depth":466,"text":6026,"children":10044},[10045],{"id":6069,"depth":472,"text":6070},{"id":6076,"depth":466,"text":6077,"children":10047},[10048],{"id":6352,"depth":472,"text":6353,"children":10049},[10050],{"id":6940,"depth":4,"text":6941},{"id":7153,"depth":466,"text":7154,"children":10052},[10053,10054],{"id":7854,"depth":472,"text":7855},{"id":7975,"depth":472,"text":7976,"children":10055},[10056,10057,10058],{"id":8335,"depth":4,"text":8336},{"id":8438,"depth":4,"text":8439},{"id":8706,"depth":4,"text":8707},{"id":8769,"depth":466,"text":8770},{"id":8950,"depth":466,"text":8951},{"id":9941,"depth":466,"text":9942},{"id":10032,"depth":466,"text":10033},[2796],{},"\u002Fen\u002Farticles\u002Fjs-canvas-wave",{"title":5988,"description":5988},"en\u002Farticles\u002Fjs-canvas-wave",[3533],"_mix\u002Fex01-768x299.png","XGsTAH2MWGO4JY7zi0vCtHnyPK984EuGs39M-dI4HT4",1780987151171]