DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • React, Angular, and Vue.js: What’s the Technical Difference?
  • Web Component Solutions: A Comparison
  • Creating Scrolling Text With HTML, CSS, and JavaScript
  • Best Gantt Chart Libraries for React

Trending

  • Vibe Coding With GitHub Copilot: Optimizing API Performance in Fintech Microservices
  • The Cypress Edge: Next-Level Testing Strategies for React Developers
  • Event-Driven Architectures: Designing Scalable and Resilient Cloud Solutions
  • Unlocking Data with Language: Real-World Applications of Text-to-SQL Interfaces
  1. DZone
  2. Coding
  3. Languages
  4. Scrolling With Konva.js and React

Scrolling With Konva.js and React

This article explores a solution for displaying large HTML Canvases efficiently, focusing on limiting the visible area and scrolling through content.

By 
Marat Minulin user avatar
Marat Minulin
·
Apr. 30, 24 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
1.7K Views

Join the DZone community and get the full member experience.

Join For Free

Let's assume we have a situation where we have a large HTML Canvas, say it is 2,000 x 10,000 pixels in dimensions, with a considerable number of objects depicted on it. Displaying the entire Сanvas at once would lead to performance issues, especially on mobile devices.

In this article, I would like to explore one of the efficient solutions to this problem. The core idea is to limit the visible area to the screen's dimensions and only display a portion of the content. The remaining content can be viewed by scrolling through the document.

Step 1: Preparing the Document

Let's start with preparing the document, and as an example, we will define a list of pages that we want to display on the HTML Canvas:

JavaScript
 
import imageURL from "./lorem-ipsum.png";

// ...

const pages = [
  { pageId: 1, width: 2016, height: 2264, imageURL, offsetY: 0 },
  { pageId: 2, width: 2016, height: 2264, imageURL, offsetY: 2264 },
  { pageId: 3, width: 2016, height: 2264, imageURL, offsetY: 4528 }
];


For each page, width, height, and an image URL are specified, which need to be displayed on the HTML Canvas. As you may notice, the total size of the content is quite large and equals 2016 x 6792 = 2016 x 2264 * 3. In this example, the document contains only 3 pages, and in the case of more pages (dozens or even hundreds), the content size increases proportionally.

Step 2: Display the Pages

The next step is to display the pages, and for that, we will add a Page component responsible for individually rendering each page:

JavaScript
 
import { Image } from "react-konva";
import useImage from "use-image";

export default function Page({ width, height, imageURL, offsetY }) {
  const [image] = useImage(imageURL);

  if (!image) {
    return null;
  }

  return (
    <Image
      x={0}
      y={0}
      image={image}
      width={width}
      height={height}
      offsetY={offsetY}
    />
  );
}


The useImage hook is utilized within the component for image loading. It loads an image based on the provided URL and creates a DOM element with the same src value. The loaded image is then passed to the Image component from the “react-konva” library, thereby displaying the page image on the HTML Canvas.

Step 3: Implement a Component

Next, we'll implement a component responsible for displaying the entire list of pages:

JavaScript
 
import { Layer, Stage } from "react-konva";

import Page from "./Page.js";

// ...

export default function App() {
  return (
    <Stage width={window.innerWidth} height={window.innerHeight}>
      <Layer>
        {pages.map((page) => (
          <Page
            key={page.pageId}
            width={page.width}
            height={page.height}
            imageURL={page.imageURL}
            offsetY={-page.offsetY}
          />
        ))}
      </Layer>
    </Stage>
  );
}


To display each page, we used the previously implemented Page component. Then, we passed the list of pages as children to the Layer and Stage components from the “react-konva” library.

The Layer component serves as a graphical container used for displaying and managing a set of shapes and elements. Each Layer contains a set of Konva.js objects, such as shapes, images, text, etc., which can be added, removed, and modified. Layer components are used to set the hierarchy of objects and control the rendering order of elements.

The Stage component is the top-level component that contains all Layer components and handles events on the HTML Canvas. The Stage is the main component on which graphical objects are displayed.

Step 4: Implement Ability to Scroll Through Pages

And last but not least, let's implement the ability to scroll through the pages. The library's documentation describes several options for navigation. We will implement one of these options, the idea of which is to display only a portion of the content and scroll the remaining content using an external container. When the user scrolls the container, a CSS style transform: translate(scrollTop, scrollLeft) is applied to the container to keep it in place, while simultaneously changing the position of the Stage to scroll the content. This way, we can navigate through the document using scrolling.

Let's implement this approach. For this, we will add a state to store the scroll position and an event handler to keep track of its updated position when scrolling the document:

JavaScript
 
const [scroll, setScroll] = useState({ left: 0, top: 0 });

...

const handleScroll = useCallback((event) => {
  const { scrollLeft, scrollTop } = event.currentTarget;

  setScroll({ left: scrollLeft, top: scrollTop });
}, []);


We will need the value of the scroll position to set coordinates in the Stage component and as a value for the transform CSS property, as we described above.

JavaScript
 
const stageStyles = useMemo(() => {
  return { transform: `translate(${scroll.left}px, ${scroll.top}px)` };
}, [scroll]);


We also need to calculate the dimensions of the content, specifically, its width and height. The content's height will be the sum of the heights of all pages, and the content's width will be the width of the widest page.

JavaScript
 
const contentStyles = useMemo(() => {
  const { width, height } = pages.reduce(
    (acc, page) => ({
      width: Math.max(acc.width, page.width),
      height: acc.height + page.height
    }),
    {
      width: 0,
      height: 0
    }
  );
  return { width, height };
}, []);


Now that we have all the necessary calculations and handlers, let's bring them all together into a single component. The final implementation of the application will be as follows:

JavaScript
 
import { Layer, Stage } from "react-konva";

import Page from "./Page.js";

// ...

export default function App() {
  const [scroll, setScroll] = useState({ left: 0, top: 0 });

  const handleScroll = useCallback((event) => {
    const { scrollLeft, scrollTop } = event.currentTarget;

    setScroll({ left: scrollLeft, top: scrollTop });
  }, []);

  const stageStyles = useMemo(() => {
    return { transform: `translate(${scroll.left}px, ${scroll.top}px)` };
  }, [scroll]);

  const contentStyles = useMemo(() => {
    const { width, height } = pages.reduce(
      (acc, page) => ({
        width: Math.max(acc.width, page.width),
        height: acc.height + page.height
      }),
      {
        width: 0,
        height: 0
      }
    );
    return { width, height };
  }, []);

  return (
    <div className="Scroll" onScroll={handleScroll}>
      <div className="Content" style={contentStyles}>
        <Stage
          x={-scroll.left}
          y={-scroll.top}
          width={window.innerWidth}
          height={window.innerHeight}
          style={stageStyles}
        >
          <Layer>
            {pages.map((page) => (
              <Page
                key={page.pageId}
                width={page.width}
                height={page.height}
                imageURL={page.imageURL}
                offsetY={-page.offsetY}
              />
            ))}
          </Layer>
        </Stage>
      </div>
    </div>
  );
}

You can view a functional application and explore it in more detail on the CodeSandbox playground.

Conclusion

In this article, we've discussed the implementation of an application that enables viewing documents with a large number of pages. In the next article, we will continue exploring the “react-konva” library and further enhance the functionality of our viewer.

CSS Document HTML Library React (JavaScript library)

Opinions expressed by DZone contributors are their own.

Related

  • React, Angular, and Vue.js: What’s the Technical Difference?
  • Web Component Solutions: A Comparison
  • Creating Scrolling Text With HTML, CSS, and JavaScript
  • Best Gantt Chart Libraries for React

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!