feat: 平滑视角

This commit is contained in:
unanmed 2024-08-29 22:07:26 +08:00
parent ef50b985cb
commit cd9c95d24d
9 changed files with 468 additions and 14 deletions

View File

@ -23,8 +23,12 @@ export class Container<E extends EContainerEvent = EContainerEvent>
* @param type absolute表示绝对位置static表示跟随摄像机移动 * @param type absolute表示绝对位置static表示跟随摄像机移动
* @param cache * @param cache
*/ */
constructor(type: RenderItemPosition = 'static', cache: boolean = true) { constructor(
super(type, cache); type: RenderItemPosition = 'static',
cache: boolean = true,
fall: boolean = false
) {
super(type, cache, fall);
this.type = type; this.type = type;
} }

View File

@ -9,6 +9,7 @@ import { LayerGroupFilter } from '@/plugin/fx/gameCanvas';
import { LayerGroupAnimate } from './preset/animate'; import { LayerGroupAnimate } from './preset/animate';
import { LayerGroupPortal } from '@/plugin/fx/portal'; import { LayerGroupPortal } from '@/plugin/fx/portal';
import { LayerGroupHalo } from '@/plugin/fx/halo'; import { LayerGroupHalo } from '@/plugin/fx/halo';
import { FloorViewport } from './preset/viewport';
let main: MotaRenderer; let main: MotaRenderer;
@ -37,6 +38,7 @@ Mota.require('var', 'loading').once('loaded', () => {
const animate = new LayerGroupAnimate(); const animate = new LayerGroupAnimate();
const portal = new LayerGroupPortal(); const portal = new LayerGroupPortal();
const halo = new LayerGroupHalo(); const halo = new LayerGroupHalo();
const viewport = new FloorViewport();
layer.extends(damage); layer.extends(damage);
layer.extends(detail); layer.extends(detail);
layer.extends(filter); layer.extends(filter);
@ -46,6 +48,7 @@ Mota.require('var', 'loading').once('loaded', () => {
layer.getLayer('event')?.extends(door); layer.getLayer('event')?.extends(door);
layer.getLayer('event')?.extends(shadow); layer.getLayer('event')?.extends(shadow);
layer.extends(animate); layer.extends(animate);
layer.extends(viewport);
render.appendChild(layer); render.appendChild(layer);
// console.log(render); // console.log(render);

View File

@ -119,6 +119,7 @@ export interface ERenderItemEvent {
interface TickerDelegation { interface TickerDelegation {
fn: TickerFn; fn: TickerFn;
timeout?: number;
endFn?: () => void; endFn?: () => void;
} }
@ -179,11 +180,18 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
protected cacheDirty: boolean = true; protected cacheDirty: boolean = true;
/** 是否启用缓存机制 */ /** 是否启用缓存机制 */
readonly enableCache: boolean = true; readonly enableCache: boolean = true;
/** 是否启用transform下穿机制 */
readonly transformFallThrough: boolean = false;
constructor(type: RenderItemPosition, enableCache: boolean = true) { constructor(
type: RenderItemPosition,
enableCache: boolean = true,
transformFallThrough: boolean = false
) {
super(); super();
this.enableCache = enableCache; this.enableCache = enableCache;
this.transformFallThrough = transformFallThrough;
this.type = type; this.type = type;
this.cache.withGameScale(true); this.cache.withGameScale(true);
@ -222,7 +230,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
if (this.hidden) return; if (this.hidden) return;
this.emit('beforeRender', transform); this.emit('beforeRender', transform);
this.needUpdate = false; this.needUpdate = false;
const tran = this.transform; const tran = this.transformFallThrough ? transform : this.transform;
const ax = -this.anchorX * this.width; const ax = -this.anchorX * this.width;
const ay = -this.anchorY * this.height; const ay = -this.anchorY * this.height;
@ -320,7 +328,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
RenderItem.tickerMap.set(id, delegation); RenderItem.tickerMap.set(id, delegation);
RenderItem.ticker.add(fn); RenderItem.ticker.add(fn);
if (typeof time === 'number' && time < 2147438647 && time > 0) { if (typeof time === 'number' && time < 2147438647 && time > 0) {
setTimeout(() => { delegation.timeout = window.setTimeout(() => {
RenderItem.ticker.remove(fn); RenderItem.ticker.remove(fn);
end?.(); end?.();
}, time); }, time);
@ -332,6 +340,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
const delegation = RenderItem.tickerMap.get(id); const delegation = RenderItem.tickerMap.get(id);
if (!delegation) return false; if (!delegation) return false;
RenderItem.ticker.remove(delegation.fn); RenderItem.ticker.remove(delegation.fn);
window.clearTimeout(delegation.timeout);
if (callEnd) delegation.endFn?.(); if (callEnd) delegation.endFn?.();
return true; return true;
} }

View File

@ -165,7 +165,7 @@ export class Damage extends Sprite<EDamageEvent> {
private needUpdateBlocks: Set<number> = new Set(); private needUpdateBlocks: Set<number> = new Set();
constructor() { constructor() {
super('absolute', false); super('absolute', false, true);
this.block = new BlockCacher(0, 0, core._WIDTH_, 1); this.block = new BlockCacher(0, 0, core._WIDTH_, 1);
this.type = 'absolute'; this.type = 'absolute';
@ -175,11 +175,11 @@ export class Damage extends Sprite<EDamageEvent> {
this.damageMap.setAntiAliasing(true); this.damageMap.setAntiAliasing(true);
this.damageMap.size(core._PX_, core._PY_); this.damageMap.size(core._PX_, core._PY_);
this.setRenderFn((canvas, camera) => { this.setRenderFn((canvas, transform) => {
const { ctx } = canvas; const { ctx } = canvas;
const { width, height } = canvas; const { width, height } = canvas;
ctx.imageSmoothingEnabled = false; ctx.imageSmoothingEnabled = false;
this.renderDamage(camera); this.renderDamage(transform);
ctx.drawImage(this.damageMap.canvas, 0, 0, width, height); ctx.drawImage(this.damageMap.canvas, 0, 0, width, height);
}); });
} }

View File

@ -107,11 +107,14 @@ export class LayerGroup extends Container implements IAnimateFrame {
/** 地图显示层 */ /** 地图显示层 */
layers: Map<FloorLayer, Layer> = new Map(); layers: Map<FloorLayer, Layer> = new Map();
/** 这个地图组的摄像机 */
camera: Transform = new Transform();
private needRender?: NeedRenderData; private needRender?: NeedRenderData;
private extend: Map<string, ILayerGroupRenderExtends> = new Map(); private extend: Map<string, ILayerGroupRenderExtends> = new Map();
constructor() { constructor() {
super('static'); super('static', true);
this.setHD(true); this.setHD(true);
this.setAntiAliasing(false); this.setAntiAliasing(false);
@ -128,6 +131,17 @@ export class LayerGroup extends Container implements IAnimateFrame {
binder.bindThis(); binder.bindThis();
} }
protected render(canvas: MotaOffscreenCanvas2D): void {
const { ctx } = canvas;
this.sortedChildren.forEach(v => {
if (v.hidden) return;
ctx.save();
v.renderContent(canvas, this.camera);
ctx.restore();
});
}
/** /**
* *
* *
@ -633,7 +647,7 @@ export class Layer extends Container {
protected backMap: MotaOffscreenCanvas2D = new MotaOffscreenCanvas2D(); protected backMap: MotaOffscreenCanvas2D = new MotaOffscreenCanvas2D();
/** 最终渲染至的Sprite */ /** 最终渲染至的Sprite */
main: Sprite = new Sprite('static', false); main: Sprite = new Sprite('absolute', false, true);
/** 渲染的层 */ /** 渲染的层 */
layer?: FloorLayer; layer?: FloorLayer;
@ -670,7 +684,7 @@ export class Layer extends Container {
moving: Set<LayerMovingRenderable> = new Set(); moving: Set<LayerMovingRenderable> = new Set();
constructor() { constructor() {
super('absolute', false); super('absolute', false, true);
// this.setHD(false); // this.setHD(false);
this.setAntiAliasing(false); this.setAntiAliasing(false);

View File

@ -0,0 +1,390 @@
import { logger } from '@/core/common/logger';
import { HeroRenderer } from './hero';
import { ILayerGroupRenderExtends, LayerGroup } from './layer';
import { Transform } from '../transform';
import { LayerGroupFloorBinder } from './floor';
import { hyper, TimingFn } from 'mutate-animate';
import { RenderAdapter } from '../adapter';
export class FloorViewport implements ILayerGroupRenderExtends {
id: string = 'viewport';
group!: LayerGroup;
hero!: HeroRenderer;
transform!: Transform;
binder!: LayerGroupFloorBinder;
/** 是否启用视角控制拓展 */
enabled: boolean = true;
/** 渐变的速率曲线 */
transitionFn: TimingFn = hyper('sin', 'out');
/** 加减速的速率曲线 */
movingEaseFn: TimingFn = t => t ** 2;
/** 突变时的渐变时长 */
transitionTime: number = 600;
/** 当前视角移动速度 */
private speedX: number = 0;
private speedY: number = 0;
/** X方向移动状态0表示静止1表示加速过程2表示匀速过程3表示减速过程 */
private movingStatusX: 0 | 1 | 2 | 3 = 0;
/** X方向加减速过程进度 */
private movingEaseProgressX: number = 0;
/** Y方向移动状态0表示静止1表示加速过程2表示匀速过程3表示减速过程 */
private movingStatusY: 0 | 1 | 2 | 3 = 0;
/** Y方向加减速过程进度 */
private movingEaseProgressY: number = 0;
/** X方向移动结束坐标 */
private movingEndX: number = 0;
/** X方向移动结束坐标 */
private movingEndY: number = 0;
/** X方向进入第一阶段的时刻 */
private movingAccX: number = 0;
/** Y方向进入第一阶段的时刻 */
private movingAccY: number = 0;
/** X方向进入第二阶段的时刻 */
private movingConstantX: number = 0;
/** Y方向进入第二阶段的时刻 */
private movingConstantY: number = 0;
/** X方向进入第三阶段的时刻 */
private movingDeaccX: number = 0;
/** Y方向进入第三阶段的时刻 */
private movingDeaccY: number = 0;
/** X方向进入第一阶段的横坐标 */
private movingAccPosX: number = 0;
/** Y方向进入第一阶段的横坐标 */
private movingAccPosY: number = 0;
/** X方向进入第二阶段的横坐标 */
private movingConstantPosX: number = 0;
/** Y方向进入第二阶段的横坐标 */
private movingConstantPosY: number = 0;
/** X方向进入第三阶段的横坐标 */
private movingDeaccPosX: number = 0;
/** Y方向进入第三阶段的横坐标 */
private movingDeaccPosY: number = 0;
/** X方向进入第一阶段的进度 */
private movingAccProgressX: number = 0;
/** Y方向进入第一阶段的进度 */
private movingAccProgressY: number = 0;
/** X方向进入第三阶段的进度 */
private movingDeaccProgressX: number = 0;
/** Y方向进入第三阶段的进度 */
private movingDeaccProgressY: number = 0;
/** 移动的加减速时长 */
private movingEaseTime: number = 0;
/** 当前视角位置 */
private nx: number = 0;
private ny: number = 0;
/** 委托ticker */
private delegation: number = -1;
/** 渐变委托ticker */
private transition: number = -1;
/** 移动委托ticker */
private moving: number = -1;
/** 是否在渐变过程中 */
private inTransition: boolean = false;
/** 是否在移动过程中 */
private inMoving: boolean = false;
/**
*
*/
disable() {
this.enabled = false;
}
/**
*
*/
enable() {
this.enabled = true;
}
/**
*
* @param x
* @param y
*/
getBoundedPosition(x: number, y: number) {
const width = core._WIDTH_;
const height = core._HEIGHT_;
const minX = (width - 1) / 2;
const minY = (height - 1) / 2;
const floor = core.status.maps[this.binder.getFloor()];
const maxX = floor.width - minX - 1;
const maxY = floor.height - minY - 1;
// return { x, y };
return { x: core.clamp(x, minX, maxX), y: core.clamp(y, minY, maxY) };
}
/**
*
* @param x
* @param y
*/
setPosition(x: number, y: number) {
const { x: nx, y: ny } = this.getBoundedPosition(x, y);
this.group.removeTicker(this.transition, false);
this.nx = nx;
this.ny = ny;
}
/**
*
* @param x
* @param y
*/
moveTo(x: number, y: number) {
const { x: nx, y: ny } = this.getBoundedPosition(x, y);
if (this.inTransition) {
const distance = Math.hypot(this.nx - nx, this.ny - ny);
const time = core.clamp(distance * 200, 200, 600);
this.createTransition(nx, ny, time);
} else {
this.createTransition(nx, ny, 200);
// const moveSpeed = 1000 / this.hero.speed;
// this.speedX = moveSpeed;
// this.speedY = moveSpeed;
// this.movingEndX = x;
// this.movingEndY = y;
// this.movingEaseTime = moveSpeed * 2;
// this.processMoving(x, y);
}
}
private processMoving(x: number, y: number) {
this.movingEndX = x;
this.movingEndY = y;
if (!this.inMoving) {
this.movingStatusX = 0;
this.movingEaseProgressX = 0;
this.movingStatusY = 0;
this.movingEaseProgressY = 0;
this.inMoving = true;
} else {
}
}
/**
*
* @param x
* @param y
*/
mutateTo(x: number, y: number) {
const { x: nx, y: ny } = this.getBoundedPosition(x, y);
this.createTransition(nx, ny, this.transitionTime);
}
private createTransition(x: number, y: number, time: number) {
const start = Date.now();
const end = start + time;
const sx = this.nx;
const sy = this.ny;
const dx = x - sx;
const dy = y - sy;
this.speedX = 0;
this.speedY = 0;
this.inTransition = true;
this.group.removeTicker(this.transition, false);
this.transition = this.group.delegateTicker(
() => {
const now = Date.now();
if (now >= end) {
this.group.removeTicker(this.transition, true);
return;
}
const progress = this.transitionFn((now - start) / time);
const tx = dx * progress;
const ty = dy * progress;
this.nx = tx + sx;
this.ny = ty + sy;
},
time,
() => {
this.nx = x;
this.ny = y;
this.inTransition = false;
}
);
}
private createMoving() {
this.moving = this.group.delegateTicker(() => {
const nx = this.nx;
const ny = this.ny;
const now = Date.now();
const dx = Math.sign(this.movingEndX - nx);
const dy = Math.sign(this.movingEndY - ny);
const fn = this.movingEaseFn;
// 进行进度判断
if (this.movingEndX !== nx && this.movingStatusX === 0) {
this.movingStatusX = 1;
this.movingAccX = now;
this.movingAccProgressX = this.movingEaseProgressX;
this.movingAccPosX = nx - fn(this.movingAccProgressX) * dx;
}
if (this.movingEndY !== ny && this.movingStatusY === 0) {
this.movingEaseProgressY = 1;
this.movingAccY = now;
this.movingAccProgressY = this.movingEaseProgressY;
this.movingAccPosY = ny - fn(this.movingAccProgressY) * dy;
}
if (
Math.abs(this.movingEndX - nx) <= 1 &&
this.movingStatusX !== 3
) {
this.movingStatusX = 3;
this.movingDeaccX = now;
this.movingDeaccProgressX = this.movingEaseProgressX;
this.movingDeaccPosX =
nx - (fn(this.movingDeaccProgressX) + 1) * dx;
}
if (
Math.abs(this.movingEndY - ny) <= 1 &&
this.movingStatusY !== 3
) {
this.movingStatusY = 3;
this.movingDeaccY = now;
this.movingDeaccProgressY = this.movingEaseProgressY;
this.movingDeaccPosY =
ny - (fn(this.movingDeaccProgressY) + 1) * dy;
}
if (this.movingEaseProgressX >= 1 && this.movingStatusX === 1) {
this.movingStatusX = 2;
this.movingConstantX = now;
this.movingConstantPosX = nx;
}
if (this.movingEaseProgressY >= 1 && this.movingStatusY === 1) {
this.movingStatusY = 2;
this.movingConstantY = now;
this.movingConstantPosY = ny;
}
if (this.movingEaseProgressX <= 0 && this.movingStatusX === 3) {
this.nx = this.movingEndX;
this.movingStatusX = 0;
this.movingEaseProgressX = 0;
}
if (this.movingEaseProgressY <= 0 && this.movingStatusY === 3) {
this.ny = this.movingEndY;
this.movingStatusY = 0;
this.movingEaseProgressY = 0;
}
// 平滑视角位置计算
if (this.movingEaseProgressX === 1) {
// 加速阶段
this.movingEaseProgressX =
(now - this.movingAccX) / this.movingEaseTime;
const tx = fn(this.movingEaseProgressX) * dx;
this.nx = this.movingAccPosX + tx;
} else if (this.movingEaseProgressX === 2) {
// 匀速阶段
const time = now - this.movingConstantX;
this.nx = this.movingConstantPosX + time * this.speedX * dx;
} else if (this.movingEaseProgressX === 3) {
// 减速阶段
this.movingEaseProgressX =
1 - (now - this.movingDeaccX) / this.movingEaseTime;
const tx = fn(this.movingEaseProgressX) * dx;
this.nx = this.movingDeaccPosX + tx;
}
if (this.movingEaseProgressY === 1) {
// 加速阶段
this.movingEaseProgressY =
(now - this.movingAccY) / this.movingEaseTime;
const ty = fn(this.movingEaseProgressY) * dy;
this.ny = this.movingAccPosY + ty;
} else if (this.movingEaseProgressY === 2) {
// 匀速阶段
const time = now - this.movingConstantY;
this.ny = this.movingConstantPosY + time * this.speedY * dy;
} else if (this.movingEaseProgressY === 3) {
// 减速阶段
this.movingEaseProgressY =
1 - (now - this.movingDeaccY) / this.movingEaseTime;
const ty = fn(this.movingEaseProgressY) * dy;
this.ny = this.movingDeaccPosY + ty;
}
});
}
private create() {
let nx = this.nx;
let ny = this.ny;
const halfWidth = core._PX_ / 2;
const halfHeight = core._PY_ / 2;
this.delegation = this.group.delegateTicker(() => {
if (this.nx === nx && this.ny === ny) return;
const cell = this.group.cellSize;
const half = cell / 2;
nx = this.nx;
ny = this.ny;
const ox = nx * cell;
const oy = ny * cell;
core.bigmap.offsetX = ox;
core.bigmap.offsetY = oy;
this.group.camera.setTranslate(
-ox + halfWidth - half,
-oy + halfHeight - half
);
this.group.update(this.group);
});
// this.createMoving();
}
awake(group: LayerGroup): void {
this.group = group;
this.transform = group.transform;
const ex1 = group.getLayer('event')?.getExtends('floor-hero');
const ex2 = group.getExtends('floor-binder');
if (
ex1 instanceof HeroRenderer &&
ex2 instanceof LayerGroupFloorBinder
) {
this.hero = ex1;
this.binder = ex2;
this.create();
adapter.add(this);
} else {
logger.error(
15,
`Viewport extends needs 'floor-hero' extends as dependency.`
);
group.removeExtends('viewport');
}
}
onDestroy(group: LayerGroup): void {
group.removeTicker(this.delegation);
group.removeTicker(this.transition);
group.removeTicker(this.moving);
adapter.remove(this);
}
}
const adapter = new RenderAdapter<FloorViewport>('viewport');
adapter.receive('mutateTo', (item, x, y) => {
item.mutateTo(x, y);
return Promise.resolve();
});
adapter.receive('moveTo', (item, x, y) => {
item.moveTo(x, y);
return Promise.resolve();
});
adapter.receive('setPosition', (item, x, y) => {
item.setPosition(x, y);
return Promise.resolve();
});
const hook = Mota.require('var', 'hook');
hook.on('changingFloor', (_, loc) => {
adapter.all('setPosition', loc.x, loc.y);
});

View File

@ -19,8 +19,12 @@ export class Sprite<E extends ESpriteEvent = ESpriteEvent> extends RenderItem<
* @param type absolute表示绝对位置Transform改变 * @param type absolute表示绝对位置Transform改变
* @param cache * @param cache
*/ */
constructor(type: RenderItemPosition = 'static', cache: boolean = true) { constructor(
super(type, cache); type: RenderItemPosition = 'static',
cache: boolean = true,
fall: boolean = false
) {
super(type, cache, fall);
this.type = type; this.type = type;
this.renderFn = () => {}; this.renderFn = () => {};
} }

View File

@ -95,7 +95,7 @@ export class Portal extends Sprite {
private delegation: number; private delegation: number;
constructor() { constructor() {
super('static', false); super('static', false, true);
this.particleSetting = mainSetting.getSetting('fx.portalParticle')!; this.particleSetting = mainSetting.getSetting('fx.portalParticle')!;

View File

@ -10,6 +10,7 @@ import { BluePalace } from '@/game/mechanism/misc';
import { backDir } from './utils'; import { backDir } from './utils';
import type { TimingFn } from 'mutate-animate'; import type { TimingFn } from 'mutate-animate';
import EventEmitter from 'eventemitter3'; import EventEmitter from 'eventemitter3';
import type { FloorViewport } from '@/core/render/preset/viewport';
// 向后兼容用,会充当两个版本间过渡的作用 // 向后兼容用,会充当两个版本间过渡的作用
@ -18,6 +19,7 @@ interface Adapters {
'door-animate'?: RenderAdapter<LayerDoorAnimate>; 'door-animate'?: RenderAdapter<LayerDoorAnimate>;
animate?: RenderAdapter<LayerGroupAnimate>; animate?: RenderAdapter<LayerGroupAnimate>;
layer?: RenderAdapter<Layer>; layer?: RenderAdapter<Layer>;
viewport?: RenderAdapter<FloorViewport>;
} }
interface MoveEvent { interface MoveEvent {
@ -38,11 +40,13 @@ export function init() {
const doorAnimate = Adapter.get<LayerDoorAnimate>('door-animate'); const doorAnimate = Adapter.get<LayerDoorAnimate>('door-animate');
const animate = Adapter.get<LayerGroupAnimate>('animate'); const animate = Adapter.get<LayerGroupAnimate>('animate');
const layer = Adapter.get<Layer>('layer'); const layer = Adapter.get<Layer>('layer');
const viewport = Adapter.get<FloorViewport>('viewport');
adapters['hero-adapter'] = hero; adapters['hero-adapter'] = hero;
adapters['door-animate'] = doorAnimate; adapters['door-animate'] = doorAnimate;
adapters['animate'] = animate; adapters['animate'] = animate;
adapters['layer'] = layer; adapters['layer'] = layer;
adapters['viewport'] = viewport;
} }
let moving: boolean = false; let moving: boolean = false;
@ -193,6 +197,17 @@ export function init() {
} }
stepEnding = adapter.all('move', moveDir); stepEnding = adapter.all('move', moveDir);
if (portal && portalData) {
adapters.viewport?.all(
'mutateTo',
portalData.x,
portalData.y
);
} else {
const { nx, ny } = getNextLoc();
adapters.viewport?.all('moveTo', nx, ny);
}
await stepEnding; await stepEnding;
moveEmit.emit('stepEnd'); moveEmit.emit('stepEnd');
@ -260,11 +275,13 @@ export function init() {
callback?: () => void callback?: () => void
) { ) {
if (portal && portalData) { if (portal && portalData) {
const before = moveDir;
const { x, y, dir } = portalData; const { x, y, dir } = portalData;
core.setHeroLoc('x', x); core.setHeroLoc('x', x);
core.setHeroLoc('y', y); core.setHeroLoc('y', y);
core.setHeroLoc('direction', dir); core.setHeroLoc('direction', dir);
portal = false; portal = false;
moveDir = before;
} else if (!noPass) { } else if (!noPass) {
const { nx, ny } = getNextLoc(); const { nx, ny } = getNextLoc();
core.setHeroLoc('x', nx, true); core.setHeroLoc('x', nx, true);
@ -929,6 +946,7 @@ export function init() {
if (moving) return; if (moving) return;
const sx = core.getHeroLoc('x'); const sx = core.getHeroLoc('x');
const sy = core.getHeroLoc('y'); const sy = core.getHeroLoc('y');
adapters.viewport?.all('mutateTo', ex, ey);
const locked = core.status.lockControl; const locked = core.status.lockControl;
core.lockControl(); core.lockControl();
@ -956,6 +974,18 @@ export function init() {
core.setHeroLoc('y', ey); core.setHeroLoc('y', ey);
callback?.(); callback?.();
}; };
// ----- 视角处理
////// 瞬间移动 //////
control.prototype.moveDirectly = function (
destX: number,
destY: number,
ignoreSteps: number
) {
adapters.viewport?.all('mutateTo', destX, destY);
return this.controldata.moveDirectly(destX, destY, ignoreSteps);
};
}); });
loading.once('loaded', () => { loading.once('loaded', () => {