question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Sharp text and icons on a zooming stage

See original GitHub issue

It should be a common use case to want to have objects on a stage that only follow the translation of the parent, ignoring scale and rotation. I needed it for a stage that switches between icons and detail objects depending on zoom levels, and came up with the following code that might be of some use to the project. I’ve used it for pixel sharp text and icons and it works great.

It would be possible to disable scale and rotation separately as well, but this is a simple solution that I suppose covers the most common case.

Note that text that should rotate with the parent to get the correct translation, but ignore rotation around its own origin in order to stay horizontal and pixel sharp, can be achieved by calling paralyzeTransformInheritance(myStagedText, false, true) and then myStagedText.rotation = -parent.rotation

See also this previously published request.

class ParalyzedInheritanceTransform extends PIXI.Transform {
  updateTransform(parentTransform)
    {
      const lt = this.localTransform;
      if (this._localID !== this._currentLocalID)
      {
          // get the matrix values of the displayobject based on its transform properties..
          lt.a = this._cx * this.scale.x;
          lt.b = this._sx * this.scale.x;
          lt.c = this._cy * this.scale.y;
          lt.d = this._sy * this.scale.y;
          lt.tx = this.position.x - ((this.pivot.x * lt.a) + (this.pivot.y * lt.c));
          lt.ty = this.position.y - ((this.pivot.x * lt.b) + (this.pivot.y * lt.d));
          this._currentLocalID = this._localID;
          // force an update..
          this._parentID = -1;
      }
      if (this._parentID !== parentTransform._worldID)
      {
          // concat the parent matrix with the objects transform.
          const pt = parentTransform.worldTransform;
          const wt = this.worldTransform;
          if(this.ignoreParentScaleAndRotation) {
            wt.a = lt.a;
            wt.b = lt.b;
            wt.c = lt.c;
            wt.d = lt.d;
            wt.tx = lt.tx + pt.tx;
            wt.ty = lt.ty + pt.ty;
          } else {
            wt.a = (lt.a * pt.a) + (lt.b * pt.c);
            wt.b = (lt.a * pt.b) + (lt.b * pt.d);
            wt.c = (lt.c * pt.a) + (lt.d * pt.c);
            wt.d = (lt.c * pt.b) + (lt.d * pt.d);
            wt.tx = (lt.tx * pt.a) + (lt.ty * pt.c) + pt.tx;
            wt.ty = (lt.tx * pt.b) + (lt.ty * pt.d) + pt.ty}
          if(this.truncateTranslation) {
            wt.tx = Math.floor(wt.tx);
            wt.ty = Math.floor(wt.ty)}
          this._parentID = parentTransform._worldID;
          // update the id of the transform..
          this._worldID++;
      }
    }
}

//Modifies 'child' so that it does not care about parent scaling and rotation,
//and/or truncates the translation. An example 'child' object for this function
//is an object on some zoomable stage, such as an icon, that should not resize
//when zooming, and/or remain pixel sharp while zooming.
export function paralyzeTransformInheritance(child, ignoreParentScaleAndRotation, truncateTranslation) {
  var t = child.transform;
  if(!(t instanceof ParalyzedInheritanceTransform)) {
    t = new ParalyzedInheritanceTransform();
    t.setFromMatrix(child.transform.worldTransform);
    child.transform = t}
  t.ignoreParentScaleAndRotation = ignoreParentScaleAndRotation;
  t.truncateTranslation = truncateTranslation;
  return child}

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:4
  • Comments:7 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
RieMarscommented, Dec 3, 2021

Been trying to understand how to use the above code but I found I had to do this to make it keep the X,Y translation of a child within it’s parent relative to the parent’s scale but without scaling the child (eg. like an icon stays in the same place on the map (not in the same place on the screen) and doesn’t get bigger when you zoom in on Google maps):

if(this.ignoreParentScaleAndRotation) {
  wt.a = lt.a;
  wt.b = lt.b;
  wt.c = lt.c;
  wt.d = lt.d;
  wt.tx = (lt.tx * pt.a) + (lt.ty * pt.c) + pt.tx
  wt.ty = (lt.tx * pt.b) + (lt.ty * pt.d) + pt.ty
}

Now this works for Sprites but doesn’t work with my Text or Graphics (Line, Circle or Box objects).

I was using it by:

const image = PIXI.Sprite.from('image.png')
image.position.x = 50
image.position.y = 50
paralyzeTransformInheritance(image, true, true)
canvasContainer.addChild(image)

^ That works (the sprite’s scale is locked but it’s position moves with the parent’s scale

const myText = new PIXI.Text('Hello World', { fill: '#999' })
myText.anchor.set(0.5, 1)
myText.position.y = 50
myText.position.x = 50
paralyzeTransformInheritance(myText, true, true)
canvasContainer.addChild(myText)

^ This doesn’t work(the text disappears when applying the paralyzeTransformInheritance(myText, true, true) line.

I’ve also tried looking at PIXI.TransformStatic but the documentation doesn’t really explain what that does or how to use it.

The playground link in the Wiki linked above - https://www.pixiplayground.com/#/edit/qjI_XLeNbxnRmI~2Mndwg - that doesn’t appear to have anything relevant?

Something that saved me to keep lines from scaling was @pixi/graphics-smooth which has a mode that’s equivalent to SVG’s ‘non-scaling-stroke’ attribute.

And whilst, yes, I could calculate all the translations for the text separately, there’s separate tweens on the parent and children that it’d be great if we could just apply one transform on a parent that the children’s scale isn’t affected.

@molst if I’m misunderstanding your code, it’d be great if you can provide an example? @ivanpopelyshev is there any solutions other than the code above for child Text and Graphics ignoring parent scale?

0reactions
ivanpopelyshevcommented, Feb 10, 2020

Unfortunately google had a hard time finding it for me.

Yeah, there’s that problem 😃 OK, added it to wiki

Read more comments on GitHub >

github_iconTop Results From Across the Web

Sharp text and icons on a zooming stage · Issue #6393 - GitHub
I've used it for pixel sharp text and icons and it works great. It would be possible to disable scale and rotation separately...
Read more >
Zooming stage relative to pointer position - Konva
I find the problem! Turns out my node hierarchy is like this: Group.add(Rect) Group.add(Icon) And the rect is added into transformer (i.e: transformerNode([ ......
Read more >
Adjust your zoom and view options – Figma Help Center
You can customize your preferences for design files the Zoom/view options menu. Adjust your zoom settings, or toggle on other view options like...
Read more >
How to Zoom in Premiere Pro for Simple Transitions
Using zoom makes static subjects come to life, and helps smooth ... In our effects controls window, click the stopwatch icon next to...
Read more >
Change Zoom advanced options for accessibility on Mac
Choose how the zoomed image moves with the pointer: Continuously with Pointer: Make the screen image move with the pointer. When Pointer Reaches...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found