Search code examples
javascriptreactjskonvajsreact-konvakonva

Hide transparent image from download while using React Konva


I want to download only the papayawhip square box without the transparent image behind it.

dont download transparent image

I am using React Konva. I have the following code:

import * as React from "react"
import { Stage, Layer, Rect } from "react-konva"
import type { Stage as StageType } from "konva/types/Stage"
import { observer } from "mobx-react"

import "./styles.css"
import { useStore } from "./context"

const downloadURI = (uri: string | undefined, name: string | undefined) => {
    const link = document.createElement("a")
    link.download = name as string
    link.href = uri as string
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
}

function App() {
    const [fillColor, setFillColor] = React.useState("")
    const [downloadClicked, setDownloadClicked] = React.useState(false)
    const stageRef = React.useRef<StageType>(null)
    const transparentBackground = new window.Image()
    transparentBackground.src =
        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAAABlBMVEUAAADY2NjnFMi2AAAAAXRSTlMAQObYZgAAABVJREFUGNNjYIQDBgQY0oLDxBsIQQCltADJNa/7sQAAAABJRU5ErkJggg=="

    const store = useStore()
    const { win, canvas, browser, pad } = store

    return (
        <div className="flex">
            <div id="sidebar">
                <h1 className="text">
                    Center <span>papayawhip</span> Canvas
                </h1>
                <input
                    type="text"
                    placeholder="Enter Fill Color"
                    value={fillColor}
                    onChange={(e) => setFillColor(e.target.value)}
                />
                <button
                    className="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-indigo-600 border border-transparent rounded-md shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
                    onClick={() => {
                        setDownloadClicked(true)
                        const options = { mimeType: `image/png`, quality: 1, pixelRatio: 1 }
                        const img = stageRef.current?.getStage().toDataURL(options)
                        downloadURI(img, "download.png")
                        setDownloadClicked(false)
                    }}
                >
                    Download Image
                </button>
            </div>
            <Stage
                ref={stageRef}
                width={canvas.width}
                height={canvas.height}
                id="konva"
            >
                <Layer>
                    {fillColor === "" && downloadClicked && (
                        <Rect
                            width={browser.width + 200}
                            height={browser.height + 200}
                            x={pad / 2}
                            y={(win.height - browser.height) / 2}
                            fillPatternImage={transparentBackground}
                            fill={fillColor}
                        />
                    )}
                    <Rect
                        width={browser.width}
                        height={browser.height}
                        x={pad / 2}
                        y={(win.height - browser.height) / 2}
                        fill="papayawhip"
                    />
                </Layer>
            </Stage>
        </div>
    )
}

export default observer(App)

Codesandbox → https://codesandbox.io/s/add-padding-to-centered-canvas-with-sidebar-gqhhl?file=/src/App.tsx

How do I download only the papayawhip rectangle part?


Solution

  • I solved it with the help of Anton. Vanquished Wombat's approach was the same as well.

    const backgroundSelector = React.useRef(null)
    
    /* gave a `name` to `transparentBackground` rectangle */
    <Rect
      name="transparentBackground"
    />
    
    /* then searched for it & hid it */
    stageRef.current?.findOne(".transparentBackground").hide()
    
    /* download the image */
    downloadURI(img, "download.png")
    
    /* show it again */
    stageRef.current?.findOne(".transparentBackground").show()
    

    Updated Codesandbox → https://codesandbox.io/s/add-padding-to-centered-canvas-with-sidebar-gqhhl?file=/src/App.tsx