首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在运行时以编程方式更改SVG类

在运行时以编程方式更改SVG类
EN

Stack Overflow用户
提问于 2021-11-16 23:34:56
回答 2查看 679关注 0票数 0

-I希望多次将相同的 SVG绘制到canvas上,但每次我都希望以编程方式更改该SVG中特定类的颜色。

举个例子,以下面这个房子的图片为例:

这个房子的SVG有以下几个类:

代码语言:javascript
复制
<style>

  .window-class {
    fill: lime;
  }

  .door-class {
    fill: blue;
  }

  .house-class {
    fill: tan;
  }

  .roof-class {
    fill: red;
  }

</style>

如何以编程方式访问这些特定的样式类,以便为我绘制的每个新房子更改它们的颜色值?

我正在构造SVG,方法是创建一个Image,然后使用以下代码将该图像绘制到一个canvas上:

代码语言:javascript
复制
    // 1. Create the CANVAS and the CONTEXT:
    var theCanvas = document.getElementById("theCanvas");
    var theContext = theCanvas.getContext("2d");
    theContext.fillStyle = "lightblue";
    theContext.fillRect(0, 0, theCanvas.width, theCanvas.height);

    // 2. Define the SVG data:
    var imageData = '<svg id="HOUSE" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="240.26" height="311.24" viewBox="0 0 240.26 311.24"><defs><style>.house-class {fill: tan;}.roof-class {fill: red;}.roof-class, .window-class, .door-class {stroke: #000;stroke-miterlimit: 10;}.window-class {fill: lime;}.door-class {fill: blue;}</style></defs><g id="House"><rect class="house-class" x="30.08" y="131.74" width="173.07" height="179"/><path d="M270,242V420H98V242H270m1-1H97V421H271V241Z" transform="translate(-67.39 -109.76)"/></g><polygon id="Roof" class="roof-class" points="1.11 131.74 239.11 131.74 117.11 0.74 1.11 131.74"/><rect id="Window2" class="window-class" x="145.11" y="160.74" width="30" height="42"/><rect id="Window1" class="window-class" x="58.61" y="160.74" width="30" height="42"/><rect id="Door" class="door-class" x="92.11" y="228.74" width="52" height="82"/></svg>';

    var DOMURL = window.URL || window.webkitURL || window;

    var img = new Image();
    var svg = new Blob([imageData], { type: 'image/svg+xml;charset=utf-8' });
    var url = DOMURL.createObjectURL(svg);

    img.onload = function () {
        theContext.drawImage(img, 0, 0);
        DOMURL.revokeObjectURL(url);
    }

    img.src = url;

通常情况下,我可以通过以下方法获得特定类的颜色:

代码语言:javascript
复制
    let nodeList = document.getElementsByClassName("window-class");

然后,我将遍历这个nodeList,在我发现的每个元素中,使用这个window-class进行样式设置,我会这样做:

代码语言:javascript
复制
        element.style.fill = -whatever-the-next-color-would-be-;

但是,由于我正在以上面所示的方式创建我的映像,我不知道如何才能获得它的SVG的特定类。

有什么想法吗?

==============================

更新:

有人指出,不包括多次绘制图像/SVG的代码,因此如下所示:

代码语言:javascript
复制
        // GLOBAL VARIABLES:
        const TOTAL_IMAGES = 3;  // could be 30, or 300
        const canvasWidth = 250;
        const canvasHeight = 320;

        var canvasX, canvasY = 0;

        // COLOR VARIABLES:
        var colorCounter = 0;
        let houseColorsArray = ["fuchsia", "gold", "lighblue"]; // Will have lots more colors for all of these 
        let windowColorsArray = ["yellow", "pink", "lightgreen"];
        let roofColorsArray = ["maroon", "crimson", "darkred"];
        let doorColorsArray = ["darkBlue", "purple", "darkslategray"];


        // CLASS-NAMES
        let classNamesToPaintArray = [".house-class", ".door-class", ".window-class", ".roof-class"];



        function designOneHouse(theCanvas) {
            console.log("\n\n==========================\n=");
            console.log("= =>>In 'designOneHouse()'!\n");

            // 1. Create a Color-Scheme:
            let houseColor = houseColorsArray[colorCounter];
            let doorColor = doorColorsArray[colorCounter];
            let windowColor = windowColorsArray[colorCounter];
            let roofColor = roofColorsArray[colorCounter];
            console.log("  ->Current 'houseColor' = ", houseColor);
            console.log("  ->Current 'doorColor' = ", doorColor);
            console.log("  ->Current 'windowColor' = ", windowColor);
            console.log("  ->Current 'roofColor' = ", roofColor);

            let context = theCanvas.getContext("2d");


            // Iterate the ColorCounter - making sure we don't overflow the ColorsArrays:
            colorCounter++;
            if(colorCounter == houseColorsArray.length) {
                colorCounter = 0;
            }


            // Now GET-AT and PAINT the Individual SVG Components.
            // STRATEGY:
            // 1. Iterate through the Array containing all the CLASS-NAMES who's color I want to change.
            // 2. For each of these classes, I'll need to iterate through all the HTML elements that are OF that class type
            //    (there may be like 10 elements that are all styled by the same Style; I want all of them to be updated!)
            // 

            for(classNameCounter = 0; classNameCounter < classNamesToPaintArray.length; classNameCounter++) {
                let currentClassName = classNamesToPaintArray[classNameCounter];
                console.log("currentClassName = " + currentClassName);

                let nodeList = document.getElementsByClassName(currentClassName);
                console.log("nodeList = " + nodeList);
                console.log("nodeList LENGTH = " + nodeList.length);

                for(var counter = 0; counter < nodeList.length; counter++) {
                    console.log("\n\n===>>IN FOR LOOP -- Node-COUNTER = " + counter);
                    let currentNode = nodeList[counter];
                    console.dir("  > 'childNodes[0]' of 'currentNode' = ");
                    console.dir(currentNode.childNodes[0]);

                    let elements = document.querySelectorAll(".door-class");
                    // Change the text of multiple elements with a loop
                    elements.forEach(element => {
                        element.style.fill = "pink";
                    });

                }

            }

        }



        function makeCanvasGrid() {
            console.log("\n\n====>In 'makeCanvasGrid()'!\n");

            for(var canvasCounter = 0; canvasCounter < TOTAL_IMAGES; canvasCounter++) {
                console.log("\n >FOR LOOP - canvasCounter = " + canvasCounter);

                // 1. Create a new Canvas Object:
                let newCanvas = document.createElement("canvas");
                newCanvas.setAttribute("width", canvasWidth);
                newCanvas.setAttribute("height", canvasHeight);
                newCanvas.setAttribute("id", "newCanvas" + canvasCounter);
                // Log-out just to verify the "id" property was set correctly:
                console.log("  >newCanvas.id  = " + newCanvas.id);

                // 2. Place the Canvas at (x,y) (top, left) coordinates:
                newCanvas.style.position = "absolute";
                newCanvas.style.left = canvasX; //"100px";
                newCanvas.style.top = canvasY;  //"100px";

                designOneHouse(newCanvas);


                // Check the current Canvas' (X, Y) coords, and if needed, reset X to 0 and SKIP to the next "ROW" of Canvasses:
                if(canvasCounter > 0 && canvasCounter % 3 == 0) {
                    console.log("  >>NEXT ROW PLEASE!!!! canvasCount = ", canvasCounter);
                    canvasX = 0;
                    canvasY += canvasHeight + 20;
                }
                else {
                    canvasX += canvasWidth + 10;
                }
            }
        }


        makeCanvasGrid();

因此,当我现在运行这个命令时,控制台显示nodeList是空的:

代码语言:javascript
复制
    nodeList LENGTH = 0

因此,这一说法基本上是行不通的:

代码语言:javascript
复制
    let nodeList = document.getElementsByClassName(currentClassName);
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-11-17 05:04:39

要操纵房子的DOM,SVG必须在DOM中。因此,我已经将SVG封装在一个<div>中并隐藏了div。我已经把它放到了屏幕之外,但我可以用其他方式隐藏div。

一旦您这样做,您的下一个问题是您正在更改元素的fill,但这将被您的SVG中的CSS所覆盖。所以你必须删除那些CSS样式。

第三,您正在创建画布对象,但没有将它们附加到DOM。

另外,由于canvasX未初始化,因此您将得到一个错误。加上CSS长度必须有单位。所以你需要newCanvas.style.left = canvasX + "px"等等。

你还错误地查找了你的元素。getElementsByClassName(".hose-class")什么也找不到。它必须是getElementsByClassName(".hose-class")

最后,我重写了元素查找和颜色分配代码。我把每个配色方案打包成一系列的配色对象。它使类到颜色的映射更加简单。

代码语言:javascript
复制
// GLOBAL VARIABLES:
const TOTAL_IMAGES = 3;  // could be 30, or 300
const canvasWidth = 250;
const canvasHeight = 320;

var canvasX = 0, canvasY = 0;

// COLOR VARIABLES:
var colorCounter = 0;

let houseColorSchemes = [ {".house-class": "fuchsia",
                           ".door-class": "darkblue",
                           ".window-class": "yellow",
                           ".roof-class": "maroon"},
                     
                          {".house-class": "gold",
                           ".door-class": "purple",
                           ".window-class": "pink",
                           ".roof-class": "crimson"},
                     
                          {".house-class": "lightblue",
                           ".door-class": "darkslategray",
                           ".window-class": "lightgreen",
                           ".roof-class": "darkred"} ];
                   

// CLASS-NAMES
let classNamesToPaintArray = [".house-class", ".door-class", ".window-class", ".roof-class"];

// SVG template
let houseSVG = document.getElementById("HOUSE");


function designOneHouse(theCanvas) {
  console.log("\n\n==========================\n=");
  console.log("= =>>In 'designOneHouse()'!\n");

  let context = theCanvas.getContext("2d");

  // Now GET-AT and PAINT the Individual SVG Components.
  // STRATEGY:
  // 1. Iterate through the Array containing all the CLASS-NAMES who's color I want to change.
  // 2. For each of these classes, I'll need to iterate through all the HTML elements that are OF that class type
  //    (there may be like 10 elements that are all styled by the same Style; I want all of them to be updated!)
  // 

  let colorScheme = houseColorSchemes[colorCounter];
  
  classNamesToPaintArray.forEach(className => {
    let elements = houseSVG.querySelectorAll(className);
    
    elements.forEach(element => element.style.fill = colorScheme[className]);
  });
  

  var imageData = houseSVG.outerHTML;

  var DOMURL = window.URL || window.webkitURL || window;

  var img = new Image();
  var svg = new Blob([imageData], { type: 'image/svg+xml;charset=utf-8' });
  var url = DOMURL.createObjectURL(svg);

  img.onload = function () {
    context.drawImage(img, 0, 0);
    DOMURL.revokeObjectURL(url);
  }

  img.src = url;


  // Iterate the ColorCounter - making sure we don't overflow the ColorsArrays:
  colorCounter++;
  if(colorCounter == houseColorSchemes.length) {
    colorCounter = 0;
  }


}



function makeCanvasGrid() {
  console.log("\n\n====>In 'makeCanvasGrid()'!\n");

  for(var canvasCounter = 0; canvasCounter < TOTAL_IMAGES; canvasCounter++) {
    console.log("\n >FOR LOOP - canvasCounter = " + canvasCounter);

    // 1. Create a new Canvas Object:
    let newCanvas = document.createElement("canvas");
    newCanvas.setAttribute("width", canvasWidth);
    newCanvas.setAttribute("height", canvasHeight);
    newCanvas.setAttribute("id", "newCanvas" + canvasCounter);
    // Log-out just to verify the "id" property was set correctly:
    console.log("  >newCanvas.id  = " + newCanvas.id);

    // 2. Place the Canvas at (x,y) (top, left) coordinates:
    newCanvas.style.position = "absolute";
    newCanvas.style.left = canvasX + "px"; //"100px";
    newCanvas.style.top = canvasY + "px";  //"100px";

    document.body.appendChild(newCanvas);

    designOneHouse(newCanvas);


    // Check the current Canvas' (X, Y) coords, and if needed, reset X to 0 and SKIP to the next "ROW" of Canvasses:
    if(canvasCounter > 0 && canvasCounter % 3 == 0) {
      console.log("  >>NEXT ROW PLEASE!!!! canvasCount = ", canvasCounter);
      canvasX = 0;
      canvasY += canvasHeight + 20;
    }
    else {
      canvasX += canvasWidth + 10;
    }
  }
}


