The color of the SVG is not visible in the PNG rendition

I have applied a style to an svg image and successfully changed the fill color using a random colors function in JavaScript. However, when I convert the svg to a png format after making these color changes, the PNG file retains the original colors instead of the updated ones.

<style type="text/css">
:root{
    --back:#662D91;
    --shadow:0.28;
    --highlight:#F3B4AE;
    --stomach:#EA8A82;
    --lightHigh:#FEE9E5;
    --lasso:#9B5D12;
    --gloves:#29A6DE;--glovesStroke:#000674;--gloves2:#29A6DE;--gloves3:#1C7FC9;--gloves4:#1D7DC6;--gloves5:#1C7DC4;
    --hatBelow:#DB8556;
    --hatBelow2:#8A3C13;
    --hatInner:#8C4017;
    --hatInner2:#934A24;
    --hatInner3:#9D5C3A;
    --stripe:#8A7454;
    --stripeEnd:#382000;

}
    .st0{fill:var(--back);}
    .st1{opacity:var(--shadow);}
    .st2{fill:var(--highlight);}
    .st3{fill:var(--stomach);}
    .st4{fill:var(--lightHigh);}

    (other CSS styles omitted for brevity)
</style>

This is the style element within my svg that I am modifying with the following code:

  root.style.setProperty('--back',colors[0]);

The 'colors' variable contains an array of randomly generated colors created by a script embedded within the svg. Can someone please assist me with resolving this issue?

Here is how I am converting the svg to png:

<script>
var svgString = new XMLSerializer().serializeToString(document.querySelector('svg'));
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var DOMURL = self.URL||self.webkitURL||self;
var img = new Image();
var svg = new Blob([svgString],{type: "image/svg+xml;charset=utf-8"});
var url = DOMURL.createObjectURL(svg);
img.onload = function(){
ctx.drawImage(img,0,0);
var png = canvas.toDataURL("image/png");
document.querySelector('#png-container').innerHTML = '<img src="'+png+'"/>';
DOMURL.revokeObjectURL(png);
};
img.src = url;
</script>

Answer №1

Upon further reflection, it became clear why the original approach didn't work as expected. The svg blob is limited to only include contents within the svg tag itself, meaning any CSS variables set on the html tag outside of it are not recognized. The styles defined with :root are scoped within the SVG, causing a disconnect when removed from the HTML context, leading :root to reference the svg tag instead.

The reason for this behavior remains ambiguous, but in cases involving canvas, CSS variables set on ancestor elements of the svg tag tend to be disregarded. To rectify this issue, one can apply inline styles directly to either the svg tag or its child elements.

Below is an adjusted version with a solution implemented. Disregard any modifications made to the JS code; it replicates the same functionality while setting the CSS variable on the svg tag rather than the html tag.

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>SVG thingy</title>
  </head>
  <body>
    <svg
      viewBox="0 0 10 10"
      xmlns="http://www.w3.org/2000/svg"
    >
      <style>
        :root {
          --color: red;
        }

        circle {
          fill: var(--color);
        }
      </style>

      <circle
        cx="5"
        cy="5"
        r="4"
      />
    </svg>

    <canvas></canvas>

    <div id="png-container"></div>

    <script>
      const root = document.documentElement; // html tag
      // root.style.setProperty('--color', 'black'); // Doesn't work

      const svgElement = document.querySelector('svg');
      svgElement.style.setProperty('--color', 'black'); // Works
      
      const svgString = new XMLSerializer().serializeToString(document.querySelector('svg'));
      const svgBlob = new Blob([svgElement.outerHTML], { type: 'image/svg+xml;charset=utf-8' });
      const url = URL.createObjectURL(svgBlob);
      
      const canvasElement = document.querySelector('canvas');
      const ctx = canvasElement.getContext('2d');
      
      const img = new Image();
      img.src = url;
      img.onload = () => {
        ctx.drawImage(img, 0, 0);
        const png = canvasElement.toDataURL('image/png');
        document.querySelector('#png-container').innerHTML = '<img src="' + png + '"/>';
        URL.revokeObjectURL(png);
      };
    </script>
  </body>
</html>

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

Exploring the internet with Internet Explorer is like navigating a jungle of if statements

I am facing an issue with my code that works well in Chrome, but encounters errors in IE. The if condition functions correctly in Chrome, however, in IE it seems like the first condition always gets executed despite the value of resData. Upon analysis thro ...

Guide to exporting several Chart.JS graphs to a PDF document with each graph on a separate page

I am currently utilizing the JavaScript code provided below to generate a pdf file containing multiple charts. However, all the charts are being placed on a single page. My inquiry is regarding how I can customize the code to have each chart on its own sep ...

