SVG and dynamic resizing - vuejs - vue.js

I am pulling hair from my head for weeks already.
I can't figure out how to deal with SVG files.
Essentially I want to have all my svg icons as components in vue, so I started converting them to components.
Here is an example of svg
<svg xmlns="http://www.w3.org/2000/svg">
<path d="M.75 10.5A.749.749 0 010 9.75v-7.5a.749.749 0 01.75-.75h6a.749.749 0 01.75.75.75.75 0 01-.75.75H1.5v6h6V6.75A.75.75 0 018.25 6a.75.75 0 01.75.75v3a.75.75 0 01-.75.75zm3.97-3.22a.75.75 0 010-1.06l4.5-4.5a.727.727 0 01.08-.053L8.379.746A.437.437 0 018.687 0h2.876A.437.437 0 0112 .437v2.876a.436.436 0 01-.745.308l-.921-.921a.588.588 0 01-.053.08l-4.5 4.5a.749.749 0 01-.531.22.749.749 0 01-.531-.22z"/>
</svg>
Now I was thinking to wrap it something like this:
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
:width="width"
:height="height"
viewBox="0 0 18 18"
:aria-labelledby="iconName"
role="presentation"
>
<title
:id="iconName"
lang="en"
>{{ iconName }} icon</title>
<g :fill="iconColor">
<slot/>
</g>
</svg>
</template>
But no matter what I do it does not resize, I tried to change viewBox to 0 0 100 100 or dynamic with 0 0 width height, with last one it does change but in not the way I understand it.
Also I struggled to make path to fill svg container, so if I change width and height svg is always bigger than path which also is not nice behavior. The only way I managed to be the size I want (but not dynamic) is by adding transform="scale(1.34,1.34)" to path, but that's not a fix.
What is the best way to create icons like that ? I fried my brain.

It helps to ensure the SVG files are created or exported nicely from whatever program (Figma, Illustrator, etc) to ensure there's no excess whitespace from the artboard included in the SVG. The SVG code provided looks like the SVG was placed on an artboard which was much larger than the graphic itself.
Beyond that, I manually experimented with the viewBox numbers to get a decent result (correct exporting avoids this manual work). Once the viewBox attributes are correctly set, you can apply other manipulations via classes or inline css to achieve your desired result.
<svg viewbox="0 0 12 11" xmlns="http://www.w3.org/2000/svg">
<path d="M.75 10.5A.749.749 0 010 9.75v-7.5a.749.749 0 01.75-.75h6a.749.749 0 01.75.75.75.75 0 01-.75.75H1.5v6h6V6.75A.75.75 0 018.25 6a.75.75 0 01.75.75v3a.75.75 0 01-.75.75zm3.97-3.22a.75.75 0 010-1.06l4.5-4.5a.727.727 0 01.08-.053L8.379.746A.437.437 0 018.687 0h2.876A.437.437 0 0112 .437v2.876a.436.436 0 01-.745.308l-.921-.921a.588.588 0 01-.053.08l-4.5 4.5a.749.749 0 01-.531.22.749.749 0 01-.531-.22z"/>
</svg>
I would recommend using class or style binding for hooking up any css styles in Vue, the docs cover this here: https://v2.vuejs.org/v2/guide/class-and-style.html
Demo with plain SVG and Vue versions: https://codepen.io/nikcornish/pen/OJXoWvj

You mean you want to change the size of the icon itself right?
If so, the width and the height attributes won't do anything for you. You can just apply regular css to your svg for that matter.
example:
.my-svg {
width: 100px;
height: 100px;
}
Regarding the viewBox attribute, that is a different concept:
The viewBox attribute defines the position and dimension, in user
space, of an SVG viewport.
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox
EDIT:
Please note that you can also use svg-to-vue-component to use SVGs as components.

Related

SVG, Vue: If 'viewBox' is reactive scaling doesnt work. Why?

<svg width=300px height=75px viewbox="0 0 450 100">...</svg> // It works. Scaling of the viewbox to 300px is OK.
<svg width=300px height=75px :viewbox="viewbox">...</svg> // All the same, but 'viewbox' is reactive property. Scaling doesnt work.
<svg width=300px height=75px :viewbox="'0 0 450 100'">...</svg> // Doesnt work too
viewbox = '0 0 450 100' — Vue data property
Try here: https://jsfiddle.net/y0zgefs8/
Solved.
Use :view-box.camel instead :viewbox

How to change color of SVG on react native?

