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");
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.
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.
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>
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With