Skip to content

Commit

Permalink
Merge pull request #95 from xpadev-net/develop
Browse files Browse the repository at this point in the history
release: v0.2.70
  • Loading branch information
xpadev-net authored Jan 12, 2024
2 parents 977ff8f + 088e06c commit 31ab614
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 73 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@xpadev-net/niconicomments",
"version": "0.2.69",
"version": "0.2.70",
"description": "NiconiComments is a comment drawing library that is somewhat compatible with the official Nico Nico Douga player.",
"main": "dist/bundle.js",
"types": "dist/bundle.d.ts",
Expand Down
1 change: 1 addition & 0 deletions src/@types/IComment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
export interface IComment {
comment: FormattedCommentWithSize;
invisible: boolean;
index: number;
loc: CommentLoc;
width: number;
long: number;
Expand Down
1 change: 1 addition & 0 deletions src/@types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export type BaseOptions = {
showFPS: boolean;
useLegacy: boolean;
video: HTMLVideoElement | undefined;
lazy: boolean;
};
export type Options = Partial<BaseOptions>;

Expand Down
7 changes: 5 additions & 2 deletions src/comments/BaseComment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,22 @@ class BaseComment implements IComment {
public readonly pluginName: string = "BaseComment";
public image?: IRenderer | null;
public buttonImage?: IRenderer | null;
public index: number;

/**
* コンストラクタ
* @param comment 処理対象のコメント
* @param renderer 描画対象のレンダラークラス
* @param index コメントのインデックス
*/
constructor(comment: FormattedComment, renderer: IRenderer) {
constructor(comment: FormattedComment, renderer: IRenderer, index: number) {
this.renderer = renderer;
this.posY = 0;
this.posY = -1;
this.pos = { x: 0, y: 0 };
comment.content = comment.content.replace(/\t/g, "\u2003\u2003");
this.comment = this.convertComment(comment);
this.cacheKey = this.getCacheKey();
this.index = index;
}
get invisible() {
return this.comment.invisible;
Expand Down
4 changes: 2 additions & 2 deletions src/comments/FlashComment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ class FlashComment extends BaseComment {
private _globalScale: number;
override readonly pluginName: string = "FlashComment";
override buttonImage: IRenderer;
constructor(comment: FormattedComment, renderer: IRenderer) {
super(comment, renderer);
constructor(comment: FormattedComment, renderer: IRenderer, index: number) {
super(comment, renderer, index);
this._globalScale ??= getConfig(config.commentScale, true);
this.buttonImage = renderer.getCanvas();
}
Expand Down
6 changes: 3 additions & 3 deletions src/comments/HTML5Comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ import { BaseComment } from "./BaseComment";

class HTML5Comment extends BaseComment {
override readonly pluginName: string = "HTML5Comment";
constructor(comment: FormattedComment, context: IRenderer) {
super(comment, context);
this.posY = 0;
constructor(comment: FormattedComment, context: IRenderer, index: number) {
super(comment, context, index);
this.posY = -1;
}

override get content() {
Expand Down
1 change: 1 addition & 0 deletions src/definition/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const defaultOptions: BaseOptions = {
showFPS: false,
useLegacy: false,
video: undefined,
lazy: false,
};

let config: BaseConfig;
Expand Down
41 changes: 29 additions & 12 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class NiconiComments {
public showFPS: boolean;
public showCommentCount: boolean;
private lastVpos: number;
private processedCommentIndex: number;
private comments: IComment[];
private readonly renderer: IRenderer;
private readonly collision: Collision;
private readonly timeline: Timeline;
Expand Down Expand Up @@ -135,26 +137,29 @@ class NiconiComments {
right: [],
};
this.lastVpos = -1;
this.preRendering(parsedData);
this.processedCommentIndex = 0;

this.comments = this.preRendering(parsedData);

logger(`constructor complete: ${performance.now() - constructorStart}ms`);
}

/**
* 事前に当たり判定を考慮してコメントの描画場所を決定する
* @param rawData コメントデータ
* @returns コメントのインスタンス配列
*/
private preRendering(rawData: FormattedComment[]) {
const preRenderingStart = performance.now();
if (options.keepCA) {
rawData = changeCALayer(rawData);
}
let instances = rawData.reduce<IComment[]>((pv, val) => {
pv.push(createCommentInstance(val, this.renderer));
let instances = rawData.reduce<IComment[]>((pv, val, index) => {
pv.push(createCommentInstance(val, this.renderer, index));
return pv;
}, []);
this.getCommentPos(instances);
this.sortComment();
this.getCommentPos(instances, instances.length, options.lazy);
this.sortTimelineComment();

const plugins: IPluginList = [];
for (const plugin of config.plugins) {
Expand All @@ -175,25 +180,34 @@ class NiconiComments {

setPlugins(plugins);
logger(`preRendering complete: ${performance.now() - preRenderingStart}ms`);
return instances;
}

/**
* 計算された描画サイズをもとに各コメントの配置位置を決定する
* @param data コメントデータ
* @param end 終了インデックス
* @param lazy 遅延処理を行うか
*/
private getCommentPos(data: IComment[]) {
private getCommentPos(data: IComment[], end: number, lazy: boolean = false) {
const getCommentPosStart = performance.now();
for (const comment of data) {
if (comment.invisible) continue;
if (this.processedCommentIndex + 1 >= end) return;
for (const comment of data.slice(this.processedCommentIndex, end)) {
if (comment.invisible || (comment.posY > -1 && !lazy)) continue;
if (comment.loc === "naka") {
processMovableComment(comment, this.collision, this.timeline);
processMovableComment(comment, this.collision, this.timeline, lazy);
} else {
processFixedComment(
comment,
this.collision[comment.loc],
this.timeline,
lazy,
);
}
this.processedCommentIndex = comment.index;
}
if (lazy) {
this.processedCommentIndex = 0;
}
logger(
`getCommentPos complete: ${performance.now() - getCommentPosStart}ms`,
Expand All @@ -203,7 +217,7 @@ class NiconiComments {
/**
* 投稿者コメントを前に移動
*/
private sortComment() {
private sortTimelineComment() {
const sortCommentStart = performance.now();
for (const vpos of Object.keys(this.timeline)) {
const item = this.timeline[Number(vpos)];
Expand All @@ -228,8 +242,10 @@ class NiconiComments {
* @param rawComments コメントデータ
*/
public addComments(...rawComments: FormattedComment[]) {
const comments = rawComments.reduce<IComment[]>((pv, val) => {
pv.push(createCommentInstance(val, this.renderer));
const comments = rawComments.reduce<IComment[]>((pv, val, index) => {
pv.push(
createCommentInstance(val, this.renderer, this.comments.length + index),
);
return pv;
}, []);
for (const plugin of plugins) {
Expand Down Expand Up @@ -333,6 +349,7 @@ class NiconiComments {
if (comment.invisible) {
continue;
}
this.getCommentPos(this.comments, comment.index + 1);
comment.draw(vpos, this.showCollision, cursor);
}
}
Expand Down
128 changes: 77 additions & 51 deletions src/utils/comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -585,25 +585,15 @@ const isBanActive = (vpos: number): boolean => {
* @param comment 固定コメント
* @param collision コメントの衝突判定用配列
* @param timeline コメントのタイムライン
* @param lazy Y座標の計算を遅延させるか
*/
const processFixedComment = (
comment: IComment,
collision: CollisionItem,
timeline: Timeline,
lazy: boolean = false,
) => {
let posY = 0,
isChanged = true,
count = 0;
while (isChanged && count < 10) {
isChanged = false;
count++;
for (let j = 0; j < comment.long; j++) {
const result = getPosY(posY, comment, collision[comment.vpos + j]);
posY = result.currentPos;
isChanged = result.isChanged;
if (result.isBreak) break;
}
}
const posY = lazy ? -1 : getFixedPosY(comment, collision);
for (let j = 0; j < comment.long; j++) {
const vpos = comment.vpos + j;
arrayPush(timeline, vpos, comment);
Expand All @@ -618,52 +608,21 @@ const processFixedComment = (
* @param comment nakaコメント
* @param collision コメントの衝突判定用配列
* @param timeline コメントのタイムライン
* @param lazy Y座標の計算を遅延させるか
*/
const processMovableComment = (
comment: IComment,
collision: Collision,
timeline: Timeline,
lazy: boolean = false,
) => {
const beforeVpos =
Math.round(-288 / ((1632 + comment.width) / (comment.long + 125))) - 100;
const posY = (() => {
if (config.canvasHeight < comment.height) {
return (comment.height - config.canvasHeight) / -2;
}
let posY = 0;
let isChanged = true;
while (isChanged) {
isChanged = false;
for (let j = beforeVpos, n = comment.long + 125; j < n; j++) {
const vpos = comment.vpos + j;
const leftPos = getPosX(comment.comment, vpos);
let isBreak = false;
if (
leftPos + comment.width >= config.collisionRange.right &&
leftPos <= config.collisionRange.right
) {
const result = getPosY(posY, comment, collision.right[vpos]);
posY = result.currentPos;
isChanged ||= result.isChanged;
isBreak = result.isBreak;
}
if (
leftPos + comment.width >= config.collisionRange.left &&
leftPos <= config.collisionRange.left
) {
const result = getPosY(posY, comment, collision.left[vpos]);
posY = result.currentPos;
isChanged ||= result.isChanged;
isBreak = result.isBreak;
}
if (isBreak) return posY;
}
}
return posY;
})();
const posY = lazy ? -1 : getMovablePosY(comment, collision, beforeVpos);
for (let j = beforeVpos, n = comment.long + 125; j < n; j++) {
const vpos = comment.vpos + j;
const leftPos = getPosX(comment.comment, vpos);
if (timeline[vpos]?.includes(comment)) break;
arrayPush(timeline, vpos, comment);
if (
leftPos + comment.width + config.collisionPadding >=
Expand All @@ -683,6 +642,69 @@ const processMovableComment = (
comment.posY = posY;
};

const getFixedPosY = (comment: IComment, collision: CollisionItem) => {
let posY = 0,
isChanged = true,
count = 0;
while (isChanged && count < 10) {
isChanged = false;
count++;
for (let j = 0; j < comment.long; j++) {
const result = getPosY(posY, comment, collision[comment.vpos + j]);
posY = result.currentPos;
isChanged = result.isChanged;
if (result.isBreak) break;
}
}
return posY;
};

const getMovablePosY = (
comment: IComment,
collision: Collision,
beforeVpos: number,
) => {
if (config.canvasHeight < comment.height) {
return (comment.height - config.canvasHeight) / -2;
}
let posY = 0;
let isChanged = true;
let lastUpdatedIndex: number | undefined = undefined;
while (isChanged) {
isChanged = false;
for (let j = beforeVpos, n = comment.long + 125; j < n; j += 5) {
const vpos = comment.vpos + j;
const leftPos = getPosX(comment.comment, vpos);
let isBreak = false;
if (lastUpdatedIndex !== undefined && lastUpdatedIndex === vpos) {
return posY;
}
if (
leftPos + comment.width >= config.collisionRange.right &&
leftPos <= config.collisionRange.right
) {
const result = getPosY(posY, comment, collision.right[vpos]);
posY = result.currentPos;
isChanged ||= result.isChanged;
if (result.isChanged) lastUpdatedIndex = vpos;
isBreak = result.isBreak;
}
if (
leftPos + comment.width >= config.collisionRange.left &&
leftPos <= config.collisionRange.left
) {
const result = getPosY(posY, comment, collision.left[vpos]);
posY = result.currentPos;
isChanged ||= result.isChanged;
if (result.isChanged) lastUpdatedIndex = vpos;
isBreak = result.isBreak;
}
if (isBreak) return posY;
}
}
return posY;
};

/**
* 当たり判定からコメントを配置できる場所を探す
* @param currentPos 現在のy座標
Expand All @@ -700,11 +722,13 @@ const getPosY = (
let isBreak = false;
if (!collision) return { currentPos, isChanged, isBreak };
for (const collisionItem of collision) {
if (collisionItem.index === targetComment.index || collisionItem.posY < 0)
continue;
if (
currentPos < collisionItem.posY + collisionItem.height &&
currentPos + targetComment.height > collisionItem.posY &&
collisionItem.owner === targetComment.owner &&
collisionItem.layer === targetComment.layer
collisionItem.layer === targetComment.layer &&
currentPos < collisionItem.posY + collisionItem.height &&
currentPos + targetComment.height > collisionItem.posY
) {
if (collisionItem.posY + collisionItem.height > currentPos) {
currentPos = collisionItem.posY + collisionItem.height;
Expand Down Expand Up @@ -779,6 +803,8 @@ const parseFont = (font: CommentFont, size: string | number): string => {

export {
getDefaultCommand,
getFixedPosY,
getMovablePosY,
getPosX,
getPosY,
isBanActive,
Expand Down
6 changes: 4 additions & 2 deletions src/utils/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@ import { config } from "@/definition/config";
* コメントのインスタンスを生成する
* @param comment コメント
* @param context 描画対象のCanvasコンテキスト
* @param index コメントのインデックス
* @returns プラグインまたは内臓のコメントインスタンス
*/
const createCommentInstance = (
comment: FormattedComment,
context: IRenderer,
index: number,
) => {
for (const plugin of config.commentPlugins) {
if (plugin.condition(comment)) {
return new plugin.class(comment, context);
return new plugin.class(comment, context, index);
}
}
return new HTML5Comment(comment, context);
return new HTML5Comment(comment, context, index);
};

export { createCommentInstance };

0 comments on commit 31ab614

Please sign in to comment.