Large fonts on canvas take long time in Chrome - html

has anyone noticed or found a solution to the problem I've been experiencing? It takes a long time to render large fonts (>100px) in Chrome on the canvas using fillText(). I need to have a much faster frame rate, but once the fonts get big it take like a second to load each frame. In firefox it runs well though...
UPDATE:
Here is the pertinent code that is running in my draw() function which runs every 10 milliseconds on interval. If anything pops out to you, that would be great. I'll try to profiler thing though, thanks.
g.font = Math.floor(zoom) + "px sans-serif";
g.fillStyle = "rgba(233,233,245," + (ZOOM_MAX-zoom*(zoom*0.01))/(ZOOM_MAX) + ")";
for (h=0; h<76; h++)
{
h_offset = 2.75*h*Math.floor(zoom);
// only render if will be visible, because it tends to lag; especially in Chrome
hpos = Math.floor(half_width + std_offset + h_offset);
if (hpos > (-half_width)-h_offset && hpos < WIDTH+h_offset)
{
g.fillText(1950+h, hpos, anchor_y - 0);
}
}
g.font = "600 " + Math.floor(zoom/40) + "px sans-serif";
g.fillStyle = "rgba(233,233,245," + (ZOOM_MAX-zoom*(zoom*0.0001))/(ZOOM_MAX) + ")";
for (h=0; h<76; h++)
{
h_offset = 2.75*h*Math.floor(zoom);
hpos = Math.floor(half_width + std_offset + h_offset);
if (hpos > (-half_width)-h_offset && hpos < WIDTH+h_offset)
{
// see if we should bother showing months (or will it be too small anyways)
if (zoom/40 > 2)
{
// show months
for (i=0; i<12; i++)
{
i_offset = 0.175*i*zoom;
ipos = Math.floor(WIDTH/2 + std_offset + i_offset + h_offset) + 10;
if (ipos > -half_width && ipos < WIDTH)
{
g.fillText(months[i], ipos, anchor_y - 20);
}
}
}
}
}
g.font = "600 " + Math.floor(zoom/350) + "px sans-serif";
g.fillStyle = "rgba(233,233,245," + (ZOOM_MAX-zoom/5)/(ZOOM_MAX*2.25) + ")";
for (h=0; h<76; h++)
{
h_offset = 2.75*h*Math.floor(zoom);
// only render if will be visible, because it tends to lag; especially in Chrome
hpos = Math.floor(half_width + std_offset + h_offset);
if (hpos > (-half_width)-h_offset && hpos < WIDTH+h_offset)
{
// see if we should bother showing months (or will it be too small anyways)
if (zoom/40 > 2)
{
// show months
for (i=0; i<12; i++)
{
i_offset = 0.175*i*zoom;
ipos = Math.floor(WIDTH/2 + std_offset + i_offset + h_offset) + 10;
// see if we should bother showing days (or will it be too small anyways)
if (zoom/350 > 2)
{
// show days
for (j=0; j<31; j++)
{
j_offset = 0.005*j*zoom + zoom*0.005;
jpos = Math.floor(half_width + std_offset + j_offset + i_offset + h_offset);
if (jpos > -half_width && jpos < WIDTH)
{
g.fillText(days[i][j], jpos, anchor_y - 20);
selected_days += 'm: '+i+', d: '+j+' | ';
}
}
}
}
}
}
}

We'd need a lot more information, I'm not convinced that drawing a large font is actually whats causing the performance issues. Drawing such a large font works extremely quickly on my machines for any browser that I've tried.
The first thing you should do is open up the Chrome profiler and then run the code, and see if it is actually the ctx.fillText call that is taking up the time. I imagine its actually something else.
It's possible you are calling something too much, like setting ctx.font over and over unnecessarily. Setting ctx.font on some browsers actually takes significantly longer to do than calls to fillRect! If your font changes in the app you can always cache.
Here's a test back from October: http://jsperf.com/set-font-perf
As you can see, in many versions of Chrome setting the font unnecessarily doubles the time it takes! So make sure you set it as little as possible (with caching, etc).

Related

Create one time popup with materialize css

Is there any way to evade jquery and make notification be shown only one time per browser ?
For example, goes to website, notification pops up and that is it, next time when user comes to site from same browser notification wont be showen to him.
I would mainly try to evade adding jquery just for that, so if anyone knows a way to do this with materializecss or some plain html i would be thankful.
How do you trigger the notification?
You could do a basic localStorage check for example to "detect" if the notification has been displayed or not:
function foo() {
const hasSeenNotification = window.localStorage.getItem('shown');
if (!hasSeenNotification) {
window.localStorage.setItem('shown', true);
// show notification here
// ...
}
}
You need to add cookies.
And then check is it is exists:
if (GetCookieShowMessageDocument('ShowPoPUP'))
{
...
}
Here is a sample:
function GetCookieShowMessageDocument(c_name) {
var i, x, y, ARRcookies = document.cookie.split(";");
for (i = 0; i < ARRcookies.length; i++) {
x = ARRcookies[i].substr(0, ARRcookies[i].indexOf("="));
y = ARRcookies[i].substr(ARRcookies[i].indexOf("=") + 1);
x = x.replace(/^\s+|\s+$/g, "");
if (x == c_name) {
return unescape(y);
}
}
}
function SetCookieShowMessageDocument(name, value, days) {
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
var expires = "; expires=" + date.toGMTString();
}
else var expires = "";
document.cookie = name + "=" + escape(value) + expires + "; path=/";
}

Elements on page hide after scale increase

I use jQuery to change scale of my web page. Here is the code:
<script>
var currFFZoom = 1;
var currIEZoom = 1;
function plus(){
var step = 0.1;
currFFZoom += step;
if (currFFZoom > 10) currFFZoom = 10;
$('body').css('transform','scale(' + currFFZoom + ')');
};
function minus(){
var step = 0.1;
currFFZoom -= step;
$('body').css('transform','scale(' + currFFZoom + ')');
};
</script>
It works fine, but when i increase the scale, some elements moves out of the browser edge and I can't scroll page to them. So, they become unreachable.
PICTURE OF MAILFUNCTION
How I can fix it?
So, I solved the problem. First, I changed
html
{ height: 0%; }
And width needs to be calculated using jquery. This code should be bind to some event handler (button click or something else):
currFFZoom = $('body').css('transform','scale');
$('html').width(currFFZoom*100 + '%');
Worked for me. The whole page is scrollable

localStorage causes browser to crash

