How can I apply a rectangular mask to an SVG image in d3?

Is there a way to mask an SVG image with a gray background rectangle using d3.js v5 in order to display a different color partially? I am having trouble achieving this effect.

SVG

<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     viewBox="0 0 53.545 53.545" style="enable-background:new 0 0 53.545 53.545;" xml:space="preserve">
<g>
    <g>
        <circle cx="26.686" cy="4.507" r="4.507"/>
        <path d="M28.256,11.163c-1.123-0.228-2.344-0.218-3.447,0.042c-7.493,0.878-9.926,9.551-9.239,16.164
            c0.298,2.859,4.805,2.889,4.504,0c-0.25-2.41-0.143-6.047,1.138-8.632c0,3.142,0,6.284,0,9.425c0,0.111,0.011,0.215,0.016,0.322
            c-0.003,0.051-0.015,0.094-0.015,0.146c0,7.479-0.013,14.955-0.322,22.428c-0.137,3.322,5.014,3.309,5.15,0
            c0.242-5.857,0.303-11.717,0.317-17.578c0.244,0.016,0.488,0.016,0.732,0.002c0.015,5.861,0.074,11.721,0.314,17.576
            c0.137,3.309,5.288,3.322,5.15,0c-0.309-7.473-0.32-14.949-0.32-22.428c0-0.232-0.031-0.443-0.078-0.646
            c-0.007-3.247-0.131-6.497-0.093-9.742c1.534,2.597,1.674,6.558,1.408,9.125c-0.302,2.887,4.206,2.858,4.504,0
            C38.678,20.617,36.128,11.719,28.256,11.163z"/>
    </g>
</g>
</svg>

d3.js

private createSVG(rect): void {

    this.svg = d3.select(rect)
      .append('svg')
      .attr('class', 'psych-chart')
      .attr('width', this.width/2)
      .attr('height', this.height / 2)
      .attr("viewBox", '0 0 65 60')

  }

  private createChart() {

    let that = this;

    const rect = this.svgContainer.nativeElement;
    this.createSVG(rect);

    mask();

    d3.xml("../assets/images/person.svg")
      .then(xml => {

        var personFigure = xml.getElementsByTagName('g')[1];

        that.svg.node().append(personFigure);

        that.person = that.svg.select('g')
          .attr('transform', `translate(${that.margin.left}, ${that.margin.top})`)
          .attr('class', 'person-svg')


        that.person.select('path')
          .style('fill-opacity', 1)
          .style('fill', '#e7e7e7')
          .style('stroke-width', 0.3)
          .style('stroke', '#e7e7e7')
          .attr('mask', 'url(#person-mask)')

        that.person.select('circle')
          .style('fill', '#e7e7e7')
          .style('stroke-width', 0.3)
          .style('stroke', '#e7e7e7')
          .attr('mask', 'url(#person-mask)');
          
       

        styleImportedSVG();  

      });
    
   

    function mask() {
      const mask = that.svg
        .append('defs')
        .append('mask')
        .attr('id', 'person-mask')
        // add image in the clip-path
        
      // add background which is clipped
      mask.append('rect')
        .style('fill', '#01579b')
        .attr('width', 65)
        .transition()
        .duration(5000)
        .attr('height', 60)
        
        
    }


    function styleImportedSVG() {


      //that.svg.on('mouseover', function () {
      //  d3.selectAll('path')
      //    .attr('fill-opacity', 0.1)
      //    .attr('stroke-opacity', 0.3)
      //})

    }

   
  }

I'm currently experiencing difficulties with the masking feature not working as intended. My goal is to fill the SVG image with a masked rectangle, only showing part of it based on a specified value (e.g., 10%, 20%). How can I make this work?

jsfiddle

Answer №1

After some trial and error, I managed to find a solution. However, I believe there may be a more efficient method to achieve the same result. Any suggestions for improvement would be highly valued.

this.maskHeight = (parseFloat(percentage) / 100 * parseFloat(this.mask.attr('height')));

    


    // adding a clipped background
    this.mask.append('rect')
      .attr('fill', '#fff')
      .attr('width', 65)
      .transition()
      .ease(d3.easeBounce)
      .duration(2000)
      .attr('height', this.maskHeight)


   
    d3.xml("../assets/images/person.svg")
      .then(xml => {

        var personFigure = xml.getElementsByTagName('g')[1];

        // inserting person image into svg 
        that.svg.node().append(personFigure);

        // applying styles
        // person svg as the background
        that.svg.select('g')
          .attr('transform', `translate(${that.margin.left}, ${that.margin.top})`)
          .attr('class', 'person-svg-bg')

        var clone = that.svg
          .select('g').node()
          .cloneNode(true);

        that.svg.node().append(clone)

        var circle = that.svg.selectAll('circle')
          .attr('id', function (d, i) { return 'person-head-' + i })
          .style('fill', function (d, i) { if (i === 0) { return '#FFCC00' } else { return 'none' } })
          .style('stroke', function (d, i) { if (i === 0) { return null } else { return '#E7E7E7' } })
          .style('stroke-width', function (d, i) { if (i === 0) { return null } else { return 0.6 } })
          .attr('mask', function (d, i) { if (i === 0) { return 'url(#person-mask)' } else { return null } })

        var path = that.svg.selectAll('path')
          .attr('id', function (d, i) { return 'person-path-' + i })
          .style('fill', function (d, i) { if (i === 0) { return '#FFCC00' } else { return 'none' } })
          .style('stroke', function (d, i) { if (i === 0) { return null } else { return '#E7E7E7' } })
          .style('stroke-width', function (d, i) { if (i === 0) { return null } else { return 0.6 } })
          .attr('mask', function (d, i) { if (i === 0) { return 'url(#person-mask)' } else { return null } })

      });

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

