Canvasのプレビューと書き出し画像がズレる…実際にハマった原因と解決方法

Photo Timelineやモックアップ作成アプリ、Image Crop Studioを開発していて、一番苦労した部分があります。

苦労した点は、「画面で見えているプレビュー」と「保存された画像」が一致しないという問題です。

最初は「Canvasって画像を書くだけでしょ?」くらいに思っていました。

ですが実際に開発してみると、

  • 保存すると位置がズレる
  • サイズが違う
  • 余白が増える
  • 拡大率がおかしい

という問題が次々に発生しました。

今回は、私がCanvasを理解するまでに学んだことをまとめます。

Canvasとは?

Canvas(HTML5 Canvas)は、ブラウザ上で画像や図形、文字などを自由に描画できるHTMLの要素です。

通常のHTMLでは、画像や文字を配置して画面を作りますが、CanvasではJavaScriptを使って一つひとつ描画していきます。

例えば、

  • 写真の編集
  • モックアップ画像の生成
  • SNS投稿画像の作成
  • お絵描きアプリ
  • グラフや図形の表示
  • ゲーム

など、多くのWebアプリで利用されています。

Canvasには次のような特徴があります。

  • JavaScriptで自由に描画できる
  • PNGやJPEGとして画像を書き出せる
  • 図形・画像・文字を組み合わせられる
  • 描画速度が速く、リアルタイム編集にも向いている

今回私が開発したPhoto TimelineImage Crop Studioでも、最終的な画像を生成するためにCanvasを利用しています。

ただ、Canvasは「画像を置く」だけの仕組みではありません。

座標を指定して一枚のキャンバスにすべて描画する」という考え方になっているため、最初は少し戸惑いました。

実際に開発を進める中で、一番苦労したのがプレビュー画面と書き出した画像を一致させることでした。

次は、その原因について紹介します。

なぜプレビューと書き出しが違うの?

一番最初に勘違いしていたのは、

「画面に表示されているもの=保存される画像」

ではないということです。

実はCanvasには

  • ブラウザ上で表示している見た目
  • Canvas内部の描画領域

という2つの世界があります。

例えば

画面表示
1000 × 600

Canvas内部
2000 × 1200

という状態も普通に存在します。

HTMLでCSSだけを使ってCanvasを拡大縮小すると、見た目と実際の描画サイズが変わってしまいます。MDNでも、CanvasはCSSではなくwidthheight属性で描画領域を指定することが推奨されています。

そのため

「画面ではちょうどいい」

と思っていても、

保存すると全然違う位置になることがあります。

Canvasは座標ですべて管理している

CanvasはPhotoshopのようなレイヤーではありません。

基本的には

画像A
x = 100
y = 200

画像B
x = 350
y = 120

というように、

すべて座標で管理しています。

つまり

  • 画像
  • テキスト
  • 図形

全部

drawImage(...)
fillText(...)
fillRect(...)

で描画しているだけです。

「見た目を動かす」のではなく、

座標を書き換えている

という感覚になります。

CSSで位置を動かしても保存画像には反映されない

これもかなりハマりました。

例えば

transform: scale(0.8);

translate(...)

で見た目を調整しても、

Canvasへ保存するときには関係ありません。

保存画像では

Canvasが持っている

x
y
width
height

だけが使われます。

つまり

画面だけCSSで動かすと

プレビューと保存画像がズレます。

私が一番苦労したのは倍率

Photo TimelineやImage Crop Studioでは

プレビュー画面は小さく表示しています。

例えば

保存サイズ

1200px

でも

画面では

400px

くらいで表示しています。

このとき

ドラッグした座標は

画面

の座標になります。

しかし保存時は

1200px

基準になります。

つまり

保存座標

=
画面座標 × 倍率

という計算を毎回行わないといけません。

プレビュー専用の座標と保存用の座標を分けた

最初は

座標は1つ

で管理していました。

しかし途中で限界が来ました。

最終的には

元データ

↓

倍率計算

↓

プレビュー表示

↓

倍率計算

↓

保存

という流れに変更しました。

これによって

  • ドラッグ位置
  • 拡大率
  • 書き出し

が一致するようになります。

Canvasは「描き直す」世界

HTMLでは

<div>

を動かしているイメージですが、

Canvasは違います。

Canvasでは

背景

↓

画像

↓

文字

↓

装飾

毎回全部描き直しています。

そのため

画像を書き出す処理では

もう一度

drawImage()
drawImage()
fillText()

を最初から実行しています。

この考え方を理解すると

Canvasがかなり分かりやすくなりました。

Canvasは怖くない!

正直、Canvasは最初かなり難しかったし嫌厭していました。

「画面の表示」と「Canvas内部の座標」は別物という考え方が大切です。

一度仕組みが分かると画像編集アプリやモックアップ作成ツールなど、Webだけでは実現できない表現を作れるようになります。

私自身もまだ勉強中ですが、今ではWebアプリ開発に欠かせない技術の一つになっています。

タイトルとURLをコピーしました