konvajs/konva

Smooth Scrolling of Bitmap Render on LED Display

djrobby opened this issue · 3 comments

Hello, I am working on an app that renders bitmap characters onto and LED display using the Konva library. I have everything working except smooth scrolling behavior of the rendered bitmaps.

Issue: Whenever the rendered characters are set to scroll, the scrolling seems really choppy. Each time the pixels are rendered at the new X position, it appears that some pixels are not filled. See scrolling video below. Here is the code I am using to scroll the bitmap nodes and update their X position:

const handleScrollEvents = frame => {
  scrollNodes() &&
    scrollNodes().map((n, nIdx) => {
      // default scrollSpeed = 40
     // default canvasViewScale = 1
      var newX = (frame.timeDiff * cfg.scrollSpeed * cfg.canvasViewScale) / 1000;
      if (n.x() < -n.getAttr("bitmapWidth")) {
        // default canvasWidth = 480
        // default canvasViewScale = 1
        n.x(cfg.canvasWidth * cfg.canvasViewScale);
      } else {
        n.x(n.x() - newX);
      }
    });
};

Static Video - Here's a video of what the rendered bitmap characters look like with color changing animation but not scrolling:

IMAGE ALT TEXT HERE

Scrolling Video - Here's a video of what the rendered bitmap characters look like with scrolling:

IMAGE ALT TEXT HERE

The scrolling itself is great, but what can I do to ensure that on each update of the new X position, the pixels are fully filled with their color? This will prevent it from appearing like some pixels are dead, which can make the scroll behavior on an LED display look choppy.

I am not sure how can I help here from konva side.

Probably you need to do a bit of different position calculation. You may need to be strict with LED positions. So text doesn't go between them.

Actually, the LED display is just another monitor (setup as a secondary display) and we simply mirror what is on the PC's main display onto this secondary LED display. I am able to see the exact same behavior (at a pixel level) on a regular monitor as well but you really need to squint your eyes as pixels on a 1920x1080 display are very tiny.

Problem fixed! newX was being calculated as a float value, resulting in fractional pixels that do not exist in real pixel-to-pixel mapped space. I simply updated the function to interpolate the node's new X position as the nearest whole number as follows, and now everything works as expected.

const handleScrollEvents = frame => {
  scrollNodes() &&
    scrollNodes().map((n, nIdx) => {
      // default scrollSpeed = 40
     // default canvasViewScale = 1
      var newX = (frame.timeDiff * cfg.scrollSpeed * cfg.canvasViewScale) / 1000;
      if (n.x() < -n.getAttr("bitmapWidth")) {
        // default canvasWidth = 480
        // default canvasViewScale = 1
        n.x(cfg.canvasWidth * cfg.canvasViewScale);
      } else {
        n.x(Math.round(n.x() - newX));
      }
    });
};