野球リーグスコア管理システムの開発

2019-01-13に作成

image
野球リーグスコア管理システムキャップ野球情報局に関する進捗です。

使っている技術など

  • NodeJS
  • ReactJS
  • netlify
  • MySQL
  • materializecss
  • react-bootstrap
  • react-bootstrap-table-next

旧システムについてはこちらの記事をご覧ください。

残りタスクリスト

trello

所有者限定モードのためこのボードには投稿できません ボードとは?

蓋速計測器をリリースしました

蓋速計測器をリリースしました

EgULFyPVoAEFJi6.jpeg

基本的に動画から蓋速を算出する場合はコマ送りできるソフト(aviutlなど)を使ってフレーム数から到達時間を計算しているのですが、aviutlはwindows専用なので、誰でも同じように簡単に速度を算出できる仕組みがあれば便利だと思い、reactから動画を扱えるコンポーネントを使って、コマ送り機能と、リリースの瞬間とキャッチの瞬間の時間を記録すれば誰でも速度を計算できる機能を実装しました。

実装

video-reactはtypescriptの型がないようだったので少し面倒でした。

9.22*3.6/(cTime-rTime)
9.22mを(「キャッチした時間」-「リリースした時間」)の差で割って3.6(秒速から時速への変換)をかけます。

import React, { createRef, useEffect, useState, useMemo } from "react";
import { Player, ControlBar, ForwardControl } from "video-react";

const calcSpeed = () => {
  const playerRef = createRef();
  const [fileURL, setFileURL] = useState(null);
  const [rTime, setRTime] = useState(null);
  const [cTime, setCTime] = useState(null);

  return (
    <>
      <Player
        fluid={false}
        height={400}
        width={'95vw'}
        ref={playerRef}
        autoPlay
        src={fileURL}
      ></Player>
      <input
        onChange={(e) => {
          if (e.target.files.length > 0) {
            const url = URL.createObjectURL(e.target.files[0]);
            setFileURL(url);
          }
        }}
        type="file"
        multiple={false}
        accept=".mp4"
      />
      <button
        onClick={() => {
          const player = playerRef.current.getState();
          playerRef.current.seek(player.player.currentTime + 0.03);
          console.log(player.player.currentTime);
        }}
      >
        +0.03s
      </button>
      <button
        onClick={() => {
          const player = playerRef.current.getState();
          playerRef.current.seek(player.player.currentTime - 0.03);
          console.log(player.player.currentTime);
        }}
      >
        -0.03s
      </button>
      <button
        onClick={()=>{
          const player = playerRef.current.getState();
          setRTime(player.player.currentTime);
        }}
      >リリース</button>
      <button
        onClick={()=>{
          const player = playerRef.current.getState();
          setCTime(player.player.currentTime);
        }}
      >キャッチ</button>
      <br/>リリース:{rTime}
      <br/>キャッチ:{cTime}
      <br/>蓋速:{cTime && rTime && (cTime - rTime > 0) ? (9.22*3.6/(cTime-rTime)).toFixed(2)+'km/h':''}
    </>
  );
};

export default calcSpeed;

情報局のチームページレイアウトの刷新

CSS

チームページのレイアウトを新しくしました

各チームのトップページに新たに大きな写真を採用し、チーム名の縦置きレイアウトを廃止しました。テーマカラーの表示は維持しつつ、横置きに変更しました。
レスポンシブで最大横幅を1000pxにし、基本2カラム、スマホは1カラムにしています。
チーム紹介とメンバー紹介はだいぶteamsの影響を受けました。

チーム紹介

メンバー紹介

自分で入力した自己紹介の一部を表示するようにしました。

戦績

試合結果一覧のデザインを取り込みました。
若干デザイン崩れなので再考の余地ありというところです。

選手成績

元々打撃・投手成績タブに分けていたものを統合しました。

OGPサーバをvercelに移設した

ソース

前回、puppeteer-clusterで同時起動の制御をしたはずだったのだが、
書き方が悪いのか同時アクセスすると暴走してVPSの応答がなくなってしまうので、vercelに分離した。

vercelとは

vercel

herokuと同じようにサーバサイドのプログラムをデプロイできるサービス。
netlifyはフロントエンド特化。

OGPサーバの用途

ルーティングに苦戦

vercelはnow.jsonでルーティングを記述している。
正規表現で各メソッドにreq.query.idを渡す場合の記述の仕方はこんな感じ。

 "routes": [
        { "src": "/cap-baseball/(?<id>[^/]*)", "dest": "/screenshotCap.js?id=$1" },
        { "src": "/(?<id>[^/]*)", "dest": "/screenshot.js?id=$1" }
    ]

puppeteerでスクリーンショット

このあたりは前回の記事で説明しているので不要かなと思う。
cluster使わない書き方に戻してます。

const browser = await puppeteer.launch({
    args: chrome.args,
    executablePath: await chrome.executablePath,
    headless: chrome.headless,
  });
  const page = await browser.newPage();
  await page.goto("https://pawapro-gen.netlify.app/view/" + id);
  await page.setCacheEnabled(false);
  await page.waitForSelector("#main");
  const fullPage = await page.$("#main");
  const fullPageSize = await fullPage.boundingBox();
  const VIEWPORT = { width: 600, height: 450, deviceScaleFactor: 1 };
  await page.setViewport(
    Object.assign({}, VIEWPORT, { height: fullPageSize.height })
  );
  await page.waitFor(5000);

  const elements = await page.$$("#main");
  let img;
  for (const [index, element] of elements.entries()) {
    img = await element.screenshot();
  }
  await page.close();
  await console.log("screenshot done.");
  return img;