filltext() canvas text position discrepencies between browsers - javascript

As can be seen in the screenshots at the bottom of the question or by going
directly to the game.
text is placed differently dependent on browser (firefox 15.0.1 renders differently then IE 9.9 and Chrome 21).
call to draw function:
context.fillText(this.wlines[i], this.xcoord, this.ycoord + y + (t) * this.sizey);
constructor of object:
function textItem(text, xcoord, ycoord, sizex, sizey,style, context) {
this.wlines = [];
this.text = text;
this.xcoord = xcoord;
this.ycoord = ycoord;
this.sizex = sizex;
this.sizey = sizey;
this.style = style;
if (text == null) {
text = "";
}
var lines = text.split("~");
// this is first line text
context.save();
if (this.style < 3) {
context.shadowOffsetY = 2;
context.font = 'bold 18px "palatino linotype"';
} else if (this.style == 4) {
this.font = '16px "palatino linotype"';
this.shadowOffsetX = 2;
this.shadowOffsetY = 1;
this.shadowColor = "rgba(255,255,255,1)";
}
if (this.style == 5) {
this.wlines.push(text);
} else {
for (j = 0; j < lines.length; j += 1) {
var words = lines[j].split(" ");
var lastLine = "";
var l = sizex;
var measure = 0;
for (i = 0; i < words.length; i += 1) {
var w = words[i];
measure = context.measureText(lastLine + w).width;
if (measure < l) {
lastLine += (w + " ");
} else {
//this is body text
if (this.style == 6) {
lastLine += "...";
}
this.wlines.push(lastLine);
lastLine = (w + " ");
if (this.style < 3) {
context.font = 'bold 14px "palatino linotype"';
}
}
if (i == words.length - 1) {
this.wlines.push(lastLine);
break;
}
}
}
}
context.restore();
}
text,xcoorc,ycoord,xsize,ysize are parsed from an xml file. The compond name in this example:
<sizex>196</sizex>
<sizey>20</sizey>
<xcoord>383</xcoord>
<ycoord>14</ycoord>
style is a defined value based on the text effects desired and context is the 2d context of the canvas to draw on (for layering effects).
As shown all values are exactly the same between browsers. The only check I do between browsers is
<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1"/>
in the header of the html page.
I really don't know where the line height discrepancy is coming from and any help on the matter would be appreciated.
The line height discrepancy changes depending on the text but not in a manner that I have figured out yet. If there is any information that I have omitted please don't hesitate to ask.
ff:
ff screen http://www.sunshinecompound.com/images/firefoxscreen.png
Chrome:
chrome screen http://www.sunshinecompound.com/images/googlescreen.png
Update The solution for my program at least was to build use an offset. Further I got huge performance boosts by creating the text object and then saving the text object as an image. In FF which was the browser with the greatest slowdown I saw a little bit over a 5X decrease in overall program run-time. This is despite having to recreate the text object each time text dynamically changed in the program (I change dynamic counters per second and mouse hover effects every 200ms but with the performance I'm currently getting I can probably improve that to 100ms).

Yep.
It's placed differently, scaled differently, kerned differently, aliased differently and even measured differently (as in measureText) between browsers.
If you need pixel consistency for your game then you're going to have to use images instead of text. Sorry. :(
The only way to make measureText consistent is to pre-compute.
The only way to make fillText consistent is to use images instead of text. It's must faster, anyway.
Both of these are untenable if the text is extremely dynamic, but if you only ever write say, less than 100 different pieces of text in your app, images are probably your best bet.
Otherwise, you could use a pixel font generated from an image (use drawImage with each letter or common word) and hope for OK performance, caching the longer, common strings.

Related

how to optimize a JavaScript random normal generation algo

Here is my code. This function returns a number that follows the standard normal distribution.
var rnorm = function() {
var n = 1000;
var x = 0;
var ss = 0;
for (var i = 0; i < n; i++) {
var a = Math.random();
x += a;
ss += Math.pow(a-0.5, 2);
}
var xbar = x/n;
var v = ss/n;
var sd = Math.sqrt(v);
return (xbar-0.5)/(sd/Math.sqrt(n));
};
It is simple and exploits the central limit theorem. Here is a jsfiddle running this thing 100,000 times and printing some info to the console.
https://jsfiddle.net/qx61fqpn/
It looks about right (I haven't written code to draw a histogram yet). The right proportions appear between (-1, 1), (-2, 2), (-3, 3) and beyond. The sample mean is 0, var & sd are 1, skew is 0 and kurtosis is 3.
But it's kind of slow.
I intend to write more complex functions using this one as a building block (like the chi-sq distribution).
My question is:
How can I rewrite this code to improve its performance? I'm talking speed. Right now, it's written to be clear to someone with a bit of JavaScript and statistics knowledge what I'm doing (or trying to do - in the event I've done it wrong).

Processing: Why does this sketch not work in Javascript mode?

For some reason, and I'm not sure why, my Processing sketch doesn't display in JavaScript mode. However, in Java mode it runs how it's intended. I'm pretty sure that everything used is in the Processing API, so I really have no idea why this doesn't work.
A rundown on the program. Basically, I had to create a Stock Ticker with text moving from bottom right of the screen to the left on a constant loop.
Stock Ticker:
Stock[] quotes = new Stock[12];
float t=0;
PFont text;
void setup() {
size(400,200);
//Importing "Times" font
text = createFont( "Times" ,18,true);
//Assigning Strings and Float values to Stock array index
quotes[0] = new Stock("ADBE",68.60);
quotes[1] = new Stock("AAPL",529.36);
quotes[2] = new Stock("ADSK",51.41);
quotes[3] = new Stock("CSCO",21.87);
quotes[4] = new Stock("EA",30.17);
quotes[5] = new Stock("FB",67.07);
quotes[6] = new Stock("GOOG",1201.52);
quotes[7] = new Stock("HPQ",31.78);
quotes[8] = new Stock("INTC",25.41);
quotes[9] = new Stock("MSFT",40.49);
quotes[10] = new Stock("NVDA",18.56);
quotes[11] = new Stock("YHOO",38.24);
//Assigning Position of indexs using setX method from Stock class
float x = 0;
for (int i = 0; i < quotes.length; i++)
{
quotes[i].getX(x);
x = x + (quotes[i].width());
}
t = x;
}
void draw() {
background(55);
//Rendering of Text using trans and show functions from Stock class
for (int i = 0; i < quotes.length; i++)
{
quotes[i].trans();
quotes[i].show();
}
//Transparent Rectangl;e
noStroke();
fill(10,10,10,127);
rect(0,164,width,35);
}
Stock:
class Stock {
String stockName;
float stockNum;
float x;
String show;
Stock(String n, float v)
{
stockName = n;
stockNum = v;
show = (stockName + " " + stockNum + " ");
}
//Sets position of each index
void getX(float x_)
{
x = x_;
}
//Moves text
void trans()
{
x = x - 1;
if (x < width-t)
{
x = width;
}
}
//Renders Text
void show()
{
textFont(text);
textAlign(LEFT);
fill(255);
text(show,x,height-10);
}
//Records and returns width of index
float width()
{
textFont(text);
return textWidth(show);
}
}
Two things:
According to Processing.js page, it does not work with most Java, so
you need to be careful with what libraries you use. From their
Quick Start page:
Processing.js is compatible with Processing, but is not, and will
never be, fully compatible with Java. If your sketch uses functions or
classes not defined as part of Processing, they are unlikely to work
with Processing.js. Similarly, libraries that are written for
Processing, which are written in Java instead of Processing, will most
likely not work.
Even if the libraries you use are supported, you have to be very
careful while naming your variables because Javascript being a
typeless language can result in variable name clashes that you
wouldn't normally worry about when writing in Processing in Java
mode (or in Java directly) since it is a typed language. See
Processing.js's note on this.

JavaScript Noise Function Problems

I've been trying to learn about generating noise and find that I understand most of it but I'm having a bit of trouble with a script.
I used this page as a guide to write this script in JavaScript with the ultimate purpose of creating some noise on canvas.
It's definitely creating something but it's tucked all the way over on the left. Also, refreshing the page seems to create the same pattern over and over again.
What have I done wrong that the "noisy" part of the image is smushed on the left? How can I make it look more like the cloudy perlin noise?
I don't really understand why it doesn't produce a new pattern each time. What would I need to change in order to receive a random pattern each time the script is run?
Thank you for your help!
/* NOISE—Tie it all together
*/
function perlin2d(x,y){
var total = 0;
var p = persistence;
var n = octaves - 1;
for(var i = 0; i <= n; i++) {
var frequency = Math.pow(2, i);
var amplitude = Math.pow(p, i);
total = total + interpolatenoise(x * frequency, y * frequency) * amplitude;
}
return total;
}
I've forked your fiddle and fixed a couple things to make it work: http://jsfiddle.net/KkDVr/2/
The main problem was the flawed pseudorandom generator "noise", that always returned 1 for large enough values of x and y. I've replaced it with a random values table that is queried with integer coordinates:
var values = [];
for(var i = 0; i < height; i++) {
values[i] = [];
for(var j = 0; j < width; j++) {
values[i][j] = Math.random() * 2 - 1;
}
}
function noise(x, y) {
x = parseInt(Math.min(width - 1, Math.max(0, x)));
y = parseInt(Math.min(height - 1, Math.max(0, y)));
return values[x][y];
}
However, the implementation provided in the tutorial you followed uses simplified algorithms that are really poorly optimized. I suggest you the excellent real-world noise tutorial at http://scratchapixel.com/lessons/3d-advanced-lessons/noise-part-1.
Finally, maybe you could be interested in a project of mine: http://lencinhaus.github.com/canvas-noise.
It's a javascript app that renders perlin noise on an html5 canvas and allows to tweak almost any parameter visually. I've ported the original noise algorithm implementation by Ken Perlin to javascript, so that may be useful for you. You can find the source code here: https://github.com/lencinhaus/canvas-noise/tree/gh-pages.
Hope that helps, bye!

CPU use keeps growing; is there a way to delete my unused objects in JavaScript?

I'm making a game in HTML5's Canvas + JavaScript. My CPU use for the tab keeps growing by a couple percent every second until it hits 50% (all of one core). I am successfully removing my objects from the array of objects, but the objects themselves seem to persist. I have tried "delete obj" and "obj = null" in a few places, but no luck.
Any ideas?
var falling_blocks = setInterval(add_falling_block, 1000);
...
function add_falling_block(){
b = new Object();
b.x = (randFromTo(0,sq) * scale) - boff;
b.y = -scale + boff;
b.color = "#f00";
b.moving = true;
b.number = f.length;
f.push(b);
}
function draw_falling_blocks(){
var db = new Array()
for(i = 0; i < f.length; i++){
var ba = f[i];
if(ba.y < (bottom + scale)){
ba.y += scale;
draw_block(ba.x,ba.y,"#f00");
}
else if(ba.y = (bottom + scale)){
db.push(i);
console.log(f.length);
}
}
for(i = 0; i < db.length; i++){
f.splice(db[i],1);
}
}
As I mentioned in my comment, CPU usage != memory usage. With Chrome's developer tools, it's easy to profile your application in order to locate the code which is consuming all of the CPU cycles.
Open your app, open the dev tools, and go to the Profiles tab. Click the Record button (the black circle). It will turn red; Chrome is now recording CPU usage. Let it record for a few seconds and click the Record button again. You will now see CPU usage by function.
In this case, we can see that draw_grid() has consumed 92% of CPU time – we've found our culprit!
I made some changes to the draw_grid function:
function draw_grid(){
c.strokeStyle = '#333';
for (var i = poff; i < w; i += scale) {
c.beginPath();
c.moveTo(0, i);
c.lineTo(w, i);
c.closePath();
c.stroke();
}
for (var j = poff; j < h; j += scale) {
c.beginPath();
c.moveTo(j, 0);
c.lineTo(j, h);
c.closePath();
c.stroke();
}
}
CPU usage never tops 5% now.
A few notes:
I've added beginPath() and closePath() and moved stroke() into each loop iteration.
I changed the size of each line from a constant 500px to use the appropriate variable.
You only need to set strokeStyle once. It remains the same unless you change it again.
You should write var in your loops' initializer to restrict the variable's (i and j in this case) scope to the local function. Otherwise, you're creating a global variable, which is a bad idea for a variety of reasons.
You may need to delete all properties/methods of that object in addition to deleting the pointer (variable), but memory leakage has always been a problem that's plagued JavaScript.
Have you thrown in setTimeout('',0) anywhere to allow the processor to do some other things.

Optimizing a 3D World Javascript Animation

I've recently come up with the idea to create a tag cloud like animation shaped like the earth. I've extracted the coastline coordinates from ngdc.noaa.gov and wrote a little script that displayed it in my browser. Now as you can imagine, the whole coastline consists of about 48919 points, which my script would individually render (each coordinate being represented by one span). Obviously no browser is capable of rendering this fluently - but it would be nice if I could render as much as let's say 200 spans (twice as much as now) on my old p4 2.8 Ghz (as a representative benchmark). Are there any javascript optimizations I could use in order to speed up the display of those spans?
One 'coordinate':
<div id="world_pixels">
<span id="wp_0" style="position:fixed; top:0px; left:0px; z-index:1; font-size:20px; cursor:pointer;cursor:hand;" onmouseover="magnify_world_pixel('wp_0');" onmouseout="shrink_world_pixel('wp_0');" onClick="set_askcue_bar('', 'new york')">new york</span>
</div>
The script:
$(document).ready(function(){
world_pixels = $("#world_pixels span");
world_pixels.spin();
setInterval("world_pixels.spin()",1500);
});
z = new Array();
$.fn.spin = function () {
for(i=0; i<this.length; i++) {
/*actual screen coordinates: x/y/z --> left/font-size/top
300/13/0 300/6/300
| /
|/
0/13/300 ----|---- 600/13/300
/|
/ |
300/20/300 300/13/600
*/
/*scale font size*/
var resize_x = 1;
/*scale width*/
var resize_y = 2.5;
/*scale height*/
var resize_z = 2.5;
var from_left = 300;
var from_top = 20;
/*actual math coordinates:
1 -1
| /
|/
1 ----|---- -1
/|
/ |
1 -1
*/
//var get_element = document.getElementById();
//var font_size = parseInt(this.style.fontSize);
var font_size = parseInt($(this[i]).css("font-size"));
var left = parseInt($(this[i]).css("left"));
if (coast_line_array[i][1]) {
} else {
var top = parseInt($(this[i]).css("top"));
z[i] = from_top + (top - (300 * resize_z)) / (300 * resize_z);
//global beacause it's used in other functions later on
var top_new = from_top + Math.round(Math.cos(coast_line_array[i][2]/90*Math.PI) * (300 * resize_z) + (300 * resize_z));
$(this[i]).css("top", top_new);
coast_line_array[i][3] = 1;
}
var x = resize_x * (font_size - 13) / 7;
var y = from_left + (left- (300 * resize_y)) / (300 * resize_y);
if (y >= 0) {
this[i].phi = Math.acos(x/(Math.sqrt(x^2 + y^2)));
} else {
this[i].phi = 2*Math.PI - Math.acos(x/(Math.sqrt(x^2 + y^2)));
i
}
this[i].theta = Math.acos(z[i]/Math.sqrt(x^2 + y^2 + z[i]^2));
var font_size_new = resize_x * Math.round(Math.sin(coast_line_array[i][4]/90*Math.PI) * Math.cos(coast_line_array[i][0]/180*Math.PI) * 7 + 13);
var left_new = from_left + Math.round(Math.sin(coast_line_array[i][5]/90*Math.PI) * Math.sin(coast_line_array[i][0]/180*Math.PI) * (300 * resize_y) + (300 * resize_y));
//coast_line_array[i][6] = coast_line_array[i][7]+1;
if ((coast_line_array[i][0] + 1) > 180) {
coast_line_array[i][0] = -180;
} else {
coast_line_array[i][0] = coast_line_array[i][0] + 0.25;
}
$(this[i]).css("font-size", font_size_new);
$(this[i]).css("left", left_new);
}
}
resize_x = 1;
function magnify_world_pixel(element) {
$("#"+element).animate({
fontSize: resize_x*30+"px"
}, {
duration: 1000
});
}
function shrink_world_pixel(element) {
$("#"+element).animate({
fontSize: resize_x*6+"px"
}, {
duration: 1000
});
}
I'd appreciate any suggestions to optimize my script, maybe there is even a totally different approach on how to go about this.
The whole .js file which stores the array for all the coordinates is available on my page, the file is about 2.9 mb, so you might consider pulling the .zip for local testing:
metaroulette.com/files/31218.zip
metaroulette.com/files/31218.js
P.S. the php I use to create the spans:
<?php
//$arbitrary_characters = array('a','b','c','ddsfsdfsdf','e','f','g','h','isdfsdffd','j','k','l','mfdgcvbcvbs','n','o','p','q','r','s','t','uasdfsdf','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9',);
$arbitrary_characters = array('cat','table','cool','deloitte','askcue','what','more','less','adjective','nice','clinton','mars','jupiter','testversion','beta','hilarious','lolcatz','funny','obama','president','nice','what','misplaced','category','people','religion','global','skyscraper','new york','dubai','helsinki','volcano','iceland','peter','telephone','internet', 'dialer', 'cord', 'movie', 'party', 'chris', 'guitar', 'bentley', 'ford', 'ferrari', 'etc', 'de facto');
for ($i=0; $i<96; $i++) {
$arb_digits = rand (0,45);
$arbitrary_character = $arbitrary_characters[$arb_digits];
//$arbitrary_character = ".";
echo "<span id=\"wp_$i\" style=\"position:fixed; top:0px; left:0px; z-index:1; font-size:20px; cursor:pointer;cursor:hand;\" onmouseover=\"magnify_world_pixel('wp_$i');\" onmouseout=\"shrink_world_pixel('wp_$i');\" onClick=\"set_askcue_bar('', '$arbitrary_character')\">$arbitrary_character</span>\n";
}
?>
You could always use the <canvas> element. It renders much more quickly on browsers that support it.
You'll have to use a workaround for Internet Explorer, though, until version 9 comes out. You can use ExplorerCanvas to emulate canvas support for IE. However, just know that it's very slow--possibly even slower than your algorithm. If IE support is important to you, you could ask users to install Google Chrome Frame if they want a better experience while still using the Internet Explorer browser; but other than that, there isn't much you can do to speed this kind of thing up in IE.
Well I see one easy optimization:
for(i=0;i<this.length; i++) {
You are checking this.length each time this loop iterates. This is expensive, and unless you expect the length to change, unnecessary.
Try:
for(i=0,ii=this.length;i<ii; i++) {
instead.
I don't do much speed-crucial work in JS, so I can only offer some more tentative suggestions.
You can possibly move some of the heavy calculation to a WebWorker, but I'm not sure if your situation would benefit much from it.
Check them out here: https://developer.mozilla.org/En/Using_web_workers
Also, if you are not using any of the Array object's functions for an array, try using an object with integer "keys" instead.
I've never personally done a benchmark on object vs array lookups, but I have friends who insist that objects are much faster in some cases (even though, in theory, they should be comparable).
It's a very quick and easy mod of your code, so why not try it?

Categories

Resources