As title explains the problem, I have an SVG file on my view:
<SvgUri source={require("./assets/icons/eva-icons/arrow-back.svg")} width="50" height="50"/>
How can I change the color of the svg that I am loading? fill attribute does not change anything.
Ok so it seems that the eva-icons are not fully supported by SvgUri. This is caused by a different svg/xml structure, so I opened the icon in Affinity Designer (you can probably choose any SVG Editor), then I modified the arrow-back icon (exchanged the color) and exported it again. Now it is working. You can just pass any color as fill option:
Here is the modified version of the arrow-back.svg:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 16 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><path d="M15,6.001l-11.86,0l3.63,-4.36c0.149,-0.18 0.231,-0.406 0.231,-0.64c0,-0.549 -0.452,-1.001 -1.001,-1.001c-0.297,0 -0.58,0.133 -0.77,0.361l-5,6c-0.034,0.048 -0.064,0.098 -0.09,0.15c0,0.05 0,0.08 -0.07,0.13c-0.045,0.115 -0.069,0.237 -0.07,0.36c0.001,0.124 0.025,0.246 0.07,0.36c0,0.05 0,0.08 0.07,0.13c0.026,0.052 0.056,0.103 0.09,0.15l5,6c0.19,0.229 0.473,0.361 0.77,0.36c0.234,0.001 0.46,-0.081 0.64,-0.23c0.229,-0.19 0.362,-0.472 0.362,-0.77c0,-0.233 -0.082,-0.46 -0.232,-0.64l-3.63,-4.36l11.86,0c0.549,0 1,-0.451 1,-1c0,-0.548 -0.451,-1 -1,-1Z" style="fill:#f00;fill-rule:nonzero;"/></svg>
Here is the output:

Implementing css/svg contrast filter using sharp (libvips)

I'm using libvips to transform images on the backend with a css/svg preview on the frontend to save resources.
I'm struggling with implementing the contrast css/svg filter function.
The specification shows contrast as a linear transformation in form of:
out = slope * in + intercept
where intercept should be:
intercept = - (0.5 * slope) + 0.5
This way, I can use contrast(1.25) in css preview of image modifications.
However, implementing this linear function in libvips through JS library sharp:
sharp.linear(contrast, - (0.5 * contrast) + 0.5)
Looking deeper into contrast changes for an image, the expectat result is that highs are put even higher and lows even lower. This looks like a contradition with the specification because the spec applies linear transformation so it should always multiple and add, makes highs higher but also lows a little big higher.
Using linear in sharp (and so in libvips) to change contrast the output actually looks like a brightness filter, which in the spcecification for css/svg filter is in form linear transformation without addition
out = slope * in
This looks to me like I might be misunderstanding what intercept does in svg linear function. Also, comparing svg and css shows differences. Using contrast(2) in css should mimic slope = 2 and intercept = -(0.5 * 2) + 0.5 = -0.5 in svg, which is not the case in this fiddle:
.svg {
filter: url(#contrast);
}
.css {
filter: contrast(2);
}
<img src="https://dev-cdn.swbpg.com/o/g/1515254671.jpeg" width="300">
<img class="svg" src="https://dev-cdn.swbpg.com/o/g/1515254671.jpeg" width="300">
<img class="css" src="https://dev-cdn.swbpg.com/o/g/1515254671.jpeg" width="300">
<svg>
<filter id="contrast">
<feComponentTransfer>
<feFuncR type="linear" slope="2" intercept="-0.5"/>
<feFuncG type="linear" slope="2" intercept="-0.5"/>
<feFuncB type="linear" slope="2" intercept="-0.5"/>
</feComponentTransfer>
</filter>
</svg>
You can clearly see that the second image with svg filter looks different than the third one using css filter.
Is my understanding of filters completely wrong? I would expect there should some treshold somewhere to invert multiplication into division for lows.
How can I implement css contrast in different evnironments as a linear function with the same result?
Your intuition is not correct :) For input values less than 0.5 - the formula decreases the brightness - why? Let's take a contrast value of 2 and an input value of 0.4
Output = 2*0.4 - (0.5 *2) + 0.5
Output = 0.8 - 1 + 0.5
Output = 0.3
As you can see, when the input is lower than 0.5, the output will be always be less than the input because the sum of the slope component and the first (negative) component of the intercept will be equal to the contrast multiplied by the difference between the input and 0.5
This is the formula results on unitized values (floored and ceilinged on 0/1).
Also CSS Filters use sRGB color space by default. SVG Filters use linearRGB. You need to set the SVG filter color space to sRGB by adding attribute: color-interpolation-filters="sRGB" to your svg element. When you do this - your images look the same.
.svg {
filter: url(#contrast);
}
.css {
filter: contrast(2);
}
<img src="https://dev-cdn.swbpg.com/o/g/1515254671.jpeg" width="300">
<img class="svg" src="https://dev-cdn.swbpg.com/o/g/1515254671.jpeg" width="300">
<img class="css" src="https://dev-cdn.swbpg.com/o/g/1515254671.jpeg" width="300">
<svg color-interpolation-filters="sRGB">
<filter id="contrast">
<feComponentTransfer>
<feFuncR type="linear" slope="2" intercept="-0.5"/>
<feFuncG type="linear" slope="2" intercept="-0.5"/>
<feFuncB type="linear" slope="2" intercept="-0.5"/>
</feComponentTransfer>
</filter>
</svg>
https://github.com/lovell/sharp/issues/1958
here you have a working formula from css:
filter: `contrast(${contrast})`
to sharp:
brightness = 1;
image.linear(brightness * constrast, brightness * (-(128 * contrast) + 128));
although, I havent figure out how to incorporate brightness into this yet

Clicking on stroke of dynamic path using Selenium

In SVG you can have things which are only clickable at the stroke, e.g. because there is no fill or because of pointer-events: stroke.
Example:
document.querySelector("#path1").addEventListener("click", function(e) {
console.log("Click1!")
})
document.querySelector("#path2").addEventListener("click", function(e) {
console.log("Click2!")
})
#path1:hover {
stroke: red;
}
#path2:hover {
stroke: green;
}
<svg height="300" width="300">
<path d="M64,116 C100,100 400,100 96,39" stroke="blue" stroke-width="7" fill="none" id="path1" />
<path d="M134,186 C100,100 400,100 126,69" stroke="blue" stroke-width="7" fill="none" id="path2" />
</svg>
In my project I want to write Selenium tests for I have a dynamically generated SVG <path>s which I want to click at using Selenium, the problem is that the center of the element1 is not necessarily clickable (since only the stroke of the path is).
Some ideas I had:
Clicking at a fixed offset: A possibility but since the generated path is highly dynamic it would mean a lot of tinkering with the offset value to get it right and possibly having to change the test a lot of the future.
Triggering click event through code: Would work but make the test less useful since this way it would not test whether the stroke of the path is indeed clickable. Certain bugs could evade being detected by the test this way.
Setting a non-none fill through code or replacing/adding filled a rect around the <path>: Setting fill might not guarantee either that the center is clickable. A <rect> would work but then the clickable areas of multiple paths would overlap which could mean that the wrong path gets the click.
None of these approaches are ideal. Are there any other possibilities?
(I am using Selenium for Python but I am OK with solutions with Selenium for other programming languages since normally it's easy to port.)
1) This is the default position Selenium clicks at if using the function where no offset is specified, or rather it's the center of the visible area of the element since the newest version of the WebDriver protocol but in my use case everything can be assumed to be fully visible.
P.S.
Demo showing why setting fill to something other than none wouldn't help (or pointer-events to all):
document.querySelector("#path1").addEventListener("click", function(e) {
console.log("Click1!")
})
document.querySelector("#path2").addEventListener("click", function(e) {
console.log("Click2!")
})
#path1:hover {
stroke: red;
}
#path2:hover {
stroke: green;
}
<svg height="300" width="300">
<path d="M64,116 C100,100 400,100 96,39" stroke="blue" stroke-width="7" fill="turquoise" id="path1" />
<path d="M134,186 C100,100 400,100 126,69" stroke="blue" stroke-width="7" fill="yellow" id="path2" />
</svg>

