From the blog

SVG Portraits in React Native

Mona Lisa as SVG

 

Mona Lisa as an SVG

The writing’s on the wall, vector graphics are one of the most efficient ways to display your images at any resolution. When it comes to vector graphics, SVGs are the de-facto file standard. But what happens when you want to bring that kind of versatile file to a native mobile experience?

If you’re an iOS developer you’d probably use something like SVGKit, which allows you to display images from SVG sources as if they were images. For Android you’d use a developer tool like Vector Asset Studio, which takes the SVG and converts it into an Android VectorDrawable which is similar to but not exactly the same as SVG.

Both of these options are tested and true, but can be burdensome for app developers that want to launch and maintain an app for both platforms. Reducing the variations in your asset pipeline is enormously valuable when it comes to developer time.

React Native has a clever solution to this problem available via the community maintained library react-native-svg. But before we can get into that, let’s peek inside the SVG file itself. Below is an excerpt from the Mona Lisa SVG above:

 

<svg
  width="740.54272"
  height="918.85162"
  id="svg6764"
  version="1.1">
 <g
    id="layer1"
    transform="translate(-2.5858422,-16.78522)">
     <path
        d="m 563.483,829.094 c 0,-3.943 2.252,-6.484 2.252,-11.663 0,-4.732 -2.071,-8.484 -1.67,-11.133 0.401,-2.649 3.712,-4.082 3.712,-4.082 l -6.123,-7.053 c 0,0 -2.65,5.195 -5.993,5.195 -2.779,0 -4.967,-2.879 -7.817,-3.307 -3.815,-0.573 -9.386,2.751 -9.386,2.751 0,0 8.114,-5.592 4.452,-7.423 -2.968,-1.484 -7.051,7.161 -7.051,7.161 0,0 1.861,-9.943 -1.49,-9.943 -3.353,0 -0.737,9.798 -0.737,9.798 0,0 -4.265,-8.501 -7.237,-6.831 -2.823,1.59 5.567,7.268 5.567,7.268 0,0 -7.297,-3.306 -10.631,-3.163 -4.646,0.201 -3.951,2.761 -8.201,2.761 -2.86,0 -4.028,-5.595 -4.028,-5.595 l -9.704,8.748 c 0,0 4.431,1.884 5.205,4.271 1.566,4.823 -2.331,6.691 -2.331,10.393 0,6.31 2.97,8.947 2.97,12.246 0,3.46 -6.497,6.124 -6.497,6.124 0,0 7.193,2.007 7.193,5.012 0,4.267 -3.479,5.905 -3.479,10.577 0,5.752 3.896,5.379 3.188,9.647 -0.428,2.583 -5.973,6.867 -5.973,6.867 l 8.688,8.673 c 0,0 2.827,-4.205 5.786,-4.205 2.97,0 5.283,2.583 8.164,2.583 3.337,0 5.214,-2.596 7.795,-2.596 3.335,0 5.382,5.382 5.382,5.382 0,0 1.38,-5.514 6.309,-5.514 1.521,0 3.711,2.356 6.865,2.356 3.155,0 4.863,-2.648 7.609,-2.648 2.41,0 4.921,4.642 4.921,4.642 l 8.255,-7.932 c 0,0 -5.383,-2.226 -5.383,-5.009 0,-3.104 1.856,-4.454 1.856,-10.577 0,-7.237 -2.15,-8.051 -2.15,-13.102 0,-2.261 5.104,-4.143 5.104,-4.143 0,0 -5.392,-0.815 -5.392,-6.536 z"
        id="path478"
        style="fill:#665440" />
        ...

It’s a little intimidating, isn’t it? What does g do, and why is that d property so long?

Let’s trim things down a little more and ignore that d property for now:

<svg width="740.54272" height="918.85162">
 <g transform="translate(-2.5858422,-16.78522)">
  <path
    d="..."
    style="fill:#665440" />

By now if you have any experience with HTML or XML, you’ll notice that these are just nodes defining what the SVG will render, nested nodes define what should happen to each child. In this case, g (short for group) will nudge the inner path a bit down and to the right, the path itself is filled with a very specific color: #665440. If you’ll inspect the portrait again, you should be able to pinpoint the only piece of the picture with that color: the frame of the painting.


Path Outlined in Red

But how is this squiggly path defined? By deduction, we can determine the details are defined in the d property.

 

d=”m 563.483,829.094 c 0,-3.943 2.252,-6.484 2.252,-11.663 0,-4.732 -2.071,-8.484 -1.67,-11.133 0.401….

What this d contains is step-by-step instructions for how to draw this path, called Line Commands. Without going too deep into the details, these line commands are divided by a letter and given an optional chain of numbers that are treated like parameters in a function. For example, the first instruction – m 563.483,829.094  – moves a draw point to a certain xy point in the screen to begin the line. The last instruction – z –  tells the path to draw a line connecting the end of the path to its beginning (it’s often used to finish a path).

However daunting these line commands might still seem, the core thing to know is that they are essentially a list of simple functions that affect what part of the shape to draw next. If you look back over the path above and start scanning for letters, it should start to represent many small things rather than one blob of nonsense.

Any operating system worth its salt has a core library for graphics; inside this library are simple instructions to draw straight lines, curved lines, and fill shapes. For iOS this library is called Core Graphics, and Android has a android.graphics package.

Now to bring it all back together with the library react-native-svg. It takes these SVG paths, turns them into core library instructions, and draws them on the screen for you. The underlying classes that render these paths are powerfully simple. Some are just direct wrappers for a handful of core graphics functions.  

Not only that, but it can turn these SVGs from primitive elements into React Native components, allowing you to leverage the shapes into anything in the React Native framework. You can animate them, make them interact with user inputs, or even call down to native code. For example, we can animate the paths that represent Mona Lisa’s eyes, making her blink.

Animated Mona

Animated Mona

import React from 'react'
import Svg, { G, Path } from 'react-native-svg'

const eyes_open = 'M319.575 303.043v9.805M378.915 303.043v9.805'
const eyes_closed = 'M313.375 308.808h11.366M374.75 308.808h11.366'
const blink_rate = 2; // blink every 2 seconds
const blink_duration = .5; // eyes stay closed for 1/2 second

