Combining two SVG elements into one for continuous gradient - svg

I'm trying to add animation into an SVG which has a path with a semi transparent gradient. The end effect is to have something like a lift (elevator) moving up and down inside the tower in the middle. I'm doing this by moving a rectangle up and down under a path that has hole for windows, blocking each one in turn. I have the movement sorted but it's the transparency that is breaking things up.
The main path has a linear gradient with semi transparent stops on it and if I apply the gradient to a group containing the lift and the skyline I can see the lift through the skyline. If both items were 100% solid then the effect works as intended.
I'm tacking this on to an existing project as a proof of concept so I don't get to massively restructure the SVG. And I'm building it using D3
Here's a simple version doing the same thing
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 300">
<defs>
<linearGradient id="lingrad_5" gradientUnits="userSpaceOnUse" x1="600.0027" y1="539.9998" x2="600.0027" y2="361.8376"><stop offset="0" style="stop-color:#2D4B69;stop-opacity:0.6"></stop><stop offset="0.9949" style="stop-color:#004868;stop-opacity:0.85"></stop></linearGradient>
</defs>
<g fill="url(#lingrad_5)">
<rect width="100" height="50" x="30" y="10" />
<rect width="40" height="40" x="10" y="30" />
</g>
</svg>

Related

Can an SVG element be allowed to overflow its mask?

Currently working on a project that uses a slider to compare two different images overlaid with SVG's. SVG's on the right side of the slider have a mask applied to them while the SVG's on the left should remain unchanged.
My problem is that when using a mask to apply these styles the mask clips any of the element that it doesn't cover.
Reproduction CodePen: https://codepen.io/anon/pen/VNEOPy
Super minimal repro:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200px" height="200px">
<defs>
<pattern
id="pattern"
width="10"
height="10"
patternUnits="userSpaceOnUse"
>
<circle cx="5" cy="5" r="5" fill="#999"></circle>
</pattern>
<mask id="masker">
<rect x="0" y="0" width="25%" fill="url(#pattern)"></rect>
</mask>
</defs>
<circle cx="50" cy="50" r="50" mask="url(#masker)"></circle>
</svg>
What I would like to do is apply the mask to the portion of the element that the mask covers and allow the rest of the element to remain visible/unchanged.
This CodePen demonstrates the behavior I am looking for, but requires duplicating each of the SVG's (which is unfortunately not technically feasible in my case): https://codepen.io/anon/pen/vMzwbP
Is there a way to achieve this behavior with masking? Am I looking in the wrong place?
No. You are always going to have to have two instances of the circle. One with the mask applied, and one with no mask (or the inverse mask).

SVG patterns rasterised when printing to PDF

I'm trying to create a SVG file what when printed to PDF maintains all of its parts in vector. This file uses a pattern as a fill to a path, or in the example bellow as the fill to the rect element:
<svg
width="200"
height="200"
viewBox="0 0 200 200"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
className="productWrapper"
>
<defs>
<pattern id="pattern" x="0" y="0" width="1" height="1">
<path
fill="#fcc"
d="[vector path here]"
/>
</pattern>
</defs>
<rect fill="url(#pattern)" stroke="black" width="200" height="200" />
</svg>
The issue I'm facing is that when printed to PDF the rect element or any path at the top level of the SVG file stays vector, but all vector content inside the pattern is rasterized.
A very minimal example of this can be found here:
https://6j8953j8rn.codesandbox.io/
Any way to get the SVG to render in vector when printing to PDF?
One tool that kept vector info after converting from SVG (including <pattern>) to PDF (I used it as CLI) is https://github.com/typst/svg2pdf
Some online converters also could do it while the most of other tools I tried make content inside <pattern> raster.

Is it possible to center a SVG pattern image fill without using JavaScript?

