dom-to-image icon indicating copy to clipboard operation
dom-to-image copied to clipboard

when using -webkit-background-clip: text with background image image background not rendered

Open eljuani opened this issue 7 years ago • 26 comments

Use case: description, code

I have a div with this style: -webkit-text-fill-color: transparent; background: #edbc8e; background-image: url(fondo.jpg); -webkit-background-clip: text;

Expected behavior

It should render the masked text background on the canvas

Actual behavior (stack traces, console logs etc)

But it doesn't. If I use a gradient like background: linear-gradient(to bottom, #3c332b 0%,#2f2820 49%,#352f27 100%); it works, but not with an image.

Browsers

  • [ ] Chrome 49+

eljuani avatar May 11 '17 08:05 eljuani

if im using on a div: background-image: linear-gradient(90deg, $backColor 50%, transparent 50%, transparent), linear-gradient($nextdeg, $barColor 50%, $backColor 50%, $backColor);

its not rendering it, you maybe know why?

joni7777 avatar May 15 '17 21:05 joni7777

I am having this problem, but only in Chrome.

Fiddle https://jsfiddle.net/2m8dcr63/405/

GavinRF avatar Sep 04 '18 00:09 GavinRF

Left = screenshot / Right = output of dom-to-image on Chrome Version 68.0.3440.106 clip_test

.clipText { color: transparent; -webkit-text-fill-color: transparent; background-clip: text; -webkit-background-clip: text; background-size: cover; background-image: url(../img/forest.jpg) ; }

GavinRF avatar Sep 06 '18 07:09 GavinRF

I have the same problem.

Chrome Version 71.0.3578.98

ghost avatar Jan 11 '19 15:01 ghost

Same problem

stripedypaper avatar Oct 21 '19 03:10 stripedypaper

Same problem

lorryrio avatar Dec 03 '19 06:12 lorryrio

+1

bravelincy avatar Jan 13 '20 10:01 bravelincy

+1

birch-jayton avatar Apr 01 '20 23:04 birch-jayton

+1

Thul999 avatar May 24 '20 22:05 Thul999

any sollution ? i need to solve this problem for my current project

nurulalamador avatar Sep 26 '20 10:09 nurulalamador

+1

Quang-Khoa avatar Oct 02 '20 03:10 Quang-Khoa

+1

stephenwong1991 avatar Oct 13 '20 05:10 stephenwong1991

Hello, is there anyone who has found a solution to this issue? I suspect it's got something to do with the vendor prefix since the domtoimage.toPng works perfectly on Firefox, but not on Chrome. Would be highly appreciated if someone knows how to fix this issue. Thank you in advance!

pdji1602003 avatar Oct 20 '20 07:10 pdji1602003

I resolve this issue!

reason:

chrome must use -webkit-background-clip style to expose the text clip effect;

but dom-to-image use cssText will clear -webkit-background-clip style.

it make the issue happen!

solution:

because the dom-to-image use the native API XMLSerializer to serializeToString ; we can hack by dom-xml

import { XMLSerializer } from "xmldom";
window['XMLSerializer'] = XMLSerializer; // override

let _serializeToString = XMLSerializer.prototype.serializeToString;
XMLSerializer.prototype.serializeToString = (node, isHtml, nodeFilter) => {
  let string = _serializeToString(node, isHtml, nodeFilter);
  if(node.hasAttribute("textClip")){ // u can use other mark to change
    string = string.replace("; background-image: ","; -webkit-background-clip: text; background-image:");  // add the -webkit-background-clip
  }
  return string;
}

have fun~

krapnikkk avatar May 06 '21 08:05 krapnikkk

same problem here. Havent checked @krapnikkk solution yet... but it generally doesnt work with clipped images. Not only with text

torian257x avatar May 06 '21 21:05 torian257x

this is firefox: image

torian257x avatar May 06 '21 21:05 torian257x

Thanks @krapnikkk for finding the cause. The code wouldn't always work in all cases because it assumes background-image isn't the first style property. I have come up with a better way:

const _serializeToString = XMLSerializer.prototype.serializeToString;
XMLSerializer.prototype.serializeToString = function (node) {
  return _serializeToString
    .call(this, node)
    .replace(
      /background-image:/g,
      '-webkit-background-clip: text; background-image:',  // Add the -webkit-background-clip
    );
};

yangshun avatar Aug 14 '21 05:08 yangshun

@yangshun I tried your solution but while clip text works, I have a div with a background (without clipping) and It doesn't get rendered anymore (neither Firefox or Chrome). If I remove your solution it works again.

This is the css:

.border {
  padding: 15px 15px 0px 15px;
  display: inline-block;
  position: relative;
  margin: 0 16px 10px 16px;
  max-width: 600px;
  z-index: 0;
}

