Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to scale markers upon image resize? #194

Open
SadmanYasar opened this issue Oct 5, 2024 · 2 comments
Open

How to scale markers upon image resize? #194

SadmanYasar opened this issue Oct 5, 2024 · 2 comments

Comments

@SadmanYasar
Copy link

SadmanYasar commented Oct 5, 2024

I have a Nextjs app using Markerjs for annotating images. I set the width to 500px but on mobile view, its width is full. It works but due to resizing of the image, the canvas seems to resize but the markers are not scaling to the image. I am using a combination of both markerjs2 and markerjs live. So by default, it calls the showMarkers method attached below and when editing, it shows the markerArea. The edit is enabled using a state after clicking a row in a table. All the states are stored in a statemap so the markers are displayed on top of the original image instead of rendered image after adding a marker. Showing rendered image instead fixes this issue as its just showing an image, but I am looking for a way to make the markers responsive.

The code for showing markers:

const showMarkerArea = useCallback((rowId: string) => {
        if (!imgRef.current || !isImageLoaded) return;

        if (markerArea.current) {
            markerArea.current.close();
        }

        if (markerView.current) {
            markerView.current.close();
        }

        markerArea.current = new markerjs2.MarkerArea(imgRef.current);

        markerArea.current.availableMarkerTypes = [
            markerjs2.EllipseMarker
        ];

        markerArea.current.settings.defaultColorSet = ['#EF4444', '#3B82F6', '#10B981', '#F59E0B', '#6366F1'];

        if (el.current) {
            markerArea.current.targetRoot = el.current;
        }

        markerArea.current.addEventListener('markercreate', (event) => {
            if (event.marker) {
                event.marker.notes = rowId;
            }
        });

        markerArea.current.addEventListener('render', (event) => {
            updateStateMap(rowId, event.state);
        });

        markerArea.current.addEventListener('close', (event) => {
            updateStateMap(rowId, event.markerArea.getState());
        });

        markerArea.current.settings.displayMode = "inline";

        markerArea.current.show();

        if (stateMap[rowId]) {
            markerArea.current.restoreState(stateMap[rowId]);
        }

        console.log('stateMap', stateMap);
    }, [stateMap, updateStateMap, isImageLoaded]);

    const showMarkers = useCallback((target: HTMLImageElement) => {
        if (!isImageLoaded) return;

        if (markerView.current) {
            markerView.current.close();
        }

        if (markerArea.current) {
            markerArea.current.close();
        }

        markerView.current = new mjslive.MarkerView(target);

        if (el.current) {
            markerView.current.targetRoot = el.current;
        }

        const allMarkers = Object.values(stateMap).flatMap(state => state.markers);

        markerView.current.addEventListener("load", (mv) => {
            mv.markers.forEach((m) => {
                m.outerContainer.style.opacity = "0.5"; // Set all markers to 0.5 opacity
                if (hoveredItem && m.notes === hoveredItem) {
                    m.outerContainer.style.opacity = "1"; // Set matching markers to 1 opacity
                }
            });
        });

        const viewState: markerjs2.MarkerAreaState = {
            width: target.width,
            height: target.height,
            markers: allMarkers
        };

        console.log('viewState', viewState);

        try {
            markerView.current.show(viewState);
        } catch (error) {
            console.error('Error showing markers:', error);
            console.log('Current viewState:', viewState);
        }
    }, [stateMap, hoveredItem, isImageLoaded]);

The code for displaying the image:

<div className='flex w-full flex-col md:flex-row gap-4'>
                <div className="relative border rounded-lg max-sm:w-full max-w-[500px] max-h-[500px]" ref={el}>
                    <img
                        ref={imgRef}
                        src={src}
                        crossOrigin="anonymous"
                        alt='Image to annotate'
                        className='max-w-[500px] max-h-[400px]'
                        onLoad={handleImageLoad}
                    />
                </div>
</div>

I also using popup instead of inline but the marker positions are inconsistent. Would appreciate any guidance on this. TIA.

@ailon
Copy link
Owner

ailon commented Oct 6, 2024

I haven't looked deeply enough but what rubs me a bit wrong on the first glance is that you are manually/explicitly setting width/height in the state before opening it with marker.js Live:

        const viewState: markerjs2.MarkerAreaState = {
            width: target.width,
            height: target.height,
            markers: allMarkers
        };

Not sure why you do that but this would mess up the internal scaling as it's done based on the width/height of the whole annotation in relation to the position and dimensions of individual markers.

@SadmanYasar
Copy link
Author

I haven't looked deeply enough but what rubs me a bit wrong on the first glance is that you are manually/explicitly setting width/height in the state before opening it with marker.js Live:

        const viewState: markerjs2.MarkerAreaState = {
            width: target.width,
            height: target.height,
            markers: allMarkers
        };

Not sure why you do that but this would mess up the internal scaling as it's done based on the width/height of the whole annotation in relation to the position and dimensions of individual markers.

Thanks! This was definitely the issue. It's fixed after I removed the width and height for the viewState.

I'm just facing one issue which is after resizing the image from mobile to desktop, the markerjs live doesn't scale the markers, only after markerArea is reinitialised it gets updated in the markerjs live. Is this because of the width set for the image? I tried setting width to full in the image but it doesn't seem to fix it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants