Highlight multiple path elements within SVG <g> on event action - events
I have an SVG which has multiple path elements contained within a group (it's a map with multiple islands). I want all the islands (each represented by a path within the group) to be highlighted when the user initiates an action such as onclick.
I can get each element to highlight by doing this:
<script type="text/ecmascript">
<![CDATA[
var _undo_render_hash = new Object;
function onAreaClick(evt) {
// Undo previous highlight ...
for(var i in _undo_render_hash) {
document.getElementById(i).setAttribute("style", _undo_render_hash[i]);
}
// Reset undo stack ...
_undo_render_hash = new Object;
// Highlight this ...
var change = evt.target;
_undo_render_hash[change.id] = change.getAttribute("style")
change.setAttribute("style", "opacity:1; stroke:green; stroke-width:3");
}
]]>
</script>
And then in the document having something like this:
<g id="Comhairle_nan_Eilean_Siar" onclick="onAreaClick(evt)">
<path d="M218.641,49.68c-7.79,6.53-14.651,13.989-19.681,23.28c5.158-0.089,5.851,5.187,10.561,5.52
c7.229,0.514,8.971-7.178,15.359-8.88c0.016,1.914-0.065,5.869-0.96,7.92c-3.403,7.813-18.518,2.504-25.199,1.2
c-2.494,4.745,2.057,11.877-2.16,14.4c-3.206,1.918-8.535-0.242-10.08,3.6c-0.606,1.507-0.103,3.258,0,4.8
c1.748,1.852,4.421,0.283,6.72,0c1.417,2.63,0.715,6.368-2.88,6c0.886,2.395,3.484,3.076,3.6,6.24
c-4.017,1.442-10.129-0.689-14.159,0.72c0.559,1.287,3.194,1.784,3.6,3.601c0.75,3.359-3.252,6.585-3.12,10.08
c-4.764-1.083-9.61,3.419-14.16,2.88c-2.724-0.322-3.863-2.384-5.76-4.08c-0.331,1.269-0.527,2.673-1.44,3.359
c-2.143,0.743-4.523-0.247-5.76-1.199c-1.449,1.431-3.391,2.368-4.8,3.84c1.44,3.359,4.801,4.798,5.76,8.64
c-3.942-0.331-10.234-3.217-14.88-2.88c-6.333,0.46-3.063,7.522-0.479,10.32c-2.172,2.07-5.325,0.009-7.681,0.239
c-4.061,0.398-8.587,6.609-11.279,9.36c-3.73,3.812-6.437,7.813-10.561,9.84c-1.912-4.06-3.364-6.849-6.479-9.6
c-3.927-3.466-10.139-4.854-10.08-11.04c3.815,0.022,4.96,2.719,6.239,5.28c6.865-1.135,5.284-10.717,14.16-9.841
c0.157-1.276-1.402-3.928-0.479-6c4.959-2.283,13.127,3.235,17.52-0.72c-0.652-0.853-0.797-2.692-1.2-4.32
c-0.391-1.582-1.24-3.078-0.96-3.84c-6.283-5.527-17.934-6.946-26.159-11.04c-2.425-1.206-6.217-2.982-5.04-6.72
c4.851,0.01,9.509-0.931,9.119-5.76c-0.326-4.061-5.008-4.486-7.68-6.72c-0.909-2.646-1.201-5.376-1.68-8.16
c-0.46-2.676-1.58-5.595-1.2-8.16c0.578-3.899,5.171-6.548,3.36-10.8c1.446-0.008,2.471,0.408,4.08,0.239
c0.734-4.944,1.731-9.628,4.56-12.479c7.463,4.137,13.87,9.33,17.76,17.04c2.904-1.336,4.214-4.267,5.76-6.96
c3.43,1.182,9.073,2.812,9.841-0.96c1.174-5.771-7.64-13.473-3.841-19.2c1.145-1.725,4.916-3.022,7.44-4.32
c2.276-1.17,5.278-3.83,7.68-3.84c0.969-0.004,2.588,1.384,3.841,1.44c3.428,0.153,8.889-3.59,11.76-6.24
c6.317-5.831,14.279-9.8,22.319-13.68c4.311-2.081,8.819-3.566,12-6c2.371-1.814,3.864-4.521,5.521-6
c0.204-0.044,0.227,0.094,0.24,0.239c2.891,1.724,5.448,4.414,8.16,7.681c2.588,3.118,5.115,5.628,5.279,9.359
C221.368,34.817,211.062,43.433,218.641,49.68z"/>
<path d="M44.16,169.2c1.14,1.858-0.598,3.167-0.24,5.52c0.411,2.7,3.443,4.906,6.24,5.04
c8.161,0.392,14.983-9.597,23.04-9.12c1.205,1.26,2.406,2.029,2.4,3.36c-0.01,1.919-2.489,2.896-2.4,4.56
c0.055,1.041,1.147,2.174,1.92,2.641c4.312,2.598,13.878-0.731,17.28,2.88c0.164,4.52-5.563,9.653-5.76,2.159
c-1.912,0.602-2.41,1.726-4.32,1.681c-4.39-0.104-6.364-7.368-10.561-7.44c-1.626-0.027-3.817,0.905-4.079,3.12
c-0.598,5.049,5.754,6.326,7.68,9.601c-2.523,7.254,7.267,4.608,11.76,4.8c0.29,4.37-3.093,5.066-5.52,6.72
c-4.387-5.06-17.802-5.835-23.04-0.96c5.834,3.937,12.206,8.58,22.08,7.68c0.258,1.184,1.034,1.846,1.199,3.12
c-2.392,2.849-7.622,1.498-11.76,1.68c-2.038,0.091-4.323,0.584-6.24,0.24c-2.577-0.462-4.418-2.99-6.479-3.359
c-4.983-0.896-7.753,2.42-12.24,1.92c-3.857-2.997-3.507-8.639-5.76-12.721c-3.031-5.492-10.032-7.792-18.24-6.479
c-2.548-2.172-4.34-5.101-7.2-6.96c4.087-2.713,2.958-10.643,5.28-15.12c5.915-2.263,6.876,9.052,13.68,7.92
c4.908-0.816,6.09-10.913,11.04-12.479C44,169.2,44.08,169.2,44.16,169.2z"/>
<path d="M60.48,224.88c-1.123,3.356-5.712,3.248-7.44,6c-0.189,4.43,5.952,2.528,8.16,4.56
c-5.562,1.186-12.328-0.868-18-1.439c-0.145-6.089-5.911-1.854-9.12-2.641c-2.348-0.574-4.384-6.269-2.64-8.88
c2.262-3.386,14.088-4.188,18-1.2c1.309,1,0.646,2.567,2.64,3.601C54.909,226.023,58.178,223.486,60.48,224.88z"/>
<path d="M38.88,238.561c1.151,5.955,11.284,5.267,9.36,12.72c0.696,1.465,2.814,1.506,4.56,1.921
c-0.191,1.327-2.382,0.657-2.64,1.92c0.414,4.786,3.711,6.688,3.84,11.76c-1.044,1.436-2.94,2.019-5.279,2.16
c-0.516,5.726-1.513,10.968-7.2,11.52c0.217,3.38,3.585,5.16,4.08,8.88c0.644,4.846-0.931,7.977-4.32,9.84
c-0.208,4.385,8.039,3.485,7.44,7.44c-0.272,1.796-5.073,2.562-7.921,2.64c-5.482,0.151-10.604-1.434-12.479-4.8
c-4.678-8.392-3.109-23.416-3.12-35.279c6.781-5.746-2.917-23.356,2.64-29.761C29.915,237.129,35.103,237.705,38.88,238.561z"/>
<path d="M15.84,322.801c1.478,2.442,2.316,5.523,3.601,8.16c-4.067,0.868-7.218,5.468-11.76,5.279
c-2.323-0.096-2.966-1.522-5.521-1.68C5.635,329.556,9.217,324.657,15.84,322.801z"/>
</g>
However, only each one of the islands lights up. I'm just getting to grips with SVG - please can someone advise how to structure the document so that each element within the group will have the action applied to it?
Here is a simple example showing how you can use CSS classes on the parent group to easily style the individual elements:
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head>
<meta http-equiv="content-type"
content="application/xhtml+xml; charset=utf-8" />
<title>SVG Styling via CSS Classes</title>
<style type="text/css" media="screen">
body { background:#eee; margin:2em }
svg { display:block; border:1px solid #ccc; background:#fff; margin:auto }
.islands * { stroke-width:2px; stroke:#700; fill:#620; fill-opacity:0.5 }
.over * { stroke-width:4px; stroke:#930; fill:#ff0; fill-opacity:0.9 }
</style>
</head><body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full"
viewBox="-350 -250 700 500" width="800px" height="600px">
<g class="islands"><circle r="70" /><circle cx="150" cy="20" r="50" /></g>
</svg>
<script type="text/javascript"><![CDATA[
var svg = document.getElementsByTagName('svg')[0];
var svgNS = svg.getAttribute('xmlns');
svg.addEventListener('mouseover',function(e){
var g = e.target.parentNode;
g.setAttribute('class',g.getAttribute('class')+' over');
},false);
svg.addEventListener('mouseout',function(e){
var g = e.target.parentNode;
g.setAttribute('class',g.getAttribute('class').replace(' over',''));
},false);
]]></script>
</body></html>
You can see this as a working example on my site:
http://phrogz.net/SVG/css-driven-styles.xhtml
To answer your original question, you would do something like the following:
function onAreaClick(evt){
var island = evt.target;
var parent = island.parentNode;
var others = parent.getElementsByTagName('path');
for (var i=0,len=others.length;i<len;++i){
others[i].setAttribute(...);
}
Variations might include using parent.childNodes, or using direct presentational attributes (e.g. others[i].stroke='#0f0';) instead of setting the style attribute as a string.
This may not directly answer your question, but it is related, and hopefully of some use.
You can use CSS :hover rules to do mouseover highlighting without even using scripting.
<style>
*:hover { fill: red !important; }
</style>
Here's an example showing that, the style is at the top of the file, and there's a short script snippet at the end to reuse the same map data for the zoomed-in view.
Create a polyline for your path, using as many as points you want.
<polyline points="fill points of your paths here" style="">
<set attributeName="stroke" from="original colur" to="red" begin="mouseover" end="mouseout"/>##
</polyline>
Note: attributename="stroke" is important!
Related
Media query in d3.js only partially successful
I want to resize a d3.js chart using media queries, and apply transitions. The graph setup in html is: <g id="d3js_graph"> <svg class="d3svg"></svg> <script src="JS\D3_BarChart.js"></script> </g> I applied a media query to the d3svg class and used a transform: #media (min-width: 1080px) { .d3svg { transform: translate(-30px,-30px); } } That works to move the graph, but now the graph shows with the left half blanked out, which implies that the graph but not its container have been moved. So I reversed it and applied the media query to the d3js_graph ID (in the opening g tag above): #media (min-width: 1080px) { #d3js_graph { transform: translate(-30px,-30px); } } But that does nothing, so that’s not the element. It’s the .d3svg class shown above. I could use a transition in the d3.js file: d3.select("body").transition().style("color", "red"); but how to I do d3.select within a media query in the d3.js file? I added a media query in the file, but it simply caused the graph to disappear from the screen. So my question is: why is the graph partially blanked out on the left side when I move it using a transform? What’s the controlling element for a media query on d3.js? Thanks for any help.
This... <g id="d3js_graph"> <svg class="d3svg"></svg> <script src="JS\D3_BarChart.js"></script> </g> ... is invalid. <g> elements are SVG elements, and can only be used inside SVGs. What you want is an HTML <div>: <div id="d3js_graph"> <svg class="d3svg"></svg> <script src="JS\D3_BarChart.js"></script> </div> Also, I'd consider moving that <script> to outside the div.
compare class of path elements to a dataset
I am really new to d3 and I want to make a choropleth map with the d3 datamaps so if have to compare the classes of the pathways to my dataset. So far I did manage to get the path's html <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js"</script> <script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.6.9/topojson.min.js"></script> <script src="/d3map/datamaps.world.min.js"></script> <div id="container" style="position: relative; width: 500px; height: 300px;"></div> <script src="d3map.js"></script> js code var map = new Datamap({element: document.getElementById('container'), }) var country = d3.selectAll("path") Country is an array containing 177 elements one element in country has this form: <path d="bunch of numbers" class="datamaps-subunit AFG" style="etc"></path> i want to see if the country code in the class (e.g. AFG) matches one in my dataset. Country.class gives undefined I am probably missing something really obvious
Here's an example fiddle: https://jsfiddle.net/g2v5sgmz/ You can filter specific classed elements in an array with the filter method like this: var country = d3.selectAll("path"); var afg = country.filter(function(d,i){ if( d3.select(this).classed("AFG") ){ return d3.select(this); } })
d3 append copy of g element on click to separate div
I have in-line svg code three rect elements I've copied into my html document from Inkscape. I would like to be able to click any one of these rects and have all three of them appear in a separate div on the right side of the screen, zoomed in. I have enclosed each individual rect in a g element with an ID. I have also enclosed all three rects in a class tag of "section1". Here's the fiddle: https://jsfiddle.net/hh2ek44m/ I have tried all sorts of combinations like this to append the clicked on g element or group of g elements to the #zoombox div. d3.selectAll("g") .on("click", function(d) { d3.select("#zoombox").append("#section1") }); I have also experimented with using 'this'. In my working file (very large), I have many sections of rows that appear too small on the screen to visually inspect, and I would like the user to be able to click to zoom in on a section of rows in a separate space on the screen. I'd like to the original rows to remain unchanged, and for the zoomed in section image area to update when a new section is clicked. I know I'm asking a lot here, so if you could push me in the right direction at all I'd appreciate it. I've looked at many examples of on-click behavior such as http://bl.ocks.org/eesur/4e0a69d57d3bfc8a82c2 and http://bl.ocks.org/d3noob/5d621a60e2d1d02086bf. I eventually want my three rows to be on the left side of the page, and the zoomed in image of the three on the left, similar to this beautiful d3 vis, http://bl.ocks.org/syntagmatic/0613ee9324e989a6fb6b. Thanks.
This code has some problems: d3.selectAll("g").on("click", function(d) { d3.select("#zoombox").append("#section1") }); First, since #zoombox is a <div>, you cannot append an SVG element directly to it. You have to append an SVG first: d3.select("#zoombox").append("svg") But, even doing that, the next step is not simple: you cannot append an ID to it. So, you could probably append an selection: var myGroup = d3.select("#section1") But this will not work as well, because the selection is an array. The logical solution would be using the SVG element itself: var myGroup = d3.select("#section1").node() But, again, this will not work! Solution (out of many): Use the SVG use element: var myGroup = d3.select(".section1"); var myGroupId = d3.select(myGroup).node().attr("id");//get the ID of the group myGroup.on("click", function(){ d3.select("#zoombox") .append("svg") .append("use") .attr("xlink:href", "#" + myGroupId);//the ID of the cloned group }); Here is a demo (I changed your SVG a bit, it was too big for this snippet), click on your group to clone it: var myGroup = d3.select(".section1"); var myGroupId = d3.select(myGroup).node().attr("id"); myGroup.on("click", function(){ d3.select("#zoombox").append("svg").append("use").attr("xlink:href", "#" + myGroupId); }); div { display: inline-block; } <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> <div id ="zoombox"></div> <svg width="100"> <g class = "section1" id="myUniqueID"> <g id="section114r1"> <rect fill="0000ff" id="section114r1" width="3" height="30" x="35.0462" y="58.15918" /></g> <g id="section114r2"><rect fill="#0000ff" id="section114r2" width="3" height="30" x="30.88818" y="58.159" /></g> <g id="section114r3"> <rect fill="#0000ff" id="section114r3" width="3" height="30" x="26.73694" y="58.15927" /></g> <g id="section114r4"><rect fill="#0000ff" id="section114r4" width="3" height="30" x="22.56696" y="58.17471" /></g> </g> </svg>
How to communicate 'externally' between Adobe Animate CC animations?
From the script in the html page, I'm trying to control what happens in the Adobe Animate CC animations I've created. For example, here you'll see a script that doesn't work that's trying to tell the ship animation to gotoAndPlay(5). Anyhow, the ship animation is not responding to that. I'm guessing it's because I'm not addressing/naming it correctly. Help me talk to my animations. See the code below. <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Lets talk to each other</title> <script src="http://code.createjs.com/easeljs-0.8.1.min.js"></script> <script src="http://code.createjs.com/tweenjs-0.6.1.min.js"></script> <script src="http://code.createjs.com/movieclip-0.8.1.min.js"></script> <script src="ship.js"></script> <script src="car.js"></script> <script> function init () { var canvas, stage, exportRoot; canvas = document.getElementById("canvas_ship"); exportRoot = new libs_ship.ship(); stage = new createjs.Stage(canvas); stage.addChild(exportRoot); stage.update(); createjs.Ticker.setFPS(libs_ship.properties.fps); createjs.Ticker.addEventListener("tick", stage); canvas = document.getElementById("canvas_car"); exportRoot = new libs_car.car(); stage = new createjs.Stage(canvas); stage.addChild(exportRoot); stage.update(); createjs.Ticker.setFPS(libs_car.properties.fps); createjs.Ticker.addEventListener("tick", stage); } function Tell_Canvas_Ship_to_gotoAndPlay5(){ canvas_ship.gotoAndPlay(5); } </script> </head> <body onload="init();" style="background-color:#D4D4D4"> <canvas id="canvas_ship" width="300" height="250" style="background-color:#FFFFFF"></canvas> <canvas id="canvas_car" width="300" height="250" style="background-color:#FFFFFF"></canvas> </body> </html>
It looks like "canvas_ship" is the ID of your actual canvas element. I think what you are trying to do is control the content you are adding to it. If so, you can call gotoAndPlay on the exportRoot, which is a MovieClip instance with that API. exportRoot.gotoAndPlay(5); The issue you will have though, is that once you create your canvas_ship stage and content, you are overwriting the variables. I recommend changing the name of the second canvas, exportRoot, and stage. You could also add both elements to one canvas. Is there any reason you are using two canvases, other than that you used the exported content from 2 FLAs? var stage = new createjs.Stage("canvas_ship"); var ship = new libs_ship.ship(); var car = new libs_car.car(); stage.addChild(ship, car); createjs.Ticker.addEventListener("tick", stage); Where is your Tell_Canvas_Ship_to_gotoAndPlay5 method getting called from? Is it a frame script in Animate/Flash?
I've received help and will now share the answer. You are welcome. Just invite me over for breakfast sometime. In Adobe Animate, you'll need to change the library namespace (in the Publish settings in the Advanced tab I think) to lib_jerry or whatever custom name you come up with... so long as it's different than the other animation. Then just follow the setup in this code. You can call the functions from within the Animate animations. <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Container</title> <script src="https://code.createjs.com/createjs-2015.11.26.min.js"></script> <script src="tommy.js"></script> <script src="jerry.js"></script> <script> var canvas, stage, tomtom, jerjer; function init() { var exportRoot; //Tommy canvas = document.getElementById("canvas_tommy"); tomtom = new lib_tommy.tommy(); stage = new createjs.Stage(canvas); stage.addChild(tomtom); stage.update(); createjs.Ticker.setFPS(lib_tommy.properties.fps); createjs.Ticker.addEventListener("tick", stage); //Jerry canvas = document.getElementById("canvas_jerry"); jerjer = new lib_jerry.jerry(); stage = new createjs.Stage(canvas); stage.addChild(jerjer); stage.update(); createjs.Ticker.setFPS(lib_jerry.properties.fps); createjs.Ticker.addEventListener("tick", stage); } function button_from_tommy_was_clicked(){ tomtom.gotoAndPlay(5); } function button_from_jerry_was_clicked(){ jerjer.gotoAndPlay(5); } </script> </head> <body onload="init();" style="background-color:#D4D4D4;margin:0px;"> <canvas id="canvas_tommy" width="970" height="90" style="background-color:#727272"></canvas> <canvas id="canvas_jerry" width="970" height="90" style="background-color:#727272"></canvas> </body> </html>
How to draw only the visible pixels which are >0% alpha with a custom color in canvas?
I would like to make a good performance hit test for png images and other shapes. I don't really care what shapes they are because with this technique there is no performance issues at checking (not setup). I intent to collect all the images on the screen in a secondary canvas just for hit test. For each image drawn I will create a new color which is attached to that particular image. Then I draw all of them in the canvas, each image will have a different fill color. When I click on a pixel (x, y) it will get the color (r, g, b). Every color is mapped to a image, so I get the image clicked with no error (I don't waste with finding what was hit with that click). I know it will be limited to 256*256*256=16 777 216 items because those are all the colors but I don't think it will be a problem for now... So what I really need is to know how to put those fill colors on the secondary canvas which is based only on the visible pixels for each image. UPDATE As you can see to the right it's the hit test map. So if I click on the black shade (c) I instantly know I've clicked on the blue box without any other calculation. One improvement it would be to cache the alpha data. Also reuse the same alpha data for each image instance (we must take care about scaling and rotation...). thanks
Here’s how you would color-mask the non-transparent pixels of a canvas image. Be sure you replace "PutYourImageHere.png" with your own image url. <!doctype html> <html> <head> <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script> <style> body{ background-color: ivory; } canvas{border:1px solid blue;} </style> <script> $(function(){ var img=new Image(); img.onload=function(){ var red=255; var blue=0; var green=0; var canvasCopy=document.getElementById("canvasCopy"); var ctxCopy=canvasCopy.getContext("2d"); var c=document.getElementById("canvas"); var ctx=c.getContext("2d"); ctx.drawImage(this,0,0); var imgData=ctx.getImageData(0,0,c.width,c.height); for (var i=0;i<imgData.data.length;i+=4) { if(imgData.data[i+3]>0){ imgData.data[i]=red; imgData.data[i+1]=green; imgData.data[i+2]=blue; imgData.data[i+3]=255; } } ctxCopy.putImageData(imgData,0,0); } img.src = "PutYourImageHere.png"; }); // end $(function(){}); </script> </head> <body> <canvas id="canvas" width="300" height="300"></canvas> <canvas id="canvasCopy" width="300" height="300"></canvas> </body> </html>