diff --git a/packages/oh-my-live2d/src/modules/index.ts b/packages/oh-my-live2d/src/modules/index.ts index 362cab1..e71fe7e 100644 --- a/packages/oh-my-live2d/src/modules/index.ts +++ b/packages/oh-my-live2d/src/modules/index.ts @@ -3,9 +3,7 @@ import type { Options } from '../types/index.js'; export const bootstrap = (): ((options: Options) => LoadOhMyLive2D) => { const loadOml2d = (options: Options): LoadOhMyLive2D => { - const oml2d = new LoadOhMyLive2D(); - - void oml2d.setup(options); + const oml2d = new LoadOhMyLive2D(options); return oml2d; }; diff --git a/packages/oh-my-live2d/src/modules/load-oml2d.ts b/packages/oh-my-live2d/src/modules/load-oml2d.ts index c01457e..f996574 100644 --- a/packages/oh-my-live2d/src/modules/load-oml2d.ts +++ b/packages/oh-my-live2d/src/modules/load-oml2d.ts @@ -1,22 +1,28 @@ +import { isNumber, mergeDeep } from 'tianjie'; + import { Events } from './events.js'; import { OhMyLive2D } from './oml2d.js'; import { DEFAULT_OPTIONS } from '../config/index.js'; +import { CommonStyleType } from '../types/common.js'; import type { EventFn, LoadEventFn } from '../types/events.js'; import { DefaultOptions } from '../types/index.js'; -import { Options } from '../types/options.js'; +import { ModelOptions, Options } from '../types/options.js'; import { handleCommonStyle, loadOml2dSDK, mergeOptions } from '../utils/index.js'; export class LoadOhMyLive2D { options: DefaultOptions = DEFAULT_OPTIONS; private events: Events; private oml2d?: OhMyLive2D; - constructor() { + + constructor(options: Options) { this.events = new Events(); + void this.setup(options); } + /** * 安装组件 */ - async setup(options: Options): Promise { + private async setup(options: Options): Promise { this.options = mergeOptions(DEFAULT_OPTIONS, options); const { PIXI, PixiLive2dDisplay } = await loadOml2dSDK(this.options.importType, this.options.libraryUrls); @@ -33,6 +39,15 @@ export class LoadOhMyLive2D { return this.oml2d?.modelIndex; } + /** + * 当前模型选项 + */ + get model(): ModelOptions | undefined { + if (isNumber(this.modelIndex)) { + return this.oml2d?.models.currentModelOptions; + } + } + /** * 主动提示消息, 调用时会提前自动关闭当前空闲消息播放器, 调用结束之后会自动开启空闲消息播放器 * @param message 提示信息 @@ -72,12 +87,16 @@ export class LoadOhMyLive2D { } /** - * 设置舞台大小 + * 设置当前模型的舞台样式 * @param size */ - setStageSize(size: { width?: number; height?: number }): void { - this.oml2d?.stage.setStyle(handleCommonStyle(size)); + setStageStyle(style: CommonStyleType): void { + this.oml2d?.stage.setStyle(handleCommonStyle(style)); this.oml2d?.pixiApp?.resize(); + + if (this.model) { + this.model.stageStyle = mergeDeep(this.model?.stageStyle, style); + } } /** @@ -163,14 +182,44 @@ export class LoadOhMyLive2D { const { x, y } = position; this.oml2d?.models.setPosition(x, y); + if (this.model) { + this.model.position = [x || 0, y || 0]; + } } + /** + * 设置当前模型的旋转角度 + * @param rotation + */ + setModelRotation(rotation: number): void { + this.oml2d?.models.setRotation(rotation); + if (this.model) { + this.model.rotation = rotation; + } + } + + /** + * 设置当前模型的锚点位置 + * @param anchor + */ + setModelAnchor(anchor: { x?: number; y?: number }): void { + const { x, y } = anchor; + + this.oml2d?.models.setAnchor(x, y); + + if (this.model) { + this.model.anchor = [x || 0, y || 0]; + } + } /** * 设置当前模型缩放 * @param value */ setModelScale(value: number): void { this.oml2d?.models.setScale(value); + if (this.model) { + this.model.scale = value; + } } /** diff --git a/packages/oh-my-live2d/src/modules/models.ts b/packages/oh-my-live2d/src/modules/models.ts index 1df4912..73bbc87 100644 --- a/packages/oh-my-live2d/src/modules/models.ts +++ b/packages/oh-my-live2d/src/modules/models.ts @@ -79,6 +79,12 @@ export class Models { if (isNumber(this.currentModelOptions.volume)) { this.PixiLive2dDisplay.SoundManager.volume = this.currentModelOptions.volume; } + + // 设置锚点 + this.setAnchor(...(this.currentModelOptions.anchor || [])); + + // 旋转角度 + this.setRotation(this.currentModelOptions.rotation); } /** @@ -122,6 +128,20 @@ export class Models { this.model!.y = y; } + /** + * 设置模型旋转角度 + */ + setRotation(value: number = 0): void { + this.model!.rotation = (Math.PI * value) / 180; + } + + /** + * 设置模型在舞台中的锚点位置 + */ + setAnchor(x: number = 0, y: number = 0): void { + this.model!.anchor.set(x, y); + } + /** * 播放动作 */ diff --git a/packages/oh-my-live2d/src/types/model.ts b/packages/oh-my-live2d/src/types/model.ts index 52774fd..e374a37 100644 --- a/packages/oh-my-live2d/src/types/model.ts +++ b/packages/oh-my-live2d/src/types/model.ts @@ -22,6 +22,19 @@ export interface ModelOptions { */ scale?: number; + /** + * 模型的旋转角度, 单位:度 (0-360) + * @default 0 + */ + rotation?: number; + + /** + * 设置模型在舞台中的锚点位置 + * @valueType [x: number, y: number] + * @default [0,0] + */ + anchor?: [x: number, y: number]; + /** * 模型在舞台中的位置。x: 横坐标, y: 纵坐标 * diff --git a/tests/vite-app/src/main.ts b/tests/vite-app/src/main.ts index a930089..f84d1ca 100644 --- a/tests/vite-app/src/main.ts +++ b/tests/vite-app/src/main.ts @@ -11,17 +11,20 @@ const foo = async () => { // dockedPosition: 'right', mobileDisplay: false, primaryColor: 'pink', + models: [ - { - path: '/models/Pio/model.json', - motionPreloadStrategy: 'ALL', - showHitAreaFrames: true, - scale: 0.4 - }, + // { + // rotation: 30, + // path: '/models/Pio/model.json', + // motionPreloadStrategy: 'ALL', showHitAreaFrames: true, + // scale: 0.4 + // }, { name: 'shizuku', path: 'https://registry.npmmirror.com/oml2d-models/latest/files/models/shizuku/shizuku.model.json', scale: 0.2, + // rotation: 360, + // anchor: [0.5, 0.1], motionPreloadStrategy: 'ALL', position: [0, 100], volume: 0, @@ -139,6 +142,11 @@ const foo = async () => { return; } }); + + oml2d.onStageSlideIn(() => { + oml2d.setStageSize({ width: 600, height: 600 }); + console.log(oml2d.model); + }); // oml2d.onStageSlideIn(() => { // console.log('111'); // setTimeout(() => {