20210511のJavaScriptに関する記事は26件です。

【Javascript】clip-pathでスライドアニメーションを作ってみた

初めに 学習した内容をもとにスライドーアニメーションを作ってみました。 ※内容に間違いなどがある場合はご指摘をよろしくお願いします。 完成形のイメージ 矢印ボタンを押すとイメージが左から右へとスライドします。 Main Concept ① イメージが入っている箱を横に並べます。(親要素のpositionをrelativeにし、子要素のイメージ箱をabsoluteにする) ② イメージコンテナ(イメージが入っている5つの箱の親要素)にclip-pathで四角い形で切り抜きます。これが可視領域になります。 ③ ボタンをクリックした時にsetIntervalを使って、イメージの位置(left)が徐々に増えるようにします。また、一定の数(count)が実行されたらclearIntervalを指定し止まるようにします。 ④image0.jpg 〜 image4.jpgの絶対位置(left)を全て左から右へずらすことによってスライド効果が生まれます。 htmlで箱を用意する slide_mainタグの中にイメージが入るslide_main_imagesとボタンになるslide_buttonnext要素を作成します。ボタンの場合javascriptの処理が必要なのでslide_buttonという名前のidも用意します。 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="css/app.css"> <title>Slide</title> </head> <body> <section class="slide"> <div class="slide_main"> <div class="slide_main__images"> <div class="s0"></div> <div class="s1"></div> <div class="s2"></div> <div class="s3"></div> <div class="s4"></div> </div> <div id="slide__button" class="slide_buttons__next"></div> </div> </section> <script src="main.js"></script> </body> </html> sassの記述 slideのイメージが入る部分とボタンの部分を分けて記述します。ボタンの作り方は前回の記事の通りです。 また、clip-pathをslide_main__images要素に指定します。四角い形で大きさはそれぞれのイメージと同じです。 slide要素にpositionをrelativeにし、5つのイメージのdiv要素はabsoluteでそれぞれ親要素を基準にして位置を決めておきました。こうすることによってイメージの箱が横にずらりと並びます。はみ出るイメージはoverflow:hiddenを指定して隠します。 .slide { height: 400px; margin: 0 auto; position: relative; &_main { height: 400px; margin: 0 auto; &__images { position: relative; padding: 5% 0 5%; margin: 0 auto; width: 90%; height: 280px; clip-path: inset(10% 25% 10% 25%); overflow: hidden; & div { height: 280px; cursor: pointer; &.s0 { top: 70px; width: 50%; position: absolute; } &.s1 { top: 70px; width: 50%; position: absolute; } &.s2 { top: 70px; width: 50%; position: absolute; } &.s3 { top: 70px; width: 50%; position: absolute; } &.s4 { top: 70px; width: 50%; position: absolute; } } } } } .slide_buttons__next { position: absolute; top: 200px; right: 300px; cursor: pointer; color: lightgray; width: 30px; height: 30px; &:hover { transition-duration: 0.2s; transform: translateX(10px); } &::after { content: ""; display: block; width: 15px; height: 15px; position: absolute; margin-top: 2.5px; border-top: 1px solid #ccc; border-right: 1px solid #ccc; transform: rotate(45deg); } } javascriptの記述 イメージファイルが入っている配列imagesを用意します。それぞれのイメージが入るdivタグをquerySelectorで指定し、styleでイメージをアサインします。また、ボタンをクリックしたらそれぞれのイメージのleft位置をずらすようにしました。setIntervalでクリックイベントが走る回数をcountでコントロールします。また、countが49回をオーバーするとclearIntervalでsetIntervalを止めます。 "use strict"; const images = [ "images/0.jpg", "images/1.jpg", "images/2.jpg", "images/3.jpg", "images/4.jpg", ]; let increase = -1; let init = -75; const displayImage0 = document.querySelector(".s0"); displayImage0.style.backgroundImage = `url(${images[0]})`; const displayImage1 = document.querySelector(".s1"); displayImage1.style.backgroundImage = `url(${images[1]})`; const displayImage2 = document.querySelector(".s2"); displayImage2.style.backgroundImage = `url(${images[2]})`; const displayImage3 = document.querySelector(".s3"); displayImage3.style.backgroundImage = `url(${images[3]})`; const displayImage4 = document.querySelector(".s4"); displayImage4.style.backgroundImage = `url(${images[4]})`; function slide_next() { increase = increase + 1; displayImage0.style.left = `calc(${init}% + ${increase}%)`; displayImage1.style.left = `calc(${init + 50}% + ${increase}%)`; displayImage2.style.left = `calc(${init + 100}% + ${increase}%)`; displayImage3.style.left = `calc(${init + 150}% + ${increase}%)`; displayImage4.style.left = `calc(${init + 200}% + ${increase}%)`; if (displayImage0.style.left == `calc(25%)`) { init = init - 200; displayImage4.style.backgroundImage = `url(${images[0]})`; displayImage2.style.backgroundImage = `url(${images[4]})`; } } slide_next(); document.getElementById("slide__button").addEventListener("click", () => { let count = 0; const intervalId = setInterval(() => { slide_next(); count++; if (count > 49) { clearInterval(intervalId); } }, 25); }); 参考記事 https://developer.mozilla.org/ja/docs/Web/API/WindowOrWorkerGlobalScope/setInterval https://techacademy.jp/magazine/5537 https://techplay.jp/column/548
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】テキストボックスに「リンク名」と「URL」を入力しボタンをクリックすると、入力されたリンク名とリンク先URLが設定されたa要素を追加するプログラムを作成

<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>8-5 課題</title> <script> function add_element(){ //-------------リンク名とURLのIDを取得---------------------------------------------------- var inputText = document.getElementById("txt").value; //#txtの値を取得 テキストボックスの値 var inputUrl = document.getElementById("url").value; //#urlの値を取得 //------------------------------------------------------------------------------------- //-------------<a>要素を作成------------------------------------------------------------ var element_a = document.createElement('a'); //------------------------------------------------------------------------------------- element_a.innerHTML = inputText; // テキストを指定 txtのid element_a.href = inputUrl; //<a>タグのURLを指定 urlのid //-------------output要素のIDを取得---------------------------------------------------- var output = document.getElementById('output'); //------------------------------------------------------------------------------------- //-------------output要素にリンクを挿入------------------------------------------------- output.appendChild(element_a); //アペンドチャイルド aタグがoutputのidの子供の要素として追加 //------------------------------------------------------------------------------------- } //-----------javascriptの開始---------------------------------------------------------- window.onload = function(){ //-------------addのIDを取得------------------------------------------------------------- var add = document.getElementById("add"); //------------------------------------------------------------------------------------- //---------------リンク追加ボタンを押下することで、イベント発火、add_element関数を実行--------------- add.addEventListener('click',add_element,false); //------------------------------------------------------------------------------------- } </script> </head> <body> <form> リンク名: <input type="text" id="txt" > URL: <input type="text" id="url" > <button id ="add" type="button" >リンク追加</button> </form> <!-- ------------------------------------------------------- --> <div> <p id="output"></p> </div> </body> <!-- ------------------------------------------------------- --> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

p5.js Web Editor で #obniz を動かす( #obnizOS + #M5StickC )

ある時からちょこちょこ利用している JavaScriptライブラリの p5.js。 これをオンラインで実行できる環境として p5.js Web Editor というものがあります。 そんな中、ふと「p5.js Web Editor と obniz を組み合わせて使えないかな?」と思い、試してみたのが今回の内容です。 obnizOS の簡単な処理を動かす p5.js Web Editor上で処理を書いていく前に、まずは HTML+JavaScript の中で簡単な処理を動かし、動作確認を行ってから次に進んでいこうと思います。 動作テスト用に、まずは以下の公式ページにある、サンプルプログラムを実行して動かしてみます。 ●起動とWi-Fi設定 - obniz Docs  https://obniz.com/ja/doc/reference/m5stickc/quick-start 上記の画像にある「プログラムエディタ」のリンクをクリックした先では、以下の内容が実行できました。 <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" /> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://unpkg.com/obniz@3.14.0/obniz.js" crossorigin="anonymous" ></script> </head> <body> <div id="obniz-debug"></div> <h3>Connect From Your Browser</h3> <button class="btn btn-primary" id="on">LED ON</button> <button class="btn btn-primary" id="off">LED OFF</button> <div> <input type="text" id="text" value="Hello World?" /> <button class="btn btn-primary" id="showtime">Print on obniz</button> </div> <div>Press "M5" Button!</div> <div id="print"></div> <script> var obniz = new Obniz.M5StickC("OBNIZ_ID_HERE"); obniz.onconnect = async function() { obniz.display.drawing(false); obniz.display.clear(); obniz.display.print("Hello World?"); obniz.display.drawing(true); obniz.buttonA.onchange = function(state) { $("#print").text(`button=${state}`); }; $("#showtime").on("click", function() { obniz.display.drawing(false); obniz.display.clear(); obniz.display.print($("#text").val()); obniz.display.drawing(true); }); $("#on").click(function() { obniz.led.on(); obniz.display.drawing(false); obniz.display.clear(); obniz.display.print("ON⭐"); obniz.display.drawing(true); }); $("#off").click(function() { obniz.led.off(); obniz.display.drawing(false); obniz.display.clear(); obniz.display.print("OFF?️"); obniz.display.drawing(true); }); }; </script> </body> </html> 上記は特に問題なく動きました。 そして、上記と公式ドキュメントを見つつ少し内容を変えたものを作り、こちらでも動作確認をしてみました。 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>obnizOSのテスト</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.css" /> <script src="https://unpkg.com/obniz@3.14.0/obniz.js"></script> </head> <body> <section class="section"> <div class="container"> <h1 class="title">操作用ボタン</h1> <button class="button is-success is-light" type="button" onclick="onButtonClick()" > 処理実行 </button> </div> </section> <script> console.log("start!!"); let obniz = new Obniz.M5StickC("OBNIZ_ID_HERE"); console.log(obniz.connectionState); console.log("connecting..."); obniz.onconnect = async function () { console.log(obniz.connectionState); obniz.display.drawing(false); obniz.display.clear(); obniz.display.print("Hello World?"); obniz.display.drawing(true); obniz.buttonA.onchange = function (state) { console.log(state); if (state === true) { obniz.led.off(); obniz.display.drawing(false); obniz.display.clear(); obniz.display.print("OFF?️"); obniz.display.drawing(true); } }; }; async function onButtonClick() { if (obniz.connectionState === "connected") { console.log("clicked!"); obniz.led.on(); obniz.display.drawing(false); obniz.display.clear(); obniz.display.print("clicked⭐"); obniz.display.drawing(true); } } </script> </body> </html> p5.js Web Editor で環境を整える それでは、p5.js Web Editor で obniz を動かすプログラムを書いてみます。 デフォルトでは p5.js Web Editor上で表示されるのは sketch.js ですが、画面左のメニューを開いて index.html を編集します。 具体的には、以下を追加します。 <script src="https://unpkg.com/obniz@3.14.0/obniz.js"></script> p5.js Web Editor で処理を書いていく そして、sketch.js を編集していきます。デフォルトの内容は以下のとおりです。 function setup() { createCanvas(400, 400); } function draw() { background(220); } まずは、obniz と接続する処理を書き足して実行してみます。具体的には以下になります。 console.log("start!!"); let obniz = new Obniz.M5StickC("OBNIZ_ID_HERE"); console.log(obniz.connectionState); function setup() { createCanvas(400, 400); console.log("connecting..."); obniz.onconnect = async function () { console.log(obniz.connectionState); obniz.display.drawing(false); obniz.display.clear(); obniz.display.print("Hello World?"); obniz.display.drawing(true); }; } そして上記を実行すると、以下のエラーが表示されました。 エラーの対応 調べてみると、「https の上で wss ではなく ws を使おうとしたら出るもの」のようでした。 Chrome の開発者ツールを開いて見た感じ、wss での接続を行っていそうな感じなのですが。 そんな中、ふと以下の仕様が頭をよぎり、「何か影響しているかも?」と思って確かめてみました。  ●ローカルコネクト - obniz Docs   https://obniz.com/ja/doc/reference/common/connection/localconnect やったことは、PC と obniz がローカルで通信できないよう、PC を別の Wi-Fi につなぐというもの(両方のデバイスを自宅の Wi-Fi につないでいたのですが、片方をスマホのテザリングにして同一ネットワークではない状態にしました)。 その状態でプログラムを実行すると問題なく動作しましたが、本当に上記が原因なのか・良い対処法が他にないか等は、もう少し調査したいところです。 (上の方で掲載していた HTML+JavaScript のファイルを、Netlify にアップして https で動かすと、普通に動いたりしたっぽいんだけど...) 【追記】 ローカルコネクトを使わないようにする その後、ローカルコネクトを利用しない設定にできると知り、試してみました。  ●複数プログラムからの接続 - obniz Docs   https://obniz.com/ja/doc/reference/common/connection/multi-connectioz 結論から書くと、この対処を行うことで、上で書いていたエラーがでなくなりました。 具体的には、以下のような書きかえを行いました。 // 書きかえ前 let obniz = new Obniz.M5StickC("OBNIZ_ID_HERE");    ↓ // 書きかえ後 let obniz = new Obniz.M5StickC("OBNIZ_ID_HERE",{local_connect : false}); その他の対応 他に、プログラムの中を見てみると、以下のメッセージが出ていました。 調べてみた感じだと、これが表示されないようにするには JavaScript のプログラムに /*jshint esversion: 8 */ という記載を加えると良さそう? p5.js Web Editor上の処理を追加する さらに、p5.js Web Editor で書いたプログラムに、PC のキーボード操作・obniz のボタン操作を使う処理を追加して動かしてみます。 /*jshint esversion: 8 */ console.log("start!!"); let obniz = new Obniz.M5StickC("OBNIZ_ID_HERE",{local_connect : false}); console.log(obniz.connectionState); function setup() { createCanvas(400, 400); console.log("connecting..."); obniz.onconnect = async function () { console.log(obniz.connectionState); obniz.display.drawing(false); obniz.display.clear(); obniz.display.print("Hello World?"); obniz.display.drawing(true); obniz.buttonA.onchange = function (state) { if (state === true) { obniz.led.off(); obniz.display.drawing(false); obniz.display.clear(); obniz.display.print("OFF?️"); obniz.display.drawing(true); } }; }; } function keyPressed() { if (keyCode === LEFT_ARROW) { if (obniz.connectionState === "connected") { console.log("clicked!"); obniz.led.on(); } } } 上記のエラー回避方法を使いつつ書きかえたプログラムを実行すると、無事に意図した通りの動作をさせられるのを確認できました。 まとめ 今後、p5.js の描画まわりの処理を連動させるようなものや、ml5.js 等の p5.js のライブラリを組み合わせたものを何か試せればと思っています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

備忘録 Goでginを使う時に、cssやjsのファイルが読み込めない問題の解決

書いた理由 go言語でginを使っていたら、htmlからjsが読み込めない問題が発生したので、忘れないようにメモっておきます。また、ginを使わない場合でも同様の操作が必要らしいです。 cssの読み込みも同様にやるので、省略します。 ディレクトリについて githubに今回のコードはおいてあります。 correct.goが正常に動作するプログラムで、incorrect.goが正常に動作しないプログラムです。 ./ ├── correct.go ├── incorrect.go └── src ├── index.html └── js └── main.js htmlとjs ボタンを押すと、alertでPushed!!と出るだけの簡単なプログラムです。 index.html <html> <head> <title>gin-js</title> <script defer="defer" src="./js/main.js" ></script> </head> <body> <button id="btn">Push!!</button> </body> </html> main.js document.getElementById("btn").onclick=function(){ alert("Pushed!!") } 失敗するプログラム /に接続すると、index.htmlが表示されるいたって簡単なプログラムです。 incorrect.go package main import "github.com/gin-gonic/gin" func main() { router := gin.Default() router.LoadHTMLGlob("src/*.html") router.GET("/", func(ctx *gin.Context) { ctx.HTML(200, "index.html", gin.H{}) }) router.Run() } 実行すると/js/main.jsにGETリクエストをして、404が返ってきています。 成功するプログラム correct.go package main import "github.com/gin-gonic/gin" func main() { router := gin.Default() router.Static("/js", "src/js/") // ここを追加するだけ! // router.Static("HTMLで読み込むURL","読み込むファイルがあるディレクトリの場所") router.LoadHTMLGlob("src/*.html") router.GET("/", func(ctx *gin.Context) { ctx.HTML(200, "index.html", gin.H{}) }) router.Run() } 実行すると/js/main.jsにGETリクエストをして、200が返ってきて成功しています! 静的なファイルにはこのメソッドを使用するらしいです。(詳しくは自分で調べて...) router.Static("HTMLで読み込むURL","読み込むファイルがあるディレクトリの場所") 最後に 調べても全然出てこなく、30分ぐらいかかったので備忘録&周知のために記事を書きました。 誰かの役に立ったら幸いです。 今回のリポジトリ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

0からはじめるChrome拡張開発

私は、仕事でExcelVBAとか使うぐらいでしたが、Chromeも使うことが多いので Chrome側で仕事を便利にできるようなものを作ろうと拡張機能に手を出し始めているところ 自分の勉強がてら記事作成していこうかと思います。 必要なもの ・Google Chrome ・Visual Studio Code(メモ帳でも可) 最小構成を作る。 適当なフォルダを作成して、フォルダ内に以下のファイル名でファイルを作成する。 manifest.json { "manifest_version": 2, "name": "拡張機能サンプル", "version": "0.0.1", "description": "最小構成" } 拡張機能のインストール Google Chromeのアドレスバーに、「chrome://extensions」と入力する。 (オプションから「その他のツール」>「拡張機能」でも良い) デベロッパーモードのスイッチをON。 作成したフォルダごと画面に投げ込む。 エラーが出なければ、インストール完了。 おわりに 全く何もしない拡張機能ではあるが、最初の一歩はここからがいいと思います。 Javascriptに慣れていない人・Chrome上でのデバッグに慣れていない人が Web上にあるチュートリアルっぽいやつをやると意外とひっかかることもあり(自分がそうでした) ちゃんと動くやつを少しずつ変えていくと、詰まったところがわかりやすいと思います。 次は、Web上によくあるハローワールド的なものに行こうかなと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

0からはじめるChrome拡張開発1

私は、仕事でExcelVBAとか使うぐらいでしたが、Chromeも使うことが多いので Chrome側で仕事を便利にできるようなものを作ろうと拡張機能に手を出し始めているところ 自分の勉強がてら記事作成していこうかと思います。 必要なもの ・Google Chrome ・Visual Studio Code(メモ帳でも可) 最小構成を作る。 適当なフォルダを作成して、フォルダ内に以下のファイル名でファイルを作成する。 manifest.json { "manifest_version": 2, "name": "拡張機能サンプル", "version": "0.0.1", "description": "最小構成" } 拡張機能のインストール Google Chromeのアドレスバーに、「chrome://extensions」と入力する。 (オプションから「その他のツール」>「拡張機能」でも良い) デベロッパーモードのスイッチをON。 作成したフォルダごと画面に投げ込む。 エラーが出なければ、インストール完了。 おわりに 全く何もしない拡張機能ではあるが、最初の一歩はここからがいいと思います。 Javascriptに慣れていない人・Chrome上でのデバッグに慣れていない人が Web上にあるチュートリアルっぽいやつをやると意外とひっかかることもあり(自分がそうでした) ちゃんと動くやつを少しずつ変えていくと、詰まったところがわかりやすいと思います。 次は、Web上によくあるハローワールド的なものに行こうかなと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】スタートボタンを作成し、1秒毎に文字の色が変わる画面を作成せよ。停止ボタンも作成し、押下すると色の変化を止める処理を作成せよ

<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>要素の作成・追加</title> <script> // 変数を定義 var interval_id; var start_flag = false; // スタート処理中はtrue //-------------------------------------------------------- function start_flash_text() { // 停止しているときのみ、スタート処理を開始(二重クリック防止) if (start_flag === false) { // 1秒ごとにflash_textを実行 interval_id = setInterval(flash_text, 1000); start_flag = true; } } //-------------------------------------------------------- function flash_text() { var flash = document.getElementById('flash'); // p要素の文字色を赤か青に変更 if (flash.style.color === 'red') { flash.style.color = 'blue'; } else { flash.style.color = 'red'; } } //-------------------------------------------------------- function stop_flash_text() { // setIntervalによる繰り返し動作を停止 clearInterval(interval_id); start_flag = false; } //-------------------------------------------------------- window.onload = function() { var start = document.getElementById('start'); start.addEventListener('click', start_flash_text, false); var stop = document.getElementById('stop'); stop.addEventListener('click', stop_flash_text, false); } </script> </head> <body> <p id="flash">flash text</p> <button id="start">start</button> <button id="stop">stop</button> </body> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】現在時刻をリアルタイムで一定時間繰り返す画面を作成せよ。(西暦から秒まで)

<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>一定間隔で繰り返し実行</title> <script> function count_time() { var timer = document.getElementById('timer'); var date = new Date(); timer.innerHTML = date.toLocaleString(); } window.onload = function() { // 1秒(1000ミリ秒)ごとにcount_time関数を実行 setInterval(count_time, 1000); } </script> </head> <body> <div id="timer"></div> </body> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nuxt.js × three.js × ammo.js でSpeedPizzaを作った時のメモ(成功)

概要 nuxt.js × three.js × ammo.js の開発に関するメモ。 外部OBJファイル等をloadしてsceneにaddしてから、それに対して、 物理演算(自由落下と当たり判定)を物理エンジンを使って加えられるかの実験メモ。 前回さくっと作ってみたら、object同士のcollision(当たり判定)が動作しない結果になった。 悔しいから色々試して、動作したバージョンができたので、メモとして残しておく。 前回の投稿の続きになるので、前提や環境は以下を参照。 nuxt.js × three.js × ammo.js でSpeedPizzaを作った時のメモ(失敗) 原因 結論からいうと前回の投稿の、「addしたobjectからshape→bodyを作成してPhysicsWorldに追加」の「そこで、makeShape関数によって、読み込んだobjectのgeometryからshapeを形成することにした。」の部分が原因。 makeShape関数の部分を細かく言うと、geometry(BufferGeometry)から頂点情報を取り出し、頂点情報からammo.jsのbtBvhTriangleMeshShapeを作成している。 色々調べたら、btBvhTriangleMeshShapeではmesh同士のcollisionが動作しないということがわかった、、、orz そこで、btConvexHullShapeを使って、shapeを作り直すことにした。 誤っているソース ThreeCanvas.vue <script type="module"> ... 略 export default { ... 略 methods: { ... 略 makeShape(mesh) { const triangleMesh = new Ammo.btTriangleMesh(true, true); triangleMesh.setScaling( new Ammo.btVector3(mesh.scale.x, mesh.scale.y, mesh.scale.z) ); const geometry = mesh.geometry; if (geometry instanceof THREE.BufferGeometry) { const vertexPositionArray = geometry.attributes.position.array; for (let i = 0; i * 3 < geometry.attributes.position.count; i++) { triangleMesh.addTriangle( new Ammo.btVector3( vertexPositionArray[i * 9], vertexPositionArray[i * 9 + 1], vertexPositionArray[i * 9 + 2] ), new Ammo.btVector3( vertexPositionArray[i * 9 + 3], vertexPositionArray[i * 9 + 4], vertexPositionArray[i * 9 + 5] ), new Ammo.btVector3( vertexPositionArray[i * 9 + 6], vertexPositionArray[i * 9 + 7], vertexPositionArray[i * 9 + 8] ), false ); } } else if (geometry instanceof THREE.Geometry) { for (let i = 0; i < geometry.faces.length; i++) { const face = geometry.faces[i]; if (face instanceof THREE.Face3) { const vec1 = new Ammo.btVector3(0, 0, 0); const vec2 = new Ammo.btVector3(0, 0, 0); const vec3 = new Ammo.btVector3(0, 0, 0); vec1.setX(face[0].x); vec1.setY(face[0].y); vec1.setZ(face[0].z); vec2.setX(face[1].x); vec2.setY(face[1].y); vec2.setZ(face[1].z); vec3.setX(face[2].x); vec3.setY(face[2].y); vec3.setZ(face[2].z); triangleMesh.addTriangle(vec1, vec2, vec3, true); } } } const shape = new Ammo.btBvhTriangleMeshShape(triangleMesh, true, true); return shape; }, ... 略 }, }; </script> 正解ソースはこちら ThreeCanvas.vue <script type="module"> ... 略 export default { ... 略 methods: { ... 略 makeShape(mesh) { const shape = new Ammo.btConvexHullShape(); const geometry = mesh.geometry; if (geometry instanceof THREE.BufferGeometry) { const vertexPositionArray = geometry.attributes.position.array; for (let i = 0; i * 3 < geometry.attributes.position.count; i++) { shape.addPoint( new Ammo.btVector3( vertexPositionArray[i * 9], vertexPositionArray[i * 9 + 1], vertexPositionArray[i * 9 + 2] ), new Ammo.btVector3( vertexPositionArray[i * 9 + 3], vertexPositionArray[i * 9 + 4], vertexPositionArray[i * 9 + 5] ), new Ammo.btVector3( vertexPositionArray[i * 9 + 6], vertexPositionArray[i * 9 + 7], vertexPositionArray[i * 9 + 8] ), false ); } } else if (geometry instanceof THREE.Geometry) { for (let i = 0; i < geometry.faces.length; i++) { const face = geometry.faces[i]; if (face instanceof THREE.Face3) { shape.addPoint( new Ammo.btVector3(face[0].x, face[0].y, face[0].z), new Ammo.btVector3(face[1].x, face[1].y, face[1].z), new Ammo.btVector3(face[2].x, face[2].y, face[2].z), true ); } } } shape.setLocalScaling( new Ammo.btVector3(mesh.scale.x, mesh.scale.y, mesh.scale.z) ); return shape; }, ... 略 }, }; </script> おまけ createRigidBody関数内で、bodyに色々な係数を渡すことができるので、それもメモっておこう。 ThreeCanvas.vue <script type="module"> ... 略 export default { ... 略 methods: { ... 略 createRigidBody(threeObject, physicsShape, mass, pos, quat) { physicsShape.setMargin(0.05); const transform = new Ammo.btTransform(); transform.setIdentity(); transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z)); transform.setRotation( new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w) ); const motionState = new Ammo.btDefaultMotionState(transform); const localInertia = new Ammo.btVector3(0, 0, 0); physicsShape.calculateLocalInertia(mass, localInertia); const rbInfo = new Ammo.btRigidBodyConstructionInfo( mass, motionState, physicsShape, localInertia ); const body = new Ammo.btRigidBody(rbInfo); // この部分 body.setRestitution(1);//反発 body.setFriction(0.6);//摩擦 body.setDamping(0, 0.04);//減衰 body.setAngularFactor(new Ammo.btVector3(1, 1, 1));//回転の向き body.setLinearFactor(new Ammo.btVector3(1, 1, 1));//平行移動の向き threeObject.userData.physicsBody = body; this.rigidBodies.push(threeObject); body.setActivationState(4); this.physicsWorld.addRigidBody(body); return body; }, ... 略 }, }; </script> 最終的には、こんな感じになった 跳ねすぎて、ほとんど落ちた まとめ とりあえずはうまくいった。 意外と、外部のモデルファイルからloadしたobjectで、collisionを動作させる情報が少ないということがわかった。特にammo.jsは情報が少ない。 誰かの役に立ってくれたらと願います。 細かい部分はもっと勉強しなければ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js 勉強メモ】single file component(.vueファイル)のtemplateルートは1つの要素のみにしなければならない

はじめに 仕事で使う事になったので1からVue.jsについて学んだ(元々Angularでプロダクト開発をやっていた事はあるがAngularはだめか・・・)。 ちゃんと覚えておかないとまずそうな事を備忘録として1つ1つ残しておく。 single file component(.vueファイル)のtemplateルートは1つの要素のみにしなければならない Vue.jsのsingle file componentでは、<template></template>で定義されるtemplateルートには、1つの要素だけが存在するようにしなければならない。 以下のソースコード1のように、複数の要素があるとThe template root requires exactly one element( テンプレートルートには、1つの要素のみが必要です)というエラーになる。 sample.vue <template> <p>いいね({{ number }})</p> <button @click="increment">+1</button> </template> <script> // 省略・・・ </script> <style></style> 正しい書き方は以下。 sample.vue <template> <div> <p>いいね({{ number }})</p> <button @click="increment">+1</button> </div> </template> <script> // 省略・・・ </script> <style></style> Vue.jsの勉強メモ一覧記事へのリンク Vue.jsについて勉強した際に書いた勉強メモ記事のリンクを集約した記事。 https://qiita.com/yuta-katayama-23/items/dabefb59d16a83f1a1d4 ソースコード全体を見たい場合はここ ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AR.jsで簡単なARを作る!

用意するもの ・マーカーになるpattファイル ・htmlファイル 以上!超簡単 pattファイルの作成 以下で簡単に作成できる。 https://jeromeetienne.github.io/AR.js/three.js/examples/marker-training/examples/generator.html pattファイルとimgファイルを両方ダウンロードするのがベター。 htmlファイルの作成 <!DOCTYPE html> <html> <script src="https://aframe.io/releases/1.0.0/aframe.min.js"></script> <script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar.js"></script> <body style="margin: 0px; overflow: hidden;"> <a-scene embedded arjs> <a-marker type="pattern" url="patt.patt"> <a-box position="0 -1 0" scale="1 1 1" color="grey"></a-box> </a-marker> <a-entity camera></a-entity> </a-scene> </body> </html> <a-marker type="pattern" url="patt.patt">に作成したpattファイルのパスを上げる。 ARを体験してみる。 上記で作成したhtmlにアクセスすると、カメラが起動する。 そのカメラでpattファイルを作成した時にダウンロードしたimageファイルを映してみると、3DのBOXが出てくる。 すっごい簡単。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React アプリを AWS EC2 にデプロイ

はじめに フロントエンド React + バックエンド Django REST Framework でアプリを作成したが、ローカル環境でしか動かすことができないので、AWS EC2 インスタンス上にデプロイして外部からもアクセスができるようにする。今回はフロントエンドである React アプリをデプロイすることとし、Web サーバーソフトウェアには、nginx を用いる。なお React アプリはすでに作成されており、コードは GitHub に push してあることを前提とする。React/Next.jsアプリケーションを作成し、AWS EC2を使って本番環境にデプロイするまでを参考にした。 EC2 インスタンスの作成 Amazon Linux 2 AMI (HVM), SSD Volume Type という無料利用枠対象のものを使用して作成した。OS は RHEL7 / CentOS7 をベースとしているらしい。セキュリティグループ設定で HTTP 設定の追加を忘れないこと。設定されていると、以下のようになっているはず。 その他インスタンス作成に関することはここでは割愛する。以下の作業はすべて作成した EC2 インスタンスに SSH 接続を行った状態での作業であるので注意。接続方法は SSH を使用した Linux インスタンスへの接続を参照。 nginx のインストールおよびセットアップ 以下コマンドで nginx をインストールする。 $ yum update -y $ sudo amazon-linux-extras install nginx1 以下コマンドで、nginx の自動起動設定を行い、起動する。 $ sudo systemctl enable nginx # 自動起動設定 $ sudo systemctl start nginx.service # 起動 この状態で、EC2 インスタンスの「パブリック IPv4 アドレス」または「パブリック IPv4 DNS」にアクセスして、問題なく接続できることを確認する。 次に、/etc/nginx/nginx.conf(一部抜粋)に以下を追記する。 /etc/nginx/nginx.conf http { ... server { ... location / { proxy_pass http://localhost:3000; } ... } } 上記でリバースプロキシの設定ができたので、このインスタンスに対するリクエストは localhost:3000 へ転送される。 設定を反映させるために、一度 ngnx を再起動しておく。 $ sudo systemctl restart nginx React アプリの起動 GitHub に push されているアプリをクローンして起動する。まず git をインストールして、レポジトリをクローンする。 $ sudo yum -y install git $ git clone https://github.com/<your repository> React アプリが動作する環境を作成するため、パッケージ管理ツールである npm をインストールする。(以下コマンドは Node.js をインストールしているが、合わせて npm もインストールされる。)インストール方法の詳細はNode.js Binary Distributions を参照。 $ sudo su - root # curl -fsSL https://rpm.nodesource.com/setup_16.x | bash - # yum install -y nodejs アプリをビルドして、起動する。 $ cd <your repository> $ npm install # package.json に記載されたパッケージを一括インストール $ npm run build # ビルド実行 $ npm run start 再度 EC2 インスタンスに接続すると、アプリが表示されていることが確認できる。 おわりに フロントエンドのアプリを EC2 上にデプロイした。バックエンド側も合わせてデプロイしたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LeafletでGPSログ(GPX)地図:ルート上に30分間隔で時間をラベルで表示。

地図データを扱うJavaScriptライブラリ「Leaflet」を使って、GPSログ(GPXファイル)の地図表示を試している。 今回、ルート上に30分間隔で時間をラベルで表示するようにした。 詳細は以下。 LeafletでGPSログ(GPX)地図:ルート上に30分間隔で時間をラベルで表示。 https://2ndart.hatenablog.com/entry/2021/05/11/150933
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DOMやwindowに依存しているPrismのプラグインをNode.js上で動かす方法

Prism.jsのline-numbersプラグインなどは、ブラウザ上での動作を前提とした設計でDOMやwindowに依存しているため、Node.jsのようなサーバーサイドでrequireしても動作しません。 解決策 ではどうやったら動くか?解決策は次の通りです。 DOMに依存している問題の解決策 DOMに依存しているコードが動くようにするために、jsdomを使います。 グローバルオブジェクトwindowに依存している問題の解決策 line-numbersプラグインなどは、window.Prismがundefinedだとプラグインのロードをやめてしまいます。この解決策としては、vmモジュールを使い、擬似的にグローバルオブジェクトとしてwindowがあるサンドボックス環境を作り、その中で、プラグインコードをevalしてやるようにします。要するにPrismにブラウザ環境だと思わせるようにするということです。 上の解決策を講じたコード 次が上の解決策を施したコードです。 loadPlugin.ts import fs from "fs"; import { JSDOM } from "jsdom"; import Prism from "prismjs"; import vm from "vm"; export function createLoadPlugin() { const { window } = new JSDOM(""); window.Prism = Prism; const ctx = vm.createContext(window); return function load(plugin: string): void { const filename = require.resolve( `prismjs/plugins/${plugin}/prism-${plugin}` ); const src = fs.readFileSync(filename, "utf-8"); vm.runInContext( // language=JavaScript ` try { const self = window; ${src}; } catch (err) { console.error(err); } `, ctx ); }; } 使い方は次の通り。 usage.ts import { createLoadPlugin } from "./loadPlugin"; const loadPlugin = createLoadPlugin(); loadPlugin("line-numbers"); loadPlugin("diff-highlight"); loadPlugin("autolinker"); loadPlugin("inline-color"); これで様々なプラグインがロードできるようになります。 ハイライトはPrism.highlightElementを用いる Node.jsでハイライトするにはPrism.highlightを用いるのが一般的ですが、DOM依存のプラグインはそのメソッドでは動かないので、Prism.highlightElementを使います。 実際にhighlightElementを使う場合は、DOMが必要なので次のようなユーティリティ関数を作っておくとよいです。 highlight.ts import { JSDOM } from "jsdom"; import Prism from "prismjs"; export function highlight( code: string, { language = "none", lineNumbers = false, }: { language?: string; lineNumbers?: boolean } = {} ): string { const { window } = new JSDOM(""); const pre = window.document.createElement("pre"); const codeElm = window.document.createElement("code"); pre.appendChild(codeElm); codeElm.textContent = code; codeElm.setAttribute( "class", [`language-${language}`] .concat(lineNumbers ? ["line-numbers"] : []) .join(" ") ); Prism.highlightElement(codeElm); return pre.outerHTML; } 最終的なコード 上で作った、loadPluginとhighlightを使った最終的なコードです。 main.ts import loadLanguages from "prismjs/components/index"; import { highlight } from "./highlight"; import { createLoadPlugin } from "./loadPlugin"; loadLanguages(); const loadPlugin = createLoadPlugin(); loadPlugin("line-numbers"); loadPlugin("diff-highlight"); loadPlugin("autolinker"); loadPlugin("inline-color"); const code = `span.foo { background-color: navy; color: #BFD; }`; const html = highlight(code, { language: "css", lineNumbers: true }); console.log(html); 上のhtml内容は次のようになります。 See the Pen Prism line-numbers inline-colors by suin (@suin) on CodePen.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptで日付のバリデーションチェックをする方法

var yyyy = document.form.year.value; var mm = document.form1.month.value; var dd = document.form2.date.value; var date = new Date(yyyy, mm-1, dd); if(date.getFullYear() != yyyy || date.getMonth() != mm-1 || date.getDate() != dd) { alert("期限の入力が不正です。"); } else { window.location.href = 'sample.html'; } 解説 想定しているのは、○年○月○日というように、○の部分を入力フォームにして、整合性チェックをするというもの。 正しい日付が入力されたら画面遷移するプログラムとなっている。 まずは、フォームに入力された値を取得してそれぞれの変数に代入。 getMonth()は、デフォルトでは0~11の範囲を取るので、mm-1とすることで整合性を取る。 それぞれの変数がgetFullYear()、getMonth()、getDate()と比較して正しい値かチェック。どれか1つでも不適合ならアラートを出す。 ハマったエラー 存在しない日付を入力しても、バリデーションを通過してしまうエラーが発生。ネットの通りに書いてもなぜか通貨…。コードを見直すと、getMonth()の()の記述漏れでした。。 気づくのに1時間かかってしまった…
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React DnD: v11.0以降の後方互換性を破るアップデート

React DnDは、Reactアプリケーションでドラッグ&ドロップを実現するライブラリです。公式の「Tutorial」にもとづいて1年前につくった作例が、少し前にライブラリをアップデートしたら動かなくなりました。昨年使ったライブラリがv10.0.2、最新はv14.0.2です。更新が頻繁なのは喜ばしいというべきでしょう。けれどその間、後方互換性を破る更新がいくつかありました。それらを書きとめておきます。 なお、v14.0に合わせてチュートリアルシリーズ「Create React App + React DnD」(全4回)を書きました。「Tutorial」の作例を実際につくってみたい方は、ぜひご覧ください(各回の解説の結びにCodeSandboxのサンプルを公開しています)。 HTML5Backendは名前つきでimportする まず、v11.0.0で、HTML5Backendがexport defaultでなくなりました。したがって、HTML5Backendは名前つきでimportしなければならないということです。 // import HTML5Backend from 'react-dnd-html5-backend' import { HTML5Backend } from 'react-dnd-html5-backend' 理由は、つぎのように説明されています。つまり、ライブラリの開発方針としてexport defaultはできるだけ使わないようにしたそうです。実際、公式「Tutorial」の作例も、コンポーネントのexportは基本的に名前つきになっていました。 In general, throughout the app we've minimized the usage of default exports as well. useDragとuseDropの引数はコールバック関数で仕様オブジェクトを返す v13.0.0では、useDragとuseDropの引数が、仕様オブジェクトでなく、仕様オブジェクトを返すコールバック関数になりました。そして、第2引数には依存配列を与えます。つまり、ReactのuseMemoと同じ構文になったのです。 これも、1年前の作例が動かなくなった理由かと思ったら、違いました。v13.1.0で、もともとの仕様オブジェクトを渡す構文も復活したからです。 useDrag(() => spec, [deps]) // useMemo型構文 useDrag(spec) // 復活 ただし、第2引数は残ります。けれど、省いて構いません。デフォルトの依存配列が用いられるからです。構文について詳しくは、「useDragフック」および「useDropフック」をご参照ください。 useDragで定める仕様オブジェクトのtypeプロパティは直下に置く 少しやっかいだったのは、v14.0.0で加えられたつぎの変更です。なにしろ「Breaking Changes」の表記がありません。内部的な改善のために、typeとitemを切り離したようです。 useDrag(() => { // item: { type: BOX } }, type: BOX, }) 以上を改めたところ、1年前の作例も動くようになりました(useDragとuseDropは、useMemo型の構文に書き替えました)。でき上がりはCodeSandboxに公開してあります。つくり方については、前出「Create React App + React DnD」(全4回)をお読みください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js上のPrismでdiff-highlightを使う方法

Prismのdiff-highlightプラグインをNode.js上で使う方法です。 diff-highlightプラグインとは? Prismのdiff言語では、行頭の+-記号で差分のシンタックスハイライトがされます。diff-highlightプラグインはJavaScriptなどの任意の言語をハイライトしながなら差分のハイライトも同時行えるものです。 Diff Highlight ▲ Prism plugins Node.jsでdiff-highlightを使うには? まず、diff言語をロードしておく必要があり、その上でprismjs/plugins/diff-highlight/prism-diff-highlightをロードするとプラグインが有効になります。 import loadLanguages from "prismjs/components/index"; loadLanguages("diff"); require("prismjs/plugins/diff-highlight/prism-diff-highlight"); JavaScriptの任意の言語をハイライトするには、Prism.highlightメソッドの第2引数をdiff言語に、第3引数をdiff-${言語名}にします。 import Prism from "prismjs"; const code = ` const a = 1; -console.log(a); +console.log(a + 1);`; const html = Prism.highlight(code, Prism.languages.diff, "diff-js"); 上のhtmlの値は次のように、行ごとにunchanged, deleted, insertedなどのクラスがつき、これが差分情報になります。Javascriptのトークンもそれごとにクラスがついているのが分かります。 <span class="token unchanged language-js" ><span class="token prefix unchanged"> </span ><span class="token keyword">const</span> a <span class="token operator">=</span> <span class="token number">1</span ><span class="token punctuation">;</span> </span ><span class="token deleted-sign deleted language-js" ><span class="token prefix deleted">-</span>console<span class="token punctuation" >.</span ><span class="token function">log</span ><span class="token punctuation">(</span>a<span class="token punctuation" >)</span ><span class="token punctuation">;</span> </span ><span class="token inserted-sign inserted language-js" ><span class="token prefix inserted">+</span>console<span class="token punctuation" >.</span ><span class="token function">log</span ><span class="token punctuation">(</span>a <span class="token operator">+</span> <span class="token number">1</span ><span class="token punctuation">)</span ><span class="token punctuation">;</span></span > スタイルを適用すると追加行と削除行に背景色がついているのが分かります: See the Pen Prism diff-highlight by suin (@suin) on CodePen. ちなみに上で使ったCSSは次の2つです: https://prismjs.com/themes/prism-okaidia.css https://prismjs.com/plugins/diff-highlight/prism-diff-highlight.css 注意点: 変更なしの行は半角スペースが必要 ハイライトするコードですが、変更なしの行は行頭にスペースを入れるのを忘れないようにしてください。次の例は、1〜2行目が正しくハイライトされないどころか、改行も失われてしまうので注意です。 const a = 1; const b = 1; +console.log(a + b); 正しくは、次のように1~2行目のconstの前にひとつ半角スペースを入れます。 const a = 1; const b = 1; +console.log(a + b);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactでドロップダウン(dropdown)メニューを実装する

経緯 Reactでドロップダウンメニューを実装する必要が出てきた。 ドロップダウンメニューを実装するには、領域外のクリックを検知する必要がある。 React上で領域外を検知する方法と、簡単にドロップダウンメニューを作る。 必要な知識 React React hooks Code import { useState, useRef, useEffect } from 'react' const Menu = () => { const [isOpen, setIsOpen] = useState(false); const dropdownRef = useRef(); useEffect(() => { document.addEventListener("mousedown", handleOutsideClick); return () => document.removeEventListener("mousedown", handleOutsideClick); }, []); const handleOutsideClick = (e) => { if (dropdownRef.current && !dropdownRef.current.contains(e.target)) { setIsOpen(false); } }; return( <> <div ref={dropdownRef} className="relative inline-block text-left"> <span className="rounded-md shadow-sm"> <button onClick={() => setIsOpen(!isOpen)} type="button" className="inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" id="options-menu" aria-haspopup="true" aria-expanded={isOpen}> MENU <svg className="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#4B5563"> <path fillRule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clipRule="evenodd" /> </svg> </button> </span> {isOpen && ( <div className='absolute top-12 right-2 w-64 p-4 bg-white z-50 shadow-lg border border-gray-100 rounded'> <ul> <li><a>menu1</a></li> <li><a>menu2</a></li> <li><a>menu3</a></li> </ul> </div> )} </div> </> ) } export default Menu 仕組み useEffectで描画時に、イベントリスナーでmousedown(クリック)を登録しておく。 また、returnでイベントリスナーのクリーンアップも設定する。 mousedown時に、mousedownされた要素が返されるため、handleOutsideClickでその要素に、ドロップダウンメニューの要素が含まれるか判定する。(ドロップダウンメニューの要素はuseRefを使用して取得) 含まれる場合、それはドロップダウンメニュー上でのクリックなので、ドロップダウンメニューは表示し続ける。 含まれない場合、それはドロップダウンメニュー外でのクリックのためドロップダウンメニューを非表示にする。 今回は、要素を丸ごと消しているが、display: hidden;やdisplay: none;でも実装可能である。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(入門)Node.jsで初めてのWebサーバーを立ち上げる Windoows10環境

node.js で サーバーを作成します。 環境 node v12.18.1 npm 6.14.4 windows10 nodeは以下のサイトを参考にして、インストールします。 https://qiita.com/satoyan419/items/56e0b5f35912b9374305 nodeのバージョン管理は以下のサイトを参考にします。 https://qiita.com/satoyan419/items/56e0b5f35912b9374305 vscodeでフォルダを作成して、 app.jsファイルに以下のコードを記入します。 app.js // モジュールをロードする DOMの代わり const http = require('http'); // サーバーを作る var server = http.createServer( (request,response)=>{ response.end('hello'); } ); // ポート番号:3000で受け付け開始 server.listen(port =3000); 最後のserver.listen(port =3000); のportの部分がないと エラー code: 'MODULE_NOT_FOUND', requireStack: [] が出きたので忘れないでください。 node.jsでは、javascriptで使用していたDOM要素の代わりに、 モジュールというものをロードします。 コマンドプロンプトを開いて、 コマンドプロンプト(cmd) node app.js localhost:3000にブラウザでアクセスしてください。 公式ドキュメントでも同様なコードが書いてあるので、参考に。 https://nodejs.org/ja/docs/guides/getting-started-guide/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

videoタグの操作色々

概要 videoタグの属性やJSでの操作について、実務で使用したものをまとめる。 属性 muted : 音声をミュートにする autoplay : 自動再生 playsinline : iOSだと自動再生されないので、これが必要 上記3つはほぼ確定で設定しておくべき(自動再生をさせたい場合) JSの操作 メソッド element.play() : 動画を再生 element.pause() : 動画を停止 イベント ended : 動画の終了時に発生するイベント その他 iPhoneの場合(アンドロイドはわからない。。)、省電力モードがONになっていると動画の自動再生はされないので注意。 スマホで一度ブラウザからホームに戻って、その後またブラウザを開いても動画は停止したままになる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

チャンネルの動画を古い順で再生

test.html <html lang="ja"><head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script> /* console.log( ytInitialData.contents.twoColumnBrowseResultsRenderer.tabs[1] .tabRenderer.content.sectionListRenderer.contents[0] .itemSectionRenderer.contents[0] .gridRenderer.items.map(v=>{ var r = v.gridVideoRenderer return r.videoId+','+r.title.runs[0].text }).join("\n") ) */ var s = ` FxjWWtdw4Hw,サンプルですよ 5tpHkvQ30Sg,マイクラB2J|Java版もゲームパッドで遊びたい!実用アプリ自作・後編 TO6KsmQIGL0,マイクラB2J|Java版もゲームパッドで遊びたい!実用アプリ自作・前編 0iNRtSDcJjQ,マイクラB2J|iPhoneのワールドで遊びたい!!! mPE_qROWQLQ,マイクラB2J|Java版もゲームパッドで遊びたい・外伝 8X_lRF9z08A,マイクラB2J|統合版のワールドで遊びたい!!! sKCzh3Tthxk,マイクラB2J|Java版も寝ころがって遊びたい!!! xGlVax-Y8fM,マイクラB2J|いっちゃんキレイなマイクラください!!! ` var data = s.trim().split("\n").map(t=>t.split(',')) $(e=>{ $('body').append( $('<div id="player">'), data.map(a=>$('<div>').attr('data-id',a[0]).append(a[1])) ) $('[data-id]').click(e=>{ location.hash = e.target.dataset.id }) var id = location.hash.slice(1)||$('[data-id]')[0].dataset.id $('[data-id='+id+']').addClass('on') location.hash = id $('head').append( $('<script>').attr('src','https://www.youtube.com/iframe_api') ) }) function onYouTubeIframeAPIReady() { var p = new YT.Player('player', { width:'100%',height:400, videoId:location.hash.slice(1), events:{ onReady(e){e.target.playVideo()}, onStateChange(e){ if (e.data ==YT.PlayerState.ENDED) { var q = $('div[data-id="'+location.hash.slice(1)+'"]').next() if(q.length>0) location.hash = q[0].dataset.id } } } }) window.onhashchange = e=>{ var s = location.hash.slice(1) $('[data-id]').removeClass() $('[data-id='+s+']').addClass('on') p.loadVideoById(s) } } </script> <style> div{line-height:40px;border-bottom:1px solid #eee;} .on{background:#eee;} </style> </head> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails] chart.jsでRailsアプリにグラフを描画する [chart.js]

記事の概要  アプリで取り扱われるデータを可視化できればと思い、chart.jsを使ってRailsアプリにグラフを描いてみます。  1. chart.jsの導入手順  2. 記事投稿数の推移グラフを描いてみる chart.jsとは?  JavaScriptで実装されたグラフをRailsアプリに組込むことができるライブラリです。  公式  https://www.chartjs.org/docs/latest/ 1. chart.jsの導入手順  Gemfileに記述する。 Gemfile gem 'chart-js-rails', '~> 0.1.4'  そしてターミナルでbundle installを実行する。 % bundle install  package.jsonに以下の通り追記。 package.json { # 省略 "dependencies": { #省略 "chart.js": "^2.7.1" # ←ここに追記 } }  そしてターミナルでyarn installを実行。 % yarn install 2. 記事投稿数推移グラフを描いてみる  ここでは、とあるArticleモデルの投稿数推移について、Articlesコントローラーでデータを加工してarticles#indexにグラフ描画することをゴールとします。  横軸が日付、縦軸が投稿数です。なお、日時データを扱う"groupdate" gemは導入済みとして進めます。  まず、グラフ描画の基本構成は下記のようになります。公式ページのサンプル参照。 app/views/articles/index.html.erb <canvas id="myChart" width="200" height="100"> </canvas> <script> var ctx = document.getElementById("myChart").getContext('2d'); var myChart = new Chart(ctx, { type: 'bar', # 'bar'でグラフタイプを縦棒グラフに指定  data: { labels: <%= @chartlabels %>, # 横軸にとるデータ(今回は投稿日付)を埋め込む datasets: [{ label: "投稿数", data: <%= @chartdatas %>, # 縦軸にとるデータ(今回は投稿数)を埋め込む backgroundColor: 'rgba(255, 80, 120, 1.0)', borderColor: 'rgba(255, 80, 120, 1.0)', fill: false }] }, }); </script>  グラフに渡すデータをarticles_controllerで用意しましょう。インスタンス変数を定義します。 app/controllers/articles_controller class ArticlesController < ApplicationController def index @articles = Article.all @article_by_day = @articles.group_by_day(:created_at).size # groupdateのgroup_by_dayメソッドで投稿日(created_at)に基づくグルーピングして個数計上。 # => {Wed, 05 May 2021=>23, Thu, 06 May 2021=>20, Fri, 07 May 2021=>3, Sat, 08 May 2021=>0, Sun, 09 May 2021=>12, Mon, 10 May 2021=>2} @chartlabels = @article_by_day.map(&:first).to_json.html_safe # 投稿日付の配列を格納。文字列を含んでいると正しく表示されないので.to_json.html_safeでjsonに変換。 # => "[\"2021-05-05\",\"2021-05-06\",\"2021-05-07\",\"2021-05-08\",\"2021-05-09\",\"2021-05-10\"]" @chartdatas = @article_by_day.map(&:second) # 日別投稿数の配列を格納。 # => [23, 20, 3, 0, 12, 2] end end  Railsアプリを起動し/indexへアクセスすると縦棒グラフが描画されている。  次に、累計投稿数を折れ線グラフにして、先ほどのグラフに重ねてみます。 app/views/articles/index.html.erb <canvas id="myChart" width="200" height="100"> </canvas> <script> var ctx = document.getElementById("myChart").getContext('2d'); var myChart = new Chart(ctx, { type: 'bar',   data: { labels: <%= @chartlabels %>, datasets: [{ label: "日別投稿数", data: <%= @chartdatas %>, backgroundColor: 'rgba(255, 80, 120, 1.0)', borderColor: 'rgba(255, 80, 120, 1.0)', fill: false },{ //ここから追記 label: "累積投稿数", data: <%= @cumulative %>, // 縦軸にとる累積投稿数データを埋め込む backgroundColor: 'rgba(255, 80, 120, 0.2)', borderColor: 'rgba(255, 80, 120, 1.0)', type: 'line', // 'line'でグラフタイプを折線グラフに指定 }] }, }); </script>  累積投稿数データを配列にして、インスタンス変数として用意します。 app/controllers/articles_controller class ArticlesController < ApplicationController def index @articles = Article.all @article_by_day = @articles.group_by_day(:created_at).size @chartlabels = @article_by_day.map(&:first).to_json.html_safe @chartdatas = @article_by_day.map(&:second) # ここから追記 @cumulative = [] sum=0 @chartdatas.each do |a| sum = sum + a @cumulative<<sum end end end  アプリを起動し/indexへアクセスすると、線グラフも表示されました。 まとめ  chart.jsを活用し、記事投稿数推移グラフを縦棒+折れ線グラフで表現できました。データベースから加工したデータをグラフで表現することができるので、アプリの管理者機能、可視化など幅広く活用できそうです。カスタマイズも柔軟にできそうなので、今後も記事で取扱いたいと思います。  Rubyでのデータ加工(もっと簡潔にできそう)とJavaScript記述の良いトレーニングにもなりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptモダンフロント技術『GASP ScrollTrigger』を使って実務で速攻使えそうなスライドインアニメを作ってみる

GASP ScrollTriggerとは? 以下のようなスクロールアニメが爆速実装できるライブラリ。 コーポレートサイトとかLP製作に良さそう。 日本語のドキュメントが少なかったので解説記事を書きます。 実装コード 適当にHTMLファイルを作って以下をコピペしましょう。解説は後ほど index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>ScrollTrigger DEMO</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.2/gsap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.2/ScrollTrigger.min.js"></script> </head> <body style="background-color: #EEE; margin: 0;"> <header style="height: 100px; background-color: #FFF; display: flex; align-items: center; justify-content: space-between; padding: 0 26px; position: sticky; top: 0;"> <span style="font-weight: bold; font-size: 30px; letter-spacing: 2px;">SITE-LOGO</span> <span style="font-weight: bold; font-size: 20px; margin-right: 30px; letter-spacing: -10px;">・・・</span> </header> <div> <div style="margin: 80px 120px 40px;"> <div style="font-weight: bold; font-size: 70px;">Title</div> <div style="font-weight: bold; font-size: 20px; color: #888;">タイトル</div> </div> <div id="bg01" style="margin-bottom: 200px; position: relative; background-size: cover; width: 100%; height: 400px; background-image: url('https://s3-ap-northeast-1.amazonaws.com/onseo/uploads/article/thumbnail/9/1623193_s.jpg'); filter: saturate(10%) contrast(70%) brightness(200%);"> <div id="pop01" style="padding: 60px; width: 500px; position: absolute; background-color: #FFF;"> <div style="font-weight: bold; font-size: 40px; margin-bottom: 40px;">POP-Title</div> <div>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</div> </div> </div> </div> <div> <div style="margin: 80px 120px 40px;"> <div style="font-weight: bold; font-size: 70px;">Title</div> <div style="font-weight: bold; font-size: 20px; color: #888;">タイトル</div> </div> <div id="bg02" style="margin-bottom: 200px; position: relative; background-size: cover; width: 100%; height: 400px; background-image: url('https://s3-ap-northeast-1.amazonaws.com/onseo/uploads/article/thumbnail/12/3019301_s.jpg'); filter: saturate(10%) contrast(70%) brightness(200%);"> <div id="pop02" style="padding: 60px; width: 500px; position: absolute; background-color: #FFF;"> <div style="font-weight: bold; font-size: 40px; margin-bottom: 40px;">POP-Title</div> <div>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</div> </div> </div> </div> <footer style="height: 200px; background-color: #333; display: flex; align-items: center; padding: 0 26px;"> <span style="color: #FFF;font-weight: bold; font-size: 30px; letter-spacing: 2px;">SITE-LOGO</span> </footer> <script> const bg01 = '#bg01' const pop01 = '#pop01' const start01 = 40 gsap.set(bg01, { opacity: 0 }) gsap.to(bg01, { scrollTrigger: { trigger: bg01, start: start01 }, duration: 1, opacity: 1 }) gsap.set(pop01, { opacity: 0, top: '20%', left: '60%' }) gsap.to(pop01,{ scrollTrigger: { trigger: pop01, start: start01 }, duration: 1, opacity: 1, left: '50%' }) const bg02 = '#bg02' const pop02 = '#pop02' const start02 = 570 gsap.set(bg02, { opacity: 0 }) gsap.to(bg02, { scrollTrigger: { trigger: bg02, start: start02 }, duration: 1, opacity: 1 }) gsap.set(pop02, { opacity: 0, top: '20%', right: '60%' }) gsap.to(pop02,{ scrollTrigger: { trigger: pop02, start: start02 }, duration: 1, opacity: 1, right: '50%' }) </script> </body> </html> 実装手順 1. CDNを2つほど読み込む <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.2/gsap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.2/ScrollTrigger.min.js"></script> 2. DOMを作る <div id="bg01"> <div id="pop01"> テキスト等 </div> </div> 3. JavaScriptを書く const bg01 = '#bg01' const pop01 = '#pop01' const start01 = 40 gsap.set(bg01, { opacity: 0 }) gsap.to(bg01, { scrollTrigger: { trigger: bg01, start: start01 }, duration: 1, opacity: 1 }) gsap.set(pop01, { opacity: 0, top: '20%', left: '60%' }) gsap.to(pop01,{ scrollTrigger: { trigger: pop01, start: start01 }, duration: 1, opacity: 1, left: '50%' }) gsap.set(セレクタ, { プロパティ })で初期状態を指定できます。 gsap.to(セレクタ, { scrollTrigger: { trigger: セレクタ, start: スタート位置 }, { プロパティ } })でスクロール時のアニメーションを指定できます。 セレクタ jQueryと同じセレクタの指定方式。 id指定なら頭にシャープ(#id_name) クラス指定なら頭にドット(.class_name) セレクタ名は.set()と.to()でどうせ2回書くので変数にまとめておくと良いです。 プロパティ プロパティ名 内容 opacity 透明度 top 上下位置(上から数える) bottom 上下位置(下から数える) left 左右位置(左から数える) right 左右位置(右から数える) duration アニメーション開始から完了までの時間(秒) 他にも色々あるみたいですが、CSSの@keyframesで使うプロパティと大体同じっぽい? 正確な情報は公式ドキュメントを参考にしてください。 スタート位置 start: 40みたいに直接数字を入れるとページ全体の絶対位置指定になります。 start: '40px'みたいに書くと親要素に対する相対位置。 (おまけ)動作解説 一例として、#pop01の要素について見ていきましょう。 #pop01は40pxほど画面を下にスクロールするとフェードインしながら右側からスライドインします。この動作については、以下の2つの動作の組み合わせで成り立っています。 フェードイン動作 .set()でopacity: 0 にした上で、 .to()で opacity: 1 にしてるので、 要素がフェードインするように見えています。 右側からスライドインする動作 .set()でleft: '60%'した上で、 .to()で left: '50%' してるので、 要素が少し左側にスライド移動しているように見えます。 #pop01に関わるJavaScriptコード const pop01 = '#pop01' const start01 = 40 gsap.set(pop01, { opacity: 0, top: '20%', left: '60%' }) gsap.to(pop01,{ scrollTrigger: { trigger: pop01, start: start01 }, duration: 1, opacity: 1, left: '50%' }) 以上、GSAP ScrollTriggerを使ったスクロールアニメーションの実装方法でした。 所感 私はフロントの経験が少ないのですが、モダンな感じのスクロールアニメが非常に簡単に実装できて大変便利に感じました。環境の依存もなく使い勝手も抜群なので、しばらくコーポレートサイトやLPの制作業務ではこちらを使用していこうと思います。 まとめ スクロールアニメーション実装にはGASP ScrollTriggerというライブラリが便利 JavaScriptでセレクタとプロパティ指定だけで簡単にスクロールアニメーションを実装できる ライブラリの環境依存も少なく、CDNで簡単に使用できる 筆者プロフィール 26歳現役フルスタックエンジニア。Ruby/PHP/JSVue/AWS/nginxなどが主です。プログラミングの教材執筆やメンターしてます。VTuberとしてプログラミング解説雑談配信等もします。 Twitter : https://twitter.com/soeno_onseo Github : https://github.com/Fumiya-Soeno HP : http://www.onseo.info/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptモダンフロント技術『GSAP ScrollTrigger』を使って実務で速攻使えそうなスライドインアニメを作ってみる

GSAP ScrollTriggerとは? 以下のようなスクロールアニメが爆速実装できるライブラリ。 コーポレートサイトとかLP製作に良さそう。 日本語のドキュメントが少なかったので解説記事を書きます。 実装コード 適当にHTMLファイルを作って以下をコピペしましょう。解説は後ほど index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>ScrollTrigger DEMO</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.2/gsap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.2/ScrollTrigger.min.js"></script> </head> <body style="background-color: #EEE; margin: 0;"> <header style="height: 100px; background-color: #FFF; display: flex; align-items: center; justify-content: space-between; padding: 0 26px; position: sticky; top: 0;"> <span style="font-weight: bold; font-size: 30px; letter-spacing: 2px;">SITE-LOGO</span> <span style="font-weight: bold; font-size: 20px; margin-right: 30px; letter-spacing: -10px;">・・・</span> </header> <div> <div style="margin: 80px 120px 40px;"> <div style="font-weight: bold; font-size: 70px;">Title</div> <div style="font-weight: bold; font-size: 20px; color: #888;">タイトル</div> </div> <div id="bg01" style="margin-bottom: 200px; position: relative; background-size: cover; width: 100%; height: 400px; background-image: url('https://s3-ap-northeast-1.amazonaws.com/onseo/uploads/article/thumbnail/9/1623193_s.jpg'); filter: saturate(10%) contrast(70%) brightness(200%);"> <div id="pop01" style="padding: 60px; width: 500px; position: absolute; background-color: #FFF;"> <div style="font-weight: bold; font-size: 40px; margin-bottom: 40px;">POP-Title</div> <div>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</div> </div> </div> </div> <div> <div style="margin: 80px 120px 40px;"> <div style="font-weight: bold; font-size: 70px;">Title</div> <div style="font-weight: bold; font-size: 20px; color: #888;">タイトル</div> </div> <div id="bg02" style="margin-bottom: 200px; position: relative; background-size: cover; width: 100%; height: 400px; background-image: url('https://s3-ap-northeast-1.amazonaws.com/onseo/uploads/article/thumbnail/12/3019301_s.jpg'); filter: saturate(10%) contrast(70%) brightness(200%);"> <div id="pop02" style="padding: 60px; width: 500px; position: absolute; background-color: #FFF;"> <div style="font-weight: bold; font-size: 40px; margin-bottom: 40px;">POP-Title</div> <div>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</div> </div> </div> </div> <footer style="height: 200px; background-color: #333; display: flex; align-items: center; padding: 0 26px;"> <span style="color: #FFF;font-weight: bold; font-size: 30px; letter-spacing: 2px;">SITE-LOGO</span> </footer> <script> const bg01 = '#bg01' const pop01 = '#pop01' const start01 = 40 gsap.set(bg01, { opacity: 0 }) gsap.to(bg01, { scrollTrigger: { trigger: bg01, start: start01 }, duration: 1, opacity: 1 }) gsap.set(pop01, { opacity: 0, top: '20%', left: '60%' }) gsap.to(pop01,{ scrollTrigger: { trigger: pop01, start: start01 }, duration: 1, opacity: 1, left: '50%' }) const bg02 = '#bg02' const pop02 = '#pop02' const start02 = 570 gsap.set(bg02, { opacity: 0 }) gsap.to(bg02, { scrollTrigger: { trigger: bg02, start: start02 }, duration: 1, opacity: 1 }) gsap.set(pop02, { opacity: 0, top: '20%', right: '60%' }) gsap.to(pop02,{ scrollTrigger: { trigger: pop02, start: start02 }, duration: 1, opacity: 1, right: '50%' }) </script> </body> </html> 実装手順 1. CDNを2つほど読み込む <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.2/gsap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.2/ScrollTrigger.min.js"></script> 2. DOMを作る <div id="bg01"> <div id="pop01"> テキスト等 </div> </div> 3. JavaScriptを書く const bg01 = '#bg01' const pop01 = '#pop01' const start01 = 40 gsap.set(bg01, { opacity: 0 }) gsap.to(bg01, { scrollTrigger: { trigger: bg01, start: start01 }, duration: 1, opacity: 1 }) gsap.set(pop01, { opacity: 0, top: '20%', left: '60%' }) gsap.to(pop01,{ scrollTrigger: { trigger: pop01, start: start01 }, duration: 1, opacity: 1, left: '50%' }) gsap.set(セレクタ, { プロパティ })で初期状態を指定できます。 gsap.to(セレクタ, { scrollTrigger: { trigger: セレクタ, start: スタート位置 }, { プロパティ } })でスクロール時のアニメーションを指定できます。 セレクタ jQueryと同じセレクタの指定方式。 id指定なら頭にシャープ(#id_name) クラス指定なら頭にドット(.class_name) セレクタ名は.set()と.to()でどうせ2回書くので変数にまとめておくと良いです。 プロパティ プロパティ名 内容 opacity 透明度 top 上下位置(上から数える) bottom 上下位置(下から数える) left 左右位置(左から数える) right 左右位置(右から数える) duration アニメーション開始から完了までの時間(秒) 他にも色々あるみたいですが、CSSの@keyframesで使うプロパティと大体同じっぽい? 正確な情報は公式ドキュメントを参考にしてください。 スタート位置 start: 40みたいに直接数字を入れるとページ全体の絶対位置指定になります。 start: '40px'みたいに書くと親要素に対する相対位置。 (おまけ)動作解説 一例として、#pop01の要素について見ていきましょう。 #pop01は40pxほど画面を下にスクロールするとフェードインしながら右側からスライドインします。この動作については、以下の2つの動作の組み合わせで成り立っています。 フェードイン動作 .set()でopacity: 0 にした上で、 .to()で opacity: 1 にしてるので、 要素がフェードインするように見えています。 右側からスライドインする動作 .set()でleft: '60%'した上で、 .to()で left: '50%' してるので、 要素が少し左側にスライド移動しているように見えます。 #pop01に関わるJavaScriptコード const pop01 = '#pop01' const start01 = 40 gsap.set(pop01, { opacity: 0, top: '20%', left: '60%' }) gsap.to(pop01,{ scrollTrigger: { trigger: pop01, start: start01 }, duration: 1, opacity: 1, left: '50%' }) 以上、GSAP ScrollTriggerを使ったスクロールアニメーションの実装方法でした。 所感 私はフロントの経験が少ないのですが、モダンな感じのスクロールアニメが非常に簡単に実装できて大変便利に感じました。環境の依存もなく使い勝手も抜群なので、しばらくコーポレートサイトやLPの制作業務ではこちらを使用していこうと思います。 まとめ スクロールアニメーション実装にはGSAP ScrollTriggerというライブラリが便利 JavaScriptでセレクタとプロパティ指定だけで簡単にスクロールアニメーションを実装できる ライブラリの環境依存も少なく、CDNで簡単に使用できる 筆者プロフィール 26歳現役フルスタックエンジニア。Ruby/PHP/JSVue/AWS/nginxなどが主です。プログラミングの教材執筆やメンターしてます。VTuberとしてプログラミング解説雑談配信等もします。 Twitter : https://twitter.com/soeno_onseo Github : https://github.com/Fumiya-Soeno HP : http://www.onseo.info/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クライアントサイドジョインを捗らせるライブラリ

firestore を DB にプロダクトを作っていると、クライアントサイドジョインをしまくると思います。 このライブラリを使うと SQL っぽく javascript の object の配列をジョインできるので、クライアントサイドジョインが捗ります。 https://github.com/mtraynham/lodash-joins firestore は NoSQL なので上手く非正規化できたら、クライアントサイドジョインは減るかもですが、中々難しいですよね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptで時間の差分を計算する

Webアプリを作成する中で、ユーザーが選択した時刻の差分を計算して表示することを実装しようとしたのですが、時間計算が簡単そうで、結構詰まったので、メモに残すことにしました。 やりたいこと 時間の差分を計算して、「00:00:00」のような感じにして表示したい const NowTime = new Date(); const ChangeTime = new Date(); let TimeDefference = NowTime.getTime() - ChangeTime.getTime() let Hour = TimeDefference / (1000 * 60 * 60); //「時間」の部分を「Hour」に代入 let Minute = (Hour - Math.floor(Hour)) * 60; //「分」の部分をMinuteに代入 let diffSecond = (Minute - Math.floor(Minute)) * 60; TimeDefference = (('00' + Math.floor(Hour)).slice(-2) + ':' + ('00' + Math.floor(Minute)).slice(-2) + ':' + ('00' + Math.floor(Second)).slice(-2)); Math.floorは数値の小数点以下の切り捨て処理を行う方法です。 .slice()メソッドは文字列を切り分けるメソッドで、指定した引数の位置で文字列を切り分けることができます。 .slice(-2)は右端から2番目の文字列だけを抽出することになり、"0021"だと"21"だけを抽出できます。 このままのコードだと動かないので、ChangeTimeには、ユーザーが選択した時刻を代入するような処理を書いてください。 また、入力した時刻が現在時刻と早いとうまくいかないので、この点の修正等も次の記事で書きたいと思います。 もっといい方法がありましたら、ご教示願います。 参考記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む