.border:before {
  background-image: linear-gradient(145deg, #fff, #ffffff0a);
  border-radius: 15px;
  bottom: 0;
  content: "";
  padding: 5px;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask-composite: exclude;
  -webkit-mask-composite: destination-out;
  z-index: -1;
}

Any suggestion?

ArtemisGraphic avatar Sep 08 '21 14:09 ArtemisGraphic

@ArtemisGraphic Ah I see. My solution assumes that all the background gradient will want -webkit-background-clip on it. In that case, I can think of two ways, but unsure if they will work:

  1. Add a -webkit-background-clip: border-box to that class to override the value -webkit-background-clip: text added by the monkey-patched serializeToString
  2. Change the implementation of XMLSerializer.prototype.serializeToString. Do a check on node first and only do the .replace() if intended
XMLSerializer.prototype.serializeToString = function (node) {
  const value = _serializeToString.call(this, node);

  if (someCondition) {
    return value;
  }
  
  return value.replace(
      /background-image:/g,
      '-webkit-background-clip: text; background-image:',  // Add the -webkit-background-clip
    );
};

yangshun avatar Sep 08 '21 14:09 yangshun

@yangshun the first solution does not work unfortunately, I tried the second but can't think of a condition that would exclude the replace effect only for classes with clip. I tried value.hasAttribute("textClip") but I get "it's not a function" error.

ArtemisGraphic avatar Sep 08 '21 15:09 ArtemisGraphic

@ArtemisGraphic value is a string. So you can only use string methods on it.

yangshun avatar Sep 08 '21 15:09 yangshun

@krapnikkk I'm trying to use your script but I get 'serializeToString' called on an object that does not implement interface XMLSerializer.' I'm using dom-to-image on the client, do you have any suggestion?

ArtemisGraphic avatar Sep 16 '21 12:09 ArtemisGraphic

Thanks @krapnikkk for finding the cause. The code wouldn't always work in all cases because it assumes background-image isn't the first style property. I have come up with a better way:

const _serializeToString = XMLSerializer.prototype.serializeToString;
XMLSerializer.prototype.serializeToString = function (node) {
  return _serializeToString
    .call(this, node)
    .replace(
      /background-image:/g,
      '-webkit-background-clip: text; background-image:',  // Add the -webkit-background-clip
    );
};

Your solution works, however this removes the font-family from the text I try to render. The finished result as a clipped background, but the "Times" font.

Any idea on how to fix that?

the-unknown avatar Apr 01 '22 14:04 the-unknown

Thanks @krapnikkk for finding the cause. The code wouldn't always work in all cases because it assumes background-image isn't the first style property. I have come up with a better way:

const _serializeToString = XMLSerializer.prototype.serializeToString;
XMLSerializer.prototype.serializeToString = function (node) {
  return _serializeToString
    .call(this, node)
    .replace(
      /background-image:/g,
      '-webkit-background-clip: text; background-image:',  // Add the -webkit-background-clip
    );
};

Nice.. thank u bro..

feri-irawan avatar Nov 09 '22 00:11 feri-irawan

Modify the dom-to-image.js source code:

return new XMLSerializer().serializeToString(node); => return new XMLSerializer().serializeToString(node).replace(/background-image/gi, "-webkit-background-clip:text;background-image");

yhealjsp avatar Nov 14 '22 07:11 yhealjsp

cause some broswer do not support background-clip in 'background' style well.

UPDATE BASE 2.6.0

  • inject to dom-to-image.js
const SPECIAL_BACKGROUND_STYLE = ['background-clip', 'background-color'];

const _serializeToString = XMLSerializer.prototype.serializeToString;
XMLSerializer.prototype.serializeToString = function (node) {
  const res = _serializeToString
    .call(this, node)
    .replace(
      /-webkit-text-fill-color:/g,
      '-webkit-background-clip: text; -webkit-text-fill-color:',  // Add the -webkit-background-clip
    );
  return res;
};
  • update processClone processing Promise link
    return Promise.resolve()
            .then(cloneStyle)
            .then(clonePseudoElements)
            .then(copyUserInput)
            .then(fixSvg)
            .then(fixBackground)  // add this line
            .then(function () {
                return clone;
            });

  • inject new function into function processClone
// add style use cssText
function fixBackground() {
    const originalStyle = window.getComputedStyle(original);
    const cloneStyle = clone.style;
    SPECIAL_BACKGROUND_STYLE.forEach((name: string) => {
      const value = originalStyle.getPropertyValue(name);
      if (value) {
        cloneStyle.cssText += `${name}: ${value} !important;`;
      }
    });
}
  • update function cloneStyle()
function cloneStyle() {
    copyStyle(window.getComputedStyle(original), clone.style);

    function copyStyle(source, target) {
        if (source.cssText) target.cssText = source.cssText;
        else copyProperties(source, target);

        function copyProperties(source, target) {
            util.asArray(source).forEach(function (name) {
                if(!SPECIAL_BACKGROUND_STYLE.includes(name)) { // skip special style
                  target.setProperty(
                      name,
                      source.getPropertyValue(name),
                      source.getPropertyPriority(name)
                  );
                }
            });
        }
    }
}

abcdefe avatar Aug 23 '23 08:08 abcdefe