更改一条路径中图案的位置,但不影响 svg 中具有相同图案的其他路径

问题描述 投票:0回答:2

我用相同的图案填充了很多路径。这些路径属于同一类。 我想单独更改其中一条路径中图案的位置,将图案放在我想要的位置,但不影响其他路径中的图案位置。我该怎么做?

例如,这里我用图案填充了三个矩形。我想通过拖动滑块来更改 rectA 中图案的位置。目前在我的代码中,它将同时更改所有三个形状中图案的位置。

<script src="https://unpkg.com/[email protected]/dist/d3.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/d3.min.js"></script>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<svg width="300" height="100"> 
<defs>
  <pattern id="myPattern"
           x="0" y="0" width="40" height="40"
           patternUnits="userSpaceOnUse" patternTransform="rotate(0)">

    <rect id="rotateRect" x="5" y = "5" width = "30" height = "30" fill = " #87CEFA "/>

  </pattern>
</defs>
  <rect id="rectA" class="myClass" x="0" y="0" width="100" height="100"
        style="stroke: #000000;" />   
  <rect id="rectB" class="myClass" x="100" y="0" width="100" height="100"
        style="stroke: #000000;" />   
  <rect id="rectC" class="myClass" x="200" y="0" width="100" height="100"
        style="stroke: #000000;" />   
</svg>

<p style=font-size:15px>Move Pattern in Rect A </p>
<input id="slider" type="range" min="0" max="100" value="5" step='1' >

<script>
  d3.selectAll(".myClass")
  .attr("fill", "url(#myPattern)")
  
  const slider = document.getElementById("slider")
  const myPattern = document.getElementById("myPattern")
  
  slider.oninput = function(){
    myPattern.setAttribute("patternTransform", "translate(0"+","+slider.value+") ")
  }
</script>

我想要的结果是这样的:

css svg d3.js
2个回答
2
投票

根本原因是模式 id 是一个 global id;第一个定义的 id 将在所有形状上设置图案。

  • 因此为每个模式使用唯一的模式id

  • 或将

    <svg>
    放入native JavaScript Web 组件 (JSWC) 与 ShadowDOM,

    所以所有 id 值都是 local(到 ShadowDOM)值:

<svg-squares></svg-squares>
<svg-squares transform="25"></svg-squares>
<svg-squares transform="66"></svg-squares>

<script>
  customElements.define("svg-squares", class extends HTMLElement {
    connectedCallback() {
      this.style.display = "inline-block";
      this.attachShadow({mode:"open"})
          .innerHTML = `
             <svg width="100" height="100"> 
              <defs>
               <pattern id="P" width="40" height="40" patternUnits="userSpaceOnUse">
                <rect x="5" y="5" width ="30" height="30" fill="#87CEFA"/>
               </pattern>
             </defs>
             <rect fill="url(#P)" width="100" height="100" stroke="black" x="0" y="0"/>   
            </svg>` + 
            // slider
            `<br><input type="range" min="0" max="100" value="5"               
                        oninput="this.getRootNode().host.transform(this.value)">`;

      this.transform(this.getAttribute("transform")||0);
    }
    transform(value) {
      this.shadowRoot
          .querySelector("pattern")
          .setAttribute("patternTransform", `translate(0 ${value})`)
    }
  })
</script>


1
投票

您还可以使用

href
属性轻松创建“子”模式。
所以这些新模式是基于并且更重要的是链接到初始模式模板。

<svg width="300" height="100">
  <defs>
    <pattern id="basePattern" x="0" y="0" width="40" height="40" patternUnits="userSpaceOnUse" patternTransform="rotate(0)">
      <rect id="rotateRect" x="5" y="5" width="30" height="30" fill="#87CEFA" />
    </pattern>
    <!-- child pattern will inherit graphics from base pattern -->
    <pattern id="childPattern" href="#basePattern" class="childPattern" style="color:blue" patternTransform="rotate(45) scale(0.25)" />
  </defs>
  <rect id="rectA" class="rect" x="0" y="0" width="100" height="100" style="stroke: #000000;" fill="url(#basePattern)"></rect>
  <rect id="rectB" class="rect" x="100" y="0" width="100" height="100" style="stroke: #000000;" fill="url(#childPattern)"></rect>
</svg>

实际上类似于

<use>
参考文献。然而,在许多方面截然不同,因为引用的模式无法继承填充颜色等属性。

<svg width="300" height="100">
  <defs>
    <pattern id="basePattern" x="0" y="0" width="40" height="40" patternUnits="userSpaceOnUse" patternTransform="rotate(0)">

      <rect id="patternRect" x="5" y="5" width="30" height="30" fill=" #87CEFA " />

    </pattern>
  </defs>
  <rect id="rectA" class="rect" x="0" y="0" width="100" height="100" style="stroke: #000000;" />
  <rect id="rectB" class="rect" x="100" y="0" width="100" height="100" style="stroke: #000000;" />
  <rect id="rectC" class="rect" x="200" y="0" width="100" height="100" style="stroke: #000000;" />
</svg>

<p>
  A <input class="sliderPattern" id="sliderA" type="range" min="0" max="3" value="1" step='0.1'> B <input class="sliderPattern" id="sliderB" type="range" min="0" max="3" value="1" step='0.1'> C <input class="sliderPattern" id="sliderC" type="range" min="0"
    max="3" value="1" step='0.1'>
</p>
<p><button id="randomColor">Random Color (applied to base pattern)</button>
</p>

<script>
  const basePattern = document.getElementById("basePattern");
  const patternDefs = basePattern.closest('svg').querySelector('defs');
  const rects = document.querySelectorAll('.rect');
  const slider = document.getElementById("slider")
  //duplicate patterns
  for (let i = 0; i < rects.length; i++) {
    let rect = rects[i];
    let newPattern = document.createElementNS('http://www.w3.org/2000/svg', 'pattern');
    newPattern.id = "childPattern" + i;
    newPattern.setAttribute("href", "#basePattern");
    newPattern.classList.add("childPattern");
    rect.setAttribute('fill', `url(#childPattern${i})`);
    patternDefs.appendChild(newPattern);
  }
  const patterns = document.querySelectorAll('.childPattern');
  const sliders = document.querySelectorAll('.sliderPattern');
  sliders.forEach(function(slider, i) {
    slider.addEventListener("change", (e) => {
      let scale = e.currentTarget.value;
      let patternTransformBase = basePattern.getAttribute("patternTransform");
      patterns[i].setAttribute("patternTransform", patternTransformBase + ` scale(${scale})`)
    })
  });
  randomColor.addEventListener('click', (e) => {
    let random = Math.random();
    let newCol = `hsl(${180*random}deg 30% 60%)`;
    patternRect.style.fill = newCol
  });
</script>

© www.soinside.com 2019 - 2024. All rights reserved.