Tips for aligning items in the center of konvajs

I am currently developing a meme-editor using React and incorporating KonvaJS to create a Canvas-like functionality. My challenge lies in centering the items within the canvas stage, as there seems to be a property overriding my styling attempts. Here is a snippet of the return statement in my React component:

<div className="mycanvas">
    <Stage width={500} height={500} className="stage">
        <Layer>
            <Image image={image} className="meme" />
            {textFields.map((text) => (
                <Text
                    text={text}
                    draggable
                    fontFamily="Impact"
                    fontSize="30"
                    stroke="white"
                />
            ))}
        </Layer>
    </Stage>
</div>

And this is how the final output appears.

https://i.sstatic.net/XVHf1.png

To illustrate where the image should be centered, I have set the background of the wrapper to blue.

I have experimented with applying CSS to classes such as "mycanvas," "stage," and "meme," as well as "konvajs-content" (which appeared in my inspector). Despite trying properties like align-items: center and margin: auto, conventional CSS methods don't seem to work effectively in this scenario. It appears to be an issue related to the overall styling of KonvaJS components, but unfortunately, I haven't been able to find a solution on platforms like Stack Overflow or in the Konva documentation.

Answer №1

In this scenario, CSS alone is not sufficient to achieve the desired outcome. When an image is placed onto a canvas using specific height and width coordinates (x and y), the pixels of the image blend into the rasterized canvas. Essentially, the image loses its independent identity outside of the canvas.

To center the image within the canvas, some mathematical calculations are necessary to determine the correct x and y coordinates that will position the image at the center of the canvas.

Interactive Demo

For instance, if your canvas is 500px tall and the image's height is 350px, you should set the y position to 75px (i.e., (500 - 350) / 2).

The demonstration code below exemplifies how to replicate the behavior of CSS object-fit: contain. This functionality adjusts the image to fill the canvas in one direction while centering it in the other direction.

import { useState, useEffect } from "react";
import { Stage, Layer, Image, Text } from "react-konva";

function Example() {
  const w = window.innerWidth;
  const h = window.innerHeight;
  const src = "https://konvajs.org/assets/yoda.jpg";

  const [image, setImage] = useState(null);
  const [pos, setPos] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const image = new window.Image();
    image.src = src;
    image.addEventListener("load", handleLoad);

    function handleLoad(event) {
      const image = event.currentTarget;
      /* after the image is loaded, you can get it's dimensions */
      const imgNaturalWidth = image.width;
      const imgNaturalHeight = image.height;

      /* 
        calculate the horizontal and vertical ratio of the 
        image dimensions versus the canvas dimensions
      */
      const hRatio = w / imgNaturalWidth;
      const vRatio = h / imgNaturalHeight;

      /*
        To mimic CSS Object-Fit behaviour like "contain," 
        select the smaller of the horizontal and vertical ratios.
        
        For a "cover" effect, use Math.max instead.
      */
      const ratio = Math.min(hRatio, vRatio);
      /* 
        Scale the image to fit the canvas 
      */
      image.width = imgNaturalWidth * ratio;
      image.height = imgNaturalHeight * ratio;

      /* 
        Calculate offsets for centering the image inside the canvas
      */
      const xOffset = (w - image.width) / 2;
      const yOffset = (h - image.height) / 2;

      setPos({
        x: xOffset,
        y: yOffset
      });
      setImage(image);
    }

    return () => {
      image.removeEventListener("load", handleLoad);
    };
  }, [src, h, w]);

  return (
    <Stage width={w} height={h} style={{ background: "black" }}>
      <Layer>
        <Image x={pos.x} y={pos.y} image={image} />
        <Text
          text="I am centered"
          fontFamily="Impact"
          fontSize={50}
          stroke="white"
          strokeWidth={1}
          x={pos.x}
          y={pos.y}
        />
      </Layer>
    </Stage>
  );
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Text aligned at the center of the Y and X axis

I am looking to center my content along the Y axis instead of only on the X axis. To prevent the page from expanding beyond its current size, I have applied the following CSS: html { overflow-y: hidden; overflow-x: hidden } What I want to achieve is havi ...

Is it possible to integrate this ReactJs project into my Electron-based web application?

Currently, I am a novice in JavaScript and am working on developing a web application for my school project using Electron.js. At the moment, my project consists of JS, CSS, and HTML code only. You can view the homepage of my program on Codepen. [Codepen] ...

Issues with animation functionality in Google Chrome

