😀

ブラウザの中でスタンド使いになってみた。

2019/06/27に公開

はじめに

今までは真面目に開発していたが、少しふざけたものを作ってみようと思い、今回のChrome拡張を作ってみました。

それと以前拝見した以下の記事や

カタカタカタッターンを可視化した

やAtomのactivate-power-modeに影響され、今回作るChrome拡張を決めました。

リリースしたChrome拡張概要

textareaやinput上でキータイプをするとエフェクトが表示されます。
先ほど紹介したChrome拡張と同様の仕様ですね。

そこにactivate-power-modeのコンボのような連続したキータイプの際に別のエフェクトを発生させます。

jojo_action.gif

キータイプを500ms以内の間隔で連続30回以上タイプした後にエンターキーを押すとセリフと効果音が表示されます。

タイプするたびにlocalstorageにタイプした時間と連続タイプ数を保存します。

Jojo experience - chromeウェブストア

作り方

ソースは以下のリポジトリにあります。
temori1919/jojo_experience

今回はcontent_scriptsスクリプトという形式で作成します。

{
  "manifest_version": 2,
  "name": "Jojo experience",
  "version": "1.0.0",
  "description": "スタンド使いになれるChrome Extensionです",
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": [
      	"jquery.min.js",
      	"soundEffect.js",
      	"caretposition.js"
      ]
    }
  ],
  "web_accessible_resources": [
    "images/*"
  ],
  "permissions": [
    "storage"
  ]
}

キャレット位置の取得にcaretposition.jsというjsライブラリを使用させていただきました。

[jQuery対応] textareaのキャレット座標(XY座標)を取得するjavascriptライブラリを作った

document.onkeydown = e => {

  const current = document.activeElement

  if (e.key === 'Backspace') {
    return true
  }
  if (current.type === 'textarea' || current.type === 'text' || current.type === 'search') {
    (async () => {
      // タイピング時間の記録
      let type_time = localStorage.type_time
      // タイピング数のカウント
      let type_count = localStorage.type_count

      // タイピングの感覚が500ミリ秒以下ならタイピング数をカウントする
      if (type_time !== undefined && type_count !== undefined && (new Date().getTime() - type_time) < 500) {
        localStorage.type_count = parseInt(type_count) + 1
      } else {
        localStorage.type_count = 1
      }

      localStorage.type_time = new Date().getTime()

      const isEnter = e.key === 'Enter'
      // キャレットの位置の取得
      const caretPosition = Measurement.caretPos(current)

      let prefix = isEnter ? 'ora2' : 'ora'
      let imgUrl = chrome.extension.getURL('images/' + prefix + '.png')

      // オラ
      let image = await appnedCss('images/' + prefix + '.png', isEnter ? rand(80, 100) : rand(10, 20), caretPosition.top + rand(-10, 10), caretPosition.left + rand(-10, 10))
      image = await animateRemove(image, isEnter, caretPosition)

      // タイプカウントの回数が30以上でエンターをタイプしたらセリフと効果音を表示
      if (isEnter && type_count > 30) {
        localStorage.type_count = 1
        // 時
        image = await appnedCss('images/toki.png', rand(80, 100), caretPosition.top, caretPosition.left, 400)
        image = await animateRemove(image, isEnter, caretPosition, 200)

        // エフェクト
        image = await appnedCss('images/effect' + rand(1, 4) + '.png', rand(80, 100), caretPosition.top, caretPosition.left, 600)
        await animateRemove(image, isEnter, caretPosition)
      }
    })()
  }
}

/**
 * 乱数生成
 *
 * @param min
 * @param max
 * @returns {number}
 */
function rand(min, max) {
  return Math.floor(Math.random() * (max - min) + min)
}

/**
 * imgをappend
 *
 * @param imgUrl
 * @param size
 * @param top
 * @param left
 * @param delay
 * @returns {Promise<any>}
 */
function appnedCss(imgUrl, size, top, left, delay) {
  return new Promise(resolve => {
    delay = delay === undefined ? 0: delay
    setTimeout(() => {
      imgUrl = chrome.extension.getURL(imgUrl)
      img = $(`<img width="${size}">`)
      img.attr('src', imgUrl)
      img.css({
        'position': 'absolute',
        'top': top,
        'left': left,
        'zIndex': 100000
      })
      $('body').append(img)
      resolve(img)
    }, delay)
  })
}

/**
 * imgのアニメーションと削除
 *
 * @param img
 * @param isEnter
 * @param positon
 * @param delay
 * @returns {Promise<any>}
 */
function animateRemove(img, isEnter, positon, delay) {
  return new Promise(resolve => {
    delay = delay === undefined ? 0: delay
    setTimeout(() => {
      const size = isEnter ? rand(80, 100) : rand(10, 20)
      img.animate({
        'top': positon.top + rand(-40, 40),
        'left': positon.left + rand(-40, 40),
        'width': size + (isEnter ? rand(30, 50) : rand(10, 20)),
        'opacity': 0
      }, 1000, () => {
        img.remove()
      })
      resolve(null)
    }, delay)
  })
}

本体はES2017で書いています。
コメントは適当なのでご容赦下さいw

キータイプするとタイプした時間とキータイプ回数がカウントUPされます。
前回時間とタイプした時間が500ms以内ならカウントUP、それ以外なら1を保存します。

それとimgを追加してアニメーションで表示しています。
また、エンターが押された後の処理はasync awaitで直列的に処理させるようにしています。

最後に

ちょっとふざけたものを作りたくて、今回の拡張をリリースしました。
正直邪魔にしかならない拡張機能なので、お試しで入れた場合は少し使って削除するのがいいと思いますww

興味がある方は追加してみて下さい。

Jojo experience - chromeウェブストア

Discussion