how to detect mouseover rects inside SVG - javascript

I have simple SVG chart like this:
<svg id="ss" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="chart" width="1000" height="300" >
<g>
<rect fill="#aaaaaa" stroke-width="0" stroke="none" height="1" width="1000" y="200" x="0" /> </g>
<g class="bar">
<rect height="81.858" width="30" y="118.142" x="10" />
</g>
<g class="bar">
<rect height="111.6012" width="30" y="88.3988" x="55" />
</g>
<g class="bar">
<rect height="66.98639999999999" width="30" y="133.0136" x="100" />
</g>
</svg>
How to use pure javascript to detect mouse over the rect element? And knowing which rect element is being over?

What's wrong with directly assigning the event?
var rects = document.querySelectorAll( 'rect' );
for( var rect of rects ) {
rect.addEventListener( 'mouseover', cb );
}
function cb(){
// the "this" object is your reference to the rect hovered over
console.log( this );
}

Related

Apply SVG mask on hover

I am trying to apply a svg mask when the cursor enters a svg circle. The masking is working correctly but the events are not fired.
How can I properly set event listeners to svg elements so that when the circle contained in <svg id="container"> is hovered, mask is applied to <rect id="masked">?
const svg = document.getElementById('container')
svg.addEventListener('onmouseover', (e) => {
console.log('hover')
const masked = document.getElementById('masked')
masked.setAttribute('fill', 'grey')
masked.setAttribute('fill-opacity', '70%')
masked.setAttribute('mask', 'url(#myMask)')
})
svg.addEventListener('onmouseleave', (e) => {
console.log('leave')
const masked = document.getElementById('masked')
masked.setAttribute('fill', 'none')
masked.setAttribute('fill-opacity', '100%')
masked.removeAttribute('mask')
})
html,
body {
margin: 0;
width: 100%;
height: 100%;
background-color: yellow;
}
svg,
circle {
pointer-events: all;
}
<svg id="mySVG" width="600" height="250">
<mask id="myMask" x="0" y="0" width="600" height="250">
<rect width="400" height="250" fill="white" />
<circle cx="25" cy="25" r="25" fill="black" />
</mask>
<rect id="masked" x="0" y="0" width="600" height="250" fill="grey"></rect>
<!-- The following svg element is supposed to trigger events -->
<svg id="container">
<!-- This circle overlaps the circle in <mask> -->
<circle cx="25" cy="25" r="25" fill="none" stroke="black" />
</svg>
</svg>
The events are called mouseover and mouseleave, not onmouseover and onmouseleave.
const svg = document.getElementById('container')
svg.addEventListener('mouseover', (e) => {
console.log('hover')
const masked = document.getElementById('masked')
masked.setAttribute('fill', 'grey')
masked.setAttribute('fill-opacity', '70%')
masked.setAttribute('mask', 'url(#myMask)')
})
svg.addEventListener('mouseleave', (e) => {
console.log('leave')
const masked = document.getElementById('masked')
masked.setAttribute('fill', 'none')
masked.setAttribute('fill-opacity', '100%')
masked.removeAttribute('mask')
})
html,
body {
margin: 0;
width: 100%;
height: 100%;
background-color: yellow;
}
svg,
circle {
pointer-events: all;
}
<svg id="mySVG" width="600" height="250">
<mask id="myMask" x="0" y="0" width="600" height="250">
<rect width="400" height="250" fill="white" />
<circle cx="25" cy="25" r="25" fill="black" />
</mask>
<rect id="masked" x="0" y="0" width="600" height="250" fill="grey"></rect>
<!-- The following svg element is supposed to trigger events -->
<svg id="container">
<!-- This circle overlaps the circle in <mask> -->
<circle cx="25" cy="25" r="25" fill="none" stroke="black" />
</svg>
</svg>

Variable text in SVG defined text tag

I would like to re-use def-ined shapes in SVG but with variable text.
Is the following somehow possibel ?
<svg width="1000pt" height="1000pt" viewBox="0.00 0.00 1000.00 1000.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="shape">
<rect x="10" y="0" rx="5" ry="5" width="80" height="40" style="fill:lightblue;stroke-width:0,opacity:0.5" />
<text text-anchor="middle" x="40" y="20" font-family="Helvetica,sans-Serif" font-size="8.00">variable_text</text>
</g>
</defs>
<g transform="translate(0 0)">
<use xlink:href="#shape" text="test" />
</g>
<g transform="translate(100 0)">
<use xlink:href="#shape" text="test2" />
</g>
</svg>
EDIT: since the solution would probably involve some javascript I have added the tag.
Clone the template and adjust as necessary.
let shape = document.getElementById("shape");
Array.from(document.getElementsByTagName("use")).forEach((use) => {
let text = use.getAttribute("text");
let clone = shape.cloneNode(true);
// might want to do something more robust here
clone.children[1].textContent = text;
use.parentNode.appendChild(clone);
use.parentNode.removeChild(use);
})
<svg width="1000pt" height="1000pt" viewBox="0.00 0.00 1000.00 1000.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="shape">
<rect x="10" y="0" rx="5" ry="5" width="80" height="40" style="fill:lightblue;stroke-width:0,opacity:0.5" />
<text text-anchor="middle" x="40" y="20" font-family="Helvetica,sans-Serif" font-size="8.00">variable_text</text>
</g>
</defs>
<g transform="translate(0 0)">
<use xlink:href="#shape" text="test" />
</g>
<g transform="translate(100 0)">
<use xlink:href="#shape" text="test2" />
</g>
</svg>

