bubkoo / html-to-image

✂️ Generates an image from a DOM node using HTML5 canvas and SVG.
MIT License
5.4k stars 505 forks source link

Cannot convert to PNG on Firefox browser desktop #391

Open rushkii opened 1 year ago

rushkii commented 1 year ago

I want to convert HTML element to a PNG, I was waiting the conversion but the package is not convert to a PNG if the element has background-image style. This only happens on Firefox, I've tried on MS Edge and it is working normally and tried on Chrome mobile too and it's working.

Expected Behavior

Convert to PNG

Current Behavior

html-to-image not converting as it's should be on Firefox when the HTML element has background-image style.

Possible Solution

I don't have a solution

Steps To Reproduce

Code:

const UIDProfile = (props: UIDProfileType) => {
    const refConvert = useRef<HTMLDivElement | null>(null);
    const refDownload = useRef<HTMLAnchorElement | null>(null);
    const [cardImg, setCardImg] = useState<string>("");
    const [isConverted, setIsConverted] = useState<boolean>(false);
    const [isCardImgLoaded, setIsCardImgLoaded] = useState<boolean>(false);
    const [isCopied, setIsCopied] = useState<boolean>(false);

    const playerInfo = props.uidData.playerInfo;
    const abyss = `${playerInfo.towerFloorIndex}-${playerInfo.towerLevelIndex}`;

    const onUIDCopied = async () => {
        copy(props.uidData.uid.toString());
        // await navigator.clipboard.writeText("wdwad");
        setIsCopied(true);
        // alert("UID has been copied to the clipboard.")
    }

    const convertToImage = async () => {
        setIsConverted(true);
        const refCurrent = refConvert?.current as HTMLDivElement;
        console.log(refCurrent.clientWidth, refCurrent.offsetWidth)
        const cardImg = await toPng(refCurrent, {includeQueryParams: true, quality: 1, width: refCurrent.clientWidth});
        setCardImg(cardImg);
        console.log(cardImg);
    }

    const downloadCardImg = () => {
        let current = refDownload.current as HTMLAnchorElement;
        current.href = cardImg;
        current.download = `${playerInfo.nickname}-${props.uidData.uid}.png`;
        current.click();
    }

    const closeImg = () => {
        setIsConverted(false);
    }

    return (
        <div className="flex flex-col justify-center items-center">
            <div className="w-screen relative">
                <div className="font-genshin text-white antialiased overflow-x-auto mx-2 rounded-lg sm:w-[40rem] 2lg:w-[95rem] h-[25rem]"
                    ref={refConvert}
                    style={{backgroundImage: `url('${backgroundCard.src}')`}}>
                    {/* h-[18.5rem] */}
                    <div className={`rounded-tl-lg rounded-tr-lg shadow-lg bg-gray-600 h-[16.5rem] border border-[#ffffff85] w-[28rem] flex p-3`}
                        style={{background: `linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)), url('${getNameCard(props.uidData)}')`,
                                maskImage: "linear-gradient(to left, rgba(217, 217, 217, 0) 0%, rgba(217, 217, 217, 1) 40%)",
                                WebkitMaskImage: "linear-gradient(to left, rgba(217, 217, 217, 0) 0%, rgba(217, 217, 217, 1) 40%)"
                        }}>
                    </div>

                    <div className="absolute top-0 sm:pl-5 lg:pl-2 flex h-full">
                        <div className="flex flex-col pt-4">
                            <div className="flex">
                                <div className="flex flex-col justify-center items-center">
                                    <div className="overflow-hidden bg-[#00000043] rounded-full border-2 border-[#0000005c]">
                                        <Image
                                            src={getAvatarProfilePicture(props.uidData)}
                                            alt="Character Profile Picture"
                                            width={100}
                                            height={100}
                                            className="rounded-lg shadow-lg antialiased"/>
                                    </div>
                                    <div className="flex justify-center items-center mt-3 text-sm rounded-lg bg-[#00000032] py-1">
                                        <div className="pl-2">{props.uidData.uid}</div>
                                        <div className="px-2 text-lg">
                                            <HiOutlineClipboardDocument onClick={onUIDCopied} className="cursor-pointer"/>
                                        </div>
                                    </div>
                                </div>
                                <div className="w-[18rem] sm:ml-5 lg:ml-10 pr-1 flex flex-col justify-between">
                                    <div className="flex justify-between">
                                        <div className="sm:text-base lg:text-lg w-full font-semibold rounded-md sm:px-1 py-[2px] lg:p-1 bg-[#00000032]">
                                            {playerInfo.nickname}
                                        </div>
                                    </div>
                                    <div className="sm:text-sm lg:text-base mt-3 max-h-10 overflow-hidden text-ellipsis whitespace-nowrap">
                                        {playerInfo.signature} Lorem ipsum dolor sit, amet consectetur adipisicing elit. Voluptate rerum modi incidunt repellat ex qui quisquam dolore vitae, assumenda ea consequuntur repellendus harum quasi iure itaque maxime nulla quaerat autem.
                                    </div>
                                    <div className="mt-3">
                                        <div className="flex justify-between text-xs my-1 p-1 rounded-md bg-[#00000032]">
                                            <div className="flex flex-row justify-center items-center">
                                                <GiUpgrade className="mr-1"/>
                                                Adventure Rank
                                            </div>
                                            <div>{playerInfo.level}</div>
                                        </div>
                                        <div className="flex justify-between text-xs p-1 rounded-md bg-[#00000032]">
                                            <div className="flex flex-row justify-center items-center">
                                                <TbWorld className="mr-1"/>
                                                World Level
                                            </div>
                                            <div>{playerInfo.worldLevel}</div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <div className="flex justify-between mt-3 text-sm">
                                <div>
                                    <div className="flex flex-col justify-center items-center w-max">
                                        <Image src={achievementIcon} alt="Achievement" width={50} height={0}/>
                                        Achievements {playerInfo.finishAchievementNum}
                                    </div>
                                </div>
                                <div className=" border-separate border-white border"></div>
                                <div>
                                    <div className="flex flex-col justify-center items-center w-max mr-10">
                                        <Image src={abyssIcon} alt="Spiral Abyss" width={50} height={0}/>
                                        Spiral Abyss {abyss}
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div className="relative max-w-[35rem] mx-5 py-1 flex justify-between bg-black overflow-hidden">
                            <div className="absolute">w</div>
                            <div className="relative mx-5 py-1 flex justify-between w-[50rem] right-36">
                                {/* <div>{`${props.uidData.avatarInfoList[0].avatarId}`}</div> */}
                                    <Image src={getAvatarArtFromListByIndex(props.uidData, 0)} alt="Character Image" fill/>
                                {/* <div>Klee</div> */}
                            </div>
                            <div className="absolute right-0">w</div>
                        </div>
                        <div>AWDauwih</div>
                    </div>
                </div>
            </div>
            <div className="flex justify-center items-center mt-3 text-sm font-lato sm:font-bold" onClick={convertToImage}>
                <button className="p-2 text-center bg-sky-300 text-black rounded-sm cursor-pointer">Convert to PNG</button>
            </div>
            <div className={`${isCopied ? "block": "hidden"} z-[999]`}>
                <div className="fixed left-0 top-0 w-full h-full flex justify-center items-center">
                    <div className="rounded-md bg-sky-600 p-2 font-lato font-bold">UID has been copied to the clipboard.</div>
                </div>
            </div>
            <div className={`${isConverted ? "block": "hidden"} z-[999]`}>
                <div className="fixed left-0 top-0 w-full h-full bg-[#000000b0] backdrop-blur-sm flex flex-col justify-center items-center">
                    <Image src={cardImg} alt="Converted Card" width={500} height={500}/>
                    <div className="font-lato font-bold">
                        <div className="bg-sky-600 mt-3 rounded-l-sm rounded-r-sm text-white flex flex-row">
                            <a className="w-[5.5rem] text-sm p-2 text-center cursor-pointer overflow-hidden" ref={refDownload} onClick={downloadCardImg}>
                                Download
                            </a>
                            <div className="w-[5rem] text-sm p-2 text-center cursor-pointer border-white border-l" onClick={closeImg}>
                                Close
                            </div>
                        </div>
                    </div>
                    {/* <div className="flex flex-row space-x-5 font-lato font-bold">
                        <a className="mt-3 text-sm p-2 text-center bg-sky-300 text-black rounded-lg cursor-pointer" ref={refDownload} onClick={downloadCardImg}>Download</a>
                        <button className="mt-3 text-sm p-2 text-center bg-red-600 text-white rounded-lg" onClick={closeImg}>Close</button>
                    </div> */}
                </div>
            </div>
            {/* <div className="absolute flex justify-center items-center">
                <Image src={cardImg} alt="Converted Card" width={500} height={500}/>
            </div> */}
        </div>
    );
}

Additional Context

image

Your Environment

NextJS (13.2.3), ReactJS (18.2.0), Visual Studio Code.

vivcat[bot] commented 1 year ago

👋 @rushkii

Thanks for opening your first issue here! If you're reporting a 🐞 bug, please make sure you include steps to reproduce it. To help make it easier for us to investigate your issue, please follow the contributing guidelines.

We get a lot of issues on this repo, so please be patient and we will get back to you as soon as we can.

DenisCor commented 1 year ago

Same for me, PNG won't convert in Firefox only! Im trying to use it to download dendrograms, large ones won't download while smaller ones download just fine. Im suspecting the svgToDataURL function in your repo - https://github1s.com/bubkoo/html-to-image/blob/master/src/util.ts#L195 possibly the encodeURIComponent not working properly in Firefox because of the size of the string in some cases. "encodeURIComponent() is not designed to handle very large input strings, and attempting to encode a very large string can cause performance issues or even crash the browser. To avoid this problem, you should consider breaking the input string into smaller pieces and encoding each piece separately." Hopefully this will get fixed soon!