JavaScript - Konva.js - 位置ずれしないようにする


クラウディア 


1. 概要
2. 描画
3. 保存
4. 参考サイト

1. 概要

 さて、前ページまで書いたものを操作する際、解像度の違うモニタ上で操作したり、「Windows」以外のクライアント、例えば、「iPad」で表示すると、背景画像と描画している図形の相対位置が、ずれたりします。  「node server.js」上で展開して、「.pdf」化する際(「JavaScript - express + bodyParser + puppeteer」参照)も同様に位置ずれを起こします。  本ページでは、そういう位置ずれを起こさないようにするには、どうしたらいいか、ということについて記述します。  ただし、うまく説明できていない恐れがありますので、ご注意ください。

2. 描画

 ここも説明するのが、難しい。  スケーリングとオフセットを定義する変数を用意します。

let bgScale = 1;
let bgOffsetX = 0;
let bgOffsetY = 0;
 背景画像を描画する際に、スケーリングとオフセットの変数に値を設定します。  (説明のため、便宜的に行番号をいれています)

const imageObj = new Image();
imageObj.src = "/path/背景画像.png";
imageObj.onload = () => {
	const stage = this.stage;
	const layer = this.layer;

	// 論理サイズ(そのまま)
	const stageWidth = stage.width();
	const stageHeight = stage.height();

	// 背景画像スケーリング
	const imgWidth = imageObj.width;
	const imgHeight = imageObj.height;
	const scaleX = stageWidth / imgWidth;
	const scaleY = stageHeight / imgHeight;
	const scale = Math.min(scaleX, scaleY);

	const background = new Konva.Image({
		image: imageObj,

		// 中央に配置
		width:  imgWidth  * scale,
		height: imgHeight * scale,
		x: (stageWidth -  imgWidth  * scale) / 2,
		y: (stageHeight - imgHeight * scale) / 2,
		listening: false,
	});

	this.layer.add(background);
	background.setZIndex(0);
	this.layer.batchDraw();

	// **ピクセル比は 1 に固定**
	layer.getCanvas().setPixelRatio(1);

	// ここで スケーリングとオフセットをとっている
	// これを保存時に かけて 取得時に 逆変換しないと
	// Windows と iOS で座標位置が 異なってしまう
	bgScale = Math.min(stageWidth / imgWidth, stageHeight / imgHeight);
	bgOffsetX = (stageWidth  - imgWidth  * bgScale) / 2;
	bgOffsetY = (stageHeight - imgHeight * bgScale) / 2;
	resolve();
};
 7~27行で、描画領域(stage)に合わせて、背景画像をスケーリングしています。  描画領域の縦横比と背景画像の縦横比を比較して、小さいほうの拡大/縮小率で、背景画像が描画領域にすっぽり収まるようにスケーリングしています。  36~41行で、描画領域と背景画像のスケールに合わせて、座標の縦横オフセットに設定する値を取得しています。  実際に描画する際は。  円であれば。

// 逆スケーリングして 展開
const group1 = new Konva.Group({
  x: bgOffsetX + item.circleX * bgScale,
  y: bgOffsetY + item.circleY * bgScale,
  draggable: this.is_ssdeploy_edit
});
const circle = new Konva.Circle({
  radius: 10,
  fill: '#D60000',
});
 てな感じで、中心座標について、オフセットをプラスして座標値をスケーリングします。  矩形であれば。

const group2 = new Konva.Group({
  x: bgOffsetX + item.rectX * bgScale,
  y: bgOffsetY + item.rectY * bgScale,
  listening: this.is_ssdeploy_edit,
});
const rect = new Konva.Rect({
  width: 100, height: 50,
  fill: '#cceeff', stroke: '#333',
  cornerRadius: 10,
});
 てな感じで、左上座標について、オフセットをプラスして座標値をスケーリングします。

3. 保存

 上記で描画したものを保存する際。

const layout = this.groups.map(group => {
  const circle = group.findOne(node => node.className === 'Circle');
  const rect   = group.findOne(node => node.className === 'Rect');
  const circlePos = circle.getAbsolutePosition();
  const rectPos   = rect.getAbsolutePosition();
  // スケーリングして保存
  return {
    id: text.text(),
    circleX: (circlePos.x - bgOffsetX) / bgScale,
    circleY: (circlePos.y - bgOffsetY) / bgScale,
    rectX: (rectPos.x - bgOffsetX) / bgScale,
    rectY: (rectPos.y - bgOffsetY) / bgScale,
  };
});
try {
  const response = await axios.post('/regist_deploy', {
    pouring_id: this.pouring_id,
    item_json: JSON.stringify(layout)
  });
  window.location.href = '/home';
} catch(err) {
  this.errorSnackbar = true;
  this.errorMessage = `登録に失敗しました。`;
}
 と、描画の際と逆に変換して、いわゆる絶対座標的な値にして保存します。  こうすることで、この保存した座標値を描画の際にまたスケーリング・オフセット変換することで、別のプラットフォームで保存した内容をほぼ再現することができるようになります。

4. 参考サイト

 本ページは、「ChatGPT」くんを参考にさせていただきました。

AbemaTV 無料体験