Adding image to SVG circles

I'm trying to add some background image to SVG nodes "circle".
I have read a lot of stackoverflow answer and everybody is saying that we need to add a node into a to define our image.
I tried this but my image is not displaying at all.
Here is a fiddle of my code: https://jsfiddle.net/baapu6wz/ .
What did I miss ?
<svg baseProfile="full" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" height="390" width="1629">
<g transform="translate(40,0)">
<g>
<line y2="149.3060251652327" x2="819.8567597731511" y1="222.22245513917517" x1="854.7332277213098" style="stroke: #999;" stroke-width="5"></line>
<line y2="213.47136779636722" x2="768.9096407109324" y1="213.47136779636722" x1="768.9096407109324" style="stroke: #999;" stroke-width="5"></line>
<line y2="213.47136779636722" x2="768.9096407109324" y1="149.3060251652327" x1="819.8567597731511" style="stroke: #999;" stroke-width="5"></line>
</g>
<g>
<g transform="translate(854.7332277213098, 222.22245513917517)">
<circle fill="url(#image1);" fillOpacity="0.5" r="16"></circle>
<text x="20" dy="3">pagx</text>
</g>
<g transform="translate(768.9096407109324, 213.47136779636722)">
<circle fill="url(#image1);" fillOpacity="0.5" r="10"></circle>
<text x="20" dy="3">xzreds</text>
</g>
<g transform="translate(819.8567597731511, 149.3060251652327)">
<circle fill="url(#image1);" fillOpacity="0.5" r="14"></circle>
<text x="20" dy="3">jzkcwv</text>
</g>
</g>
</g>
<defs>
<pattern width="16" height="16" patternUnits="userSpaceOnUse" y="0" x="0" id="image1">
<image xlink:href="https://www.google.fr/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png" height="16" width="16" y="0" x="0"></image>
</pattern>
</defs>
You have a typo.
fill="url(#image1);"
should be
fill="url(#image1)"
Remove the semicolon.
https://jsfiddle.net/baapu6wz/1/

manipulate svg file within a fabric.js canvas

I am trying to manipulat an svg file with fabric.js. I am loading the svg string fabric.loadSVGFromString into the canvas correctly. My question is how can I manipulate some parts of the svg.
For example, I imported an electronic breadbord with lot of holes as shown in the pic below .
How could I detect if a hole has been clicked? How could I get the clicked part?
Thank you
I did a very short demo of what you could do.
The point is create a svg that can be imported as single shapes, lock the shapes on the canvas, then handle click events.
var canvas = new fabric.Canvas("bb");
svg = '<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny" x="0in" y="0in" width="6.50331in" height="2.1in" viewBox="0 0 468.238 151.2" ><g partID="57380"><g id="breadboardbreadboard"> <g id="background"> <rect fill="#D9D9D9" height="151.199" width="468.238" y="0"/> </g> <g id="stripes"><rect fill="#B3B0B0" height="0.4" width="468.238" y="20.932"/> <rect fill="#B3B0B0" height="0.4" width="468.238" y="129.475"/> </g> <g > <rect fill="#FF0000" height="0.4" width="468.238" y="19.2"/> <rect fill="#FF0000" height="0.4" width="468.238" y="148.799"/> <rect fill="#0000FF" height="0.4" width="468.238" y="2.4"/> <rect fill="#0000FF" height="0.4" width="468.238" y="132"/> </g> <g > <rect fill="#CCC9C9" height="7.2" width="468.238" y="71.2"/> </g> <g id="sockets"> <g id="pin1A"> <path fill="#BFBFBF" d="M8.526,115.2c0,-1.322,1.072,-2.394,2.394,-2.394s2.394,1.071,2.394,2.394"/> <path fill="#E6E6E6" d="M13.313,115.2c0,1.322,-1.072,2.394,-2.394,2.394s-2.394,-1.071,-2.394,-2.394"/> <circle r="1.197" fill="#383838" cx="10.92" cy="115.2"/> </g> <g id="pin1B"> <path fill="#BFBFBF" d="M8.526,108c0,-1.322,1.072,-2.395,2.394,-2.395s2.394,1.072,2.394,2.395"/> <path fill="#E6E6E6" d="M13.313,108c0,1.321,-1.072,2.393,-2.394,2.393S8.526,109.322,8.526,108"/> <circle r="1.197" fill="#383838" cx="10.92" cy="108"/> </g> <g id="pin1C"> <path fill="#BFBFBF" d="M8.526,100.799c0,-1.321,1.072,-2.393,2.394,-2.393s2.394,1.071,2.394,2.393"/> <path fill="#E6E6E6" d="M13.313,100.799c0,1.322,-1.072,2.395,-2.394,2.395s-2.394,-1.072,-2.394,-2.395l0,0"/> <circle r="1.197" fill="#383838" cx="10.92" cy="100.8"/> </g></g></g></svg>';
fabric.loadSVGFromString(svg, function(objs) {
var obj;
for(var i = 0; i < objs.length; i++) {
obj = objs[i];
obj.lockMovementX = true;
obj.lockMovementY = true;
obj.hasControls= false;
canvas.add(obj);
}});
canvas.on("object:selected", function(evt) {
var id = evt.target.id;
if (id.slice(0,3) == 'pin') {
alert(id.slice(3,5));
}
});
<script src="http://www.deltalink.it/andreab/fabric/fabric.js"></script>
<canvas width="600" height="400" id="bb" ></canvas>
One approach is this:
first you have to store an array of coords of each hole on your breadboard image
after you load your background image loop over each entry in that array and create a shape (say, fabric.Circle) over the corresponding hole
right after a shape is created attach a mousedown event handler to this shape
do your click processing in that event handler
Here are a couple of references that can help you with that:
http://fabricjs.com/per-pixel-drag-drop/
mouse:down vs. mousedown in fabric.js

