Matter.jsを使ってテキストを物理演算させる
Matter.jsを使ってテキストを物理演算させる:
四角や丸などの図形を演算処理するサンプルはいくつかライブラリがありましたが、テキストを使ったサンプルが見つからなかったため、リサーチして備忘録として投稿します。ライブラリの選定ですが、物理演算のライブラリはいくつかありましたが、軽量かつスマートフォンにも対応しているMatter.jsを使っています。
Matter.jsは図形表示用にデフォルトのレンダーを使っています
。このレンダーは
通常のRenderは以下の感じに使います。
このRenderの代わりに自前で描画する。
Bodyを作る際に、text情報をoptionとして登録しておき、Custom Renderを使ってCanvas上にレンダリングする際にBody内のtext情報を使ってテキスト表示を行います。
これで物理演算されたテキスト表示が可能だが、この状態だとテキスト表示のコンテナだけが物理演算された表示に
なってしまい、テキスト自体は回転しません。そこで、Bodyを描画するときの頂点情報を元に、Bodyの傾きを算出して、テキストを回転させる。2点の座標がわかれば
物理演算をさせる対象を選択し、演算に回転などの制約を加えることができます。回転の制約を加える場合は、物理演算される前に呼ばれるイベントを登録し、コールバック内で回転速度やConstraintなど設定・制御を行うと良いようです。
デモはGitHubに置いてあります。
四角や丸などの図形を演算処理するサンプルはいくつかライブラリがありましたが、テキストを使ったサンプルが見つからなかったため、リサーチして備忘録として投稿します。ライブラリの選定ですが、物理演算のライブラリはいくつかありましたが、軽量かつスマートフォンにも対応しているMatter.jsを使っています。
Custom Render
Matter.jsは図形表示用にデフォルトのレンダーを使っています。このレンダーは
World
に物理演算対象のBodyオブジェクトを追加すると、あとは自動的にBodyを演算処理に沿って表示してくれます。ただし、このデフォルトのレンダーはテキスト表示には対応していません。代わりにCustom Renderを使うように指示されています。Custom Renderとは、演算処理されたBody情報を使ってCanvas内に自前でBodyを描画する方法のようです。通常のRenderは以下の感じに使います。
通常のレンダー
const Engine = Matter.Engine const Render = Matter.Render const engine = Engine.create() const render = Render.create({ element: document.getElementById('app'), engine: engine, options: { wireframes: false, width: 300, height: 400, background: 'rgba(255, 0, 0, 0.5)' } }) Render.run(render)
Matter.Composite.allBodies(this.engine.world)
で取得するbodies
には物理計算された結果 Body情報(座標情報など)が入っています。CustomRender
this.render() render () { // NOTE: Worldに追加した全てのBody要素を取得 const bodies = Matter.Composite.allBodies(this.engine.world) const canvas = document.getElementById('canvas') const context = canvas.getContext('2d') window.requestAnimationFrame(this.render) // NOTE: 一つのBody要素に入っている点座標(rectangleであれば4頂点情報)をつなげて // 四角を描画する for (let i = 0; i < bodies.length; i += 1) { const part = bodies[i] const vertices = bodies[i].vertices context.moveTo(vertices[0].x, vertices[0].y) for (var j = 1; j < vertices.length; j += 1) { context.lineTo(vertices[j].x, vertices[j].y) } context.lineTo(vertices[0].x, vertices[0].y) } context.lineWidth = 1.5 context.strokeStyle = '#000000' context.stroke() } }
テキスト表示
Bodyを作る際に、text情報をoptionとして登録しておき、Custom Renderを使ってCanvas上にレンダリングする際にBody内のtext情報を使ってテキスト表示を行います。テキスト付きのBodyを作成
const Bodies = Matter.Bodies const World = Matter.World const x = Math.random() * screen.width * 2 const y = 0 const wordBody = Bodies.rectangle( x, y, 200, 100, { restitution: 0.95, friction: 0, render: { fillStyle: '#FFFFFF', text: { fillStyle: '#000000', content: content, size: 50 } } }) World.add(this.engine.world, wordBody)
CustomRender内でテキストを描画
render () { var bodies = Matter.Composite.allBodies(this.engine.world) var canvas = document.getElementById('canvas') var context = canvas.getContext('2d') window.requestAnimationFrame(this.render) context.fillStyle = '#FFFFFF' context.fillRect(0, 0, canvas.width, canvas.height) context.globalAlpha = 1 context.beginPath() for (var i = 0; i < bodies.length; i += 1) { var part = bodies[i] if (part.render.text) { var fontsize = 30 var fontfamily = part.render.text.family || 'Arial' var color = part.render.text.color || '#FF0000' if (part.render.text.size) { fontsize = part.render.text.size } else if (part.circleRadius) { fontsize = part.circleRadius / 2 } var content = '' if (typeof part.render.text === 'string') { content = part.render.text } else if (part.render.text.content) { content = part.render.text.content } context.fillStyle = 'black' context.save() context.translate(part.position.x, part.position.y) context.textBaseline = 'middle' context.textAlign = 'center' context.fillStyle = color context.font = fontsize + 'px ' + fontfamily context.fillText(content, 0, 0) context.restore() context.fillStyle = 'blue' context.fillRect(part.position.x, part.position.y, 10, 10) } var vertices = bodies[i].vertices context.moveTo(vertices[0].x, vertices[0].y) for (var j = 1; j < vertices.length; j += 1) { context.lineTo(vertices[j].x, vertices[j].y) } context.lineTo(vertices[0].x, vertices[0].y) } context.lineWidth = 1.5 context.strokeStyle = '#000000' context.stroke() }
これで物理演算されたテキスト表示が可能だが、この状態だとテキスト表示のコンテナだけが物理演算された表示に
なってしまい、テキスト自体は回転しません。そこで、Bodyを描画するときの頂点情報を元に、Bodyの傾きを算出して、テキストを回転させる。2点の座標がわかれば
ata2
を使って角度を算出できるCustomRenderに追加
context.save() context.translate(part.position.x, part.position.y) // NOTE: テキストを回転させる const x = bodies[i].vertices[1].x - bodies[i].vertices[0].x const y = bodies[i].vertices[1].y - bodies[i].vertices[0].y const radian = Math.atan2(y, x) context.rotate(radian)
回転をロック
物理演算をさせる対象を選択し、演算に回転などの制約を加えることができます。回転の制約を加える場合は、物理演算される前に呼ばれるイベントを登録し、コールバック内で回転速度やConstraintなど設定・制御を行うと良いようです。回転させない制約
const Events = Matter.Events Events.on(this.engine, 'beforeUpdate', this.matterBeforeUpdate) matterBeforeUpdate (event) { // NOTE: 座標が更新される前に各ボディを回転させないように設定させる // http://brm.io/matter-js/docs/classes/Body.html#method_setAngularVelocity const Body = Matter.Body for (var i = 0; i < this.wordBodyList.length; i++) { const wordBody = this.wordBodyList[i] Body.setAngularVelocity(wordBody, 0) } }
デモはGitHubに置いてあります。
コメント
コメントを投稿