Compare transforms (v3)

Revision 3 of this benchmark created on


Setup

const createAxisDelta = () => ({
    translate: 0,
    scale: 1,
    origin: 0,
    originPoint: 0,
})

const createDelta = () => ({
    x: createAxisDelta(),
    y: createAxisDelta(),
})

const treeScale = { x: 1, y: 1 }

class AxisDelta {
	translate = 0
    scale = 1
    origin = 0
    originPoint = 0
}

class Delta {
	x = new AxisDelta()
	y = new AxisDelta()
}

function buildProjectionTransform(
    delta,
    treeScale,
    latestTransform
) {
    let transform = ""

    /**
     * The translations we use to calculate are always relative to the viewport coordinate space.
     * But when we apply scales, we also scale the coordinate space of an element and its children.
     * For instance if we have a treeScale (the culmination of all parent scales) of 0.5 and we need
     * to move an element 100 pixels, we actually need to move it 200 in within that scaled space.
     */
    const xTranslate = delta.x.translate / treeScale.x
    const yTranslate = delta.y.translate / treeScale.y
    const zTranslate = latestTransform?.z || 0
    if (xTranslate || yTranslate || zTranslate) {
        transform = `translate3d(${xTranslate}px, ${yTranslate}px, ${zTranslate}px) `
    }

    /**
     * Apply scale correction for the tree transform.
     * This will apply scale to the screen-orientated axes.
     */
    if (treeScale.x !== 1 || treeScale.y !== 1) {
        transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `
    }

    if (latestTransform) {
        const { transformPerspective, rotate, rotateX, rotateY, skewX, skewY } =
            latestTransform
        if (transformPerspective)
            transform = `perspective(${transformPerspective}px) ${transform}`
        if (rotate) transform += `rotate(${rotate}deg) `
        if (rotateX) transform += `rotateX(${rotateX}deg) `
        if (rotateY) transform += `rotateY(${rotateY}deg) `
        if (skewX) transform += `skewX(${skewX}deg) `
        if (skewY) transform += `skewY(${skewY}deg) `
    }

    /**
     * Apply scale to match the size of the element to the size we want it.
     * This will apply scale to the element-orientated axes.
     */
    const elementScaleX = delta.x.scale * treeScale.x
    const elementScaleY = delta.y.scale * treeScale.y
    if (elementScaleX !== 1 || elementScaleY !== 1) {
        transform += `scale(${elementScaleX}, ${elementScaleY})`
    }

    return transform || "none"
}


let prevTransform = ""
const prevDelta = createDelta()
const delta = createDelta()

const classDelta = new Delta()
const classPrevDelta = new Delta()

const baseAxis = createAxisDelta()
const objectDelta = {
	x: Object.create(baseAxis),
	y: Object.create(baseAxis)
}
const objectPrevDelta = {
	x: Object.create(baseAxis),
	y: Object.create(baseAxis)
}

 function copyAxisDeltaInto(delta, originDelta) {
    delta.translate = originDelta.translate
    delta.scale = originDelta.scale
    delta.originPoint = originDelta.originPoint
    delta.origin = originDelta.origin
}

 function copyClassAxisDeltaInto(delta, originDelta) {
    delta.translate = originDelta.translate
    delta.scale = originDelta.scale
    delta.originPoint = originDelta.originPoint
    delta.origin = originDelta.origin
}

 function copyObjectAxisDeltaInto(delta, originDelta) {
    delta.translate = originDelta.translate
    delta.scale = originDelta.scale
    delta.originPoint = originDelta.originPoint
    delta.origin = originDelta.origin
}

function updateDelta() {
	delta.y.originPoint = 200 * Math.random()
}

function updateClassDelta() {
	classDelta.y.originPoint = 200 * Math.random()
}

function updateObjectDelta() {
	objectDelta.y.originPoint = 200 * Math.random()
}

let transform = ""
let op = false

function hasAxisDeltaChanged(a, b) {
    return (
        a.translate !== b.translate ||
        a.scale !== b.scale ||
        a.originPoint !== b.originPoint ||
        a.origin !== b.origin
    )
}

function hasClassAxisDeltaChanged(a, b) {
    return (
        a.translate !== b.translate ||
        a.scale !== b.scale ||
        a.originPoint !== b.originPoint ||
        a.origin !== b.origin
    )
}

function hasObjectAxisDeltaChanged(a, b) {
    return (
        a.translate !== b.translate ||
        a.scale !== b.scale ||
        a.originPoint !== b.originPoint ||
        a.origin !== b.origin
    )
}

Test runner

Ready to run.

Testing in
TestOps/sec
Compare string
updateDelta()

transform = buildProjectionTransform(delta, treeScale)

if (transform !== prevTransform) {
	op = !op
}

prevTransform = transform
ready
Compare and copy
updateDelta()

if (hasAxisDeltaChanged(delta.x, prevDelta.x) || hasAxisDeltaChanged(delta.y, prevDelta.y)) {
	op = !op
}

copyAxisDeltaInto(prevDelta.x, delta.x)
copyAxisDeltaInto(prevDelta.y, delta.y)
ready
Copy class properties
updateClassDelta()

if (hasClassAxisDeltaChanged(classDelta.x, classPrevDelta.x) || hasClassAxisDeltaChanged(classDelta.y, classPrevDelta.y)) {
	op = !op
}

copyClassAxisDeltaInto(classPrevDelta.x, classDelta.x)
copyClassAxisDeltaInto(classPrevDelta.y, classDelta.y)
ready
Copy Object.create()
updateObjectDelta()

if (hasObjectAxisDeltaChanged(objectDelta.x, objectPrevDelta.x) || hasObjectAxisDeltaChanged(objectDelta.y, objectPrevDelta.y)) {
	op = !op
}

copyObjectAxisDeltaInto(objectPrevDelta.x, objectDelta.x)
copyObjectAxisDeltaInto(objectPrevDelta.y, objectDelta.y)
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.