I am needing some help understanding how to "unflip/unrotate" and image fill in SVG for a path. When I fill a path with an image and then rotate and fill the path with an image, the image also flips and rotates. But I'd like to keep the image upright and non-flipped, regardless of the rotation and flipping. The size of the picture is the bounding box of the rotated shape.
So, for example, say I have this path and this picture:
If the path is only rotated (in this case, 315 degrees), it's easy to unrotate the image by just reversing the angle in the pattern that is used for a fill (i.e. 45 degrees).
<svg name="rotate only" x="0" y="0" width="100.08" height="200" overflow="visible" fill="url(#fillImages0sp15)" stroke="#4472C4" stroke-miterlimit="8" stroke-width="2.25">
<defs>
<image id="bgImage" preserveAspectRatio="none" width="159.113" height="159.113" xlink:href="THE IMAGE URL"></image>
<pattern id="fillImages0sp15" x="-38.362" y="11.598" width="159.113" height="159.113" patternTransform="rotate(45,50.04,100)" patternUnits="userSpaceOnUse">
<use xlink:href="#bgImages0sp15"></use>
</pattern>
</defs>
<path d="M0,149.96 25.02,149.96 25.02,0 75.06,0 75.06,149.96 100.08,149.96 50.04,200 Z " transform="rotate(315,50.04,100)"></path>
</svg>
But if there any kind of flip on the path (horizontal, vertical, or both), it doesn't work by just reversing the transformation on the pattern used for the image fill. For example, if the image is rotated 315 degrees and flipped vertical, the path has transform="rotate(45,50.04,100) translate(0,200), scale(1,-1)" for flipping vertically. That works. But the image fill needs to get reset back to be upright and not flipped. So the patternTransform should just be the same transformation. But this isn't working. This is the result I get.
<svg name="flipV" x="0" y="0" width="100.08" height="200" overflow="visible" fill="url(#fillImages0sp14)" stroke="#4472C4" stroke-miterlimit="8" stroke-width="2.25">
<defs>
<image id="bgImages" preserveAspectRatio="none" width="159.113" height="159.113" xlink:href="THE IMAGE URL"></image>
<pattern id="fillImages0sp14" x="-20.671" y="-370.711" width="159.113" height="159.113" patternTransform="rotate(45,50.04,100) translate(0,200) scale(1,-1)" patternUnits="userSpaceOnUse">
<use xlink:href="#bgImages"></use>
</pattern>
</defs>
<path d="M0,149.96 25.02,149.96 25.02,0 75.06,0 75.06,149.96 100.08,149.96 50.04,200 Z " transform="rotate(45,50.04,100) translate(0,200) scale(1,-1)"></path>
</svg>
Notice the path has transform="rotate(45,50.04,100) translate(0,200) scale(1,-1) and the fill pattern with the image has patternTransform="rotate(45,50.04,100) translate(0,200) scale(1,-1). This produces the wrong results.
In fact, here's all of it. This is what I'm hoping to achieve:
Does anyone know how to set the patternTransform so that it can "unflip/unrotate" the filled image? Is it that the translate in the patternTransform needs to be calculated differently?
Rather than filling the arrow, <svg ... fill="url(#fillImages0sp14)", transforming it and then trying to somehow separate it from its fill, untransform that, and then refill it, I'd just display the image, but masked by the transformed arrow.
I don't understand why the orange border still shows up. I've made the black rectangle overly large (which helped), and I've changed the overflow="...", but neither made it disappear.
Edit: Your global stroke attributes were messing up the mask. Moving them to the displayed arrow (the only thing using that stroke) fixed the orange border issue.
P.S. xlink is deprecated. Just use href.
P.P.S. I had to add a final translate to center the transformed arrow over the image. It's easier and more accurate to move the center of the arrow to the center of the image first, and then do your transformations about that center.
<svg name="transformed" x="0" y="0" width="159.113" height="159.113" overflow="visible" xmlns="http://www.w3.org/2000/svg">
<defs>
<path id="Arrow" d="M0,149.96 25.02,149.96 25.02,0 75.06,0 75.06,149.96 100.08,149.96 50.04,200 Z" />
<use id="TransformedArrow" href="#Arrow" transform="translate(38.3625,-29.2893) rotate(45,50.04,100) translate(0,200) scale(1,-1)" />
<mask id="ArrowMask">
<!-- Everything under black will be invisible -->
<rect x="0" y="0" width="100%" height="100%" fill="black" />
<!-- Everything under white will be visible -->
<use href="#TransformedArrow" fill="white" />
</mask>
</defs>
<image width="159.113" height="159.113" mask="url(#ArrowMask)" href="https://i.stack.imgur.com/yDcGi.png" />
<use href="#TransformedArrow" fill="none" stroke="#4472C4" stroke-miterlimit="8" stroke-width="2.25" />
</svg>
I'm learning SVG. As we know, svg use border-box as box-sizing default. But when I try to add stroke to a svg, and put the svg in the initial postion - by setting x=0 and y=0. The svg seems not like a border-box strictly.
<svg width="320px" height="320px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="50" height="50" fill="red" stroke="blue" stroke-width="30">
</svg>
The effect is in here:
https://codepen.io/Aklscc/pen/XWWRPzm
And When I set x and y to a bigger number than 30, it works well. So, what's the rational?
The X and Y coordinates define the middle of the "stroke" so when you start at X=0 and/or Y=0 half the width of the line is cut off.
Move your rectangle to x="10" y="10" and you will see more in the upper right. You don't need to go to x="30" y="30", since your stroke width is 30 you only need to change the location to anything greater than x="15" y="15"and you will see the whole rectangle.
I'm working on an SVG image and I can't figure out how to erase a certain part of a path.
This is the current situation: https://gyazo.com/db59fcaf9f122e7e2c0bba5833db9ec5
There are two green letters which overlap and a red bar which does basically represent the area I want to erase so the letters don't stick directly on each other. It works fine when I have a set background colour since I can then easily overwrite lower paths, but with transparent background, this method no longer works, since it appears to make the path transparent, not the entire pixel itself.
TL;DR: How do I make a path actually render the pixel transparent, not just the path element?
You can mask the J with a white rect and a black N with an extra stroke. Next you use again the N. Please play with the stroke width of the mask <use>
svg{border:1px solid; width:90vh}
text{font-family:arial;dominant-baseline:middle}
<svg viewBox="0 0 24 24">
<defs>
<text id="n" x="7" y="14" >N</text>
<mask id="mascara">
<rect width="24" height="24" fill="white" />
<use xlink:href="#n" fill="black" stroke="black" />
</mask>
</defs>
<text x="5" y="10" style="mask: url(#mascara)">J</text>
<use xlink:href="#n" fill="black" />
</svg>
I have a simple or complex SVG graphic. For example a rotated rectangle.
Without calculating you cannot know the minimal size of the viewbox, where the graphic fits into completely.
<svg viewBox="0 0 30 30">
<rect x="20" y="0" width="100" height="20" transform="rotate(45)" fill="black" />
</svg>
The result is, that the graphic does not fit into the viewbox.
Is there any method, how to get an the minimal size of the viewbox, where the graphic is shown completely?
Ideally I do not want to declare a size/ratio of a viewbox. I just want that the minimal size is a result of the content of the SVG graphics.
Is there any disadvantage, when I do not declare the viewBox attribute at all?
Thanks for your help.
One way to do it is wrapping the transformed rectangle in a <g> element and then get the value of the bounding box for theG. Next you use the values of the bounding box (BB) to reset the viewBox of theSVG. I hope it helps.
// the bounding box for the wrapping g
let BB = theG.getBBox();
theSVG.setAttributeNS(null, "viewBox", `${BB.x} ${BB.y} ${BB.width} ${BB.height}`)
svg{border:1px solid}
<svg id="theSVG" viewBox="0 0 30 30" width="300">
<g id="theG">
<rect x="20" y="0" width="100" height="20" transform="rotate(45)" fill="black" />
</g>
</svg>
I have a reference image of the effect that I am trying to achieve using SVG.
In Photoshop the effect can be achieved by using 100% opacity with the blending mode set to 'multiply'
The colors have hex values of:
red: #EA312F, blue: #3A5BA6 and overlapping area: #35111F
I have tried a number of approaches using SVG filters to achieve a similar effect but am struggling to understand how the blending modes calculate the values.
Original Photoshop bitmap
SVG using only shapes no filters
SVG using multiply filter on vertical bar
SVG using multiply filter and opacity on vertical bar
You can see the SVG code for each of these in this JSBin http://jsbin.com/iPePuvoD/1/edit
I'm really struggling to understand the best approach to match the blue of the vertical bar and the color of the overlapping area.
Each of these shapes i'd also like to animate using a library such as http://snapsvg.io/, so i'm hoping to rely purely on filters, rather than cropping or other operations to achieve the desired results - but am open to suggestions.
Effectively, the SVG for the final attempt (4.) is this:
<svg viewBox="0 0 96 146" version="1.1" id="f-multiply-opacity" preserveAspectRatio="xMinYMin meet">
<defs>
<filter id="f_multiply" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
<feBlend in="SourceGraphic" mode="multiply"/>
<feBlend in="SourceGraphic" mode="multiply"/>
</filter>
</defs>
<g id="f_shape">
<rect x="0" y="0" width="96" height="32" fill="#EA312F" />
<rect x="0" y="50" width="96" height="32" fill="#EA312F" />
<rect x="0" y="50" width="32" height="96" opacity="0.8" fill="#3A5BA6" filter="url(#f_multiply)" />
</g>
</svg>
Would much appreciate some advice on this, I have found some good resources on SVG, but this area still seems quite difficult to get good information on.
Thanks!
See the Compositing and Blending Level 1 spec. It enables specifying the compositing and blending to use when rendering web content (including svg). It is testable in a number of browsers by a toggling a runtime flag, see here for instructions. For up-to-date browser support of mix-blend-mode see caniuse.
<svg>
<style>
circle { mix-blend-mode: multiply; }
</style>
<circle cx="40" cy="40" r="40" fill="#EA312F"/>
<circle cx="80" cy="40" r="40" fill="#3A5BA6"/>
</svg>
As jsfiddle here.
This won't work on a number of levels. Feblend takes two inputs not one. What are you blending the sourcegraphic with? If you want to blend with the background you need to use backgroundImage as your in2. If you want to blend with another shape you have to import that shape into the filter with feimage. Next problem BackgroundImage only works in IE at the moment, and feImage only works properly for referenced shapes in Chrome and Safari (Update: you can convert referenced shapes to an inline SVG data-URI and this will work cross browser).
If you are only using colored rectangles then you can generate them inside the filter using feflood and blend them there. Something like the following:
<svg x="800px" height="600px" viewBox="0 0 200 100" version="1.1" id="f-multiply-opacity" preserveAspectRatio="xMinYMin meet">
<defs>
<filter id="f_multiply" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
<feFlood x="0" y="0" width="96" height="32" flood-color="#EA312F" result="a"/>
<feFlood x="0" y="50" width="96" height="32" flood-color="#EA312F" result="b"/>
<feFlood rect x="0" y="50" width="32" height="96" flood-opacity="0.8" flood-color="#3A5BA6" result="c"/>
<feBlend in="a" in2="b" result="ab" mode="multiply"/>
<feBlend in="ab" in2="c" mode="multiply"/>
</filter>
</defs>
<g id="f_shape">
<rect filter="url(#f_multiply)" x="0" y="0" width="200" height="200"/>
</g>
</svg>
Update:
The cross platform way to use shapes within a filter is to encode them as a SVG/XML data URI within a feImage. This is supported cross browser (although it makes the code fairly hard to read.)
For all feBlend modes, the result opacity is computed as follows:
qr = 1 - (1-qa)*(1-qb)
For the compositing formulas below, the following definitions apply:
cr = Result color (RGB) - premultiplied
qa = Opacity value at a given pixel for image A
qb = Opacity value at a given pixel for image B
ca = Color (RGB) at a given pixel for image A - premultiplied
cb = Color (RGB) at a given pixel for image B - premultiplied
The following table provides the list of available image blending modes:
Image Blending Mode Formula for computing result color
normal cr = (1 - qa) * cb + ca
multiply cr = (1-qa)*cb + (1-qb)*ca + ca*cb
screen cr = cb + ca - ca * cb
darken cr = Min ((1 - qa) * cb + ca, (1 - qb) * ca + cb)
lighten cr = Max ((1 - qa) * cb + ca, (1 - qb) * ca + cb)
From http://www.w3.org/TR/SVG/filters.html#feBlendElement