class MonaLisaEyesSvg extends React.Component {
 constructor(props){
   super (props);
   this.state = { eyesPath: eyes_open };
   this.blink = this.blink.bind(this);
   this.scheduleNextBlink = this.scheduleNextBlink.bind(this);
   this.scheduleNextBlink();
 
 blink() {
   const _this = this;
   _this.setState({ eyesPath: eyes_closed }, () => {
     setTimeout(function(){
       _this.setState({ eyesPath: eyes_open });
       _this.scheduleNextBlink();
     }, blink_duration * 1000); 
   })
 
 scheduleNextBlink() {
   const _this = this;
   setTimeout(function(){
     _this.blink();
   }, blink_rate * 1000);
 
 render() {
   return <Path
   fill="none"
   stroke="#000"
   strokeWidth={5.755235920000001}
   strokeLinecap="round"
   d={this.state.eyesPath}
   />
 
}
const MonaLisaSvg = props => (
 <Svg width={296} height={367.2} viewBox="0 0 740 918" {...props}>
   <G fontFamily="DejaVu Sans, Helvetica, sans-serif">
     <Path
       d="M673.909 413.105c0-40.741 23.269-66.996 23.269-120.509 0-48.893-21.399-87.661-17.255-115.032 4.143-27.37 38.354-42.177 38.354-42.177L655.011 62.51s-27.382 53.678-61.923 53.678c-28.714 0-51.322-29.748-80.77-34.17-39.419-5.92-96.981 28.425-96.981 28.425s83.838-57.78 46-76.699c-30.667-15.333-72.855 73.992-72.855 73.992S407.712 5 373.087 5c-34.645 0-7.615 101.238-7.615 101.238s-44.068-87.837-74.777-70.581c-29.169 16.428 57.522 75.097 57.522 75.097S272.82 76.594 238.37 78.072c-48.005 2.077-40.824 28.528-84.737 28.528-29.551 0-41.62-57.81-41.62-57.81L11.747 139.178s45.784 19.466 53.781 44.13c16.18 49.834-24.085 69.135-24.085 107.386 0 65.199 30.688 92.446 30.688 126.533C72.13 452.978 5 480.504 5 480.504s74.322 20.738 74.322 51.787c0 44.089-35.947 61.014-35.947 109.287 0 59.433 40.256 55.58 32.94 99.679C71.893 767.946 14.6 812.21 14.6 812.21l89.77 89.615s29.21-43.449 59.783-43.449c30.688 0 54.587 26.69 84.355 26.69 34.48 0 53.874-26.824 80.543-26.824 34.459 0 55.61 55.61 55.61 55.61s14.258-56.974 65.188-56.974c15.715 0 38.344 24.343 70.933 24.343 32.599 0 50.247-27.36 78.62-27.36 24.901 0 50.846 47.964 50.846 47.964l85.296-81.958s-55.62-23-55.62-51.756c0-32.072 19.177-46.021 19.177-109.288 0-74.776-22.215-83.187-22.215-135.377 0-23.362 52.737-42.807 52.737-42.807s-55.713-8.421-55.713-67.534z"
       fill="#665440"
     />
     <Path
       fill="#ffeb8c"
       d="M647.881 151.536L99.615 138.88l8.473 684.873 539.793-8.442z"
     />
     <Path
       fill="#c7a941"
       d="M640.142 161.704L108.77 149.718l7.997 663.195 523.375-7.967z"
     />
     <Path
       fill="#824830"
       d="M640.142 449.362H112.397l4.37 363.55 523.375-7.966z"
     />
     <Path
       d="M112.397 449.362h527.745v-49.007s-24.478 33.033-41.93 33.033c-23.991 0-42.363-50.95-67.915-50.95-25.573 0-53.936 60.92-53.936 60.92l-197.796-7.997s-37.952-95.865-59.93-95.865c-11.995 0-33.942 47.943-33.942 47.943s-31.968-83.91-47.963-83.91c-8.824 0-18.62 21.987-25.615 41.66l1.282 104.173z"
       fill="#596b30"
     />
     <Path
       d="M168.699 453.092s-50.66 2.273-50.66 18.237c0 15.974 61.674 8.473 61.674 24.488 0 15.964-45.825 14.466-45.711 28.456.248 27.712 105.878 10.973 105.878 10.973s-79.912-6.985-79.912-16.966c0-10.002 43.18-12.74 43.18-24.736 0-13.494-72.122-11.728-72.122-22.215-.02-11.51 37.673-18.237 37.673-18.237zM586.134 459.24s-26.39 1.167-26.39 9.485c0 8.338 32.104 4.422 32.104 12.75 0 8.308-23.858 7.533-23.786 14.797.124 14.414 55.104 5.714 55.104 5.714s-41.568-3.627-41.568-8.814c0-5.218 22.463-6.654 22.463-12.864 0-7.026-37.549-6.117-37.549-11.583.01-5.951 19.622-9.485 19.622-9.485zM238.857 569.354s68.65-27.98 25.48-102.757c-30.864-53.482 0-130.842 0-130.842s-3.7-59.236 15.798-89.17c21.595-33.064 48.915-51.786 88.633-51.786 91.422 0 124.208 48.573 124.208 174.465 0 127.648 25.903 166.364 25.903 166.364l-280.022 33.726z"
       fill="#433225"
     />
     <Path
       d="M554.806 793.953v-50.558c78.765-48.945 13.494-133.104-14.135-167.15-51.725-63.803-139.717-76.605-139.717-136.968 0-55.207 46.01-94.925 17.266-166.83-10.333-25.8-77.66-47.446-77.66-47.446l-41.712 23.02c-15.799 10.065-22.753 94.885 4.319 133.745 22.039 31.639 41.722 33.56 41.722 33.56v54.133s-61.84 30.202-119.837 98.138c-51.6 60.487-74.435 196.102-77.008 236.213l406.762-9.857z"
       fill="#f4d37e"
     />
     <Path
       d="M628.497 805.132c-1.518-20.53-6.664-90.048-23.351-140.078-20.955-62.915-50.113-100.68-110.734-149.574 0 0-51.766 80.532-155.298 80.532-44.999 0-76.203-50.32-58.979-89.16-9.247 8.225-46 50.506-55.083 60.756-47.56 54.008-77.928 150.659-77.928 244.912l481.373-7.388z"
       fill="#433225"
     />
     <Path
       d="M234.538 575.398l.113 145.772s49.328-13.195 103.874-2.15c0 0-23.993 17.98-41.971 43.46-15.385 21.791-22.474 44.957-22.474 44.957M516.296 566.223s-7.966 21.988-17.978 78.91c-7.16 40.803-8.494 89.046-8.494 89.046"
       fill="none"
       stroke="#000"
       strokeWidth={5.755235920000001}
     />
     <Path
       d="M325.444 368.706s8.545 8.297 17.978 7.977c12.079-.341 18.402-7.925 18.402-7.925"
       fill="none"
       stroke="#000"
       strokeWidth={4.26734728}
       strokeLinecap="round"
     />
     <Path
       d="M360.42 299.499c5.352-4.175 12.078-6.562 18.918-6.045 6.623.527 11.986 3.606 15.416 8.256"
       fill="none"
       stroke="#000"
       strokeWidth={2.9447796}
       strokeLinecap="round"
     />
     <Path
       d="M563.258 771.986s-29.128-23.776-71.109-29.19c-33.281-4.33-82.733-2.914-114.206 3.586-37.734 7.749-49.947 19.993-49.947 19.993"
       fill="none"
       stroke="#000"
       strokeWidth={4.31901008}
     />
     <Path
       d="M498.68 731.74s18.06-3.874 36.845-3.895c17.545-.072 35.224 4.608 35.224 4.608"
       fill="none"
       stroke="#000"
       strokeWidth={5.755235920000001}
     />
     <Path
       d="M411.937 752.726s-7.677-25.749-38.24-29.21c-31.277-3.544-85.885 50.701-64.827 64.423 36.691 24.013 103.067-35.213 103.067-35.213z"
       fill="#f5c14c"
     />
     <Path
       fill="none"
       stroke="#000"
       strokeWidth={2.9964424}
       strokeLinecap="round"
       d="M372.529 763.234l-41.196 25.46M345.561 745.989l-38.2 28.497M364.273 750.515l-48.676 34.448"
     />
     <Path
       d="M326.425 738.994s7.595-19.973 43.697-20.727c31.503-.64 89.79 52.954 68.742 66.686-36.69 23.982-112.439-45.96-112.439-45.96z"
       fill="#f5df88"
     />
     <Path
       fill="none"
       stroke="#000"
       strokeWidth={3.616396}
       strokeLinecap="round"
       d="M375.215 760.227l41.186 25.49M402.173 743.013l38.22 28.456M383.46 747.497l48.678 34.45"
     />
     <Path
       fill="none"
       stroke="#8a4d4a"
       strokeWidth={3.616396}
       strokeLinecap="round"
       d="M386.891 785.222l14.869 20.417"
     />
     <Path
       d="M298.124 300.273s13.66-7.677 26.607-3.936c15.84 4.567 13.918 17.823 13.918 25.996 0 13.877-10.333 28.033-10.333 28.033s4.195 1.415 12.172 1.415c7.966 0 12.079-.94 12.079-.94"
       fill="none"
       stroke="#000"
       strokeWidth={2.9447796}
       strokeLinecap="round"
     />
     <MonaLisaEyesSvg />
     <Path
       d="M511.192 538.015l-84.355 44.12c56.095-60.869 7.181-92.517 7.181-92.517l53.202-38.84 23.972 87.237z"
       fill="#433225"
     />
     <Path
       d="M518.88 535.628s-38.324 4.33-92.033 46.507c-60.032 47.179-92.032 115.033-92.032 115.033"
       fill="none"
       stroke="#000"
       strokeWidth={5.755235920000001}
       strokeLinecap="round"
     />
   </G>
 </Svg>
)

export default MonaLisaSvg    
    

(The React Native component that created the blinking Mona Lisa above)

However, SVGs cannot be used in every situation (a picture-perfect render of Mona Lisa would be almost impossible with SVG), but with the prevalence of Flat design systems in recent years the Vector Graphic is an indispensable tool to leverage. We hope this post helped demystify a few things behind SVGs and how they can be built. This long-standing graphics format is only going to become more important as app-size, efficiency, and interactivity become more of a requirement for stellar mobile experiences.

Leave a Reply

Your email address will not be published. Required fields are marked *