Changing SVG colors

I'm starting to learn JS and i have this (probably very easy) task that I'm having problems with.
So the main task is to make the lower green triangle change it's color depending on which color i click on upper object.
I made something like this:
<!DOCTYPE html>
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>SVG, JavaScript</title>
<script type="text/javascript">
function changeColor("triangle"){
document.getElementById("group").getAttributeNS("fill");
evt.target.setAttributeNS("fill");
}
</script>
</head>
<body>
<svg height="500" width="500">
<g id="group">
<rect x="0" y="0" width="200" height="200" style="fill:gray;stroke:none;stroke-width:0"/>
<rect id="red_triangle" x="0" y="0" width="100" height="100" style="fill:red;stroke:none;stroke-width:0"/>
<rect id="yellow_triangle"x="100" y="0" width="100" height="100" style="fill:yellow;stroke:none;stroke-width:0"/>
<rect id="blue_triangle"x="0" y="100" width="100" height="100" style="fill:blue;stroke:none;stroke-width:0v"/>
<rect id="green_triangle"x="100" y="100" width="100" height="100" style="fill:lime;stroke:none;stroke-width:0"/>
<ellipse cx="100" cy="100" rx="100" ry="100" style="fill:gray;stroke:none;stroke-width:0"/>
<polygon id="triangle" points="100,225 150,300 50,300" style="fill:lime;stroke:none;stroke-width:0" onclick="changeColor("triangle")"/>
</g>
</svg>
</body>
</html>
but obviously it's not working. Could somebody help me with some suggestion?
// the string instead of argument name raises Error: unexpected string
function changeColor("triangle"){
// you get the attribute but don't do anything with it.
// and the group doesn't even have a fill attribute
document.getElementById("group").getAttributeNS("fill");
// here you try to set an attribute but setAttributeNS requires 3 arguments: namespace, attribute and value
// simpler setAttribute would be enough, but the value would still be overwriten by style="fill:whatever"
// and evt.target is undefined, there's no evt object
evt.target.setAttributeNS("fill");
}
then in your SVG:
// the quotes are broken,
// and here you pass a string which I'd assume to be the ID of an element you want to change
onclick="changeColor("triangle")"
So, all in all:
The onclick should be on the source rectangles, not the target triangle: <rect onclick="changeColor('triangle', this)" /> where 'triangle' is the ID of element you want to change, and this is a reference to the clicked rectangle.
SVG:
<svg height="500" width="500">
<g id="group">
<rect x="0" y="0" width="200" height="200" style="fill:gray;stroke:none;stroke-width:0"/>
<rect id="red_triangle" x="0" y="0" width="100" height="100" style="fill:red;stroke:none;stroke-width:0" onclick="changeColor('triangle', this)"/>
<rect id="yellow_triangle"x="100" y="0" width="100" height="100" style="fill:yellow;stroke:none;stroke-width:0" onclick="changeColor('triangle', this)"/>
<rect id="blue_triangle"x="0" y="100" width="100" height="100" style="fill:blue;stroke:none;stroke-width:0v" onclick="changeColor('triangle', this)"/>
<rect id="green_triangle"x="100" y="100" width="100" height="100" style="fill:lime;stroke:none;stroke-width:0" onclick="changeColor('triangle', this)"/>
<ellipse cx="100" cy="100" rx="100" ry="100" style="fill:gray;stroke:none;stroke-width:0"/>
<polygon id="triangle" points="100,225 150,300 50,300" style="fill:lime;stroke:none;stroke-width:0" />
</g>
</svg>
JS:
function changeColor( target, source ){
var color = source.style.fill;
document.getElementById( target ).style["fill"] = color;
}
Fiddle: http://jsfiddle.net/harg5kyz/1/

Categories