@amoy/components
Version:
345 lines (314 loc) • 11 kB
text/typescript
import * as PIXI from 'pixi.js'
import { getValue, error } from '../../utils'
const isContainer = (target: any) => {
return target instanceof PIXI.Container && !(target instanceof PIXI.Sprite ) && !(target instanceof PIXI.Graphics)
}
export function transform(uiDesignRatio: number = 1) {
function rotation(angle: string | number) {
if (typeof angle === 'string') {
const _angle = angle.trim()
const degreeUnit = Math.PI / 180
const hasUnit = _angle.endsWith('deg')
if (hasUnit) {
const [ degree ] = _angle.split('deg')
return parseFloat(degree) * degreeUnit
} else {
return parseFloat(_angle)
}
}
return angle
}
function _parseUnit(parentSize: number, value: string) {
value = value.trim()
if (value.endsWith('px')) {
return parseInt(value)
} else if (value.endsWith('%')) {
return parentSize * parseInt(value) / 100
} else {
return parseInt(value) / uiDesignRatio
}
}
function length(parentSize: number, value: string | number) {
let result: number = 0
if (typeof value === 'string') result = _parseUnit(parentSize, value)
if (typeof value === 'number') result = value / uiDesignRatio
return Math.round(result)
}
function pos(parentSize: number, spriteSize: number, value: number | string) {
if (['number', 'string'].includes(typeof value)) {
let result: number = 0
if (typeof value === 'number') {
result = value / uiDesignRatio
} else {
if (value.includes(':')) {
const [key, number] = value.split(':')
switch (key.trim()) {
case 'left':
case 'top':
result = _parseUnit(parentSize, number)
break
case 'right':
case 'bottom':
result = parentSize - spriteSize
result -= _parseUnit(parentSize, number)
break
case 'center':
result = (parentSize - spriteSize) / 2
result += _parseUnit(parentSize, number)
break
}
} else if (value === 'center') {
result = (parentSize - spriteSize) / 2
} else {
result = _parseUnit(parentSize, value)
}
}
return Math.round(result)
} else {
throw error('value must be number or string')
}
}
function actualScale(parent: Layout.element) {
const scale = {
x: parent.scale.x,
y: parent.scale.y,
}
let _parent = parent.parent
while (_parent) {
scale.x *= _parent.scale.x
scale.y *= _parent.scale.y
_parent = _parent.parent
}
return scale
}
function getPixiContainerSize(container: PIXI.Container) {
if (container.parent) {
if (container['isStage']) {
return {
width: window.innerWidth,
height: window.innerHeight,
}
} else if (container.width > 0 && container.height > 0) {
return {
width: container.width,
height: container.height,
}
} else if (isContainer(container.parent)) {
return getPixiContainerSize(container.parent)
} else {
return {
width: container.parent['width'],
height: container.parent['height'],
}
}
} else {
return {
width: window.innerWidth,
height: window.innerHeight,
}
}
}
function actualSize(target: any) {
if (isContainer(target)) {
return getPixiContainerSize(target)
} else {
const width = getValue(target, 'layout.pixilayout.width') || target.width
const height = getValue(target, 'layout.pixilayout.height') || target.height
if (target.parent) {
const { x: scaleX, y: scaleY } = actualScale(target.parent)
return {
width: ~~(width * scaleX),
height: ~~(height * scaleY),
}
} else {
return {
width,
height,
}
}
}
}
function getRatio(target: Layout.element) {
if (['sprite', 'animatedsprite'].includes(target['type']) && target['_texture']) {
return target['_texture'].width / target['_texture'].height
} else {
return 1
}
}
function _getDefaultSize(target: any) {
switch (target.type) {
case 'animatedsprite':
case 'sprite': {
return target._texture
}
case 'text': {
if (getValue(target, 'layout.container')) {
const { width, height } = target.layout.container
return {
width,
height,
}
} else {
return {
width: 1,
height: 1,
}
}
}
default: {
return {
width: 1,
height: 1,
}
}
}
}
function elementSize(target: Layout.element, style: Layout.style, parentSize: Layout.size) {
const { width, height } = style
const { width: targetWidth, height: targetHeight } = _getDefaultSize(target)
const { width: parentWidth, height: parentHeight } = parentSize
const targetRatio = getRatio(target)
let finalWidth = targetWidth / uiDesignRatio
let finalHeight = targetHeight / uiDesignRatio
if (width) {
finalWidth = length(parentWidth, width)
if (height) {
finalHeight = length(parentHeight, height)
} else if (targetRatio) {
finalHeight = finalWidth / targetRatio
}
} else if (height) {
finalHeight = length(parentHeight, height)
finalWidth = finalHeight * targetRatio
}
return {
width: Math.round(finalWidth),
height: Math.round(finalHeight),
}
}
function _getPos(style: any, keyArr: string[]) {
let result: number | string = 0
for (let i = 0; i < keyArr.length; i++) {
const key = keyArr[i]
const value = getValue(style, key)
if (value !== undefined) {
if (['x', 'y'].includes(key)) {
result = value
break
} else if (key.includes('center')) {
result = `center: ${value}`
break
} else {
result = `${key}: ${value}`
break
}
}
}
return result
}
function elementPos(targetSize: Layout.size, style: Layout.style, parentSize: Layout.size) {
const { rotation: _rotation = 0 } = style
const x = _getPos(style, ['x', 'left', 'centerX', 'right', 'center.x'])
const y = _getPos(style, ['y', 'top', 'centerY', 'bottom', 'center.y'])
const { width: parentWidth, height: parentHeight } = parentSize
const { width: targetWidth, height: targetHeight } = targetSize
return {
x: pos(parentWidth, targetWidth, x),
y: pos(parentHeight, targetHeight, y),
rotation: rotation(_rotation),
}
}
// 获取父级容器
function container(element: Layout.element) {
const { width, height } = actualSize(element)
const scale = actualScale(element)
return {
element,
width,
height,
ratio: width / height,
scale,
}
}
function rlayout2pixilayout(
container: Layout.containerStatus,
anchor: Layout.point,
rlayout: Layout.layout,
): Layout.layout {
const {
width: containerWidth,
height: containerHeight,
scale: containerScale,
element: parent,
} = container
const { x: scaleX, y: scaleY } = containerScale
let { width: sw, height: sh, x: sx, y: sy } = rlayout
sx += sw * anchor.x
sy += sh * anchor.y
if (parent['anchor']) {
sx -= containerWidth * parent['anchor'].x
sy -= containerHeight * parent['anchor'].y
} else if (parent['pivot']) {
sx -= containerWidth * parent['pivot'].x
sy -= containerHeight * parent['pivot'].y
}
sw /= scaleX
sh /= scaleY
sx /= scaleX
sy /= scaleY
return {
width: Math.round(sw),
height: Math.round(sh),
x: Math.round(sx),
y: Math.round(sy),
rotation: rlayout.rotation,
}
}
// 反向同步布局信息
function pixilayout2rlayout(
container: Layout.containerStatus,
anchor: Layout.point,
pixilayout: Layout.layout,
): Layout.layout {
const {
width: containerWidth,
height: containerHeight,
scale: { x: scaleX, y: scaleY },
element: parent,
} = container
let { width: sw, height: sh, x: sx, y: sy } = pixilayout
sw *= scaleX
sh *= scaleY
sx *= scaleX
sy *= scaleY
if (parent['anchor']) {
sx += containerWidth * parent['anchor'].x
sy += containerHeight * parent['anchor'].y
} else if (parent['pivot']) {
sx += containerWidth * parent['pivot'].x
sy += containerHeight * parent['pivot'].y
}
sx -= sw * anchor.x
sy -= sh * anchor.y
return {
width: Math.round(sw),
height: Math.round(sh),
x: Math.round(sx),
y: Math.round(sy),
rotation: pixilayout.rotation,
}
}
return {
rotation,
length,
pos,
actualScale,
actualSize,
getRatio,
elementSize,
elementPos,
container,
rlayout2pixilayout,
pixilayout2rlayout,
}
}