Svg (snapsvg) creating a speech bubble

10,518

Solution 1

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.

Solution 2

There is no specific fill with text method, but you can place this yourself and animate.

This would create a bubble and animate with text doing the same thing.

A scale transform, can be written as 'sX,Y,CX,CY'. CX/CY being a centre point to scale around. Snap will automatically try and scale this around the centre (unlike normal svg scale(x,y)).

So 's20,20' will scale the element by 20 in both x and y directions.

var b = s.path("M 200.444444444444446,200v-6h10.444444444444446v6h-4l-3.1111111111111107,1.6222222222222236l0.11111111111111072,-1.6222222222222236Z").attr({ fill: 'gray' });

b.animate({ transform: 's10,10' }, 2000)

var t = s.text(190,200,'stuff!').attr({ stroke: 'yellow', fill: 'yellow', transform: 's0.2,0.2'})
t.animate({ transform: 's2,2'}, 2000)

jsfiddle

It will just need tweaking for how you want it to look and any alignment.

Solution 3

While not using SnapSVG you could: Create some SVG (I used Illustrator):

    <?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<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"
     viewBox="0 0 200 115.9" enable-background="new 0 0 150 115.9" xml:space="preserve">
<g>
    <g>
        <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="33.5436" y1="98.5111" x2="92.6585" y2="11.8584">
            <stop  offset="0" style="stop-color:#FFFFFF"/>
            <stop  offset="1" style="stop-color:#D1D3D4"/>
        </linearGradient>
        <path fill="url(#SVGID_1_)" stroke="#000000" stroke-miterlimit="10" d="M138.5,14.8c-7.8-2.2-33.8-3.8-64.7-3.8
            c-31.2,0-57.4,1.6-64.9,3.8c-0.4,0.1-0.8,0.2-1.1,0.4c-0.3,0.1-0.6,0.3-0.7,0.4c-1.5,1-2.4,2.7-2.4,4.6L14,80.4
            c0.3,2.6,1.6,4.4,3.4,5.1c1.7,0.8,5.9,1.6,11.8,2.2l9.7,0.8c6.6,0.4,14.4,0.7,22.8,0.8c-1.2,6.9,0.4,9.1-4,13.3
            c-8.7,8.3-14.1,7.7-14.1,7.7c1.4,0.5,11,1.7,20-4.8c6.9-4.9,6.1-8.4,7.7-16.2c0,0,0,0,0,0c28.4,0,51.8-1.9,54.5-4.3
            c1.4-0.9,2.4-2.6,2.8-4.7l14.6-60.2C143.1,17.5,141.1,15.3,138.5,14.8z"/>
        <g>
            <path fill="#E2E2E2" d="M138.5,14.8c-7.8-2.2-33.8-3.8-64.7-3.8c-31.2,0-57.4,1.6-64.9,3.8c-0.4,0.1-0.8,0.2-1.1,0.4
                c-0.3,0.1-0.6,0.3-0.7,0.4c-0.4,0.2-0.7,0.5-1,0.8c0.1,0,0.2-0.1,0.2-0.1C6.6,16.2,7,16.1,7.4,16c7.5-2.2,33.7-3.8,64.9-3.8
                c30.9,0,56.9,1.6,64.7,3.8c2.6,0.5,4.6,2.7,4.6,5.4L127,81.6c-0.3,1.6-0.9,3-1.8,3.9c0.2-0.1,0.4-0.2,0.5-0.3
                c1.4-0.9,2.4-2.6,2.8-4.7l14.6-60.2C143.1,17.5,141.1,15.3,138.5,14.8z"/>
        </g>
    </g>
</g>
<text id="bubbleText" transform="matrix(1 0 0 1 21.6668 57.9542)" font-family="'MyriadPro-Regular'" font-size="15.3912">POW! Shazaam. </text>
</svg>

Manipulate the text via JS

document.getElementById('bubbleText').textContent = "new text";

Scale the SVG with the 'viewBox' property on the SVG root element

JSFiddle

Solution 4

if you set the svg height and width to 100% the svg node will be responsive to it's container and with viewbox you can control the view of the inside elements;

the svg will then be responsive to whatever scaling applied to container .

pen

Solution 5

