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 plete 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");
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 plete 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");
Share
Improve this question
edited Apr 21, 2016 at 20:58
Peter O.
32.9k14 gold badges84 silver badges97 bronze badges
asked Apr 12, 2016 at 21:41
Marc RasmussenMarc Rasmussen
20.6k83 gold badges222 silver badges383 bronze badges
3
- You've tagged Snap.svg, what have you tried in Snap so far ? – Ian Commented Apr 13, 2016 at 6:37
- @Ian ive added the svg path ive tried – Marc Rasmussen Commented Apr 14, 2016 at 20:21
- @MarcRasmussen tell us which of the presented solutions you prefer and why. Thanks. – Jan Franta Commented Apr 22, 2016 at 19:06
6 Answers
Reset to default 4The 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/2000/svg" xmlns="http://www.w3/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 plete.
$('#myText').show();
$('#myText2').fadeIn('slow');
});
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare./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/2000/svg" xmlns:xlink="http://www.w3/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 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
You probably don't want to use SVG unless the rest of your display is already SVG centric. The speech bubble will be moving around ot avoid occulding faces and controls, scaling for the text, and SVG text is its own suffering animal. The advantages of SVG are limited to its scalability, ability to be manipulated in the DOM, and applications where nested transformations are necessary.
Instead consider making speech bubbles in pure CSS as seen here. Alternately, you can prerender a set of speech bubble graphics, stretch as necessary, and write text on top of them. Purr CSS works best for quick pop-outs while prerendered graphics works best if you expect little thought bubbles, animations, or other cute additions.