How to make svg path as moveable? - javascript

I have created an SVG image. You can see it here:
jsfiddle.net/o2n56fyb/5/
In this SVG image, there is few dot line. You can see it here:
http://creativeartbd.com/demo/blockchain/
Now, I want to make those dot lines as moveable like it's running continuously. Is that possible? If so, can you tell me how?

You need to use stroke-dasharray and stroke-dashoffset for example like this:
var polygon = document.querySelector("polygon");
var dashoffset = 0;
polygon.style.strokeDashoffset = dashoffset;
function Animate() {
window.requestAnimationFrame(Animate);
dashoffset += 2;
polygon.style.strokeDashoffset = dashoffset;
}
Animate();
svg{border:1px solid #d9d9d9; display:block; margin:0 auto;max-height:100vh}
<svg width="250" height="250" viewBox="0 0 250 250">
<polygon points="50,50 200,50 200,200 50 200"
style="fill:none;
stroke:#000;
stroke-width:3;
stroke-dasharray: 30,15;"
></polygon>
</svg>
I hope this is what you need.

Related

Any workaround to this Firefox bug? SVGElement.getScreenCTM incorrect when parent element has a transform

The Firefox bug in question is https://bugzilla.mozilla.org/show_bug.cgi?id=1610093
It's a long-standing issue whereby the getScreenCTM method of an SVG element returns incorrect values when a parent above the root SVG element has a transform applied.
The following snippet should show the black circle directly under the mouse pointer inside the red box. This works correctly in webkit/blink where the css transform on the parent is properly calculated. In Firefox this bug causes the black circle to be offset by the same values as the css transform on the parent div element.
I've avoided the issue up until now by ensuring there are no transforms further up the dom tree, but I'm now developing this as a component where I won't have control over the parent dom.
Does anyone know an easy workaround for getting a correct transform matrix?
const svgEl = document.querySelector(`svg#test-1`);
const circleEl = document.querySelector(`svg#test-1 circle`);
const handleMouseMove = (e) => {
// transform screen to SVG coordinate space
const mousePoint = svgEl.createSVGPoint();
mousePoint.x = e.clientX;
mousePoint.y = e.clientY;
const mouseCoordsInCanvasSpace = mousePoint.matrixTransform(svgEl.getScreenCTM().inverse())
// set circle to coords, should be mouse center in SVG coord space
circleEl.setAttributeNS(null, 'cx', mouseCoordsInCanvasSpace.x);
circleEl.setAttributeNS(null, 'cy', mouseCoordsInCanvasSpace.y);
}
window.addEventListener('mousemove', handleMouseMove);
body{
padding: 0px;
margin: 0px;
}
svg {
border: 2px solid red;
margin-left: 0px;
}
<!DOCTYPE html>
<html>
<body>
<div style="transform: translate(50px, 50px);">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100" id="test-1">
<circle r="10" />
</svg>
</div>
</body>
</html>
That is an annoying bug! My suggestion is to use an <object> element. The source/data for the object should be the SVG.
In the following example I'm using a data URL as a data source for the <object>, so that we at least can see something here on ST. But I imagine that the SVG should be dynamic somehow. So, to make this work you will need to load the SVG as a source of a file (data="/file.svg"). If you do that you have access to the contentDocument property on <object> and in that way you have direct access to the DOM of the SVG from the "outside". It needs to run from a web server and on the same domain as well.
body{
padding: 0px;
margin: 0px;
}
object {
border: 2px solid red;
margin-left: 0px;
}
<div style="transform: translate(50px, 50px);">
<object width="100" height="100" type="image/svg+xml" data="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgaWQ9InRlc3QtMSI+CjxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij4KPCFbQ0RBVEFbCnZhciBjaXJjbGVFbDsKY29uc3QgaGFuZGxlTW91c2VNb3ZlID0gZSA9PiB7CiAgY29uc3QgbW91c2VQb2ludCA9IG5ldyBET01Qb2ludChlLmNsaWVudFgsIGUuY2xpZW50WSk7ICAKICBjb25zdCBtb3VzZUNvb3Jkc0luQ2FudmFzU3BhY2UgPSBtb3VzZVBvaW50Lm1hdHJpeFRyYW5zZm9ybShlLnRhcmdldC5nZXRTY3JlZW5DVE0oKS5pbnZlcnNlKCkpOwogIC8vIHNldCBjaXJjbGUgdG8gY29vcmRzLCBzaG91bGQgYmUgbW91c2UgY2VudGVyIGluIFNWRyBjb29yZCBzcGFjZSAKICBjaXJjbGVFbC5zZXRBdHRyaWJ1dGVOUyhudWxsLCAnY3gnLCBtb3VzZUNvb3Jkc0luQ2FudmFzU3BhY2UueCk7CiAgY2lyY2xlRWwuc2V0QXR0cmlidXRlTlMobnVsbCwgJ2N5JywgbW91c2VDb29yZHNJbkNhbnZhc1NwYWNlLnkpOwp9OwoKZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGUgPT4gewogIGNpcmNsZUVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2MxJyk7CiAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignbW91c2Vtb3ZlJywgaGFuZGxlTW91c2VNb3ZlKTsKfSk7Cl1dPgo8L3NjcmlwdD4KPGNpcmNsZSBpZD0iYzEiIHI9IjEwIiAvPgo8L3N2Zz4="></object>
</div>
Here is the SVG I use in the <object>:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<script type="text/javascript">
<![CDATA[
var circleEl;
const handleMouseMove = e => {
const mousePoint = new DOMPoint(e.clientX, e.clientY);
const mouseCoordsInCanvasSpace = mousePoint.matrixTransform(e.target.getScreenCTM().inverse());
circleEl.setAttributeNS(null, 'cx', mouseCoordsInCanvasSpace.x);
circleEl.setAttributeNS(null, 'cy', mouseCoordsInCanvasSpace.y);
};
document.addEventListener('DOMContentLoaded', e => {
circleEl = document.getElementById('c1');
document.addEventListener('mousemove', handleMouseMove);
});
]]>
</script>
<circle id="c1" r="10" />
</svg>

How to get javascript mouseover to highlight other elements in an svg file (context change)

I'm trying to solve a very simple problem: two elements; a button and a rectangle. When you hover over the button, the rectangle changes fill colour. I've tried CSS, but for some reason i can't get the 'className:hover targetClass{}' to work.
The main body is from Visio, and i've tried to edit it (*Edit:removed visio metadata):
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
width="8.26772in" height="11.6929in" viewBox="0 0 595.276 841.89" xml:space="preserve" color-interpolation-filters="sRGB" class="st3">
<style type="text/css">
<![CDATA[
.st1 {fill:#5b9bd5;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st2 {fill:#ffffff;font-family:Calibri;font-size:0.833336em}
.st3 {fill:#FFFF00;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
.button {fill:#5b9bd5;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.button:hover{fill: #FFFF00;}
]]>
</style>
<g id="ThePage">
<title>Page-1</title>
<g id="shape2" transform="translate(283.465,-531.496)">
<title>Multi-Attachment rectangle.2</title>
<desc>Button text</desc>
<rect x="0" y="799.37" width="56.6929" height="42.5197" class="button"/>
<text x="5.25" y="823.63" class="st2" >Button text</text>
</g>
<g id="shape1" transform="translate(255.118,-595.276)">
<title>Multi-Attachment rectangle</title>
<desc>Big box text</desc>
<rect x="0" y="756.85" width="113.386" height="85.0394" class="st1"/>
<text x="32.78" y="802.37" class="st2" >Big box text</text>
</g>
</g>
</svg>
My javascript code that I tried to implement but failed was:
var javaButton = document.getElementsByClassName('button');
for (var i = 0; i < javaButton.length; i++) {
javaButton[i].addEventListener('mouseover', mouseOverEffect);
javaButton[i].addEventListener('mouseout', mouseOutEffect);
}
function mouseOverEffect() {
this.classList.add("rect-highlight");
}
function mouseOutEffect() {
this.classList.remove("rect-highlight");
}
Where "rect-highlight" is in the CSS part of the svg as: .rect-highlight {fill: #ec008c;}.
I know this works, as the button changed colour if I hover over it, however need to get the context from "this" to another element. How do I do that?
*Edit: removed visio metadata
**Edit : added code body
***Edit: added code fences so markdown doesn't eat my code.
Not sure what your issue is, since you did not provide a sample SVG, but if you want to highlight related elements you can use a :hover selector.
You can get the previous and next sibling of the current element by accessing previousElementSibling and nextElementSibling respectively.
const showSiblings = (e) => toggleSiblings(e.target, true);
const resetSiblings = (e) => toggleSiblings(e.target, false);
Array.from(document.querySelectorAll('.box')).forEach(box => {
box.addEventListener('mouseenter', showSiblings);
box.addEventListener('mouseleave', resetSiblings);
});
function toggleSiblings(curr, visible) {
let prev = curr.previousElementSibling;
let next = curr.nextElementSibling;
if (prev) {
prev.classList.toggle('sib-prev', visible);
}
if (next) {
next.classList.toggle('sib-next', visible);
}
}
svg { background: #FF7F7F; }
.box { stroke-width: 4; cursor: pointer; }
.foo { fill: #4C4C7F; stroke: #00007F; }
.foo:hover { fill: #7F7FFF; }
.bar { fill: #7F7F4C; stroke: #7F7F00; }
.bar:hover { fill: #FFFF7F; }
.sib-prev.box {
stroke: #FF0000;
}
.sib-next.box {
stroke: #00FF00;
}
<svg width="260" height="196">
<rect class="box foo" x="6" y="6" width="120" height="40" />
<rect class="box bar" x="6" y="54" width="120" height="40" />
<rect class="box foo" x="6" y="102" width="120" height="40" />
<rect class="box bar" x="6" y="150" width="120" height="40" />
<rect class="box bar" x="134" y="6" width="120" height="40" />
<rect class="box foo" x="134" y="54" width="120" height="40" />
<rect class="box bar" x="134" y="102" width="120" height="40" />
<rect class="box foo" x="134" y="150" width="120" height="40" />
</svg>
When you really need a javascript solution, go for the answers with javascript.
This answer, however, shows a simple non-JS solution using vanilla CSS. You'll see a button and a SVG square. By simply using CSS :hover and :focus on both button and svg the color of the square changes 4 times...
/*
CSS Selector Reference
https://www.w3schools.com/cssref/css_selectors.asp
*/
svg { fill: red; width: 100px }
svg:hover { fill: purple }
button:hover + svg { fill: lime } /* + = immediate sibling selector */
button:focus + svg { fill: blue }
<button>hover, click and unfocus me</button>
<svg viewbox="0 0 100 100"><rect width="100" height="100" x="0" y="0" /></svg>
I wanted to fiddle around with this, because I have an interest in doing cool things with SVG exported from Visio. There may be more elegant ways to solve your problem, as suggested and demonstrated by the other posters, but perhaps my attempt will be helpful anyway.
I've created a jsFiddle that does what you want, I think. Have a look:
https://jsfiddle.net/visioguy/nv3ew0fh/
First, I made a few changes to your the <svg> code:
I gave the top-most the <g> an id="ThePage", so that I could avoid it in the js code.
I set class="button" on the the <g> that contains the rectangle, rather than at the "sub-level" rectangle, where you had it.
I added pointer-events="none" to the elements inside the button shape's group. When you mouse-over the <text> element it caused mouseout to fire, even though the cursor was still within the group. This pointer-events setting stops that behavior, but now you can't select/copy the text. That might be just fine.
There are a few styles in a separate CSS area in the jsFiddle.
Here are the CSS style additions:
/*
Make the SVG big enough to see and give
it a subtle color:
*/
svg {
background: whitesmoke;
width: 500px;
height: 500px;
}
/*
Highlight direct <rect> children of any <g>
that has this class:
*/
.rect-highlight > rect {
fill: red;
}
And here is the javaScript. I used querySelectorAll to find shapes according to CSS rules. If you add other non-button boxes to your <svg>, this code will find them and highlight them:
// Select all <g> elements that are classed as 'button':
let buttonShapes = document.querySelectorAll('.button');
console.log("buttonShapes: ", buttonShapes.length);
// Add the event handlers to all of these button shapes:
for(let buttonShape of buttonShapes) {
buttonShape.addEventListener('mouseenter', mouseEnterEffect);
buttonShape.addEventListener('mouseout' , mouseOutEffect);
}
function mouseEnterEffect(){
// Select all other <g> in the SVG that are not classed as button
// and are not id'd as ThePage:
let otherShapes = document.querySelectorAll('g:not(.button):not(#ThePage)');
console.log("otherShapes:", otherShapes.length);
for(let g of otherShapes) {
g.classList.add("rect-highlight");
}
}
function mouseOutEffect(){
// Select all shapes that are groups <g> and are classed
// with "rect-highlight":
let highlightShapes = document.querySelectorAll('g.rect-highlight');
console.log("highlightShapes:", highlightShapes.length);
for(let g of highlightShapes) {
g.classList.remove("rect-highlight");
}
}
One last note: you can remove all of the extra elements and attributes that Visio adds. Essentially anything that starts with "v:". This is meta data that Visio can read if you decide to re-import the svg into Visio at a later time. Things like Shape Data fields, User-defined cells, layers, text-formatting and other info is stored in these tags.

SVG polygon unable to highlight

I'm trying to highlight a specific polygon inside a SVG, by changing its stroke color.
Unfortunately, one side is completely covered by the second polygon's stroke.
I have tried to bring it to the front with z index, but no luck.
<button id="b1" type="button">1st</button>
<svg viewBox="0 0 1240 1000">
<g>
<polygon class="map__1" id="pol1" points="106.75,266 4,266 4,135 106.75,135 106.75,266" data-id="1">
</polygon>
<polygon class="map__2" points="178.25,168.655 106.75,240 106.75,135 145.75,135 178.25,168.655" data-id="2"></polygon>
</g>
</svg>
js
let btn1 = document.getElementById("b1");
let pol1 = document.getElementById("pol1");
btn1.addEventListener("click", myFunction);
function myFunction() {
pol1.style.stroke = "#fc0303";
}
css
.map__1, .map__2 {
stroke: #000;
stroke-width: 5px;
stroke-miterlimit: 10;
fill: #6e6e6e;
}
Here's the fiddle: https://jsfiddle.net/tfzbjxL3/
I also tried with the outline property, but it doesn't fits on other kind of polygons but squares.
Is there any way that I could manage to do this?
Thanks!
You'd need to change the order of the polygons i.e using appendChild:
function myFunction() {
pol1.style.stroke = "#fc0303";
pol1.parentNode.appendChild(pol1);
}
JSFiddle

Basic vertical SVG line

I cannot seem to find a good, working example / tutorial of how to draw a simple, vertical line which animates gradually on scroll from nothing to a full line.
I have the code below which draws a horizontal line, but I cannot seem to successfully change this to a vertical line that continues to animate.
HTML:
<svg id="mySVG">
<path fill="none" stroke="red" stroke-width="3" id="triangle" d="M1 0 L75 0"/>
</svg>
CSS:
#mySVG {
position: fixed;
top: 15%;
width: 400px;
height: 210px;
margin-left:0px;
}
JS:
<script>
// Get the id of the <path> element and the length of <path>
var triangle = document.getElementById("triangle");
var length = triangle.getTotalLength();
// The start position of the drawing
triangle.style.strokeDasharray = length;
// Hide the triangle by offsetting dash. Remove this line to show the triangle before scroll draw
triangle.style.strokeDashoffset = length;
// Find scroll percentage on scroll (using cross-browser properties), and offset dash same amount as percentage scrolled
window.addEventListener("scroll", myFunction);
function myFunction() {
var scrollpercent = (document.body.scrollTop + document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
var draw = length * scrollpercent;
// Reverse the drawing (when scrolling upwards)
triangle.style.strokeDashoffset = length - draw;
}
</script>
It should just be a matter of making your line vertical, as follows:
<svg id="mySVG">
<path fill="none" stroke="red" stroke-width="3" id="triangle" d="M1 0 L1 75"/>
</svg>
What happens when you do that?
thanks for all your replies - I ended up using CSS transitions as this seem to work better.

svg object pattern, how to multiply it

So I have a svg pattern which has 9 dots (3x3) inside with a transparent background. I have brought this in my html as an object, because I also have to change the color of the dots, I dont think I can do it with just CSS.
Now I need to repeat this object so I would have fullscreen worth of dots? How would I accomplish that?
So, it is possible to use CSS to change dots color. For example:
<object id="object" type="image/svg+xml" data="/paht/to/mysvg.svg"></object>
var a = document.getElementById("object");
var svgDoc = a.contentDocument;
var styleElement = svgDoc.createElementNS("http://www.w3.org/2000/svg", "style");
styleElement.textContent = "* { fill: #000 }";
svgDoc.documentElement.appendChild(styleElement);
And you can access to each childs(dots) in your root(svgDoc.documentElement) and manupulate it as you want, for example to multiply dots and so on via svgDoc.documentElement.childNodes.
I personally like the SVG use tag:
<svg class="icon">
<use xlink:href="#svg-icon" />
</svg>
<svg id="defs" style="display:none">
<defs>
<symbol id="svg-icon" viewBox="0 0 13 13">
<path d="M2,2 L11.1923882,11.1923882"></path>
<path d="M11.1923882,2 L2,11.1923882"></path>
</symbol>
</defs>
</svg>
This example draws you an 'X'. I can style it with CSS by targeting the <svg>:
.icon {
fill: none;
stroke-width: 2;
stroke: red;
}
Props to CSS-Tricks, where I originally picked this up.

Categories

Resources