What could be causing selenium to struggle in locating specific text on a webpage

In my webpage, I have a "tree menu" that can be opened by clicking on a button. Once the tree menu is open, I need to follow certain steps and click on different titles. For example, I would click on "19,20", then "may", and finally "2". At the top of the ...

Changing global properties in VueCli

Recently, I integrated a component library into my Vue 3 project. All instances of the component require the same styles. Instead of manually adjusting each instance's props, I opted to utilize a global property: app.config.globalProperties.$tooltipS ...

Ways to enlarge the font size of a hyperlink

I am just starting out with coding and I need to figure out how to increase the font size of my link. <!DOCTYPE html> <html> <head> <style> /* unvisited link */ a:link { color: #FFFFFF; } /* visited link */ a:visited { color: #FF ...

Angular/JS - setTimeout function is triggered only upon the occurrence of a mouse click

I'm currently working on a website that calculates the shortest path between two points in a grid utilizing AngularJS. Although I have already implemented the algorithm and can display the colored path, I am facing an issue where the color changes ti ...

Tips for resolving flickering animations in CSS and JavaScript

Is it possible to dynamically set a scale and margin for an element in order to center it fluidly using the wheel event? I am aiming to achieve a smooth transition while also adjusting scroll position on the wrapping element in a fluid manner. In the prov ...

Do you have any queries regarding JavaScript logical operators?

I'm struggling with understanding how the && and || operators work in my code. I created a small program to help myself, but it's just not clicking for me. The idea is that when you click a button, the startGame() function would be triggered. va ...

Comparison of Node.js and Express.js APIs for handling responses

Both Node.js and Express.js have their own unique response APIs. Interestingly, there are some similarities between the two, such as: Node.js: response.write(chunk[, encoding][, callback]) Express.js: res.attachment([filename]) res.download(path [, ...

Is it possible to retrieve a variable from a geojson file using Vue 3 and Vite?

For my Vue 3 project, I am trying to import a variable from a geojson file. However, when I use import line from '@static/line.geojson', my page goes blank and it seems like Vue stops working. If I use import line from '@static/line.json&ap ...

How can I check if the VPN is turned off in a React application?

import React from "react"; import { Offline, Online } from "react-detect-offline"; export default function DropDown() { return ( <> <Online>Only displayed when connected to the internet</Online> <Offline ...

What is the best method for calculating the total of a mongoose attribute?

I am attempting to calculate the sum of schema using reduce. However, the current code is not adding the items together but rather placing them next to each other. For example, 20 + 30 should result in 50, but instead it gives me 02030. Is there an issue w ...

The window.focus() function does not seem to be functioning as expected when using

I have been trying to use this code snippet to bring the window into focus. It seems to be working smoothly on Internet Explorer, but unfortunately it is not behaving as expected in Firefox. Any tips or recommendations would be greatly welcomed. ((Javas ...

Unable to "serialize" geoJSON information

While working with Leaflet JavaScript, I am attempting to retrieve data directly from GeoServer using an Ajax link. To display it nicely in a DataTables table, I need to convert it into JSON format as per DataTables instructions. However, I keep encounteri ...

Harnessing the power of the bluebird promise library

Within myPeople function, there is a promise function being called as shown below: var myPeople = function(){ var go; return new Promise (function(resolve){ User .getPeople() .then(function(allPeople){ ...

Is it necessary to adjust my font stack for optimal viewing on a high DPI tablet or computer screen?

My CSS includes the following font definition: html { font-size: 95%; font-family: Arial, Helvetica, sans-serif } I want my application to display well on PC, iOS, and Android tablets. I am aware that different devices have varying resolutions. C ...

Navigating with React Router Dom and parsing objects in search parameters

Currently, I am utilizing React Router Dom v6 and require the ability to retain object search parameters within the URL. My current approach involves: const [searchParams, setSearchParams] = useSearchParams(); const allSearchParams = useMemo(() => { ...

building responsive servers within Gulp using connect

Can I validate the availability of a server port before creating it using Gulp? Currently, this is my approach: /** * Start LiveReload Server */ gulp.task('connect', function() { var connect = require('connect'), app = ...

Where will the user's input be stored?

Consider the following HTML code: <div class="form-group"> <label class="col-md-3 col-xs-3 col-sm-3 control-label">Phone</label> <div class="col-md-4 col-xs-4 col-sm-4"> <input id="input ...

Manage Raspberry Pi through a local website

Seeking guidance on creating a system where pressing a button on a webpage triggers a signal to a raspberry pi. For example, clicking on button1 on my laravel-built website initiates operation1 on the raspberry pi side, which will be using a .Net App. An ...