localStorageTest stores an incrementally increasing value in localStorage until it hits the localStorage size limits of the browser.
function localStorageTest(key, len) {
var value = window.localStorage.getItem(key);
value = (value == null)?"":value;
var numIters = len - value.length;
while(numIters--) {
value += "1";
window.localStorage.setItem(key, value);
if (window.localStorage.getItem(key) != value) {
console.log("limit reached at " + (value.length-1).toString() + " bytes");
break;
}
}
console.log("stored " + value.length + " bytes");
}
Chrome:
localStorageTest("1", 80000);
<browser crashed>
Firefox:
localStorageTest("1", 50000);
<browser crashed>
Suspecting an increased usage of browser memory, I wrote localStorageIncrementalTest which does the same thing as localStorageTest, but puts idle periods of 5 seconds in between its operations, using a setTimeOut call.
var increment = 5000;
function localStorageIncrementalTest(key, len) {
var value = window.localStorage.getItem(key);
value = (value == null)?"":value;
var numIters = len - value.length;
var limitReached = false;
while(numIters--) {
value += "1";
window.localStorage.setItem(key, value);
if (window.localStorage.getItem(key) != value) {
console.log("limit reached at " + (value.length-1).toString() + " bytes");
limitReached = true;
break;
}
}
console.log("stored " + value.length + " bytes");
if (!limitReached) {
setTimeout(function() {
localStorageIncrementalTest(key, len+increment);
}, 5000);
}
}
Chrome :
localStorageIncrementalTest("1", 30000);
stored 30000 bytes
stored 35000 bytes
...
stored 1800000 bytes
<browser crashed>
Firefox :
localStorageIncrementalTest("1", 30000);
stored 30000 bytes
stored 35000 bytes
...
stored 60000 bytes
<browser crashed>
So localStorageIncrementalTest allows us to store more in localStorage than localStorageTest before crashing the browser.
Can anyone explain the reasons behind the browser crash, and a possible solution to avoid the same ?
I'm using Chrome 28.0.1500.71, and Firefox 26.0
Update :
function getRandomValue(size) {
var value = "";
for (var i = 0; i < size; i++) {
value += Math.ceil(Math.random()*5);
}
return value;
}
function localStorageTest2() {
for (var i = 0; i < 70000; i++) {
window.localStorage.setItem("1", getRandomValue(20000));
}
}
Chrome :
localStorageTest2();
<browser crashed>
Firefox :
localStorageTest2();
<browser crashed>
Testing localStorage2 too caused the browser to crash.
I am not sure why the browsers crash, but it is worth reporting it at crbug.com and bugzilla.mozilla.com.
However, localStorage is not meant for this kind of usage. It is a synchronous API which puts a lot of strain on the browser when it is used, especially when it is used so frequently and will cause the page to perform badly in general.
Also, your test would not yield the right results - setItem(x, y) would throw an exception when you exceed the limit. localStorage.setItem(x, y); if (y == localStorage.getItem(x)) { ... } will never reach the if part, because it will throw an exception beforehand when the limit is reached.
The alternatives are simple variables, server storage or IndexedDB.
Chrome also has the FileSystem API and the Web SQL Databases API, but it is best not to rely on those, because they are not supported in other browsers.

AS3 Switch conditions being ignored

