Svg (snapsvg) creating a speech bubble

Marc Rasmussen picture Marc Rasmussen · Apr 12, 2016 · Viewed 10.3k times · Source

I am creating a chat program with some figures on the screen moving around chatting with other people.

One of the last things I need to complete this project is when ever a person says something it is put into a scaleable speech bubble.

Since I'm very new at using SVG and this is my first real "Game" project I thought "Let's use some CSS to make sure that it scales correctly"

So I made the following CSS:

    .bubble {
    background-color: #eee;
    border: 2px solid #333;
    border-radius: 5px;
    color: #333;
    display: inline-block;
    font: 16px/24px sans-serif;
    padding: 12px 24px;
    position: relative;
}
.bubble:after,
.bubble:before {
    border-left: 20px solid transparent;
    border-right: 20px solid transparent;
    border-top: 20px solid #eee;
    bottom: -20px;
    content: '';
    left: 50%;
    margin-left: -20px;
    position: absolute;
}

/* Styling for second triangle (border) */

.bubble:before {
    border-left: 23px solid transparent;
    border-right: 23px solid transparent;
    border-top: 23px solid;
    border-top-color: inherit; /* Can't be included in the shorthand to work */
    bottom: -23px;
    margin-left: -23px;
}

But sadly that didn't work. I later found out it is because SVG does not support all CSS properties. So now I'm kind of at a lost? I am not quite sure how to create a scalable speech bubble in SVG and I was hoping one of you might be kind enough to point me in the right direction.

SVG path I have tried:

I managed to create a very small SVG path however I'm unsure how to make it bigger and make it it filled with text:

    var mesasgeBox = chatSvg.path("M 200.444444444444446,200v-6h10.444444444444446v6h-4l-3.1111111111111107,1.6222222222222236l0.11111111111111072,-1.6222222222222236Z");

Answer

Jan Franta picture Jan Franta · Apr 21, 2016

The source below require a position (x/y) to know where to appear and a max width for text wrapping. It's written as plugin, so you can use it easy. I have not optimized it and the performance can be raised by caching the letter width by font-size.
The font wrapping code is based on this solution here How to either determine SVG text box width, or force line breaks after 'x' characters?

Please replace the paper.rect inside the plugin with your prefered bubble layout.

Snap.plugin(function (Snap, Element, Paper, glob) {
     Paper.prototype.bubbletext = function (x, y, txt, maxWidth, attributes) {

        var svg = Snap();
        var abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
        var preText = svg.text(0, 0, abc);
        preText.attr(attributes);
        var letterWidth = (preText.getBBox().width / abc.length);
        svg.remove();

        var words = txt.split(" ");
        var widthCalc = 0, activeLine = 0, lines=[''];
        for (var letterCount = 0; letterCount < words.length; letterCount++) {

           var l = words[letterCount].length;
           if (widthCalc + (l * letterWidth) > maxWidth) {
              lines.push('');
              activeLine++;
              widthCalc = 0;
           }
           widthCalc += l * letterWidth;
           lines[activeLine] += words[letterCount] + " ";
        }

        var padding = 10;

        var t = this.text(x+padding, y+15+padding, lines).attr(attributes);

        t.selectAll("tspan:nth-child(n+2)").attr({
           dy: "1.2em",
           x: x+padding
        });

        var boxHeight = t.node.clientHeight + (padding * 3);
        var messageBox = this.path("M " + (x-padding) + "," + (y-padding+boxHeight) + "v-" + boxHeight + "h" +  (t.node.clientWidth + (padding*3)) + "v"+boxHeight+"h-6l-9,15l0,-15Z");
        messageBox.attr({
            fill:"rgba(0, 0, 255, .3)"
        });
        t.before(messageBox);
        return t;
     };
  });

var div = document.querySelector('div.wrap');
var bubble = Snap('100%','100%').attr({ viewBox: '0  0 200 200' });;
bubble.bubbletext(0, 0, "Hallo Mike how are you. These text is auto wraped and the bubble size automaticaly. The svg result is also scaleable. Please change this text to test.", 155,
    { "font-size": "15px", "fill": "#000"});
div.appendChild(bubble.node);

CODEPEN

UPDATE

Add your bubble layout to codepen example.

UPDATE 2
I Update the source example.