'Create a SVG where everything scales except text

To illustrate the effect I'm after, say we scale the image vertically:

Before:

example 1

After:

example 2

Note the text doesn't distort. I'm looking for an easier alternative to drawing and positioning the elements manually each time the scale changes, particularly where the text stays the same dimensions, and I thought svg could pull this off...

svg


Solution 1:[1]

Long time since this question has been made. I think that is not possible without JavaScript. If you do not have problems with the use of JavaScript, use this plugin. The plugin gets all svg elements with an specific class and creates on each element a transformation matrix:

This plugin requires that the svg has the viewBox option. It is a start point, you could adapt it to your needs ;)

(function($){
    
    var defaults = { class: "no-scale" };
    
    var methods = {
        
        //---Init method
        init: function(){
        
            //---Conform the settings
            var settings = $.extend({}, defaults);
            
            return this.each(function(index){
            
                //---Get the SVG
                var svg = $(this);
                
                //---Get the viewBox (svgRect)
                var viewBox = (svg[0].viewBox == undefined) ? false : svg[0].viewBox.animVal;
                
                //---Store the data
                svg.data({"viewBox": viewBox, settings: settings});

                //---Call to private function of resize elements
                private.updateSizes(svg);
            
            });
        
        },
        
        refresh: function(){
        
            return this.each(function(index){
            
                //---Get the SVG
                var svg = $(this);

                //---Call to private function of resize elements
                private.updateSizes(svg);
            
            });
        
        }
        
    };
    
    var private = {
    
       updateSizes: function(svg){
           
           //---Get the viewBox (svgRect)
           var viewBox = svg.data("viewBox");
           
           if(!viewBox) return;
       
           //---Get the settings
           var settings = svg.data("settings");
           
           //---Global scale
           var scalew = Math.round((svg.width() / viewBox.width) * 100) / 100;
           var scaleh = Math.round((svg.height() / viewBox.height) * 100) / 100;
           
           //---Get the resized elements
           var noScaleElements = svg.find("." + settings.class);
           
           noScaleElements.each(function(){
               
               var el = $(this);
               
               //---Set variables
               var sw = el.width();
               var sh = el.height();
               var sx = Math.round((1 / scalew) * 100) / 100;
               var sy = Math.round((1 / scaleh) * 100) / 100;
               var tx = Number( el.attr("x") ) * (1 - sx) + ((sw - sw * sx) / 2) * sx;
               var ty = Number( el.attr("y") ) * (1 - sy) + ((sh * sy - sh) / 2) * sy;
               
               var matrix = "matrix(" + sx + ",0,0," + sy + "," + tx + "," + ty + ")";
           
               $(this).attr("transform",  matrix);
           
           });
       
       }
    
    };
    
    $.fn.noScaleSVGElements = function(method){

        // Method calling logic
        if (methods[method] ) {
            
            return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
            
        } else if ( typeof method === 'object' || ! method ) {
            
            return methods.init.apply( this, arguments );
            
        } else {
            
            $.error( 'Method ' +  method + ' does not exist on jQuery.noScaleSVGElements' );
            
        }

    }
        
})(jQuery);

To use the plugin:

//---Code
$("#svg-element").noScaleSVGElements();

//---Call this method every time that the sizes need to be recalculated
$("#svg-element").noScaleSVGElements("refresh");

Here you have a snippet, change the window size and check the result:

//---Plugin jQuery
(function($){
    
    var defaults = { class: "no-scale" };
    
    var methods = {
        
        //---Init method
        init: function(){
        
            //---Conform the settings
            var settings = $.extend({}, defaults);
            
            return this.each(function(index){
            
                //---Get the SVG
                var svg = $(this);
                
                //---Get the viewBox (svgRect)
                var viewBox = (svg[0].viewBox == undefined) ? false : svg[0].viewBox.animVal;
                
                //---Store the data
                svg.data({"viewBox": viewBox, settings: settings});

                //---Call to private function of resize elements
                private.updateSizes(svg);
            
            });
        
        },
        
        refresh: function(){
        
            return this.each(function(index){
            
                //---Get the SVG
                var svg = $(this);

                //---Call to private function of resize elements
                private.updateSizes(svg);
            
            });
        
        }
        
    };
    
    var private = {
    
       updateSizes: function(svg){
           
           //---Get the viewBox (svgRect)
           var viewBox = svg.data("viewBox");
           
           if(!viewBox) return;
       
           //---Get the settings
           var settings = svg.data("settings");
           
           //---Global scale
           var scalew = Math.round((svg.width() / viewBox.width) * 100) / 100;
           var scaleh = Math.round((svg.height() / viewBox.height) * 100) / 100;
           
           //---Get the resized elements
           var noScaleElements = svg.find("." + settings.class);
           
           noScaleElements.each(function(){
               
               var el = $(this);
               
               //---Set variables
               var sw = el.width();
               var sh = el.height();
               var sx = Math.round((1 / scalew) * 100) / 100;
               var sy = Math.round((1 / scaleh) * 100) / 100;
               var tx = Number( el.attr("x") ) * (1 - sx) + ((sw - sw * sx) / 2) * sx;
               var ty = Number( el.attr("y") ) * (1 - sy) + ((sh * sy - sh) / 2) * sy;
               
               var matrix = "matrix(" + sx + ",0,0," + sy + "," + tx + "," + ty + ")";
           
               el.attr("transform",  matrix);
           
           });
       
       }
    
    };
    
    $.fn.noScaleSVGElements = function(method){

        // Method calling logic
        if (methods[method] ) {
            
            return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
            
        } else if ( typeof method === 'object' || ! method ) {
            
            return methods.init.apply( this, arguments );
            
        } else {
            
            $.error( 'Method ' +  method + ' does not exist on jQuery.noScaleSVGElements' );
            
        }

    }
    
})(jQuery);

//---Code
$("#container svg").noScaleSVGElements();

$(window).resize(function(){

    $("#container svg").noScaleSVGElements("refresh");

});
html, body{
    height: 100%;
}
body{
    margin: 0;
    padding: 0;
}
#container {
    width: 100%;
    height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
    <svg x="0px" y="0px" width="100%" height="100%" viewBox="0 0 150 150" preserveAspectRatio="none">
    <polyline fill="#FFFFFF" stroke="#231F20" stroke-width="1.2241" stroke-miterlimit="10" points="29.333,11.223 136.223,11.223 
        136.223,138.777 29.333,138.777 " vector-effect="non-scaling-stroke"/>
    <line fill="none" stroke="#231F20" stroke-width="1.2241" stroke-miterlimit="10" x1="135.447" y1="75" x2="30.109" y2="75" vector-effect="non-scaling-stroke"/>
    <text class="no-scale" x="5.1113" y="14.7451" font-family="'MyriadPro-Regular'" font-size="12">100</text>
    <text class="no-scale" x="5.1113" y="78.5215" font-family="'MyriadPro-Regular'" font-size="12">50</text>
    <text class="no-scale" x="5.1113" y="142.2988" font-family="'MyriadPro-Regular'" font-size="12">0</text>
</svg>
</div>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1