So recently I've been working on a game in Flash, using the program Flash Builder and the Flixel engine. I hit a strange snag though, one of the first things that runs is a parser I made to create the screen for each level based on a 2D array in a .txt file, and for some reason, the final letter in each line is being ignored in the switch statement that decides what that index stands for.
This is the part I'm having issues with (what happens in each condition is irrelevant, I just left it in for context, but they all behave correctly):
var row:int;
var column:int;
var currentColor:int = 0;
for (row = 0; row < m_levelData.length; row++) {
for (column = 0; column < m_levelData[0].length; column++) {
var offset:FlxPoint = new FlxPoint(0, 0);
var thisIndex:String = m_levelData[row][column];
//trace("This Line is " + m_levelData[row]);
trace("Current index is " + thisIndex);
//trace(row + " " + column);
switch(thisIndex) {
case Statics.GRID_BIN:
offset.y = (spaceSize.y / Statics.SPRITE_SCALE - BinFront.SIZE.y / Statics.SPRITE_SCALE) / 2;
offset.x = (spaceSize.x/Statics.SPRITE_SCALE - BinFront.SIZE.x/Statics.SPRITE_SCALE) / 2;
var color:uint;
if (m_colors.length < currentColor) {
color = Statics.COLOR_BLACK;
trace("No color found");
} else {
color = m_colors[currentColor];
currentColor++;
trace("Color is " + color);
}
makeBin(spaceSize.x * column + offset.x + levelOffset.x, spaceSize.y * row + offset.y + levelOffset.y, color);
break;
case Statics.GRID_BUILDER:
offset.y = (spaceSize.y/Statics.SPRITE_SCALE - BuilderMain.SIZE.y/Statics.SPRITE_SCALE)/2
offset.x = (spaceSize.x/Statics.SPRITE_SCALE - BuilderMain.SIZE.x/Statics.SPRITE_SCALE) / 2;
makeBuilder(spaceSize.x * column + offset.x + levelOffset.x, spaceSize.y * row + offset.y + levelOffset.y);
break;
case Statics.GRID_CONVEYOR:
var length:int = 0;
if (column == 0 || m_levelData[row][column - 1] != Statics.GRID_CONVEYOR) {
for (i = column; i < m_levelData[row].length; i++) {
if (m_levelData[row][i] == Statics.GRID_CONVEYOR) {
length++;
} else {
break;
}
}
offset.y = (spaceSize.y/Statics.SPRITE_SCALE - Statics.GRID_SIZE.y/Statics.SPRITE_SCALE)/2
offset.x = (spaceSize.x/Statics.SPRITE_SCALE - Statics.GRID_SIZE.x/Statics.SPRITE_SCALE) / 2;
makeConveyor(spaceSize.x * column + offset.x + levelOffset.x, spaceSize.y * row + offset.y + levelOffset.y, length);
}
break;
default:
trace("Nothing at this index, it was " + thisIndex);
}
Statics.GRID_BIN, Statics.GRID_CONVEYOR, and Statics.GRID_BUILDER are all constant strings ("a", "b", and "c" respectively), and I know that this all should be working because it was working before I switched to the parser. Now, your immediate response to that is that the problem is my parser, but I have suspicions that something is a little screwy other than that. Before the switch, I print the value of thisIndex..(trace("Current index is " + thisIndex);), and whenever it is the last letter in a line that was parsed (using split(","), even when it matches one of the switch conditions, the default condition is run and nothing happens.
Has anyone else seen something like this, or am I just making a really dumb mistake?
When you parse and compare strings loaded from a .txt you have to deal with various "hidden" characters. At line endings you might run into CR (\r) LF(\n) or both or the other way round.
It can get a bit fiddly to find out which applies to your system or .txt.
In the end you could probably make it work using String.replace() .
This has been discussed for example here: How do I detect and remove "\n" from given string in action script?
Apart from this it's hard to judge whether there's something wrong with your parser.
Your use of the switch{}-statement itself looks fine and should be working.
If you need to debug the code try feeding it with a hardcoded const :String instead of the .txt-file. Then set a breakpoint (in Flash Builder by double-clicking the line number next to the switch-statemen) and step through it line by line.

scrolling menu doesn't fully work

I know AS2 is old but I don't know as3 well enough yet and i haven't touched AS2 in years as it is.
Here is my issue: I have two horizontal thumbnail scrolling menus (one on the left and one on the right). The one on the right works perfectly.
rightEdge = gmask._x;
maskWidth = gmask._width;
sliderWidth = gslider._width;
ratio = maskWidth / (sliderWidth - maskWidth - 55);
targX = gslider._x;
gslider.onEnterFrame = function() {
this._x += (targX - this._x) / 5;
}
gslider.onMouseMove = function() {
if (gmask.hitTest(_root._xmouse, _root._ymouse, false)) {
targX = rightEdge - (gmask._xmouse / ratio);
updateAfterEvent();
}
}
I figured I could copy the code of the right one and just change the appropriate instances and vars but of course it's not that easy. I've spent hours trying different combinations but now i'm back at square one.
leftEdge = wmask._x + wmask._width;
maskWidth1 = wmask._width;
sliderWidth1 = wslider._width;
ratio1 = maskWidth1 / (sliderWidth1 - maskWidth1 - 55);
targX1 = wslider._x;
wslider.onEnterFrame = function() {
this._x += (targX1 - this._x) / 5;
}
wslider.onMouseMove = function() {
if (wmask.hitTest(_root._xmouse,_root._ymouse,false)) {
targX1 = leftEdge - (wmask._xmouse / ratio1);
updateAfterEvent();
}
}
i finally got it. i know it may not be clean but this code works perfectly for the left side scroller.
leftEdge=wmask._x + wmask._width;
maskWidth1=wmask._width;
sliderWidth1=wslider._width;
ratio1=maskWidth1/(sliderWidth1-maskWidth1+15)
targX1=wslider._x;
wslider.onEnterFrame=function(){
this._x+=(targX1-this._x)/5;
}
wslider.onMouseMove=function(){
if(wmask.hitTest(_root._xmouse,_root._ymouse,false)){
targX1=(wmask._width*2+100)-(wmask._xmouse/ratio1);
updateAfterEvent();
}
}
The only thing I see that might not be right is this
leftEdge = wmask._x + wmask._width;
Should be ...
leftEdge = wmask._x;
If you are wanting to use an exact duplicate of the original.