先日 emoscode(エモすこ) というWebサービスをリリースしました。詳しくはひとり開発アドベントカレンダーに投稿した12/4の記事をご覧ください。
お気づきの方もおられると思いますが、このサービス、コメントの数が増えてくると重大な問題が発生しています。そう、「コメントに隠れてソースコードが見辛い」というものです。当初のGUIではそれぞれのコメントをドラッグ移動で動かしてやらないと、その下に隠れているコードを見る事ができませんでした。
このたび、これを解決する画期的な新機能をリリースいたしました。それではご覧ください。
一気にせわしなくなりましたね! 師走だけに!
コメントの物理っぽい挙動はこんな感じのコードになっています。
これを requestAnimationFrame() に設定した関数から呼び出しています。
caption.ts
export class Caption
{
:
updatePosition(): void {
if (this.arrow == null) return;
if (draggingArrow == this.idComment) return;
if (this.isEditing) return;
const posCaption = this.getRect().getCenter();
const velocityTick = this.velocity.getScaled(timeElapsedSecond);
const posCaptionNext = posCaption.getAdded(velocityTick);
const posMarker = this.arrow.rectText.getCenter();
const vecNext = posMarker.getSubtracted(posCaptionNext);
const lengthNext = vecNext.getLength();
const unitVecNext = vecNext.getNormalized();
const initLength = this.arrow.initLength;
const magnitudeToModify = (lengthNext - initLength) * 0.1;
const velocityTickNext = velocityTick.getAdded(unitVecNext.getScaled(magnitudeToModify));
this.velocity = velocityTickNext.getScaled(1.0 / timeElapsedSecond);
this.setLeftTopPosition(this.x + velocityTickNext.x, this.y + velocityTickNext.y);
}
:
}
上記コードで使用している2次元ベクトル・矩形クラスは以下のようなコードになっています。当初出来合いのライブラリを探したのですが Javascript/Typescript で目的に沿うシンプルなものが見当たらず、結局自作しました。
rect.ts
export class Vector2
{
x: number = 0.0;
y: number = 0.0;
constructor(x: number, y: number)
{
this.x = x;
this.y = y;
}
setX(x: number): void
{
this.x = x;
}
setY(y: number): void
{
this.y = y;
}
w(): number
{
return this.x;
}
h(): number
{
return this.y;
}
clone(): Vector2
{
return new Vector2(this.x, this.y);
}
getSquaredLength(): number
{
return this.x * this.x + this.y * this.y;
}
getLength(): number
{
return Math.sqrt(this.getSquaredLength());
}
scale(s: number): void
{
this.x *= s;
this.y *= s;
}
getScaled(s: number): Vector2
{
let ret = this.clone();
ret.scale(s);
return ret;
}
add(vec: Vector2): void
{
this.x += vec.x;
this.y += vec.y;
}
getAdded(vec: Vector2): Vector2
{
let ret = this.clone();
ret.add(vec);
return ret;
}
subtract(vec: Vector2): void
{
this.x -= vec.x;
this.y -= vec.y;
}
getSubtracted(vec: Vector2): Vector2
{
let ret = this.clone();
ret.subtract(vec);
return ret;
}
normalize(): void
{
let length = this.getLength();
let inversedLength = 1.0 / length;
this.scale(inversedLength);
}
getNormalized(): Vector2
{
let ret = this.clone();
ret.normalize();
return ret;
}
getPerpendicular(): Vector2
{
return new Vector2(this.y, -this.x);
}
}
export class Rect2
{
posLeftTop: Vector2;
size: Vector2;
constructor(posLeftTop: Vector2, size: Vector2)
{
this.posLeftTop = posLeftTop;
this.size = size;
}
getCenter(): Vector2
{
return this.posLeftTop.getAdded(this.size.getScaled(0.5));
}
setPosition(posLeftTop: Vector2): void
{
this.posLeftTop = posLeftTop;
}
getNearestX(x: number): number
{
let distanceXLeft = Math.abs(x - this.posLeftTop.x);
let rightX = this.posLeftTop.x + this.size.w();
let distanceXRight = Math.abs(x - rightX);
return distanceXLeft < distanceXRight ? this.posLeftTop.x : rightX;
}
getNearestY(y: number): number
{
let distanceYTop = Math.abs(y - this.posLeftTop.y);
let bottomY = this.posLeftTop.y + this.size.h();
let distanceYBottom = Math.abs(y - bottomY);
return distanceYTop < distanceYBottom ? this.posLeftTop.y : bottomY;
}
}
以上となります!