mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-06-08 16:27:58 +08:00
refactor: 把样板所需的标签定义移动至 client-modules
This commit is contained in:
parent
86bc383d65
commit
d72334e80b
@ -1,6 +1,6 @@
|
||||
import { logger } from '@motajs/common';
|
||||
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)
|
||||
// 得出结论,ImageBitmap和Canvas的绘制性能不如Image,于是直接画Image就行,所以缓存基本上就是存Image
|
@ -1,7 +1,7 @@
|
||||
import { RenderAdapter } from '@motajs/render-core';
|
||||
import { logger } from '@motajs/common';
|
||||
import { ILayerRenderExtends, Layer, LayerMovingRenderable } from './layer';
|
||||
import { SizedCanvasImageSource } from './types';
|
||||
import { SizedCanvasImageSource } from '@motajs/render-elements';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { texture } from './cache';
|
||||
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 { createLoopMap } from './loopMap';
|
||||
import { createGameCanvas } from './legacy/gameCanvas';
|
||||
import { createElements } from './elements';
|
||||
|
||||
export function createGameRenderer() {
|
||||
const main = new MotaRenderer();
|
||||
@ -41,8 +42,10 @@ export function createRender() {
|
||||
createGameCanvas();
|
||||
createItemDetail();
|
||||
createLoopMap();
|
||||
createElements();
|
||||
}
|
||||
|
||||
export * from './components';
|
||||
export * from './ui';
|
||||
export * from './use';
|
||||
export * from './elements';
|
||||
|
@ -6,11 +6,6 @@ import {
|
||||
MotaOffscreenCanvas2D
|
||||
} from '@motajs/render-core';
|
||||
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;
|
||||
@ -229,322 +224,3 @@ export class Comment extends RenderItem {
|
||||
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 {
|
||||
Comment,
|
||||
ETextEvent,
|
||||
Icon,
|
||||
Image,
|
||||
Text,
|
||||
Winskin,
|
||||
Animate,
|
||||
Layer,
|
||||
LayerGroup,
|
||||
BezierCurve,
|
||||
Circle,
|
||||
Ellipse,
|
||||
@ -70,7 +65,7 @@ class RenderTagMap {
|
||||
|
||||
export const tagMap = new RenderTagMap();
|
||||
|
||||
const standardElement = (
|
||||
export const standardElement = (
|
||||
Item: new (
|
||||
type: RenderItemPosition,
|
||||
cache?: boolean,
|
||||
@ -91,7 +86,7 @@ const standardElement = (
|
||||
};
|
||||
};
|
||||
|
||||
const standardElementNoCache = (
|
||||
export const standardElementNoCache = (
|
||||
Item: new (
|
||||
type: RenderItemPosition,
|
||||
cache?: boolean,
|
||||
@ -199,44 +194,6 @@ tagMap.register('custom', (_0, _1, 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-circle', standardElementNoCache(Circle));
|
||||
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-path', standardElementNoCache(Path));
|
||||
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