makeCanvasGrid();
代码语言:javascript
复制
#house-template {
  position: absolute;
  left: -1000px;
}
代码语言:javascript
复制
<div id="house-template">

<svg id="HOUSE" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="240.26" height="311.24" viewBox="0 0 240.26 311.24">
  <defs>
    <style>
      .roof-class, .window-class, .door-class {stroke: #000;stroke-miterlimit: 10;}
    </style>
  </defs>
  <g id="House">
    <rect class="house-class" x="30.08" y="131.74" width="173.07" height="179"/>
    <path d="M270,242V420H98V242H270m1-1H97V421H271V241Z" transform="translate(-67.39 -109.76)"/>
  </g>
  <polygon id="Roof" class="roof-class" points="1.11 131.74 239.11 131.74 117.11 0.74 1.11 131.74"/>
  <rect id="Window2" class="window-class" x="145.11" y="160.74" width="30" height="42"/>
  <rect id="Window1" class="window-class" x="58.61" y="160.74" width="30" height="42"/>
  <rect id="Door" class="door-class" x="92.11" y="228.74" width="52" height="82"/>
</svg>

</div>

票数 2
EN

Stack Overflow用户

发布于 2021-11-17 04:30:32

下面是产生你想要的结果的一种方法。

  1. 下面的方法将<svg>元素作为模板使用。该模板被克隆,颜色被应用,转换成图像,并放置到每个有颜色的房子的画布中。
    • 注: SVG的结构发生了变化。class属性被用于通过普通CSS class应用填充样式的自定义data-属性data-part替换。

  1. ,每所房子的坐标位置都在一个间隔的x y坐标阵列中。该数组还指示要绘制多少房屋,

  1. ,房屋“部件”的颜色包括在一个列出房屋“部件”及其相应颜色(颜色的计数应该与房屋数量相匹配)的对象中,
  2. All <canvas> CSS被移动到样式表.

中。

我让你来调整画布上的图像大小。

代码语言:javascript
复制
const canvas = document.querySelector('canvas');
const context = canvas.getContext("2d");

const housePositions = ["0 10", "85 10", "170 10"];
const parts = {
  House: ["fuchsia", "gold", "lightblue"],
  Window: ["yellow", "pink", "lightgreen"],
  Roof: ["maroon", "crimson", "darkred"],
  Door: ["darkBlue", "purple", "darkslategray"]
};

function addHouse(colorIndex, x, y) {
  let clonedSvgElement = document.querySelector('svg').cloneNode(true);
  Object.keys(parts)
    .forEach(part => {
      clonedSvgElement.querySelectorAll(`[data-part=${part}]`)
        .forEach(item => {
          item.style.fill = parts[part][colorIndex];
        });
      const blob = new Blob([clonedSvgElement.outerHTML], { type: 'image/svg+xml;charset=utf-8' });
      const blobURL = URL.createObjectURL(blob);
      const image = new Image();
      image.onload = () => {
        context.drawImage(image, x, y, 130, 110);
        URL.revokeObjectURL(this.src);
      };
      image.src = blobURL;
    });
}

housePositions.forEach((coordString, index) => {
  const [x, y] = coordString.split(' ');
  addHouse(index, x, y);
});
代码语言:javascript
复制
canvas {
    position: absolute;
    left: 10px;
    top: 10px;
    width: 150px;
    height: 80px;
    border: 1px solid;
    background-color: lightblue;
}

svg {
    display: none;
}
代码语言:javascript
复制
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="index.css">

    <title>Document</title>
    <script defer src="index.js"></script>
</head>
<body>
<canvas></canvas>
<svg id="HOUSE" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="140" height="140" viewBox="0 0 240.26 311.24"><defs></defs><g id="House"><rect data-part="House" x="30.08" y="131.74" width="173.07" height="179"/><path d="M270,242V420H98V242H270m1-1H97V421H271V241Z" transform="translate(-67.39 -109.76)"/></g><polygon data-part="Roof" points="1.11 131.74 239.11 131.74 117.11 0.74 1.11 131.74"/><rect data-part="Window" x="145.11" y="160.74" width="30" height="42"/><rect data-part="Window"  x="58.61" y="160.74" width="30" height="42"/><rect data-part="Door" x="92.11" y="228.74" width="52" height="82"/></svg>
</body>
</html>

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69997309

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档