UNPKG

@turbox3d/graphic-component-pixi

Version:

Graphic component library based on pixi

202 lines (187 loc) 5.7 kB
import * as PIXI from 'pixi.js'; import { Mesh2D } from '@turbox3d/renderer-pixi'; import { fail, Vec2, cropImage } from '@turbox3d/shared'; import DrawUtils from '../draw-utils'; import { IFitStyle } from '../draw-utils/drawRect'; export interface IImage2dProps { x?: number; y?: number; width: number; height: number; rotation?: number; scale?: Vec2; /** * 传入的位置坐标是否是矩形中心点 */ central?: boolean; radius?: number; lineWidth?: number; lineColor?: number; lineAlpha?: number; /** * 边框内扩 0、外扩 1 */ alignment?: number; native?: boolean; fillColor?: number; fillAlpha?: number; alpha?: number; backgroundImage?: string | HTMLImageElement; materialDirection?: Vec2; fit?: IFitStyle; zIndex?: number; } /** UI组件-图片 */ export default class Image2d extends Mesh2D<IImage2dProps> { protected view: PIXI.Container; protected reactivePipeLine = [ this.updateGeometry, this.updateMaterial, this.updatePosition, this.updateRotation, this.updateScale, ]; private g = new PIXI.Graphics(); private s = new PIXI.Sprite(); componentDidMount() { this.g.zIndex = -1; this.s.zIndex = -1; this.view.addChild(this.g); this.view.addChild(this.s); } private async loadTextureResource() { const { backgroundImage } = this.props; if (!backgroundImage) { return; } let t: PIXI.Texture | undefined; if (backgroundImage instanceof HTMLImageElement) { t = await PIXI.Texture.fromLoader(backgroundImage, backgroundImage.src).catch(() => { fail('Load Image2d backgroundImage texture failed.'); }); } else { t = await PIXI.Texture.fromURL(backgroundImage).catch(() => { fail('Load Image2d backgroundImage texture failed.'); }); } return t; } async updateGeometry() { this.g.clear(); this.s.visible = false; const { width, height, radius = 0, lineWidth = 0, lineColor = 0x0, lineAlpha = 1, alignment = 0, native, fillColor = 0x0, fillAlpha = 0, alpha = 1, backgroundImage = '', fit = 'none', } = this.props; let alignParam = alignment; if (alignment === 0) { alignParam = 1; } else if (alignment === 1) { alignParam = 0; } const fillPos = { x: lineWidth * alignParam, y: lineWidth * alignParam }; const rw = width - lineWidth * 2 * alignParam; const rh = height - lineWidth * 2 * alignParam; DrawUtils.drawRect(this.g, { x: 0, y: 0, width, height, central: false, radius, lineWidth, lineColor, lineAlpha, alignment, native, fillColor, fillAlpha, alpha, }); const t = await this.loadTextureResource(); if (!t) { return; } this.s.texture = t; const { width: tw, height: th } = this.s.texture; const imgRatio = tw / th; const rectRatio = width / height; const center = { x: width / 2, y: height / 2 }; if (fit === 'none') { if (rectRatio > 1) { this.s.width = rw; this.s.height = rw / imgRatio; } else { this.s.width = rh * imgRatio; this.s.height = rh; } this.s.position.set(center.x - this.s.width / 2, center.y - this.s.height / 2); } else if (fit === 'fill') { this.s.width = rw; this.s.height = rh; this.s.position.set(fillPos.x, fillPos.y); } else if (fit === 'cover') { if (imgRatio <= rectRatio) { const ratio = rw / tw; const { element } = await cropImage(backgroundImage, { start: { x: 0, y: th / 2 - rh / ratio / 2 }, end: { x: tw, y: th / 2 + rh / ratio / 2 }}); if (element) { this.s.texture = PIXI.Texture.from(element); } this.s.width = rw; this.s.height = rh; } else { const ratio = rh / th; const { element } = await cropImage(backgroundImage, { start: { x: tw / 2 - rw / ratio / 2, y: 0 }, end: { x: tw / 2 + rw / ratio / 2, y: th }}); if (element) { this.s.texture = PIXI.Texture.from(element); } this.s.width = rw; this.s.height = rh; } this.s.position.set(fillPos.x, fillPos.y); } else if (fit === 'contain') { if (imgRatio <= rectRatio) { this.s.width = rh * imgRatio; this.s.height = rh; this.s.position.set(fillPos.x + rw / 2 - (rh * imgRatio) / 2, fillPos.y); } else { this.s.width = rw; this.s.height = rw / imgRatio; this.s.position.set(fillPos.x, fillPos.y + rh / 2 - rw / imgRatio / 2); } } // 方便做贴图的镜像翻转 this.s.anchor.set(0.5, 0.5); // anchor 设置为 0.5 后,此处需要将贴图调整回去 this.s.position.set(this.s.position.x + this.s.width / 2, this.s.position.y + this.s.height / 2); this.s.visible = true; } updateMaterial() { const { zIndex = 0, materialDirection = { x: 1, y: 1 } } = this.props; this.view.zIndex = zIndex; this.s.scale.set(materialDirection.x > 0 ? Math.abs(this.s.scale.x) : -Math.abs(this.s.scale.x), materialDirection.y > 0 ? Math.abs(this.s.scale.y) : -Math.abs(this.s.scale.y)); } updatePosition() { const { x = 0, y = 0, central = false, width, height } = this.props; const [posX, posY] = central ? [x - width / 2, y - height / 2] : [x, y]; this.view.position.x = posX; this.view.position.y = posY; } updateRotation() { this.view.rotation = this.props.rotation ?? 0; } updateScale() { this.view.scale.set(this.props.scale?.x ?? 1, this.props.scale?.y ?? 1); } }