絶賛運営中の「THE TOURNAMENT」ではトーナメント表をjsで出力してて、これはこれでインタラクティブな感じにできたりといいんですが、やはり画像で保存したいというニーズも根強い。
そこで画面のこの部分だけを画像としてダウンロードできる機能をつけてみた。
よくある機能な気がしてたし余裕かと思ったけど意外とめんどくさかったです。。
基本方針
まずは今回の要件を完璧に満たしてくれる都合のいいgemでもないかなーと探してみたけど、たぶんない。
(もしあったら教えてください)
ただ部分部分をやってくれそうなパーツは見つけたので、それを組み合わせて今回の機能を実現します。
具体的には、
- html2canvas.jsでDOM要素を指定してcanvasに変換
- HTML5のtoDataURLでcanvasをDataURLに変換
- HTML5のdownload属性にDataURLを指定してダウンロードボタンを有効化
という感じでやりました。
ちょっとめんどくさいけど順番に見ていきます。
html2canvas.jsでDOM要素を指定してcanvasに変換
DOM要素をいきなり画像にしてくれるのはなかったので、canvasに変換してくれるのでがまん。
でも簡単につかえてかなり便利。
まずは公式サイトからjsファイルをダウンロードして、railsのassets/javascriptsに配置。
application.jsから読み込んでおきます。これでとりあえず準備オッケー。
対象の要素をcanvasに変換するのは、
html2canvas($(".hoge"), {
onrendered: (canvas) ->
// xxxxx
})
という感じ。$('.hoge')のとこはキャプチャしたい要素のに置き換えてくださいませ。
xxxxxのところがcanvasに変換したあとの処理になるので次にここを見てみます。
どうでもいいけど大体ドキュメントは素のjsなので、coffee script使ってるとどう書くのかいつも悩む。。
toDataURLでcanvasからDataURLに変換
お次はこのcanvasをDataURLに変換します。
プログラミングで飯を食え。腕をあげたきゃ備忘録!「そもそもDataURLとは!?~canvasを画像に変換する方法(toDataURL())!①」
そもそもDataURLって何やっけってところから復習しつつ変換。
さっきの処理に追加して、
html2canvas($(".hoge"), {
onrendered: (canvas) ->
canvasImage = canvas.toDataURL("image/jpeg")
})
これだけ。
拡張子は"image/jpeg"と"image/png"が選べるみたいですな。
HTML5.jp「toDataURL()メソッド」
さぁ最後はこのデータをダウンロードできるようにするだけです。
HTML5のdownload属性つかってダウンロードリンクをつくる
そういうえばダウンロードリンクって作ったことがなくて、どうやるんかなーと調べたら
単純にリンク先がブラウザで解釈できん形式やとダウンロードするって感じなんですな。
そしてこれやと画像の場合ブラウザが解釈できちゃうので画面左上に画像だけ表示される、
みたいなよくあるかっこわるい感じになってしまう。別にいいんですけどね。
もうちょっと調べてみたらHTML5でdownload属性ってのができて、簡単にダウンロードリンクを実現できるみたいでした。
WORKABROAD「HTML5を使ってファイルを表示させずにダウンロードさせる方法」
いいですね。
というわけで、
/ views
%a.hoge(href="#" download="hogehgoe") ダウンロードリンクだよ
という感じの空リンクを用意しといて、DataURLが用意できたらhrefにセットします。
# javascripts
html2canvas($(".hoge"), {
onrendered: (canvas) ->
canvasImage = canvas.toDataURL("image/jpeg")
$(".hoge").attr('href', canvasImage)
})
これでリンククリックすると変換された画像がダウンロードされました!
しかし、せっかくdownload属性でファイル名指定してるのに全然効かず、download.jpegになってしまう。
hrefにDataURL指定せんかったらちゃんと反映されるから、これが悪さしてんのか。。
しばらくがんばったけど情報も少なくて解決せず。
ちょっと気持ち悪いけど我慢します。。
Bootstrapでちょっといい感じに
最後にBootstrapのデフォルトComponentをつかってダウンロードリンクの表示をいい感じにしてみます。
やりたいことは、ダウンロードの準備ができるまではリンクボタンをLoading..表示にして、
準備ができたらクリック可能状態にするという感じ。至れり尽くせりですな。
ソースはこんなので。
/ views
%a.hoge.btn.btn-sm.btn-default(href="#" download="hogehoge" data-loading-text="画像ダウンロードの準備をしています...")
画像ダウンロード
# javascripts
$(".hoge").button('loading') #=> loading状態にしとく
html2canvas($(".hoge"), {
onrendered: (canvas) ->
canvasImage = canvas.toDataURL("image/jpeg")
$(".hoge").attr('href', canvasImage)
$(".hoge").button('reset') #=> 完了したらクリック可能状態に戻す
})
なかなかいい感じに仕上がりました。
おしまい。