I have the following SVG:
<?xml version="1.0" encoding="UTF-8"?>
<svg width="480" height="1080" version="1.1" viewBox="0 0 480 1080" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<pattern id="wpi" width="100%" height="100%" patternUnits="userSpaceOnUse">
<image width="100%" height="100%" preserveAspectRatio="none" xlink:href="h_img.jpg" x="0" y="0"/>
</pattern>
</defs>
<rect class="wallpaper" width="100%" height="250" fill="url(#wpi)"/>
</svg>
The h_img jpg might vary on time, loading different images, so I can't be aware of its size in advance, yet I know that, most of the time, its height value is greater than its width but I need to apply it to a rect element that has a smaller height compared to its own width and this is for sure (despite I'm using a relative 100% value for that one). I need the pattern to fill that rect with the image maintaining its aspect ratio and the height being crop (or hide overflow) centered. I know I can use JavaScript to calculate the image relative width, resize the height accordingly to maintain the aspect ratio and calculate the offset needed to provide the centering (and I did) but... Do you know maybe is there a way to do what I need simply using the right parameters on the SVG element and no JavaScript?
I tried with:
<pattern id="wpi" width="100%" height="100%" patternContentUnits="objectBoundingBox">
<image width="1" height="1" preserveAspectRatio="xMidYMid" xlink:href="h_img.jpg" x="0" y="0"/>
</pattern>
But that doesn't get me the desired effect I need...
Thanks for your help
SOLVED EDIT:
Thanks to #ccprog for his suggestion took me on the right direction, I managed to find a way to solve like this:
<?xml version="1.0" encoding="UTF-8"?>
<svg width="480" height="1080" viewBox="0 0 480 1080" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<defs>
<pattern id="wpi" width="1" height="1">
<image width="100%" height="250" preserveAspectRatio="xMidYMid slice" xlink:href="h_img.jpg"/>
</pattern>
</defs>
<rect class="wallpaper" width="100%" height="250" fill="url(#wpi)"/>
</svg>
Notice: you need to set for the <image> just the same height and width values used for the pattern image container area (= that means the size of the element on which the pattern is going to be applied, in this case it's a <rect>).
As final words I'll say there are at least a couple of reasons for I wanted the image to stay applied within a pattern:
one is that, in this way, I can be able to use javascript code to shift the centering of the image by y and x attributes values while letting the rectangle stay in its position;
and another reason is I could change the rectangle fill to a solid color easily if I need doing it.
If you want to display an image once in a rectangular area, you do not need a pattern. Patterns are for repeating content more than once.
The attribute you were searching for is preserveAspectRatio="xMidYMid slice". See the spec.
<svg width="480" height="1080" viewBox="0 0 480 1080"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<image class="wallpaper" width="100%" height="250"
preserveAspectRatio="xMidYMid slice" xlink:href="h_img.jpg"/>
</svg>

Transforming <svg> element in SVG behaves differently in Chrome, Firefox

I have a fairly simple SVG which I've converted into a SSCCE. Here's the SVG (and a fiddle you can see for yourself):
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<rect id="leader" width="100%" height="100%" stroke="red" fill="none" />
<svg id="left" x="5%" y="5%" width="40%" height="85%">
<rect width="100%" height="100%" fill="blue"/>
</svg>
<svg id="left" x="55%" y="5%" width="40%" height="85%" transform="scale(.5)">
<rect width="100%" height="100%" fill="red"/>
</svg>
</svg>
I'm expecting a large, empty red rectangle containing two smaller rectangles: one blue one which takes up quite a bit of space and another one (red) which is half the size of the blue one. There is a translation which occurs as well, but that's not terribly important for this question.
In Firefox, I get the expected image, which is this:
However, when I view the same image in Chrome (or Safari), it seems to be ignoring my transformation, and the two rectangles are both the same size:
Is there something wrong with my SVG, is this a bug in either of these browsers, or is this an unsupported part of SVG in Chrome/Safari? There is an old bug from early 2017 which is reported to be fixed, so I'm thinking that I'm missing something about the way SVG transforms are supposed to work.
The transform attribute for an <svg> element has only been introduced for SVG 2. For now it is not supported in all browsers. (Setting a version attribute on the root element has no effect.)
You can achieve the same effect if you wrap the <svg> element with a <g> and define the transformation there. The percentage values for the positioning will still be relative to the nearest parent element establishing a viewport, which is the outer <svg>.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect id="leader" width="100%" height="100%" stroke="red" fill="none" />
<svg id="left" x="5%" y="5%" width="40%" height="85%">
<rect width="100%" height="100%" fill="blue"/>
</svg>
<g transform="scale(.5)">
<svg id="left" x="55%" y="5%" width="40%" height="85%">
<rect width="100%" height="100%" fill="red"/>
</svg>
</g>
</svg>
The bug you referenced, btw, does not apply. It's not easy to see at first glance, but the attached test case shows this refers to setting a transformation on a <g> element via script.

Convert SVG textPath to transform matrix

I have in an e-book a title page formatted with SVG, using textPath to place text along a curve:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 160 256" width="160" height="256">
<g style="text-align:center;text-anchor:middle;stroke:none;stroke-width:0;">
<path style="fill:none;"
d="m 0,72 c 32,-16 52,-20 80,-20 28,0 48,4 80,20"
id="path-upper" />
<text style="font-size:9px;"><textPath xlink:href="#path-upper" startOffset="50%"><tspan>Text to be Placed Along a Curve</tspan></textPath></text>
</g>
</svg>
The trouble is that many e-book platforms (most importantly the Kindle) do not support textPath. What I’d like to do is “compile out” the results of textPath into rotated text.
Running
$ inkscape -A curve.pdf --export-ignore-filters curve.svg
$ inkscape -l curve2.svg curve.pdf
does kinda work, yielding (for example)
<text transform="matrix(0.94068351,0.33928532,0.33928532,-0.94068351,17.864857,145.9156)"><tspan y="0" x="0">T</tspan></text>
<text transform="matrix(0.94997891,0.31231406,0.31231406,-0.94997891,20.665461,146.93493)"><tspan y="0" x="0">e</tspan></text>
<text transform="matrix(0.96048875,0.27831882,0.27831882,-0.96048875,24.497618,148.19598)"><tspan y="0" x="0">x</tspan></text>
<text transform="matrix(0.96875188,0.24803186,0.24803186,-0.96875188,28.337099,149.29823)"><tspan y="0" x="0">t</tspan></text>
but it also rewrites the rest of my SVG file—most annoyingly, adding a global transform (transform="matrix(1.3333333,0,0,-1.3333333,0,256)") which makes everything harder to read and understand.
Is there another tool or bit of code I can use to programmatically generate these rotation matrices from the path?
(Otherwise, what I need is a tool that combines the transform matrices within the SVG file after Inkscape has mangled things, but that’s a different question.)

Resources