A short SVG embedded into a div. jQuery is used to animate the size + position. A callback is used to make the words visible and the 2nd liner uses .fadeIn().

Hope it helps.

$('div').append('<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" x="0.00000000" y="0.00000000" width="100%" height="100%" id="svg3" viewbox="0 0 1000 600" >  <path fill="#FFFFFF" stroke="#000000" stroke-width="10" d="M916.902,397.331c14.126,0,17.344-9.739,17.344-9.739   c0-27.635,7.992-42.357,26.927-42.357c0,0,13.702,1.668,13.702-14.946c0-0.001,0.619-43.408-1.901-141.244   c-2.514-97.836-9.537-109.333-9.537-109.333c0-14.125-9.129-13.284-9.129-13.284c-24.67,0-53.406,4.151-53.406-30.893   c0,0,1.558-11.866-15.041-11.866c0,0-159.78-14.301-423.823-14.301c-264.041,0-375.12,2.352-375.12,2.352   c-14.125,0-13.284,9.136-13.284,9.136c0,22.479-13.575,42.622-30.319,42.622c0,0-13.705,0.341-13.705,16.949   c0,0-4.551,60.914-4.551,117.724c0,56.808,4.551,126.899,4.551,126.899c0,14.125,9.127,13.28,9.127,13.28   c24.9,0,29.944,10.568,29.944,30.322c0,0,1.038,15.744,25.709,15.744l248.677,5.155c0,0,46.81,25.855,64.76,39.665   c17.952,13.808,27.714,26.235,12.526,41.426c-6.669,6.666-11.966,12.474-9.571,21.187c2.277,8.256,26.797,22.168,29.903,23.746   c0.261,0.127,61.957,30.323,84.796,41.37c16.646,8.047,33.288,16.074,49.292,25.362c2.152,1.253,14.271,9.614,16.804,7.089   c2.484-2.479-11.174-12.959-12.823-14.315c-9.084-7.442-16.206-16.462-24.158-25.027c-12.481-13.465-25.133-26.788-37.746-40.133   c-7.044-7.464-13.884-15.167-21.144-22.43c-1.791-1.79-1.476-4.571,0.699-7.001c7.682-8.531,25.246-28.013,27.384-30.14   c2.739-2.731-1.814-7.121-1.814-7.121l-62.959-51.678L916.902,397.331z"/> <text x="200" y="200" font-size="72" color="blue" id="myText" style="display: none;" >Hello Stackoverflow</text> <text x="200" y="300" font-size="72" color="blue" id="myText2" style="display: none;" >Delayed text</text> </svg>');

$('div').draggable({
    handle: 'rect'
});

$('div').animate({ // shrink it
    width: "100px",
    height: "60px",
    top: "240px",
    left: "220px"
}, 0) 
.animate({ // animate to full size
    width: "500px",
    height: "300px",
    top: "0px",
    left: "0px"
}, 2000, function() { // show text
    // Animation complete.

     $('#myText').show();
     $('#myText2').fadeIn('slow');
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<div style="width:500px; height:300px; ">
</div>
Share:
10,518
Marc Rasmussen
Author by

Marc Rasmussen

SOreadytohelp Website

Updated on June 18, 2022

Comments

  • Marc Rasmussen
    Marc Rasmussen almost 2 years

    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");
    
  • Jan Franta
    Jan Franta about 8 years
    I add your scaling to my pen example.
  • Jan Franta
    Jan Franta about 8 years
    Thanks. Hope that match the requirement.
  • Marc Rasmussen
    Marc Rasmussen about 8 years
    Hey mate thank you for your response. is it possible to remove the animation that makes it bigger and bigger?
  • Jan Franta
    Jan Franta about 8 years
    @MarcRasmussen It's done. You have just to change the element order. That's all. Look at the Codepen example.
  • unconditionalcoder
    unconditionalcoder almost 8 years
    This is exactly what I need! any way to make the corners more round?!
  • Ian
    Ian almost 8 years
    Change the path to have rounded corners, so you will need to get some curves in there. You may find an svg already done somewhere already, or may need to play with an editor.
  • John Hatton
    John Hatton about 7 years
    Doesn't work in current Chrome or firefox. Text keeps growing as though animated, bubble never encompasses it.