Updating a ReactJS component based on the selected value

My code currently includes a select element that retrieves ID's from an API, followed by a table that displays data. When I define a state like this example: this.state = {value:23}; the table successfully displays data from I'm trying to achie ...

Is it possible to run a script continuously, without interruption, using Puppeteer and Node.js for 24 hours a day,

My current macro, the Macro Recorder, runs continuously without any breaks. It involves viewing an item in one tab, ordering a specific quantity in another tab, writing a message, and repeating the process. I am considering using Puppeteer to accomplish t ...

Transferring information from a form without using specific authorization

Objective: The objective is to display data from a form where users input their name and favorite band. Upon submission, they will be redirected to a new page which will show: Name: John Doe Favorite Band: The Who Challenge: I am unable to utilize the ...

I'm wondering if you have any insights on how to retrieve objects using Mono WebAssembly

I am looking to send a c# object back via mono-wasm, but when I attempt to do so, the returned object appears in my JavaScript file here. The C# code can be found in [image2]: C# code My JavaScript code can be found here: JS code Everything seems to wor ...

Utilizing Laravel Blade traits within Javascript script: A step-by-step guide

I am utilizing Laravel traits that are able to convert numbers into a money-currency format. If I wish to use it within a Blade template, I simply call it like this: <td class="text-center"> @money($repayment->admnin) </td> However, I am f ...

How can I execute a function when ng-click is triggered on an <a> tag, and ensure it opens in a new tab?

I am facing an issue with an element in my code, which is as follows: <a ng-click="vm.openPage(page)">{{page.pageId}}</a> Within the vm.openPage() function, there are validations that need to be performed before redirecting to the page refere ...

Is there a way to simulate Pinia and vue-i18n simultaneously?

Exploring Vue 3's Composition API with a twist: The store.ts file import { ref, Ref } from 'vue'; import { defineStore } from 'pinia'; export const useStore = defineStore('store', () => { const isLoading: Ref<bo ...

Trouble with unproductive downtime in ReactJS and JavaScript

I'm looking for a way to detect idle time and display a dialog automatically after a certain period of inactivity. The dialog should prompt the user to click a button to keep their session active. Currently, when the user clicks the button it doesn&a ...

What is the best way to introduce a time delay between two JavaScript functions?

Here is some JavaScript code that I am working with: clientData.reloadTable( "CreateCSV", "/create/file" ); $("#downloadFrame").attr("src","/download/download"); The first statement in the code above creates a CSV file on the disk. The second statement d ...

React-slick slider not functioning properly when using TypeScript

I encountered an issue while trying to implement a carousel/slider using the typescript version of the react-slick library in my Nextjs project. After importing the Slider component from the package, I received the following error: https://i.sstatic.net/6 ...

The process of sending JSON data to a Vue instance

I am facing an issue with my Vue instance where I need to pass JSON data from the backend without using HTTP requests because the data is constant. I attempted to achieve this using props, but encountered some challenges... In the DOM, it appears as <d ...

Having trouble activating Ajax code in jquery

I have integrated the swal (SweetAlert) UI for confirmation messages in my project. In my jQuery code, I have an Ajax function to remove an item when the user clicks on "Yes, Delete It." However, for some reason, the Ajax code is not triggering as expecte ...

"Utilizing Parenscript for seamless automatic returns in code execution

How can I disable the implicit return in Parenscript? I am attempting to write the code below: function() = { dialog.show();}; However, Parenscript automatically adds a return statement: (ps (lambda () (chain dialog(show)))) => function () = ...

Convenient methods in Three.js for easily detaching and attaching character weapons within a scene

I'm currently facing challenges while developing a first-person shooter game using Three.js. I've encountered major glitches with THREE.SceneUtils.detach() and THREE.SceneUtils.attach(), and I'm uncertain if they are the appropriate methods ...

`Cannot receive $emit event from child component in Vue Events`

I seem to be facing a simple issue that has me puzzled. I have a child component called "app-buttons" which contains an input field that I would like to monitor in order to filter a list based on the input value. When I place the input field in the root c ...

Enzyme 3 accesses the properties of the displayed DOM element

After retrieving the HTML with myDomNode.html(); <div aria-expanded="true"> {{someChildren}} </div> I need to verify that the aria-expanded attribute is set to true in my test. Previously, with enzyme 2.x and react 15.x, this code worked fin ...

What is the best way to iterate over JSON data and organize the output based on the key value?

Let's say I want to filter through the JSON data below and only push the home_team_conference if it matches Southeastern. My goal is to organize the data by home_team_conference in order to display each array on different pages of my website. Currentl ...

Exploring Particles with three.js

Could you please help me understand why there are no particles visible in this code snippet? I followed a tutorial and it all seems correct. (function() { var camera, scene, renderer; init(); animate(); function init() { scene = new THREE.Scene(); ...

The functionality of getElementsByClassName and appendChild is not producing the desired outcome

First of all, the solutions must strictly adhere to VanillaJS. I am presenting a straightforward HTML code snippet below: <div class="x">X</div> <div class="x">Y</div> <div class="x">Z</div> Accompanied by a block of ...

Retrieving information from embedded JavaScript code

The web page I am scraping contains inline JavaScript that dynamically generates telephone numbers inside a script tag. These numbers are not visible in the page source, making it challenging to scrape using common methods like x-path and beautiful soup. U ...