1. ホーム
  2. Web制作
  3. HTML/Xhtml

htmlでキャンバスベースのスクリーンショットを行う小さなデモ

2022-01-08 18:17:19

上部に書かれている

以前、みんなのシェアでjsを使ったスクリーンショットソリューションの説明を見た記憶があるのですが、詳細は覚えていませんが、canvasを使っているようで結構面白かった記憶があるので、今回は作者の思いを共有するために自分で書いてみる予定です。これはごく簡単な小さなデモですが、もしバグがあればissueに言及してください。 コードアドレス

レンダリング

全体的な感想

  • 開始/終了のショートカットを設定
  • 起動後にDOMをcanvasに描画し、元のDOMインターフェースを上書きする
  • マウスのスクリーンショット領域をシミュレートするためのキャンバスを追加する
  • マウスのスクリーンショット領域に対応するブラウザのインターフェイスを描くためのキャンバスを追加します(最初のキャンバスから取得)。
  • インターセプトした画像を保存する

1. 開始/終了ショートカットキーの設定

最初のパラメータは配列として渡されます。これは、ショートカットによって引き起こされる潜在的な競合のために、開始ショートカットの数は無制限であることが望ましいからです。

function screenShot(quickStartKey, EndKey) {
  // Compatibility considerations do not use... Extended String
  var keyLength = quickStartKey.length
  var isKeyTrigger = {}
  var cantStartShot = false
  ...
  quickStartKey.forEach(function(item) { // iterate through the array of parameters
    isKeyTrigger[item] = false // by default all keys in the array are not triggered
  })
  $('html').on('keyup', function(e) {
    var keyCode = e.which
    if(keyCode === EndKey) {
      ...
    } else if(!cantStartShot) {
      isKeyTrigger[keyCode] = true
      var notTrigger = Object.keys(isKeyTrigger).filter(function(item) {
        return isKeyTrigger[item] === false // check if there is a shortcut key that needs to be triggered
      })
      if(notTrigger.length === 0) { //no shortcut key to be triggered that can start the screenshot
        cantStartShot = true
        beginShot(cantStartShot)
      }
    }
  })


2. DOMをキャンバスとして描画し、本来のDOMインターフェースをオーバーライドする

ネイティブなアプローチを使う場合は MDN で、Canvas での DOM の描画について紹介しています。厄介なのは、要素 <foreignObject> を含む XML を含む SVG 画像を作成する必要がある点です。現在のブラウザが表示しているDOMをどうやって計算して取り出すかが、実は一番面倒なのです。まあ実際、筆者は手動で実装する名案を持っていないので =. =、これが選ばれました。 html2canvas というライブラリがあります。大まかには以下のように呼ばれます。

function beginShot(cantStartShot) {
    if(cantStartShot) {
        html2canvas(document.body, {
            onrendered: function(canvas) {
                // get a canvas image that matches the interface
            }
        })
    }
}

3. マウスのスクリーンショット領域をシミュレートするためのキャンバスを追加する

この領域の実装は、当初ネイティブの canvas API を使用する予定でしたが、マウスを押してドラッグした後にリアルタイムで canvas を描画する必要があるため、PS レイヤーと同様のコンセプトで、マウス移動のたびに現在のスクリーンショット ボックスを描画し、次のマウス移動が発生すると前のスクリーンショット ボックスを削除するという問題が発生しました。これは、リアルタイムの描画処理をシミュレートするためです。作者はcanvasネイティブAPIを使う方法を見つけられませんでしたが、もしそうなら、描画された画像に印をつける方法を教えてください。ここでは、著者はJqのcanvasをベースにしたライブラリである Jcanvas これは、レイヤーの概念、すなわち、1つのレイヤーに1つだけ描画することと、レイヤーに名前を付けてタグ付けする機能を提供します。これは著者のニーズを満たすもので、次のように実装されている。

$('#' + canvasId).mousedown(function(e) {
    $("#"+canvasId).removeLayer(layerName) // remove the previous layer
    layerName += 1
    startX = that._calculateXY(e).x //calculate mouse position
    startY = that._calculateXY(e).y
    isShot = true
    $("#"+canvasId).addLayer({
        type: 'rectangle', //rectangle
        ...
        name:layerName, //the name of the layer
        x: startX,
        y: startY,
        width: 1,
        height: 1
    })
}).mousemove(function(e) {
    if(isShot) {
        $("#"+canvasId).removeLayer(layerName)
        var moveX = that._calculateXY(e).x
        var moveY = that._calculateXY(e).y
        var width = moveX - startX
        var height = moveY - startY
        $("#"+canvasId).addLayer({
            type: 'rectangle',
            ...
            name:layerName,
            fromCenter: false,
            x: startX,
            y: startY,
            width: width,
            height: height
        })
        $("#"+canvasId).drawLayers(); //draw
    }
    })


4. マウスのスクリーンショット領域に対応するブラウザインターフェースを描画するためのキャンバスを追加する

var canvasResult = document.getElementById('canvasResult')
              var ctx = canvasResult.getContext("2d");
              ctx.drawImage(copyDomCanvas, moveX - startX > 0 ? startX : moveX, moveY - startY > 0 ? startY : moveY, width, height, 0, 0, width, height )
              var dataURL = canvasResult.toDataURL("image/png");

ここで、画像は drawImage によってインターセプトされ、toDataURL メソッドで base64 に変換されます。

5. インターセプトした画像の保存

function downloadFile(el, fileName, href){
      el.attr({
        'download':fileName,
        'href': href
      })
  }
  ...
downloadFile($('.ok'), 'screenShot' + Math.random().toString().split('.') [1] || Math.random() + '.png', dataURL)
// Pass in the key object, the image to save the random name, and the base64-encoded image

を使用します。 ダウンロードのaタグ 属性があり、これをクリックすると直接ダウンロードできるようになっています。

デプロイメント

依存関係

<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/jcanvas/16.7.3/jcanvas.min.js"></script>
<script src="https://cdn.bootcss.com/html2canvas/0.5.0-beta4/html2canvas.min.js"></script>


設定用ショートカット

screenShot([16, 65], 27) // start shortcut is set to shift+a; exit key is ESC

最後に

本文中の一番厄介な部分(DOMからcanvasへの書き込み、canvasのレイヤー設定)はそれぞれ2つのライブラリを使って実装されており、筆者はこれらの操作をネイティブAPIを使って実装する方法を中心にフォローしていきますが、個人的には自分で書くのはまだちょっと難しいかな、と思っています。

以上が今回の内容の全てです。勉強になると思いますし、スクリプトハウスをもっと応援して頂ければと思います。