CSS調整

2018年9月5日

html2canvasでgoogleのfeedbackみたいなの

html2canvasというライブラリが面白そうだったので触ってみました。

html2canvasとは?


htmlの要素を解析してcanvas化するライブラリです。

html2canvas - Screenshots with JavaScript

例えばとあるdivにcssでborderが指定されていて、子要素にtextNodeがあったならば、
divと同じ大きさのblockを作って、strokeでborder引いて、textで文字を書いて、同じ見た目のcanvas要素化してくれる、というかんじです。

これを実際にやろうと考えた場合、

タグは見た目にさして関係無いからいいとしても、全cssを変換する処理を用意するなんて、現実的でなくね?

とかなると思いますが、そのものズバリ、html2canvasのgithubを見てもらうとわかりますが、cssごとの変換処理が書かれていたりします。とんでもないですね。

html2canvasを使ってみる


何も考えずに使う場合、使い方はシンプルです。

<script type="text/javascript" src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>

<script type="text/javascript">
    var content = document.getElementById("content");
    html2canvas(content).then(canvas => {
        ...
    });
</script>

これだけで渡した要素がcanvas化されたものが返ってきます。

せっかくなので、Googleのフィードバック送信時の処理
(表示中の画面にマーキングしてお問い合せする機構
ここの「フィードバックを送信」でできるやつ)

みたいなのをやってみます。

ui


適当にベタhtmlで

・画面キャプチャするボタン
・画面キャプチャしたものを入れる要素
・マークした結果をキャプチャするボタン
・マークした結果を表示する要素

を置きます。

<button id="execButton" class="exec-btn" onclick="h2c();"><img src="camera.svg"/></button>
<div id="screenshot" class="screenshot" style="display:none;"></div>
<button id="okButton" class="ok-btn" onclick="c2i();" style="display:none;"><img src="done.svg"/></button>
<div id="resultBg" class="result-bg" onclick="iClose();" style="display:none;"><img id="result" class="result"/></div>


画面をキャプチャする処理


画面をhtml2canvasにお願いしてcanvas化してもらいます。
要素の表示/非表示の切り替えに手抜き感が伺えますが、気にしない。

function h2c() {
    var content = document.getElementById("content");
    html2canvas(content).then(canvas => {
        var ss = document.getElementById("screenshot");
        ss.appendChild(canvas);
        initSelector(canvas);
        
        ss.style.display = "";
        content.style.display = "none";
        document.getElementById("execButton").style.display = "none";
        document.getElementById("okButton").style.display = "";
    });
}


canvasにマークする処理


所謂、自由選択ツールのようなことをします。
イベントの貼り方に手抜き感が伺えますが、良い子はマネしないでね!

function initSelector(screenshot){
    var selector = document.createElement("canvas");
    with (selector) {
        width = screenshot.width;
        height = screenshot.height;
        className = "selector";
        style.zIndex = (screenshot.style.zIndex || 0) + 1;
    }
    
    buildSelector(selector).selectEnd = function(r) {
        with (screenshot.getContext("2d")) {
            fillStyle = "#ffeb3ba1";
            fillRect(r.sx, r.sy, r.ex, r.ey);
        }
    }
    
    screenshot.parentElement.appendChild(selector);
}
function buildSelector(canvas) {
    var context = canvas.getContext("2d");
    with (context) {
        setLineDash([6, 3]);
        strokeStyle = "#3e3e3ecf";
    }
    
    context.rect = {
        sx: 0, sy: 0, ex: 0, ey: 0, dirty: false
    };
    context.selectStarted = false;
    context.clear = function(dirty) {
        this.rect.dirty = dirty;
        this.clearRect(0, 0, this.canvas.clientWidth, this.canvas.clientHeight);
    }
    context.select = function() {
        this.clear(true);
        this.strokeRect(this.rect.sx, this.rect.sy, this.rect.ex, this.rect.ey);
    }
    context.selectEnd = null;

    canvas.addEventListener("mousedown", function (e) {
        context.rect.sx = e.pageX;
        context.rect.sy = e.pageY;
        context.selectStarted = true;
    }, false);
    canvas.addEventListener("mousemove", function (e) {
        if (!context.selectStarted) return;
        context.rect.ex = e.pageX - context.rect.sx;
        context.rect.ey = e.pageY - context.rect.sy;
        context.select();
    }, false);
    canvas.addEventListener("mouseup", function onMouseUp (e) {
        if (context.rect.dirty) {
            context.clear(false);
            if (context.selectEnd) context.selectEnd(context.rect);
        }
        context.selectStarted = false;
    }, false);
    
    return context;
}


マークした結果をキャプチャする処理


canvasをインラインイメージ化してimgタグにぶち込みます。
それ以外でやってることは例によって手抜きですね。

ついでに、プレビューを閉じて最初に戻る処理も書いておきます。

function c2i() {
    var ss = document.getElementById("screenshot");
    var canvas = ss.firstChild;
    var selector = ss.lastChild;
    document.getElementById("result").src = canvas.toDataURL();
    
    ss.removeChild(canvas);
    ss.removeChild(selector);
    ss.style.display = "none";
    document.getElementById("okButton").style.display = "none";
    document.getElementById("resultBg").style.display = "";
}
function iClose() {
    document.getElementById("resultBg").style.display = "none";
    document.getElementById("execButton").style.display = "";
    document.getElementById("content").style.display = "";
}


当たり前といえば当たり前ですが、画面のキャプチャなので、それっぽい画面がなければ意味がない・・・
でも、出来上がっている画面を用意するのって地味に面倒なんですよね。。。というわけで、github pageのデフォルトを利用します。

で、出来上がったのが以下。

Welcome to GitHub Pages

完全版のソースはこれ

なんとも簡単にそれっぽくなりました。

使い所は限られるとは思いますが、アイデア次第では色々なことができそうです。

いじょ