ぱすたけ日記

日記っぽいのを書きます。

お絵かき体験向上作戦 #kyotojs

プレゼンモード
再生
← / →で移動
fでフルスクリーン
escでおわる


お絵かき体験向上作戦

id:Pasta-K at Kyoto.js 11

こんにちは

  • 京都の大学生です。
  • Notaって会社でGyazoってサービスの開発のアルバイトをしています
  • 趣味としてBrowser Extensionについての観察を行っています
  • ステッカーあります


振り返り

この発表は京都.なんか #2で発表したReactとCanvasでお絵かき機能を作った話の続編です。

今北産業

  • Gyazoの画像編集機能はReactとcanvasを組み合わせて作っている
  • ReactとCanvas組み合わせ事例
  • canvasにはcanvas、DOMにはDOM、ReactComponentにはReactComponentの状態があり、それぞれのライフサイクルがある
  • 個々の図形のマウスイベントハンドリングを上手くやっていく
  • 良い感じに頑張っていく必要がある

前回の教訓より

  • React成分を取り払ったところ、ただ単なるcanvasの入門的な紹介になった
    • 各自の想像でReact成分を補足してください

今回のお話

  • 前回でReactとCanvasを組み合わせて良い感じにお絵かきを実装する大変ポイントと回避方法がわかった(ということになっています)ので、おもてなし感を高める。
    • 前回よりは基本的なcanvasに関する知識やテクニックの紹介です
  • 拡大縮小機能でcanvasリサイズ問題
    • 前回予告にあった『HiDPI対応』はこれの応用でできます。
  • textareaとcanvas同期問題

拡大縮小


canvasリサイズ問題

  • 編集中にcanvasサイズを拡大したい
    • Gyazoの場合だと、下に敷いてる画像サイズを実寸サイズにして、canvasもそれに追随させたい。またはそれの逆など

canvasリサイズ基礎知識

どういうリサイズを行うのか

canvasリサイズ時の意図

  • キャンバスサイズを大きくしたい
    • 先程のデモのようにプロパティを大きくしてやればOK
  • 画像自体を拡大したい
    • 拡大比率に合わせて、座標位置、フォントサイズ、線の太さなども同じ比率で大きくする

描いてる最中で拡大縮小したい

  • ということがよくあるので、初回生成時のcanvasサイズを覚えておいて、描画時にその覚えておいたcanvasサイズと現在のcanvasサイズの比から適切な倍率で描画すると良い感じになります。
    • 各図形ごとにプロパティとして保持しておいて、レンダリング時に各々の適切な倍率を計算する

textareaとcanvas同期問題

  • canvasだと色々文字装飾ができる
    • 縁取りも容易
  • 上にtextareaを重ねる形式だと楽だけど、見た目を同じに表現できないので、逐次canvas上に描画してリアルタイムに見た目を確認できるようにしたい

はい

  • textareaとcanvasで文字を同期させる
  • IME表示とカーソル
  • (おまけ)Emoji? on canvas

textareaとcanvasで文字を同期させる

  • 普通にやっていけばOKです
    • onInputで文字列を取ってcanvasに書く
    • onKeydownでカーソル移動などを取得する
  • textareaとReactについては省略しますので、各自でトライしてください

IME表示とカーソル

IME表示制御

  • font-sizeとline-heightをtextareaとcanvasContextで揃えておく
  • textareaを良い感じの位置で重ねて、opacity:0 とかにするとすんなりうまくいきます

カーソル表示

  • opacity:0にするとカーソルが視えないので、divタグとかで良い感じにカーソルモドキを表示する
  • カーソル位置を取得する
    • カーソル位置を取得する
    • 端から文字を数えていって、何行目の何文字目かを確定させる
    • その場所にカーソル風の何かを表示する

(下のコードは雰囲気で書いてるので、動くことを保証していません)

const textarea = document.querySelector('...')
const currentCaretPosition = input.selectionStart
let textCounter = 0
textarea.value.split('\n').forEach((lineText, lineIndex) => {
  if (textCounter + lineText.length < currentCaretPosition) {
    // 行内にカーソルがないなら文字数をカウントしておいて次の行へ
    textCounter += lineText.length
  } else {
    const canvas = document.createElement('canvas')
    const context = canvas.getContext('2d')
    context.font = `bold ${fontSize}px Arial,Sans-Serif`
    // 行冒頭からカーソルまでのテキストを取り出す
    const text = lineText.substr(currentCaretPosition - textCounter)
    // 文字の縁取り線などを考慮して横幅を確実に取得するために
    // CanvasRenderingContext2D.measureText()を利用する
    const cursorX = context.measureText(text).width
    const cursorY = lineHeight * lineIndex
  }
})

範囲選択を表現

  • 応用したら出来るので、皆さんへの宿題にします

(おまけ)Emoji? on canvas

まとめ

  • 本日はReactのことを一旦忘れてcanvasで文字入力や拡大縮小に関する機能を実装することについて紹介しました。
    • Reactの話を挟まないとシンプルで良いですね
    • ちなみにここにReactが登場すると
      • textareaとReactとcanvas状態対決
        • IME変換中にcanvasには描き込むが、図形情報(state)は更新しない
      • 倍率計算のためのcanvasサイズをそれぞれの図形に伝えるためのpropsバケツリレー大会またはcontextチャレンジ
      • などが勃発しますので盛り上がります
  • 皆さんも便利なお絵かきツールを書いて、体験を高めていきましょう