mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-06-08 08:18:00 +08:00
refactor: 把样板所需的标签定义移动至 client-modules
This commit is contained in:
parent
86bc383d65
commit
d72334e80b
@ -1,6 +1,6 @@
|
|||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
import { MotaOffscreenCanvas2D } from '@motajs/render-core';
|
import { MotaOffscreenCanvas2D } from '@motajs/render-core';
|
||||||
import { SizedCanvasImageSource } from './types';
|
import { SizedCanvasImageSource } from '@motajs/render-elements';
|
||||||
|
|
||||||
// 经过测试(https://www.measurethat.net/Benchmarks/Show/30741/1/drawimage-img-vs-canvas-vs-bitmap-cropping-fix-loading)
|
// 经过测试(https://www.measurethat.net/Benchmarks/Show/30741/1/drawimage-img-vs-canvas-vs-bitmap-cropping-fix-loading)
|
||||||
// 得出结论,ImageBitmap和Canvas的绘制性能不如Image,于是直接画Image就行,所以缓存基本上就是存Image
|
// 得出结论,ImageBitmap和Canvas的绘制性能不如Image,于是直接画Image就行,所以缓存基本上就是存Image
|
@ -1,7 +1,7 @@
|
|||||||
import { RenderAdapter } from '@motajs/render-core';
|
import { RenderAdapter } from '@motajs/render-core';
|
||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
import { ILayerRenderExtends, Layer, LayerMovingRenderable } from './layer';
|
import { ILayerRenderExtends, Layer, LayerMovingRenderable } from './layer';
|
||||||
import { SizedCanvasImageSource } from './types';
|
import { SizedCanvasImageSource } from '@motajs/render-elements';
|
||||||
import EventEmitter from 'eventemitter3';
|
import EventEmitter from 'eventemitter3';
|
||||||
import { texture } from './cache';
|
import { texture } from './cache';
|
||||||
import { TimingFn } from 'mutate-animate';
|
import { TimingFn } from 'mutate-animate';
|
77
packages-user/client-modules/src/render/elements/index.ts
Normal file
77
packages-user/client-modules/src/render/elements/index.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { standardElementNoCache, tagMap } from '@motajs/render-vue';
|
||||||
|
import { createCache } from './cache';
|
||||||
|
import { createFrame } from './frame';
|
||||||
|
import { createLayer, Layer, LayerGroup } from './layer';
|
||||||
|
import { createViewport } from './viewport';
|
||||||
|
import { Icon, Winskin } from './misc';
|
||||||
|
import { Animate } from './animate';
|
||||||
|
|
||||||
|
export function createElements() {
|
||||||
|
createCache();
|
||||||
|
createFrame();
|
||||||
|
createLayer();
|
||||||
|
createViewport();
|
||||||
|
|
||||||
|
// ----- 注册标签
|
||||||
|
|
||||||
|
tagMap.register('winskin', (_0, _1, props) => {
|
||||||
|
if (!props)
|
||||||
|
return new Winskin(core.material.images.images['winskin.png']);
|
||||||
|
else {
|
||||||
|
const {
|
||||||
|
image = core.material.images.images['winskin.png'],
|
||||||
|
type = 'static'
|
||||||
|
} = props;
|
||||||
|
return new Winskin(image, type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tagMap.register('layer', (_0, _1, props) => {
|
||||||
|
if (!props) return new Layer();
|
||||||
|
else {
|
||||||
|
const { ex } = props;
|
||||||
|
const l = new Layer();
|
||||||
|
|
||||||
|
if (ex) {
|
||||||
|
(ex as any[]).forEach(v => {
|
||||||
|
l.extends(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tagMap.register('layer-group', (_0, _1, props) => {
|
||||||
|
if (!props) return new LayerGroup();
|
||||||
|
else {
|
||||||
|
const { ex, layers } = props;
|
||||||
|
const l = new LayerGroup();
|
||||||
|
|
||||||
|
if (ex) {
|
||||||
|
(ex as any[]).forEach(v => {
|
||||||
|
l.extends(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (layers) {
|
||||||
|
(layers as any[]).forEach(v => {
|
||||||
|
l.addLayer(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tagMap.register('animation', (_0, _1, _props) => {
|
||||||
|
return new Animate();
|
||||||
|
});
|
||||||
|
tagMap.register('icon', standardElementNoCache(Icon));
|
||||||
|
}
|
||||||
|
|
||||||
|
export * from './animate';
|
||||||
|
export * from './block';
|
||||||
|
export * from './cache';
|
||||||
|
export * from './camera';
|
||||||
|
export * from './frame';
|
||||||
|
export * from './hero';
|
||||||
|
export * from './layer';
|
||||||
|
export * from './misc';
|
||||||
|
export * from './viewport';
|
331
packages-user/client-modules/src/render/elements/misc.ts
Normal file
331
packages-user/client-modules/src/render/elements/misc.ts
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
import { logger } from '@motajs/common';
|
||||||
|
import {
|
||||||
|
ERenderItemEvent,
|
||||||
|
RenderItem,
|
||||||
|
RenderItemPosition,
|
||||||
|
MotaOffscreenCanvas2D,
|
||||||
|
Transform
|
||||||
|
} from '@motajs/render-core';
|
||||||
|
import { SizedCanvasImageSource } from '@motajs/render-elements';
|
||||||
|
import { isNil } from 'lodash-es';
|
||||||
|
import { RenderableData, AutotileRenderable, texture } from './cache';
|
||||||
|
import { IAnimateFrame, renderEmits } from './frame';
|
||||||
|
|
||||||
|
export interface EIconEvent extends ERenderItemEvent {}
|
||||||
|
|
||||||
|
export class Icon extends RenderItem<EIconEvent> implements IAnimateFrame {
|
||||||
|
/** 图标id */
|
||||||
|
icon: AllNumbers = 0;
|
||||||
|
/** 帧数 */
|
||||||
|
frame: number = 0;
|
||||||
|
/** 是否启用动画 */
|
||||||
|
animate: boolean = false;
|
||||||
|
/** 图标的渲染信息 */
|
||||||
|
private renderable?: RenderableData | AutotileRenderable;
|
||||||
|
|
||||||
|
private pendingIcon?: AllNumbers;
|
||||||
|
|
||||||
|
constructor(type: RenderItemPosition, cache?: boolean, fall?: boolean) {
|
||||||
|
super(type, cache, fall);
|
||||||
|
this.setAntiAliasing(false);
|
||||||
|
this.setHD(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
_transform: Transform
|
||||||
|
): void {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
const renderable = this.renderable;
|
||||||
|
if (!renderable) return;
|
||||||
|
const [x, y, w, h] = renderable.render[0];
|
||||||
|
const cw = this.width;
|
||||||
|
const ch = this.height;
|
||||||
|
const frame = this.animate
|
||||||
|
? RenderItem.animatedFrame % renderable.frame
|
||||||
|
: this.frame;
|
||||||
|
|
||||||
|
if (!this.animate) {
|
||||||
|
if (renderable.autotile) {
|
||||||
|
ctx.drawImage(renderable.image[0], x, y, w, h, 0, 0, cw, ch);
|
||||||
|
} else {
|
||||||
|
ctx.drawImage(renderable.image, x, y, w, h, 0, 0, cw, ch);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const [x1, y1, w1, h1] = renderable.render[frame];
|
||||||
|
if (renderable.autotile) {
|
||||||
|
const img = renderable.image[0];
|
||||||
|
ctx.drawImage(img, x1, y1, w1, h1, 0, 0, cw, ch);
|
||||||
|
} else {
|
||||||
|
ctx.drawImage(renderable.image, x1, y1, w1, h1, 0, 0, cw, ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置图标
|
||||||
|
* @param id 图标id
|
||||||
|
*/
|
||||||
|
setIcon(id: AllIds | AllNumbers) {
|
||||||
|
if (id === 0) {
|
||||||
|
this.renderable = void 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const num = typeof id === 'number' ? id : texture.idNumberMap[id];
|
||||||
|
|
||||||
|
const { loading } = Mota.require('@user/data-base');
|
||||||
|
if (loading.loaded) {
|
||||||
|
this.setIconRenderable(num);
|
||||||
|
} else {
|
||||||
|
if (isNil(this.pendingIcon)) {
|
||||||
|
loading.once('loaded', () => {
|
||||||
|
this.setIconRenderable(this.pendingIcon ?? 0);
|
||||||
|
delete this.pendingIcon;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.pendingIcon = num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setIconRenderable(num: AllNumbers) {
|
||||||
|
const renderable = texture.getRenderable(num);
|
||||||
|
|
||||||
|
if (!renderable) {
|
||||||
|
logger.warn(43, num.toString());
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.icon = num;
|
||||||
|
this.renderable = renderable;
|
||||||
|
this.frame = renderable.frame;
|
||||||
|
}
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新动画帧
|
||||||
|
*/
|
||||||
|
updateFrameAnimate(): void {
|
||||||
|
if (this.animate) this.update(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
renderEmits.removeFramer(this);
|
||||||
|
super.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected handleProps(
|
||||||
|
key: string,
|
||||||
|
_prevValue: any,
|
||||||
|
nextValue: any
|
||||||
|
): boolean {
|
||||||
|
switch (key) {
|
||||||
|
case 'icon':
|
||||||
|
this.setIcon(nextValue);
|
||||||
|
return true;
|
||||||
|
case 'animate':
|
||||||
|
if (!this.assertType(nextValue, 'boolean', key)) return false;
|
||||||
|
this.animate = nextValue;
|
||||||
|
if (nextValue) renderEmits.addFramer(this);
|
||||||
|
else renderEmits.removeFramer(this);
|
||||||
|
this.update();
|
||||||
|
return true;
|
||||||
|
case 'frame':
|
||||||
|
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||||
|
this.frame = nextValue;
|
||||||
|
this.update();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WinskinPatterns {
|
||||||
|
top: CanvasPattern;
|
||||||
|
left: CanvasPattern;
|
||||||
|
bottom: CanvasPattern;
|
||||||
|
right: CanvasPattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EWinskinEvent extends ERenderItemEvent {}
|
||||||
|
|
||||||
|
export class Winskin extends RenderItem<EWinskinEvent> {
|
||||||
|
image: SizedCanvasImageSource;
|
||||||
|
/** 边框宽度,32表示原始宽度 */
|
||||||
|
borderSize: number = 32;
|
||||||
|
/** 图片名称 */
|
||||||
|
imageName?: string;
|
||||||
|
|
||||||
|
private pendingImage?: ImageIds;
|
||||||
|
private patternCache?: WinskinPatterns;
|
||||||
|
private patternTransform: DOMMatrix;
|
||||||
|
|
||||||
|
private static patternMap: Map<string, WinskinPatterns> = new Map();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
image: SizedCanvasImageSource,
|
||||||
|
type: RenderItemPosition = 'static'
|
||||||
|
) {
|
||||||
|
super(type, false, false);
|
||||||
|
this.image = image;
|
||||||
|
this.setAntiAliasing(false);
|
||||||
|
|
||||||
|
if (window.DOMMatrix) {
|
||||||
|
this.patternTransform = new DOMMatrix();
|
||||||
|
} else if (window.WebKitCSSMatrix) {
|
||||||
|
this.patternTransform = new WebKitCSSMatrix();
|
||||||
|
} else {
|
||||||
|
this.patternTransform = new SVGMatrix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private generatePattern() {
|
||||||
|
const pattern = this.requireCanvas();
|
||||||
|
const img = this.image;
|
||||||
|
pattern.size(32, 16);
|
||||||
|
pattern.withGameScale(false);
|
||||||
|
pattern.setHD(false);
|
||||||
|
pattern.setAntiAliasing(false);
|
||||||
|
const ctx = pattern.ctx;
|
||||||
|
ctx.drawImage(img, 144, 0, 32, 16, 0, 0, 32, 16);
|
||||||
|
const topPattern = ctx.createPattern(pattern.canvas, 'repeat');
|
||||||
|
ctx.clearRect(0, 0, 32, 16);
|
||||||
|
ctx.drawImage(img, 144, 48, 32, 16, 0, 0, 32, 16);
|
||||||
|
const bottomPattern = ctx.createPattern(pattern.canvas, 'repeat');
|
||||||
|
ctx.clearRect(0, 0, 32, 16);
|
||||||
|
pattern.size(16, 32);
|
||||||
|
ctx.drawImage(img, 128, 16, 16, 32, 0, 0, 16, 32);
|
||||||
|
const leftPattern = ctx.createPattern(pattern.canvas, 'repeat');
|
||||||
|
ctx.clearRect(0, 0, 16, 32);
|
||||||
|
ctx.drawImage(img, 176, 16, 16, 32, 0, 0, 16, 32);
|
||||||
|
const rightPattern = ctx.createPattern(pattern.canvas, 'repeat');
|
||||||
|
if (!topPattern || !bottomPattern || !leftPattern || !rightPattern) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const winskinPattern: WinskinPatterns = {
|
||||||
|
top: topPattern,
|
||||||
|
bottom: bottomPattern,
|
||||||
|
left: leftPattern,
|
||||||
|
right: rightPattern
|
||||||
|
};
|
||||||
|
if (this.imageName) {
|
||||||
|
Winskin.patternMap.set(this.imageName, winskinPattern);
|
||||||
|
}
|
||||||
|
this.patternCache = winskinPattern;
|
||||||
|
this.deleteCanvas(pattern);
|
||||||
|
return winskinPattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPattern() {
|
||||||
|
if (!this.imageName) {
|
||||||
|
if (this.patternCache) return this.patternCache;
|
||||||
|
return this.generatePattern();
|
||||||
|
} else {
|
||||||
|
const pattern = Winskin.patternMap.get(this.imageName);
|
||||||
|
if (pattern) return pattern;
|
||||||
|
return this.generatePattern();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
_transform: Transform
|
||||||
|
): void {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
const img = this.image;
|
||||||
|
const w = this.width;
|
||||||
|
const h = this.height;
|
||||||
|
const pad = this.borderSize / 2;
|
||||||
|
// 背景
|
||||||
|
ctx.drawImage(img, 0, 0, 128, 128, 2, 2, w - 4, h - 4);
|
||||||
|
const pattern = this.getPattern();
|
||||||
|
if (!pattern) return;
|
||||||
|
const { top, left, right, bottom } = pattern;
|
||||||
|
top.setTransform(this.patternTransform);
|
||||||
|
left.setTransform(this.patternTransform);
|
||||||
|
right.setTransform(this.patternTransform);
|
||||||
|
bottom.setTransform(this.patternTransform);
|
||||||
|
// 上下左右边框
|
||||||
|
ctx.save();
|
||||||
|
ctx.fillStyle = top;
|
||||||
|
ctx.translate(pad, 0);
|
||||||
|
ctx.fillRect(0, 0, w - pad * 2, pad);
|
||||||
|
ctx.fillStyle = bottom;
|
||||||
|
ctx.translate(0, h - pad);
|
||||||
|
ctx.fillRect(0, 0, w - pad * 2, pad);
|
||||||
|
ctx.fillStyle = left;
|
||||||
|
ctx.translate(-pad, pad * 2 - h);
|
||||||
|
ctx.fillRect(0, 0, pad, h - pad * 2);
|
||||||
|
ctx.fillStyle = right;
|
||||||
|
ctx.translate(w - pad, 0);
|
||||||
|
ctx.fillRect(0, 0, pad, h - pad * 2);
|
||||||
|
ctx.restore();
|
||||||
|
// 四个角的边框
|
||||||
|
ctx.drawImage(img, 128, 0, 16, 16, 0, 0, pad, pad);
|
||||||
|
ctx.drawImage(img, 176, 0, 16, 16, w - pad, 0, pad, pad);
|
||||||
|
ctx.drawImage(img, 128, 48, 16, 16, 0, h - pad, pad, pad);
|
||||||
|
ctx.drawImage(img, 176, 48, 16, 16, w - pad, h - pad, pad, pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置winskin图片
|
||||||
|
* @param image winskin图片
|
||||||
|
*/
|
||||||
|
setImage(image: SizedCanvasImageSource) {
|
||||||
|
this.image = image;
|
||||||
|
this.patternCache = void 0;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过图片名称设置winskin
|
||||||
|
* @param name 图片名称
|
||||||
|
*/
|
||||||
|
setImageByName(name: ImageIds) {
|
||||||
|
const { loading } = Mota.require('@user/data-base');
|
||||||
|
if (loading.loaded) {
|
||||||
|
const image = core.material.images.images[name];
|
||||||
|
this.setImage(image);
|
||||||
|
} else {
|
||||||
|
if (isNil(this.pendingImage)) {
|
||||||
|
loading.once('loaded', () => {
|
||||||
|
const id = this.pendingImage;
|
||||||
|
if (!id) return;
|
||||||
|
const image = core.material.images.images[id];
|
||||||
|
this.setImage(image);
|
||||||
|
delete this.pendingImage;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.pendingImage = name;
|
||||||
|
}
|
||||||
|
this.imageName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置边框大小
|
||||||
|
* @param size 边框大小
|
||||||
|
*/
|
||||||
|
setBorderSize(size: number) {
|
||||||
|
this.borderSize = size;
|
||||||
|
this.patternTransform.a = size / 32;
|
||||||
|
this.patternTransform.d = size / 32;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected handleProps(
|
||||||
|
key: string,
|
||||||
|
_prevValue: any,
|
||||||
|
nextValue: any
|
||||||
|
): boolean {
|
||||||
|
switch (key) {
|
||||||
|
case 'image':
|
||||||
|
if (!this.assertType(nextValue, 'string', key)) return false;
|
||||||
|
this.setImageByName(nextValue);
|
||||||
|
return true;
|
||||||
|
case 'borderSize':
|
||||||
|
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||||
|
this.setBorderSize(nextValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ import { hook } from '@user/data-base';
|
|||||||
import { createItemDetail } from './itemDetail';
|
import { createItemDetail } from './itemDetail';
|
||||||
import { createLoopMap } from './loopMap';
|
import { createLoopMap } from './loopMap';
|
||||||
import { createGameCanvas } from './legacy/gameCanvas';
|
import { createGameCanvas } from './legacy/gameCanvas';
|
||||||
|
import { createElements } from './elements';
|
||||||
|
|
||||||
export function createGameRenderer() {
|
export function createGameRenderer() {
|
||||||
const main = new MotaRenderer();
|
const main = new MotaRenderer();
|
||||||
@ -41,8 +42,10 @@ export function createRender() {
|
|||||||
createGameCanvas();
|
createGameCanvas();
|
||||||
createItemDetail();
|
createItemDetail();
|
||||||
createLoopMap();
|
createLoopMap();
|
||||||
|
createElements();
|
||||||
}
|
}
|
||||||
|
|
||||||
export * from './components';
|
export * from './components';
|
||||||
export * from './ui';
|
export * from './ui';
|
||||||
export * from './use';
|
export * from './use';
|
||||||
|
export * from './elements';
|
||||||
|
@ -6,11 +6,6 @@ import {
|
|||||||
MotaOffscreenCanvas2D
|
MotaOffscreenCanvas2D
|
||||||
} from '@motajs/render-core';
|
} from '@motajs/render-core';
|
||||||
import { Font } from '@motajs/render-style';
|
import { Font } from '@motajs/render-style';
|
||||||
import { logger } from '@motajs/common';
|
|
||||||
import { isNil } from 'lodash-es';
|
|
||||||
import { IAnimateFrame, renderEmits } from './frame';
|
|
||||||
import { AutotileRenderable, RenderableData, texture } from './cache';
|
|
||||||
import { SizedCanvasImageSource } from './types';
|
|
||||||
|
|
||||||
/** 文字的安全填充,会填充在文字的上侧和下侧,防止削顶和削底 */
|
/** 文字的安全填充,会填充在文字的上侧和下侧,防止削顶和削底 */
|
||||||
const SAFE_PAD = 1;
|
const SAFE_PAD = 1;
|
||||||
@ -229,322 +224,3 @@ export class Comment extends RenderItem {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EIconEvent extends ERenderItemEvent {}
|
|
||||||
|
|
||||||
export class Icon extends RenderItem<EIconEvent> implements IAnimateFrame {
|
|
||||||
/** 图标id */
|
|
||||||
icon: AllNumbers = 0;
|
|
||||||
/** 帧数 */
|
|
||||||
frame: number = 0;
|
|
||||||
/** 是否启用动画 */
|
|
||||||
animate: boolean = false;
|
|
||||||
/** 图标的渲染信息 */
|
|
||||||
private renderable?: RenderableData | AutotileRenderable;
|
|
||||||
|
|
||||||
private pendingIcon?: AllNumbers;
|
|
||||||
|
|
||||||
constructor(type: RenderItemPosition, cache?: boolean, fall?: boolean) {
|
|
||||||
super(type, cache, fall);
|
|
||||||
this.setAntiAliasing(false);
|
|
||||||
this.setHD(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(
|
|
||||||
canvas: MotaOffscreenCanvas2D,
|
|
||||||
_transform: Transform
|
|
||||||
): void {
|
|
||||||
const ctx = canvas.ctx;
|
|
||||||
const renderable = this.renderable;
|
|
||||||
if (!renderable) return;
|
|
||||||
const [x, y, w, h] = renderable.render[0];
|
|
||||||
const cw = this.width;
|
|
||||||
const ch = this.height;
|
|
||||||
const frame = this.animate
|
|
||||||
? RenderItem.animatedFrame % renderable.frame
|
|
||||||
: this.frame;
|
|
||||||
|
|
||||||
if (!this.animate) {
|
|
||||||
if (renderable.autotile) {
|
|
||||||
ctx.drawImage(renderable.image[0], x, y, w, h, 0, 0, cw, ch);
|
|
||||||
} else {
|
|
||||||
ctx.drawImage(renderable.image, x, y, w, h, 0, 0, cw, ch);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const [x1, y1, w1, h1] = renderable.render[frame];
|
|
||||||
if (renderable.autotile) {
|
|
||||||
const img = renderable.image[0];
|
|
||||||
ctx.drawImage(img, x1, y1, w1, h1, 0, 0, cw, ch);
|
|
||||||
} else {
|
|
||||||
ctx.drawImage(renderable.image, x1, y1, w1, h1, 0, 0, cw, ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置图标
|
|
||||||
* @param id 图标id
|
|
||||||
*/
|
|
||||||
setIcon(id: AllIds | AllNumbers) {
|
|
||||||
if (id === 0) {
|
|
||||||
this.renderable = void 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const num = typeof id === 'number' ? id : texture.idNumberMap[id];
|
|
||||||
|
|
||||||
const { loading } = Mota.require('@user/data-base');
|
|
||||||
if (loading.loaded) {
|
|
||||||
this.setIconRenderable(num);
|
|
||||||
} else {
|
|
||||||
if (isNil(this.pendingIcon)) {
|
|
||||||
loading.once('loaded', () => {
|
|
||||||
this.setIconRenderable(this.pendingIcon ?? 0);
|
|
||||||
delete this.pendingIcon;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.pendingIcon = num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private setIconRenderable(num: AllNumbers) {
|
|
||||||
const renderable = texture.getRenderable(num);
|
|
||||||
|
|
||||||
if (!renderable) {
|
|
||||||
logger.warn(43, num.toString());
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
this.icon = num;
|
|
||||||
this.renderable = renderable;
|
|
||||||
this.frame = renderable.frame;
|
|
||||||
}
|
|
||||||
this.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新动画帧
|
|
||||||
*/
|
|
||||||
updateFrameAnimate(): void {
|
|
||||||
if (this.animate) this.update(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy(): void {
|
|
||||||
renderEmits.removeFramer(this);
|
|
||||||
super.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected handleProps(
|
|
||||||
key: string,
|
|
||||||
_prevValue: any,
|
|
||||||
nextValue: any
|
|
||||||
): boolean {
|
|
||||||
switch (key) {
|
|
||||||
case 'icon':
|
|
||||||
this.setIcon(nextValue);
|
|
||||||
return true;
|
|
||||||
case 'animate':
|
|
||||||
if (!this.assertType(nextValue, 'boolean', key)) return false;
|
|
||||||
this.animate = nextValue;
|
|
||||||
if (nextValue) renderEmits.addFramer(this);
|
|
||||||
else renderEmits.removeFramer(this);
|
|
||||||
this.update();
|
|
||||||
return true;
|
|
||||||
case 'frame':
|
|
||||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
|
||||||
this.frame = nextValue;
|
|
||||||
this.update();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WinskinPatterns {
|
|
||||||
top: CanvasPattern;
|
|
||||||
left: CanvasPattern;
|
|
||||||
bottom: CanvasPattern;
|
|
||||||
right: CanvasPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EWinskinEvent extends ERenderItemEvent {}
|
|
||||||
|
|
||||||
export class Winskin extends RenderItem<EWinskinEvent> {
|
|
||||||
image: SizedCanvasImageSource;
|
|
||||||
/** 边框宽度,32表示原始宽度 */
|
|
||||||
borderSize: number = 32;
|
|
||||||
/** 图片名称 */
|
|
||||||
imageName?: string;
|
|
||||||
|
|
||||||
private pendingImage?: ImageIds;
|
|
||||||
private patternCache?: WinskinPatterns;
|
|
||||||
private patternTransform: DOMMatrix;
|
|
||||||
|
|
||||||
private static patternMap: Map<string, WinskinPatterns> = new Map();
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
image: SizedCanvasImageSource,
|
|
||||||
type: RenderItemPosition = 'static'
|
|
||||||
) {
|
|
||||||
super(type, false, false);
|
|
||||||
this.image = image;
|
|
||||||
this.setAntiAliasing(false);
|
|
||||||
|
|
||||||
if (window.DOMMatrix) {
|
|
||||||
this.patternTransform = new DOMMatrix();
|
|
||||||
} else if (window.WebKitCSSMatrix) {
|
|
||||||
this.patternTransform = new WebKitCSSMatrix();
|
|
||||||
} else {
|
|
||||||
this.patternTransform = new SVGMatrix();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private generatePattern() {
|
|
||||||
const pattern = this.requireCanvas();
|
|
||||||
const img = this.image;
|
|
||||||
pattern.size(32, 16);
|
|
||||||
pattern.withGameScale(false);
|
|
||||||
pattern.setHD(false);
|
|
||||||
pattern.setAntiAliasing(false);
|
|
||||||
const ctx = pattern.ctx;
|
|
||||||
ctx.drawImage(img, 144, 0, 32, 16, 0, 0, 32, 16);
|
|
||||||
const topPattern = ctx.createPattern(pattern.canvas, 'repeat');
|
|
||||||
ctx.clearRect(0, 0, 32, 16);
|
|
||||||
ctx.drawImage(img, 144, 48, 32, 16, 0, 0, 32, 16);
|
|
||||||
const bottomPattern = ctx.createPattern(pattern.canvas, 'repeat');
|
|
||||||
ctx.clearRect(0, 0, 32, 16);
|
|
||||||
pattern.size(16, 32);
|
|
||||||
ctx.drawImage(img, 128, 16, 16, 32, 0, 0, 16, 32);
|
|
||||||
const leftPattern = ctx.createPattern(pattern.canvas, 'repeat');
|
|
||||||
ctx.clearRect(0, 0, 16, 32);
|
|
||||||
ctx.drawImage(img, 176, 16, 16, 32, 0, 0, 16, 32);
|
|
||||||
const rightPattern = ctx.createPattern(pattern.canvas, 'repeat');
|
|
||||||
if (!topPattern || !bottomPattern || !leftPattern || !rightPattern) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const winskinPattern: WinskinPatterns = {
|
|
||||||
top: topPattern,
|
|
||||||
bottom: bottomPattern,
|
|
||||||
left: leftPattern,
|
|
||||||
right: rightPattern
|
|
||||||
};
|
|
||||||
if (this.imageName) {
|
|
||||||
Winskin.patternMap.set(this.imageName, winskinPattern);
|
|
||||||
}
|
|
||||||
this.patternCache = winskinPattern;
|
|
||||||
this.deleteCanvas(pattern);
|
|
||||||
return winskinPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getPattern() {
|
|
||||||
if (!this.imageName) {
|
|
||||||
if (this.patternCache) return this.patternCache;
|
|
||||||
return this.generatePattern();
|
|
||||||
} else {
|
|
||||||
const pattern = Winskin.patternMap.get(this.imageName);
|
|
||||||
if (pattern) return pattern;
|
|
||||||
return this.generatePattern();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(
|
|
||||||
canvas: MotaOffscreenCanvas2D,
|
|
||||||
_transform: Transform
|
|
||||||
): void {
|
|
||||||
const ctx = canvas.ctx;
|
|
||||||
const img = this.image;
|
|
||||||
const w = this.width;
|
|
||||||
const h = this.height;
|
|
||||||
const pad = this.borderSize / 2;
|
|
||||||
// 背景
|
|
||||||
ctx.drawImage(img, 0, 0, 128, 128, 2, 2, w - 4, h - 4);
|
|
||||||
const pattern = this.getPattern();
|
|
||||||
if (!pattern) return;
|
|
||||||
const { top, left, right, bottom } = pattern;
|
|
||||||
top.setTransform(this.patternTransform);
|
|
||||||
left.setTransform(this.patternTransform);
|
|
||||||
right.setTransform(this.patternTransform);
|
|
||||||
bottom.setTransform(this.patternTransform);
|
|
||||||
// 上下左右边框
|
|
||||||
ctx.save();
|
|
||||||
ctx.fillStyle = top;
|
|
||||||
ctx.translate(pad, 0);
|
|
||||||
ctx.fillRect(0, 0, w - pad * 2, pad);
|
|
||||||
ctx.fillStyle = bottom;
|
|
||||||
ctx.translate(0, h - pad);
|
|
||||||
ctx.fillRect(0, 0, w - pad * 2, pad);
|
|
||||||
ctx.fillStyle = left;
|
|
||||||
ctx.translate(-pad, pad * 2 - h);
|
|
||||||
ctx.fillRect(0, 0, pad, h - pad * 2);
|
|
||||||
ctx.fillStyle = right;
|
|
||||||
ctx.translate(w - pad, 0);
|
|
||||||
ctx.fillRect(0, 0, pad, h - pad * 2);
|
|
||||||
ctx.restore();
|
|
||||||
// 四个角的边框
|
|
||||||
ctx.drawImage(img, 128, 0, 16, 16, 0, 0, pad, pad);
|
|
||||||
ctx.drawImage(img, 176, 0, 16, 16, w - pad, 0, pad, pad);
|
|
||||||
ctx.drawImage(img, 128, 48, 16, 16, 0, h - pad, pad, pad);
|
|
||||||
ctx.drawImage(img, 176, 48, 16, 16, w - pad, h - pad, pad, pad);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置winskin图片
|
|
||||||
* @param image winskin图片
|
|
||||||
*/
|
|
||||||
setImage(image: SizedCanvasImageSource) {
|
|
||||||
this.image = image;
|
|
||||||
this.patternCache = void 0;
|
|
||||||
this.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过图片名称设置winskin
|
|
||||||
* @param name 图片名称
|
|
||||||
*/
|
|
||||||
setImageByName(name: ImageIds) {
|
|
||||||
const { loading } = Mota.require('@user/data-base');
|
|
||||||
if (loading.loaded) {
|
|
||||||
const image = core.material.images.images[name];
|
|
||||||
this.setImage(image);
|
|
||||||
} else {
|
|
||||||
if (isNil(this.pendingImage)) {
|
|
||||||
loading.once('loaded', () => {
|
|
||||||
const id = this.pendingImage;
|
|
||||||
if (!id) return;
|
|
||||||
const image = core.material.images.images[id];
|
|
||||||
this.setImage(image);
|
|
||||||
delete this.pendingImage;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.pendingImage = name;
|
|
||||||
}
|
|
||||||
this.imageName = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置边框大小
|
|
||||||
* @param size 边框大小
|
|
||||||
*/
|
|
||||||
setBorderSize(size: number) {
|
|
||||||
this.borderSize = size;
|
|
||||||
this.patternTransform.a = size / 32;
|
|
||||||
this.patternTransform.d = size / 32;
|
|
||||||
this.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected handleProps(
|
|
||||||
key: string,
|
|
||||||
_prevValue: any,
|
|
||||||
nextValue: any
|
|
||||||
): boolean {
|
|
||||||
switch (key) {
|
|
||||||
case 'image':
|
|
||||||
if (!this.assertType(nextValue, 'string', key)) return false;
|
|
||||||
this.setImageByName(nextValue);
|
|
||||||
return true;
|
|
||||||
case 'borderSize':
|
|
||||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
|
||||||
this.setBorderSize(nextValue);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -12,13 +12,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
Comment,
|
Comment,
|
||||||
ETextEvent,
|
ETextEvent,
|
||||||
Icon,
|
|
||||||
Image,
|
Image,
|
||||||
Text,
|
Text,
|
||||||
Winskin,
|
|
||||||
Animate,
|
|
||||||
Layer,
|
|
||||||
LayerGroup,
|
|
||||||
BezierCurve,
|
BezierCurve,
|
||||||
Circle,
|
Circle,
|
||||||
Ellipse,
|
Ellipse,
|
||||||
@ -70,7 +65,7 @@ class RenderTagMap {
|
|||||||
|
|
||||||
export const tagMap = new RenderTagMap();
|
export const tagMap = new RenderTagMap();
|
||||||
|
|
||||||
const standardElement = (
|
export const standardElement = (
|
||||||
Item: new (
|
Item: new (
|
||||||
type: RenderItemPosition,
|
type: RenderItemPosition,
|
||||||
cache?: boolean,
|
cache?: boolean,
|
||||||
@ -91,7 +86,7 @@ const standardElement = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const standardElementNoCache = (
|
export const standardElementNoCache = (
|
||||||
Item: new (
|
Item: new (
|
||||||
type: RenderItemPosition,
|
type: RenderItemPosition,
|
||||||
cache?: boolean,
|
cache?: boolean,
|
||||||
@ -199,44 +194,6 @@ tagMap.register('custom', (_0, _1, props) => {
|
|||||||
return item(props);
|
return item(props);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
tagMap.register('layer', (_0, _1, props) => {
|
|
||||||
if (!props) return new Layer();
|
|
||||||
else {
|
|
||||||
const { ex } = props;
|
|
||||||
const l = new Layer();
|
|
||||||
|
|
||||||
if (ex) {
|
|
||||||
(ex as any[]).forEach(v => {
|
|
||||||
l.extends(v);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
tagMap.register('layer-group', (_0, _1, props) => {
|
|
||||||
if (!props) return new LayerGroup();
|
|
||||||
else {
|
|
||||||
const { ex, layers } = props;
|
|
||||||
const l = new LayerGroup();
|
|
||||||
|
|
||||||
if (ex) {
|
|
||||||
(ex as any[]).forEach(v => {
|
|
||||||
l.extends(v);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (layers) {
|
|
||||||
(layers as any[]).forEach(v => {
|
|
||||||
l.addLayer(v);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
tagMap.register('animation', (_0, _1, _props) => {
|
|
||||||
return new Animate();
|
|
||||||
});
|
|
||||||
tagMap.register('g-rect', standardElementNoCache(Rect));
|
tagMap.register('g-rect', standardElementNoCache(Rect));
|
||||||
tagMap.register('g-circle', standardElementNoCache(Circle));
|
tagMap.register('g-circle', standardElementNoCache(Circle));
|
||||||
tagMap.register('g-ellipse', standardElementNoCache(Ellipse));
|
tagMap.register('g-ellipse', standardElementNoCache(Ellipse));
|
||||||
@ -245,14 +202,3 @@ tagMap.register('g-bezier', standardElementNoCache(BezierCurve));
|
|||||||
tagMap.register('g-quad', standardElementNoCache(QuadraticCurve));
|
tagMap.register('g-quad', standardElementNoCache(QuadraticCurve));
|
||||||
tagMap.register('g-path', standardElementNoCache(Path));
|
tagMap.register('g-path', standardElementNoCache(Path));
|
||||||
tagMap.register('g-rectr', standardElementNoCache(RectR));
|
tagMap.register('g-rectr', standardElementNoCache(RectR));
|
||||||
tagMap.register('icon', standardElementNoCache(Icon));
|
|
||||||
tagMap.register('winskin', (_0, _1, props) => {
|
|
||||||
if (!props) return new Winskin(core.material.images.images['winskin.png']);
|
|
||||||
else {
|
|
||||||
const {
|
|
||||||
image = core.material.images.images['winskin.png'],
|
|
||||||
type = 'static'
|
|
||||||
} = props;
|
|
||||||
return new Winskin(image, type);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
Loading…
Reference in New Issue
Block a user