Scale different parts of an SVG file differently

I need a variable height svg graphic for a ticket with jagged edges. The top and bottom segments should scale to a size that maintains a fixed aspect ratio, while the middle segment should stretch to fit the size of the container.

This image should be illustrative.

Various proportions

I found this jsFiddle , doing practically what I would like only horizontally and not vertically, but I'm not good enough at the viewbox to understand what is happening and adapt it for my needs. I just managed to corrupt my file.

My svg file is very simple at the moment; upper path, lower path and a rectangle in the middle. It looks like this:

<?xml version="1.0" encoding="utf-8"?> <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="464px" height="464px" viewBox="0 0 464 464" enable-background="new 0 0 464 464" xml:space="preserve"> <path fill="#FFF100" d="M432.846,10.522c-5.755,0.057-10.466-4.562-10.522-10.316c-0.001-0.069-0.001-0.137,0-0.206h-20.839 c0.057,5.754-4.563,10.465-10.316,10.522c-5.755,0.057-10.466-4.562-10.522-10.316c0-0.069,0-0.137,0-0.206h-20.843 c0.058,5.754-4.561,10.465-10.315,10.522S339.021,5.96,338.965,0.206c0-0.069,0-0.137,0-0.206h-20.836 c0.057,5.754-4.562,10.465-10.316,10.522c-5.754,0.057-10.465-4.562-10.522-10.316c0-0.069,0-0.137,0-0.206h-20.437 c0,24.772-20.082,44.853-44.854,44.853c-24.771,0-44.853-20.081-44.853-44.853H166.71c0.056,5.754-4.562,10.465-10.317,10.522 c-5.754,0.057-10.465-4.562-10.521-10.316c-0.001-0.069-0.001-0.137,0-0.206h-20.839c0.098,5.754-4.486,10.498-10.24,10.596 c-5.754,0.099-10.498-4.486-10.596-10.24c-0.002-0.119-0.002-0.238,0-0.356H83.355c0.057,5.754-4.563,10.465-10.317,10.522 S62.573,5.96,62.517,0.206c0-0.069,0-0.137,0-0.206H41.677c0.057,5.754-4.562,10.465-10.316,10.522 C25.607,10.579,20.896,5.96,20.839,0.206c-0.001-0.069-0.001-0.137,0-0.206H0v56h464V0h-20.839 C443.219,5.754,438.6,10.465,432.846,10.522z"/> <path fill="#FFF100" d="M432.846,453.478c-5.755-0.056-10.466,4.563-10.522,10.317c-0.001,0.068-0.001,0.137,0,0.205h-20.839 c0.057-5.754-4.563-10.466-10.316-10.522c-5.755-0.056-10.466,4.563-10.522,10.317c0,0.068,0,0.137,0,0.205h-20.843 c0.058-5.754-4.561-10.466-10.315-10.522c-5.755-0.056-10.466,4.563-10.522,10.317c0,0.068,0,0.137,0,0.205h-20.836 c0.057-5.754-4.562-10.466-10.316-10.522c-5.754-0.056-10.465,4.563-10.522,10.317c0,0.068,0,0.137,0,0.205h-20.437 c0-24.771-20.082-44.854-44.854-44.854c-24.771,0-44.853,20.082-44.853,44.854H166.71c0.056-5.754-4.562-10.466-10.317-10.522 c-5.754-0.056-10.465,4.563-10.521,10.317c-0.001,0.068-0.001,0.137,0,0.205h-20.839c0.098-5.754-4.487-10.498-10.24-10.597 c-5.754-0.098-10.498,4.486-10.596,10.24c-0.002,0.119-0.002,0.238,0,0.356H83.355c0.057-5.754-4.563-10.466-10.317-10.522 c-5.754-0.056-10.465,4.563-10.522,10.317c0,0.068,0,0.137,0,0.205H41.677c0.057-5.754-4.562-10.466-10.316-10.522 c-5.754-0.056-10.465,4.563-10.521,10.317c-0.001,0.068-0.001,0.137,0,0.205H0v-56h464v56h-20.839 C443.219,458.246,438.6,453.534,432.846,453.478z"/> <rect y="56" fill="#00AEEF" width="464" height="352"/> </svg> 

I would be very happy if someone could help me.

+6
source share
2 answers

In general, you cannot scale some parts of SVG differently than others. However, there are some simple cases (for example, the image of the pencil with which you are connected) where you can be complex and build the one that works.

How does this pencil work

It begins by defining a <symbol> element for each of the three parts of the bundle: end, body, and sharp end.

Then it merges them by adding three <svg> children. At the back, the body <symbol> stretched the entire width of the main <svg> . Then the two end caps <svg> are located in front. Each <svg> end cap is half the width. But they have preserveAspectRatio attributes that make them align with the left and right ends, respectively.

If we make the two end parts almost translucent, you will see what happens:

 :checked~svg{ width:500px; } 
 <input type="checkbox"/><br/> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <g id="source"> <rect width="200" height="100" fill="yellow"/> <circle cx="50" cy="50" r="40" fill="red"/> <rect x="50" y="10" width="100" height="80" fill="orange"/> <path d="M150,10L190,50 150,90z" fill="pink"/> </g> <symbol id="left" viewBox="0 0 50 100" preserveAspectRatio="none"> <use xlink:href="#source"/> </symbol> <symbol id="middle" viewBox="50 0 100 100" preserveAspectRatio="none"> <use xlink:href="#source"/> </symbol> <symbol id="right" viewBox="150 0 50 100" preserveAspectRatio="none"> <use xlink:href="#source"/> </symbol> </defs> <svg viewBox="0 0 1 1" preserveAspectRatio="none"> <use xlink:href="#middle" width="1" height="1"/> </svg> <svg viewBox="0 0 1 2" preserveAspectRatio="xMinYMid"> <use xlink:href="#left" width="1" height="2" opacity="0.1"/> </svg> <svg viewBox="0 0 1 2" preserveAspectRatio="xMaxYMid"> <use xlink:href="#right" width="1" height="2" opacity="0.2"/> </svg> </svg> 

You can see how the body is stretched to its full width and how the final frames are positioned at the ends to hide the stretched body.

This trick only works because the pencil has a solid background (yellow). But I assume that you want the perforated ends of your ticket to be transparent, so the trick will not work for you. If you agree that he is white, then he will be.

Which is probably easier for you is to simply stack the three pieces on top of each other.

 <svg> (the top of the ticket) <div> (containing the ticket body contents) <svg> (the bottom of the ticket) 
+4
source

Again, if you want to do this the hard way, you can use JavaScript:

  $("input#resize").on("click", function(){ if($("input#resize:checked").length){ scaleSvgBorder(); } }); function scaleSvgBorder(){ if ($("svg#myFancyBorder").length && $("svg#myFancyBorder").height() > 0){ //first we need the ratio between the SVG grid and our beloved HTML pixels. var svgRatio = $("svg#myFancyBorder").height() / $("svg#myFancyBorder").attr("viewBox").split(" ")[3]; //Next we need to figure out which scale to apply on the sides group inside our SVG. //We do this by calculating the height we want the sides to be (in pixels), and divide it by the current height of the sides (in pixels). var sidesCurrentHeightPx = $("svg#myFancyBorder #sides")[0].getBBox().height * svgRatio; //Let subtract the top & bottom of our border from the container height, in order to get the height we want the sides to be (in pixels). var nonScalingPartsHeightPx = ($("svg#myFancyBorder #top")[0].getBBox().height + $("svg#myFancyBorder #bottom")[0].getBBox().height) * svgRatio; var sidesTargetHeightPx = $("#container").height() - nonScalingPartsHeightPx; //Finally, we have the scale ratio! var scaleRatio = sidesTargetHeightPx / sidesCurrentHeightPx; //We need to subtract the scaled top from the top for an additional sides translation transformation, since they will move, because SVG scales from 0, 0! var scaledTopDifference = $("svg#myFancyBorder #top")[0].getBBox().height - ($("svg#myFancyBorder #top")[0].getBBox().height * scaleRatio); //We need to subtract the scaled sides from the sides for the bottom translation, because SVG scales from 0, 0! var scaledSidesDifference = $("svg#myFancyBorder #sides")[0].getBBox().height - ($("svg#myFancyBorder #sides")[0].getBBox().height * scaleRatio); //Notice that we have to apply the scale ratio to the translation transformation if we place it AFTER the scale transformation!!! $("svg#myFancyBorder #sides").attr("transform", "scale(1, " + scaleRatio + ") translate(0, " + (scaledTopDifference / scaleRatio) +")"); //* -1 because it has to move down. $("svg#myFancyBorder #bottom").attr("transform", "translate(0, " + ( scaledSidesDifference * -1 ) +")"); //Let stretch the viewbox to the height of the container too! $("svg#myFancyBorder").attr('viewBox', "0 0 "+ $("svg#myFancyBorder").attr("viewBox").split(" ")[2] +" "+ ($("#container").height() / svgRatio) ); } } 
 #container{ position: relative; width: 200px; height: 400px; background-color: #cde } svg#myFancyBorder{ position: absolute; width: 100%; height: auto; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <input type="checkbox" id="resize" /><label for="resize">Fit border to container</label> <div id="container"> <!-- Notice that there no width, height, x or y attribute, since we're trying to keep it simple. --> <!-- Also notice that the viewBox HAS to start with '0 0' since this is the point where SVG images scale from! --> <svg version="1.1" id="myFancyBorder" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 277.5 237.5"> <rect id="top" width="277.5" height="55"/> <g id="sides"> <line fill="none" stroke="#000000" stroke-width="5" x1="2.5" y1="55" x2="2.5" y2="182.5"/> <line fill="none" stroke="#000000" stroke-width="5" x1="275" y1="55" x2="275" y2="182.5"/> </g> <rect id="bottom" y="182.5" width="277.5" height="55"/> </svg> </div> 
+1
source

Source: https://habr.com/ru/post/1243463/


All Articles