refactor: ♻️ move transform to dom

This commit is contained in:
bubkoo
2021-04-06 09:39:15 +08:00
parent fc7a36ec6a
commit 8a0c91644e
2 changed files with 204 additions and 0 deletions

View File

@ -0,0 +1,53 @@
import { Global } from '../../global'
import type { Matrix } from '../../struct/matrix'
import type { VectorElement } from '../element'
import type { Transform } from './transform'
export namespace Util {
export function getTransformOrigin(
o: Matrix.TransformOptions,
t: Transform<Node>,
): [number, number] {
// First check if origin is in ox or originX
let ox = o.ox != null ? o.ox : o.originX != null ? o.originX : 'center'
let oy = o.oy != null ? o.oy : o.originY != null ? o.originY : 'center'
// Then check if origin was used and overwrite in that case
const { origin } = o
if (origin != null) {
;[ox, oy] = Array.isArray(origin)
? origin
: typeof origin === 'object'
? [origin.x, origin.y]
: [origin, origin]
}
// Make sure to only call bbox when actually needed
if (typeof ox === 'string' || typeof oy === 'string') {
const node = t.node
const { height, width, x, y } =
node instanceof Global.window.SVGElement
? ((t as any) as VectorElement).bbox()
: (node as HTMLElement).getBoundingClientRect()
// And only overwrite if string was passed for this specific axis
if (typeof ox === 'string') {
ox = ox.includes('left')
? x
: ox.includes('right')
? x + width
: x + width / 2
}
if (typeof oy === 'string') {
oy = oy.includes('top')
? y
: oy.includes('bottom')
? y + height
: y + height / 2
}
}
return [ox, oy]
}
}

View File

@ -0,0 +1,151 @@
import type { Point } from '../../struct/point'
import { Matrix } from '../../struct/matrix'
import { Util } from './transform-util'
import { Primer } from './primer'
export class Transform<TNode extends Node>
extends Primer<TNode>
implements Matrix.Matrixifiable {
transform(): Matrix.Transform
transform(type: keyof Matrix.Transform): number
transform(
options: Matrix.TransformOptions,
relative?: boolean | Transform<TNode> | Matrix.Raw,
): this
transform(
matrix: Matrix.MatrixLike,
relative?: boolean | Transform<TNode> | Matrix.Raw,
): this
transform(
o?: keyof Matrix.Transform | Matrix.MatrixLike | Matrix.TransformOptions,
relative?: boolean | Transform<TNode> | Matrix.Raw,
) {
if (o == null || typeof o === 'string') {
const decomposed = new Matrix(this).decompose()
return o == null ? decomposed : decomposed[o]
}
const m = Matrix.isMatrixLike(o)
? o
: {
...o,
origin: Util.getTransformOrigin(o, this),
}
const cur =
relative === true ? new Matrix(this) : new Matrix(relative || undefined)
const ret = cur.transform(m)
return this.attr('transform', ret.toString())
}
untransform() {
return this.attr('transform', null)
}
matrixify(): Matrix {
const raw = this.attr('transform') || ''
return (
raw
.split(/\)\s*,?\s*/)
.slice(0, -1)
.map((str) => {
const kv = str.trim().split('(')
return [kv[0], kv[1].split(/[\s,]+/).map((s) => Number.parseFloat(s))]
})
.reverse()
// merge every transformation into one matrix
.reduce(
(
matrix,
transform: ['matrix' | 'rotate' | 'scale' | 'translate', number[]],
) => {
if (transform[0] === 'matrix') {
return matrix.lmultiply(
Matrix.toMatrixLike(transform[1] as Matrix.MatrixArray),
)
}
return matrix[transform[0]].call(matrix, ...transform[1])
},
new Matrix(),
)
)
}
matrix(): Matrix
matrix(a: number, b: number, c: number, d: number, e: number, f: number): this
matrix(
a?: number,
b?: number,
c?: number,
d?: number,
e?: number,
f?: number,
) {
if (a == null) {
return new Matrix(this)
}
return this.attr(
'transform',
new Matrix(
a,
b as number,
c as number,
d as number,
e as number,
f as number,
).toString(),
)
}
rotate(angle: number, cx?: number, cy?: number) {
return this.transform({ rotate: angle, ox: cx, oy: cy }, true)
}
skew(x: number, y: number, cx?: number, cy?: number) {
return arguments.length === 1 || arguments.length === 3
? this.transform({ skew: x, ox: y, oy: cx }, true)
: this.transform({ skew: [x, y], ox: cx, oy: cy }, true)
}
shear(lam: number, cx?: number, cy?: number) {
return this.transform({ shear: lam, ox: cx, oy: cy }, true)
}
scale(x: number, y: number, cx?: number, cy?: number) {
return arguments.length === 1 || arguments.length === 3
? this.transform({ scale: x, ox: y, oy: cx }, true)
: this.transform({ scale: [x, y], ox: cx, oy: cy }, true)
}
translate(x: number, y: number) {
return this.transform({ translate: [x, y] }, true)
}
relative(x: number, y: number) {
return this.transform({ relative: [x, y] }, true)
}
flip(origin?: number | [number, number] | Point.PointLike): this
flip(
direction: 'both' | 'x' | 'y',
origin?: number | [number, number] | Point.PointLike,
): this
flip(
direction:
| 'both'
| 'x'
| 'y'
| number
| [number, number]
| Point.PointLike = 'both',
origin?: number | [number, number] | Point.PointLike,
) {
if (typeof direction !== 'string') {
origin = direction // eslint-disable-line
direction = 'both' // eslint-disable-line
}
return this.transform({ origin, flip: direction }, true)
}
}