I'm trying to create a simple SVG with ellipse elements, but in Safari (and only Safari), it's drawing all non-circular ellipses with doubled stroke width. If the ellipse is perfectly circular (rx == ry) then it draws it normally.
Has anyone seen this behavior before, or have any idea how to work around it? I'm seeing the behavior on macOS 10.12.1, Safari 10.0.1 (12602.2.14.0.7). It also only appears on my retina display (2014 MBP) and not on an external non-retina display.
Here's the html file I'm using:
<!DOCTYPE html>
<body>
<style>
circle {
stroke: blue;
stroke-width: 3;
fill: none;
}
ellipse {
stroke: green;
stroke-width: 3;
fill: none;
}
</style>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="50" cy="50" r="40"/> <!--Draws with normal stroke-->
<circle cx="60" cy="60" r="40"/> <!--Draws with normal stroke-->
<ellipse cx="70" cy="70" rx="40" ry="20"/> <!--Draws with doubled stroke-->
<ellipse cx="80" cy="80" rx="20" ry="20"/> <!--Draws with normal stroke-->
</svg>
</body>
And here's a screenshot of the result:
This issue is with an underlying framework. It will be rectified in the next OS update.
https://bugs.webkit.org/show_bug.cgi?id=164505#c3
Related
I have different SVGs in the project. I need to be able to programatically add a line element at the exact vertical middle of each SVG.
I tried to set the y1 and y2 coordinates as ‘50%’ but that is not honored when the SVG is scaled either by transform scale or viewBox. One of my other requirements is to have those SVGs scaled often.
I could ,of course, start calculating bounding box of each SVG on each scale change, and from there the vertical input but that sounds not elegant
The example is just something to work on. It has a line vertical coordinates set to 50% which are not honoured when a viewBox is set (button click). The blue line is no longer at the middle of the SVG when scaled...
function myFunction(){
document.getElementById("maxi").setAttribute("viewBox","0,0,492,124");
}
<svg id="maxi" version="1.2" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" width="246" height="62" font-size="23px" xml:space="preserve" >
<line id="greenline" x1="0" y1="31" x2="232" y2="31" stroke="#00FF00" stroke-width="4"/>
<line id="blueline" x1="0" y1="50%" x2="232" y2="50%" stroke="#0000FF"/>
<path class="cutContour" fill="none" stroke="#EC008C" stroke-miterlimit="3.8637" d="M6.8,2.3H225
c2.3,0,4.3,1.9,4.3,4.3v48.2c0,2.3-1.9,4.3-4.3,4.3H6.8c-2.3,0-4.3-1.9-4.3-4.3V6.6C2.5,4.2,4.4,2.3,6.8,2.3z"/>
</svg>
<input type="button" value="Click Me" onClick="myFunction();">
If the position of the line in the image doesn't need to vary - in other words if it's a static image - then transforms or viewBox manipulation is not required to resize the image. You can vary the CSS (or HTML attributes) of the SVG tag itself to resize the image. It is common to express the coordinates of the line as absolute values rather than percentages, but as you can see from #line2 below, the effect is identical:
For example (with jQuery):
<style>
svg {
outline: 1px dotted grey;
width: 300px;
}
#shrink {
display: none;
}
#line1 {
stroke: red;
stroke-width: 5px;
}
#line2 {
stroke: white;
stroke-width: 2px;
}
</style>
<p>
<button id="grow">Grow</button>
<button id="shrink">Shrink</button>
</p>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-100 0 300 100">
<line x1="-50" y1="50" x2="150" y2="50" id="line1"/>
<line x1="-40" y1="50%" x2="140" y2="50%" id="line2"/>
</svg>
<script>
$(function() {
$('#grow').click(function() {
$('svg').animate({
width: "800px"
}, 500, function() {
$('#grow').hide();
$('#shrink').show();
});
});
$('#shrink').click(function() {
$('svg').animate({
width: "300px"
}, 500, function() {
$('#shrink').hide();
$('#grow').show();
});
});
});
</script>
Codepen: https://codepen.io/MSCAU/pen/eLMOVj with additional CIRCLE and RECT behind the lines.
I'm not very sure I understand what you are asking. Please take a look & tell me if this is what you need.
function myFunction(){
let newWidth = 492;
document.getElementById("maxi").setAttribute("viewBox",`0,0,${newWidth},124`);
blueline.setAttributeNS(null,"x1", newWidth/2);
blueline.setAttributeNS(null,"x2", newWidth/2);
blueline.setAttributeNS(null,"y2", 124);
}
svg{border:1px solid;}
<svg id="maxi" version="1.2" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" width="246" height="62" font-size="23px" xml:space="preserve" >
<line id="greenline" x1="0" y1="31" x2="232" y2="31" stroke="#00FF00" stroke-width="4"/>
<line id="blueline" x1="123" y1="0" x2="123" y2="62" stroke="#0000FF"/>
<path class="cutContour" fill="none" stroke="#EC008C" stroke-miterlimit="3.8637" d="M6.8,2.3H225
c2.3,0,4.3,1.9,4.3,4.3v48.2c0,2.3-1.9,4.3-4.3,4.3H6.8c-2.3,0-4.3-1.9-4.3-4.3V6.6C2.5,4.2,4.4,2.3,6.8,2.3z"/>
</svg>
<input type="button" value="Click Me" onClick="myFunction();">
I want to use a spotlight effect but it only seems to work in Chrome, looks "just ok" in Firefox, but will not position (x,y,z) in Safari. (Other browsers not tested)
I've tried different filter and primitive units, and while this makes a difference, Safari still cannot seem to position the lighting effect in any case.
In pursuit of understanding what is going on, I've tried lots of workarounds including different userSpaceOnUse/objectBoundingBox combos, and different svg structure but have never been able to find one that works on Safari.
Examples
Default filter/primitive units:
https://jsfiddle.net/localnerve/y470d52v/
objectBoundingBox units:
https://jsfiddle.net/localnerve/uyc7o52k/
A picture is also worth a 1000 words (Safari, Chrome, FF). The spotlight on Safari is rendered off-canvas to the right and bleeds in from the right.
To show the spotlight positions on Safari are "out of whack", here's me nudging them in web inspector so I can see the spotlight render at all:
Here is the code using objectBoundingBox filter and primitive units:
<style>
body {
margin: 0;
padding: 0;
font-family: sans-serif;
font-size: 16px;
color: #fff;
}
* {
transform-origin: 50% 50% 0;
}
.scene-container {
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
}
.scene-container.content {
position: relative;
width: 100%;
height: 150vh;
background-image: linear-gradient(hsla(240, 90%, 8%, 1) 0%, 99%, hsla(217,24%,71%,0) 100%);
box-shadow: 0px -10px 20px hsl(240, 90%, 8%);
}
.spot {
height: 100%;
width: 100%;
}
</style>
<body>
<div class="scene-container content">
<h2>Here's a spotlight.</h2>
<svg class="spot" viewBox="0 0 2000 3000" preserveAspectRatio="xMidYMid">
<defs>
<filter x="-0.2" y="-0.2" width="1.4" height="1.4" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox" id="spotlight">
<feGaussianBlur in="SourceAlpha" stdDeviation="8" result="blur1"></feGaussianBlur>
<feSpecularLighting result="specOut" in="blur1" specularConstant="1.8" specularExponent="5" lighting-color="#ffffff">
<feSpotLight x="0.5" y="-0.4" z="0.03" pointsAtX="0.5" pointsAtY="0.8" pointsAtZ="0" limitingConeAngle="13.7"></feSpotLight>
</feSpecularLighting>
<feComposite in="SourceGraphic" in2="specOut" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"></feComposite>
</filter>
<clipPath id="spot-clip">
<rect x="-50" y="2840" width="2200" height="200"></rect>
</clipPath>
<filter id="spot-blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="15">
</feGaussianBlur>
</filter>
</defs>
<g transform="translate(0, -175)" filter="url(#spotlight)">
<ellipse cx="50%" cy="95.333%" rx="27%" ry="130" fill="#fff" clip-path="url(#spot-clip)" filter="url(#spot-blur)"></ellipse>
<rect x="25%" y="43.667%" width="50%" height="50%" stroke="peru" stroke-width="3%" stroke-linejoin="round" fill="#000"></rect>
</g>
</svg>
</div>
</body>
Any insight you can give is greatly appreciated.
There are known bugs in webkit/Safari for light source positioning wrt transforms and oBB units. After the webkit/blink engine schism, no-one at Apple picked these bugs up from the Chrome team.
https://bugs.webkit.org/show_bug.cgi?id=88877
https://bugs.webkit.org/show_bug.cgi?id=113059
The workaround is not to use transforms and oBB units (do any dynamic positioning or sizing via JavaScript)
We are trying to display SVG backgrounds in internet explorer. Our images are always getting cut off when a zoom other than 100% is used. This can be reproduced using the following code:
with this svg
<svg xmlns="http://www.w3.org/2000/svg" height="100" width="100" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="48" stroke="black" stroke-width="3" fill="red" />
</svg>
div {
width: 14px;
height: 14px;
background-size: 14px 14px;
background-repeat: no-repeat;
background-image: url("data:image/svg+xml;charset=utf-8,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' height='100' width='100' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='48' stroke='black' stroke-width='3' fill='red'%3E%3C/circle%3E%3C/svg%3E");
}
<div></div>
The result looks like this:
In all other browsers it renders fine. Has anyone else ever experienced this bug? Is there a workaround?
I have found one workaround which requires very little work:
Make the SVG image 2X size of the actual content (this would make the circle look like this:
<svg xmlns="http://www.w3.org/2000/svg" height="200" width="200" viewBox="0 0 200 200">
<circle cx="50" cy="50" r="48" stroke="black" stroke-width="3" fill="red" />
</svg>
Then use the :after pseudo element to create an inside element with 2x the desired size. So the html would be
<div class="circle"></div>
And the css would be
.circle {
width: 14px;
height: 14px;
position:relative;
}
.circle:after {
position: absolute;
top: 0;
left: 0;
content: ' ';
width: 28px;
height: 28px;
background-size: 28px 28px;
background-repeat: no-repeat;
background-image: url('circle.svg');
}
The extra space in the :after pseoudoelement gives IE spare canvas to draw on, but both the visible icon and the space occupied by the original container remain the same.
How can I achieve something like this using SVG?
You would need two different circle elements, one for the underlying gray color and the other for the blue stroke, then apply a stroke-dasharray and stroke-dashoffset to the blue stroke.
.track,
.filled {
stroke-width: 10;
fill: none;
}
.track {
stroke: #eee;
}
.filled {
stroke: blue;
stroke-dashoffset: 110;
stroke-dasharray: 440;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 190 190">
<circle class="track" cx="80" cy="80" r="70" />
<circle class="filled" cx="80" cy="80" r="70" />
</svg>
in http://www.w3.org/TR/css3-transforms/ there are given matrix n function which can be used with svg, but no example, plz check section 14.2, i want to know how to use them?
Thanks in advance
The question is a bit too vague, but to help I would point you at an explanation here and here for more svg
I'm not sure if there is full 3d transform support for SVG atm.
Example... fiddle
<svg id="svg">
<rect id="rect1" x="10" y="10" width="50" height="50"/>
<rect id="rect2" x="100" y="110" width="50" height="50"/>
</svg>
#rect1 {
stroke: red;
stroke-width: 5;
fill: blue;
transform: matrix( 1,2,3,4,5,6);
-ms-transform: matrix( 1,2,3,4,5,6);
-webkit-transform: matrix( 1,2,3,4,5,6);
}
#rect2 {
fill:green;
transform: rotateY(-75deg);
-ms-transform: rotateY(-75deg); /* IE 9 */
-webkit-transform: rotateY(-75deg); /* Safari and Chrome */
}
I'm not quite sure of what browser support is like as a lot is changing atm, so mileage may vary.