d3 transition SVG polygon animation not smooth in Firefox - javascript

I'm using d3 to animate an SVG polygon as follows...
https://jsfiddle.net/p6jy5t0n/35/
It's smooth on Chrome/Safari but horribly jerky on Firefox - the star even seems to dance around in terms of its positioning changing.
The code in question simply relies on chaining d3's transitions to make the star grow then shrink back to normal by increasing it's stroke-width...
d3.select(".svgStar").transition().duration(500).ease("easeElastic")
.each("end",function(){
d3.select(this).transition().duration(500).ease("easeElastic")
.style("stroke-width","0.1px")
.style("fill",starCol);
})
.style("stroke-width","2.5px")
.style("fill","#fff400");
Any ideas what could be done to get a smoother transition in Firefox?

Have you tried with D3v5. The easeElastic behaves different in Chrome compared to D3v3.
Do you know what easeElastic does to the transition?
Try d3.easeLinear for comparision.
function transformStar(){
var starCol = d3.select(".svgStar").style("fill");
var starAnimationDuration = 500;
d3.select(".svgStar").style("stroke","rgb(255, 218, 93)");
d3.select(".svgStar")
.transition().duration(starAnimationDuration).ease(d3.easeElastic)
.style("stroke-width","2.5px")
.style("fill","#fff400")
.transition().duration(starAnimationDuration).ease(d3.easeElastic)
.style("stroke-width","0.1px")
.style("fill",starCol);
}
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<div id="container">
<svg>
<g id="starG" style="transform:translateX(50px)">
<polygon class="svgStar" fill="#ffda5d" stroke="none" w="9" cursor="default" points="94,20 101,43 125,43 106,58 113,80 94,66 74,80 80,58 62,43 86,43" style="transform: translate(-94px, 18px);">
</polygon>
<text x="0" y="74" id="starTxt" style="cursor: default; font-size: 12px; font-weight: bold; text-anchor: middle; fill: rgb(20, 171, 20); stroke-width: 0px;">
Go!
</text>
</g>
</svg>
</div>
<button onclick="transformStar();">
Transform Star!
</button>

Related

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

Partially fill a shape's border with colour

I am trying to create a progress effect whereby colour fills a DOM object's border (or possibly background). The image attached should give you a better idea of what I'm going for. I have achieved the current result by adding an object with a solid background colour over the grey lines and setting its height. This object has mix-blend-mode: color-burn; applied to it which is why it only colours the grey lines underneath it.
This works okay, but ruins the anti aliasing around the circle, and also the produced colour is unpredictable (changes depending on the colour of the lines).
I feel there must be a better way of achieving this, perhaps with the canvas element. Could someone point me in the right direction please?
Thanks in advance!
This should be possible to do with Canvas and may even be possible with CSS itself by playing with multiple elements etc but I would definitely recommend you to use SVG. SVG offers a lot of benefits in terms of how easy it is to code, maintain and also produce responsive outputs (unlike Canvas which tends to become pixelated when scaled).
The following are the components:
A rect element which is the same size as the parent svg and has a linear-gradient fill. The gradient has two colors - one is the base (light gray) and the other is the progress (cyan-ish).
A mask which is applied on the rect element. The mask has a path which is nothing but the line and the circle. When the mask is applied to the rect, only this path would show through the actual background (or fill) of the rect, the rest of the area would be masked out by the other rect which is added inside the mask.
The mask also has a text element to show the progress value.
The linear-gradient has the stop offset set in such a way that it is equal to the progress. By changing the offset, we can always make sure that the path shows the progress fill only for the required length and the base (light gray) for the rest.
window.onload = function() {
var progress = document.querySelector('#progress'),
base = document.querySelector('#base'),
prgText = document.querySelector('#prg-text'),
prgInput = document.querySelector('#prg-input');
prgInput.addEventListener('change', function() {
prgText.textContent = this.value + '%';
progress.setAttribute('offset', this.value + '%');
base.setAttribute('offset', this.value + '%');
});
}
svg {
width: 200px;
height: 300px;
}
path {
stroke-width: 4;
}
#rect {
fill: url(#grad);
mask: url(#path);
}
/* just for demo */
.controls {
position: absolute;
top: 0;
right: 0;
height: 100px;
line-height: 100px;
border: 1px solid;
}
.controls * {
vertical-align: middle;
}
body {
background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
<svg viewBox='0 0 200 300' id='shape-container'>
<linearGradient id='grad' gradientTransform='rotate(90 0 0)'>
<stop offset='50%' stop-color='rgb(0,218,235)' id='progress' />
<stop offset='50%' stop-color='rgb(238,238,238)' id='base' />
</linearGradient>
<mask id='path' maskUnits='userSpaceOnUse' x='0' y='0' width='200' height='300'>
<rect x='0' y='0' width='200' height='300' fill='black' />
<path d='M100,0 100,100 A50,50 0 0,0 100,200 L100,300 M100,200 A50,50 0 1,0 100,100' stroke='white' />
<text id='prg-text' x='100' y='155' font-size='20' text-anchor='middle' fill='white'>50%</text>
</mask>
<rect id='rect' x='0' y='0' width='200' height='300' />
</svg>
<!-- just for demo -->
<div class='controls'>
<label>Set Progress:</label>
<input type='range' id='prg-input' min='0' max='100' value='50' />
</div>
If you are new to SVG you can refer to the MDN Docs (links provided below) for more information about the elements, their attributes and values.
SVG Mask Element
SVG Tutorial on Paths

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.

how to slice an image in css

I want to slice images in my html document using CSS.
Here is how I want to slice the image. I've masked it with red color:
<div class="img_section" >
<img src="http://i62.tinypic.com/1zbcqhg.jpg" alt="sliced-image" >
</div>
I don't want to use a mask layer to hide the image, because the background of document is not a solid color. I used border-radius property to do it but I couldn't. If it's not possible with CSS , so isn't it possible with js too?
You could do this using svg's clipPath to clip the image and foreignObjectto import the html within svg, since you could only apply svg clipPath to a svg element.
<svg width="445" height="257">
<clipPath id="clip">
<path d="M0,0 h444 l-130,257 h-314z" />
</clipPath>
<foreignObject clip-path="url(#clip)" width="445" height="257">
<div class="img_section">
<img src="http://i62.tinypic.com/1zbcqhg.jpg" alt="sliced-image" />
</div>
</foreignObject>
</svg>
You can do that with CSS layer max, I wrote an example to create what you want, overlayed with black mask.
HTML Code:
<div class="img_section" >
<img src="http://i62.tinypic.com/1zbcqhg.jpg" alt="sliced-image" >
<div class="image-arrow"></div>
</div>
CSS:
.img_section{
width:445;
height:257px;
position:absolute;
}
.image-arrow{
width: 0;
height: 0;
border-bottom: 257px solid black;
border-left: 130px solid transparent;
position:absolute;
right: 0;
bottom: 0;
}
There is no such way in CSS to slice the images this can only be done in LESS and SASS but you can use multiple images as a sprite (single image multiple icon)
Check this out
I'm not certain, but I think you may be looking for clip-path: https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path
Browser support is rather patchy (as of July 2015), however. http://caniuse.com/#search=clip-path

Categories

Resources