Does anyone know why the blink effect isn't working on Google Chrome? <p class="blink">at least it's not Comic Sans</p> <style> .blink { animation-duration: 1s; animation-name: blink; animation-iteration-count: infinite; anima ...

Can html-webpack-plugin be configured to create <style> elements from CSS files?

I am managing a static site with Vue and Webpack. In my project, I have a file named style.css containing global CSS rules which I import using import './styles.css' in my index.js file. Additionally, I have some .vue files that generate their o ...

Unable to access attributes of an undefined value (current state is undefined)

After completing a small project, I attempted to deploy it on Vercel. The project runs smoothly without any errors on my local machine. However, when I tried to run it on the server, I encountered the following error: "Cannot read properties of undefined ( ...

What is the best way to align an image to the left side of the screen using Bootstrap 4?

I want to create a unique login page using bootstrap. My goal is to have an image on the left side and a form on the right side of the page. I need it to be responsive so that the image will disappear on smaller screens to prevent distortion. However, I a ...

font-face not functioning properly in the live environment

I attempted to use @font-face to incorporate some unique fonts into my website. Surprisingly, everything worked perfectly fine when I tested it on localhost. However, as soon as I moved the code to a live server, the font-face stopped working in all browse ...

Filtering Objects in Javascript: A Step-by-Step Guide

Consider an array containing various object details: Array [ Object { "deleted": null, "disabled": null, "id": "456", "price": 970, "size": Object { "br": 35, ...

Move the DIV element to a static section within the DOM

In my Vue app, I have implemented methods to dynamically move a DIV called 'toolbox' to different sections of the DOM. Currently, the DIV is positioned on the bottom right of the screen and remains static even when scrolling. My goal is to use t ...

Utilizing the toggle switch functionality in Django with a boolean field implementation

My work_experience model includes an "is_working" field which is set to true when a user is currently employed at a company. I am using a toggle switch on the front end and would like to update the boolean field value for "is_working" with a click. What lo ...

React Apollo Error: There are no additional mocked responses available for the specified query mutation

Purpose: MockedProvider should mimic the createPost mutation. Current Situation: Error: No additional mocked responses available for the query: mutation... Steps to Reproduce the Problem: I have a very basic repository. Additionally, I have cre ...

The dropdown menu in Bootstrap 4 has non-functional links

Attempting to create a mega menu using Bootstrap. Started with dropdown menu code and made some modifications, but encountering issues where the links in the dropdown don't work properly unless opened in a new tab. When clicking on a link, it closes t ...

Arrange 4 divs (children) at each corner of the parent element

Currently, I am facing a challenge in positioning 4 divs in the corners of their parent div. The HTML code structure is as follows: <div class="resize"> <div class="n w res"></div> <div class="n e res"></div> < ...

Rotating arrows enhance the functionality of the accordion menu

I have successfully implemented a basic accordion with rotating arrows on click. Everything is working smoothly except for one issue: When I collapse one item and then try to collapse another, the previous arrow does not return to its default state. Is ...

Whenever a button is clicked in a custom column rendering of a React material table, the sorted state is lost. This issue occurs whenever any state update is triggered

I encountered an issue with my code involving a Collapsible list triggered by an Icon Button. After sorting the table and then expanding the list, the table reverts back to its original unsorted state. I am puzzled as to why this is happening. <Material ...

Glowing semi-opaque about spotify?

Recently, I decided to challenge myself by recreating the Spotify homepage using only pure Javascript and SCSS as a way to test my front-end development skills. You can view my progress so far at this link, although please note that it's still a work ...

What is the best way to block users from directly accessing my React app's page by typing in the URL, and instead redirect them to the login page?

I am currently in the process of developing a react-based application. This app serves as a platform for managing storage data, and my main focus right now is implementing security measures to prevent users from directly accessing certain routes by simply ...

The program encountered an issue trying to access the property 'map' of an undefined element in ReactJS utilizing AJAX

Displaying an array in a table retrieved from an AJAX request in Action while utilizing Redux. class IncomeProfile extends Component { constructor(props) { super(props) } componentDidMount() { this.props.IncomeListProfile(); } render ...

Safari's use of threeJS and overlays

My website utilizes threeJS for rendering to a canvas with a white clear color set (renderer.setClearColor(0xffffff)). There is also a div overlay displayed on top of the canvas for a drawer, which animates onto the screen. The issue arises when this set ...

Leveraging Redux and React to efficiently map state to props, while efficiently managing large volumes of data and efficiently

Utilizing sockets for server listening, the Redux store undergoes continuous updates with a vast amount of data. While updating the store is quick, the process of connecting the state to the component through the redux connect function seems to be slower, ...