Does CanvasDrawingSession.DrawSvg() work in C++/winrt?

I've loaded a series of svg images by creating and storing a CanvasSvgDocument for each, and using its LoadAsync() method to load the svg:
nextSvg = CanvasSvgDocument(resourceCreator.Device());
auto fileStream = co_await nextFile.OpenReadAsync();
co_await nextSvg.LoadAsync(resourceCreator.Device(),fileStream);
This appears to load the svg, but when I use that stored svg in a drawing session nothing appears in the CanvasControl. Other items draw fine: shapes, lines, etc. - just not svgs:
session.DrawSvg(m_svg, boxSize, top, left);
In an attempt to discover the problem I've tried using GetXml() on the svg document in the hope that would show me its contents. The result is an abbreviated svg with no drawing information. I provide that here in case it's a hint:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"></svg>
Perhaps that is all that GetXml() is supposed to return?
I have successfully drawn svgs if they loaded as imagesources; the resulting bitmap image works. But is it possible that session.DrawSvg() is just not completely implemented yet in Win2D? I'd rather render the svg directly this way if it can be done.
[Update] p.s. The svgs are version 1.1 svgs converted from pdfs by one of the online conversion services. As I mentioned, they render fine if opened in Edge or other browsers.
[Update2] Thinking perhaps there is something in the svgs that Win2D doesn't like, I tried creating a simple one using as a model an online example - it just has a rectangle and a circle, as follows. But it also does not draw:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 140"
preserveAspectRatio="xMidYMid meet">
<title>Scalable Vector Graphics (SVG) Demo</title>
<circle cx="100" cy="100" r="50" stroke="#000" stroke-width="1" fill="#ccc" />
<rect x="200" y="50" width="250" height="100" rx="5" ry="5"
stroke="#000" stroke-width="1" fill="#ccc" />
</svg>
[Update] It appears that the problem lies in the way the svg is read from the Storage File. I'll post my own answer as soon as I have enough that it might be useful to someone.
The answer is that my code contains an egregious error: As was pointed out to me at GitHub, LoadAsync() is a static method. In my first line above, I create a CanvasSvgDocument, but in the third line I ignore the results of LoadAsync. LoadASync is also a constructor, so the proper code is
auto fileStream = co_await nextFile.OpenReadAsync();
nextSvg = co_await CanvasSvgDocument::LoadAsync(resourceCreator.Device(),fileStream);
That works! Only trouble is that what I really want is to use LoadFromXaml(), another static method, and there is a different issue with that to be addressed later. Probably also my error.