Javascript String Ellipse to Length
by webdood on Apr.10, 2012, under Canvas, Javascript, Software Development, Strings
A perennial problem with HTML is correctly dealing with ellipsis on text. There is the text-overflow:ellipsis trick, but there are some situations where you need to ellipse a string at a certain number of pixels.
I think I have come up with a pretty clever solution to this.
By adding a prototype to the String Object and using a dynamically generated <canvas> tag, we can accurately measure the width of a string based on a provided font and fontSize, adding ellipsis as necessary.
////////////////////////////////////////////////////////////////////////////////
//
// String.ellipseToLength(int length, OPTIONAL string fontSize, OPTIONAL string fontFamily)
// ======================
// Returns an ellipsed string measured to length using CANVAS's measureText method
// By passing in fontSize and FontFamily, you will get accurate measurements
//
////////////////////////////////////////////////////////////////////////////////
String.prototype.ellipseToLength = function( length, fontSize, fontFamily) {
var bContinue = true,
ctx = document.createElement('canvas').getContext('2d'),
measuredLength,
retVal = this.toString(),
firstChar = retVal.charAt(0);
fontSize = (typeof fontSize==="undefined") ? "12pt" : fontSize;
fontFamily = (typeof fontFamily==="undefined") ? "Arial" : fontFamily;
ctx.font = fontSize + " " + fontFamily;
if ((typeof length==="undefined") || length===0 || retVal.length===0 || ctx.measureText(firstChar).width >= length) { // Special weird case the first character is greater than the length asked for ...
retVal = "...";
} else {
measuredLength = ctx.measureText(retVal).width;
if (measuredLength > length) { // If the measured length of the string is larger than that specified, then more work is required
while (bContinue) {
retVal = retVal.slice(0,-1);
measuredLength = ctx.measureText(retVal + '...').width;
if (measuredLength <= length) {
retVal = retVal + '...';
bContinue = false;
}
if (retVal.length===0) {
retVal = "..."
bContinue = false;
}
}
}
}
return retVal;
}
Javascript Number roundToPrecision
by webdood on Nov.17, 2011, under Javascript, Numbers
Needed a cross-browser function round to a given precision. Cannot rely on toFixed()
Number.prototype.roundToPrecision = function( precision ) {
if (precision===undefined) { precision = 2; }
var multiplier = Math.pow(10,precision);
return Math.round(this*multiplier)/multiplier;
}
Javascript Number ensureDecimalPlaces
by webdood on Nov.17, 2011, under Arrays, Javascript, Numbers
Needed a function ensure that a certain number of digits appeared after a decimal point today.
For instance 1.3 should be 1.300, 1 should be 1.000 etc.
Number.prototype.ensureDecimalPlaces = function( decimals ) {
var tempString = this.toString(),
pileOfPads = "000000000000000000000000",
decimalLocation = tempString.indexOf('.');
if (decimalLocation === -1) {
retVal = tempString + '.' + pileOfPads.substring(0,decimals);
} else {
retVal = tempString + pileOfPads;
retVal = retVal.substring(0,decimalLocation+decimals+1);
}
return retVal;
}
Example use:
var x = 12.3
console.log(x.ensureDecimalPlaces(3)) returns 12.300
HTML5 Green Screen Technique using <VIDEO> and <CANVAS>
by webdood on Sep.09, 2011, under Canvas, HTML5, Safari-Specific, Software Development
This technique allows you to composite a green-screen video over a web page.
The basic setup consists of a hidden <VIDEO> tag, another hidden <CANVAS> tag, where each frame of video is captured for further processing and a visible <CANVAS> tag where each processed frame is displayed.
When a frame of video is captured and placed into our “canvasProcessor”, each pixel is analyzed in turn. A pixel is represented by a CanvasPixelArray consisting of four values: (R)ed, (G)reen, (B)lue and (A)lpha.
When we find a pixel that fits in the range of “green”, we change that one to have a 0 (A)lpha value (and thus it becomes invisible).
Here is link to a working example.
<!DOCTYPE HTML><
<html>
<head>
<title>Green Screen Test</title>
<script type="text/javascript">
greenScreenEngine = {
_animator : null,
_canvasProcessorContext : null,
_canvasTargetContext : null,
_height : 0,
_width : 0,
_video : null,
handlerFor : {
pause : function() {
clearTimeout( this._animator );
},
play : function() {
greenScreenEngine.parseAndProcessFrame();
}
},
parseAndProcessFrame : function() {
if (this._video.paused || this._video.ended) {
return;
}
this._canvasProcessorContext.drawImage(this._video,0,0,this._width,this._height)
var videoFrame = this._canvasProcessorContext.getImageData(0,0,this._width,this._height),
numberOfPixels = videoFrame.data.length / 4;
for (var i=0;i 100 && b < 100) { // Here we look for what we perceive to be a green pixel
videoFrame.data[i * 4 + 3] = 0; // If we find one, make its alpha channel transparent
}
}
this._canvasTargetContext.putImageData(videoFrame,0,0);
this._animator = setTimeout( function() { greenScreenEngine.parseAndProcessFrame() },0);
},
setCanvasProcessor : function( canvasProcessorID, scale ) {
scale = (scale!==undefined) ? scale : 1;
this._height = this._video.videoHeight * scale;
this._width = this._video.videoWidth * scale;
var oCanvas = document.getElementById(canvasProcessorID);
oCanvas.height = this._height;
oCanvas.width = this._width;
this._canvasProcessorContext = oCanvas.getContext("2d");
this._canvasProcessorContext.drawImage(this._video,0,0,this._width,this._height)
},
setCanvasTarget : function( canvasTargetID ) {
var oCanvas = document.getElementById(canvasTargetID);
oCanvas.height = this._height;
oCanvas.width = this._width;
this._canvasTargetContext = oCanvas.getContext("2d");
},
setSourceVideo : function( videoID ) {
this._video = document.getElementById( videoID );
this._video.addEventListener("play", greenScreenEngine.handlerFor.play)
this._video.addEventListener("pause", greenScreenEngine.handlerFor.pause)
}
}
function setup() {
greenScreenEngine.setSourceVideo("video-green-screen");
greenScreenEngine.setCanvasProcessor("canvas-processor",.5);
greenScreenEngine.setCanvasTarget("canvas-target");
greenScreenEngine._video.play();
}
</script>
</head>
<body>
<div>
<video id="video-green-screen" src="talkingHead.mp4" controls="true" tabindex="0" oncanplay="setup()" style="display:none;"></video>
</div>
<div>
<canvas id="canvas-processor" style="display:none;"></canvas>
<canvas id="canvas-target" style="-webkit-transform:rotate(90deg);position:absolute;bottom:79px;"></canvas>
</div>
</body>
</html>>
Note - For the video I've used here, I had to play with the values a bit to find just the right range of values that mean "green." Also, the source video was rotated -90deg, so I had to apply a webkit-transform to it. To see how everything works, just comment out the display:none CSS.
Shannon Norrell
HTML5 Canvas – Drawing Dashed Lines (dashedLineFromTo)
by webdood on Jul.18, 2011, under Canvas, HTML5, Javascript
There is no way built-in way to draw dashed lines using <canvas>.
Therefore, I came up with a prototype method that hangs off of the CanvasRenderingContext2D Object that I am calling “dashedLineFromTo”
As you may expect, it simply draws a bit of a line, then skips a bit and draws more until it is done “connecting the dots” between point A and B.
My particular application involves generating a line graph with dashed lines.
This is, in effect, a series of dashed lines and adds a slight nuance inasmuch as when one dashed line is completed and another picks up where it left off, we must keep track of if the previous line ended in a lineTo (ie drawn pixels) or a moveTo (ie just moving the drawing cursor) and, with the new line segment, begin it with a lineTo or a moveTo to “finish off” the pixels that were left unpainted in the previous segment.
This sounds more complicated than it is.
var __dashedLineFromTo = {
isDrawing : true,
unFinishedPixelsFromLastDash : 0
}
CanvasRenderingContext2D.prototype.dashedLineFromTo = function(from,to) {
var x=from[0], y=from[1],
dashLength = 2,
dx = (to[0] - x) + .00000001,
dy = to[1] - y,
slope = dy/dx,
distanceRemaining = Math.sqrt(dx*dx + dy*dy),
bUnfinishedPixels = false,
theDashLength,
xStep;
this.moveTo(x,y);
while (distanceRemaining>=0.1) {
if (__dashedLineFromTo.unFinishedPixelsFromLastDash === 0) {
theDashLength = dashLength;
} else {
theDashLength = __dashedLineFromTo.unFinishedPixelsFromLastDash;
__dashedLineFromTo.unFinishedPixelsFromLastDash = 0;
__dashedLineFromTo.isDrawing = !__dashedLineFromTo.isDrawing
}
if (dashLength > distanceRemaining) { dashLength = distanceRemaining; bUnfinishedPixels=true; }
xStep = Math.sqrt( theDashLength*theDashLength / (1 + slope*slope) );
x += xStep;
y += slope*xStep;
this[__dashedLineFromTo.isDrawing ? 'lineTo' : 'moveTo'](x,y);
distanceRemaining -= theDashLength;
__dashedLineFromTo.isDrawing = !__dashedLineFromTo.isDrawing;
}
if (bUnfinishedPixels) {
__dashedLineFromTo.unFinishedPixelsFromLastDash = theDashLength;
}
}
function init() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.lineWidth = 2;
ctx.lineCap = 'butt';
ctx.beginPath();
ctx.dashedLineFromTo([0.2, 1.2], [99.9, 12.8]);
ctx.dashedLineFromTo([99.9, 12.8], [118.5, 5.0]);
ctx.dashedLineFromTo([118.5, 5.0], [148.5, 105.0]);
ctx.dashedLineFromTo([148.5, 105.0], [178.5, 55.0]);
ctx.dashedLineFromTo([178.5, 55.0], [218.5, 97.3]);
ctx.closePath();
ctx.stroke();
}
<body onload="init()">
<canvas id="canvas" width="912" height="339"></canvas>
</body>
Here is a working example:
Shannon Norrell
Javascript Bitwise Operators or Math.Floor? Which is better?
by webdood on Jul.15, 2011, under Software Development
I realized today that there are three ways to get at the floor of a value in Javascript.
There is:
They all work similarly. Which is faster?
I developed a test harness to compare the three different methods.
The results? Math.floor is definitely the slowest.
Safari processes the Left-shift << about 15% faster than the Double NOT ~~ operator
Chrome effectively processes ~~ and << at the same speed
Firefox processes Double NOT ~~ operator 6% faster than the Left-shift << operator
IE8 processes Left-shift << operator 5% faster than the Double NOT ~~ operator
Conclusion: use Left-shift << for a generalized, faster approach to getting at the floor of a value.
Shannon Norrell
HTML5 CSS Tables
by webdood on Jul.13, 2011, under Software Development
Interesting post about HTML5 CSS Tables
Javascript Array Sort By String Length
by webdood on Jul.08, 2011, under Arrays, Javascript
Had a need to sort an array of strings by length of each string in descending order.
Simple solution, really. Perhaps may come in handy at a later date.
////////////////////////////////////////////////////////////////////////////
//
// Array.sortByStringLength() - sorts an array of strings based on length
// ==========================
//
////////////////////////////////////////////////////////////////////////////
Array.prototype.sortByStringLength = function() {
return this.sort( byStringLength )
}
function byStringLength(a,b) {//Sort function used by Array.sortByStringLength
var retVal = 0;
if (a.length > b.length) { retVal = -1; }
return retVal;
}
Shannon Norrell
Determine if an Object is empty in Javascript
by webdood on Apr.22, 2011, under Javascript, Objects
There are times when you need to determine if an object actually contains anything.
Distinguishing {} from { foo:bar } cannot be done by checking for .length, as an Object does not provide that.
Therefore, the only way to determine if there is anything in the Object is to basically do a for-each loop on the Object itself. Now, depending on the Javascript library you are (probably) using, there will be lots of *stuff* inside the Object that was inherited from the prototype chain. Since we are looking for actual stuff, we utilize the rarely used hasOwnProperty method to determine if the *stuff* in the Object was inherited or actually a part of theObject itself.
Now, you could add this to the prototype of Object itself (for instance Object.prototype.isEmpty = function() { blah};, but I consider that bad practice as – particularly with the Object Object, from which every other type of *thing* in Javascript inherits, the prototype wil be added to all other types of Objects in Javascript as well (for instance String would then have an isEmpty method).
This is all fine and dandy if you want to “robustify” this to support all data types, but trust me, it’s much cleaner and nicer to the DOM
to do it the way I illustrate below.
////////////////////////////////////////////////////////////////////////////////
//
// objectIsEmpty( object) - determines if an Object is empty
// ======================
// You could add this to the prototype chain of Object as .isEmpty(),
// but that would be wasteful. Better to keep it as a standalone function
//
////////////////////////////////////////////////////////////////////////////////
function objectIsEmpty( oObject ) {
var eachProperty = null;
for (eachProperty in oObject) {
if (oObject.hasOwnProperty(eachProperty)) {
return false;
}
}
return true;
}
Shannon Norrell
CSS3 Tooltip Trick using CSS3 :before psuedo element, content attribute and custom data attributes
by webdood on Mar.03, 2011, under CSS3, Software Development
I was looking for a way to make an HTMLElement’s title appear faster in a WebKit application.
Turns out there is a way to capture the “title” attribute of an element using the :before pseudo element and content property, store that value in a div that only appears onhover over the original element. The problem with this technique was that the actual “title” attribute would eventually display.
Therefore, I made use of another CSS3 feature called “custom data attributes” and, rather than storing the title of the element in the “title” attribute, I used “data-title” instead.
Here is a quick example:
<!DOCTYPE html>
<head>
<style type="text/css">
.sprocket { position:relative;width:50px;height:50px;background-color:red; }
.sprocket:before { content:attr(data-title); display:none; }
.sprocket:hover::before{ width:160px; display:block; border-radius:3px; background-color:#fffdc7; padding:5px; color:black; margin-top:40px; margin-left:20px; -webkit-box-shadow: 3px 3px 3px rgba(193,193,193,.5);}
.sprocket:hover{ z-index:10; position:absolute; }
</style>
</head>
<body>
<div class="sprocket" data-title="Fancy Title Text">
</body>
</html>
Click here for an Example
Shannon Norrell



