IE9 raphael animation issue - javascript

I have problem with display of correct javascript in IE9. Other browsers (Firefox, Opera, Chrome, Safari) work well, but animation in IE is not fluent. For example see this line which can be dragged from left to right (link at the end of the post).
javascript code:
var w = 1250;
var h = 650;
var drawing = Raphael("obrazek",w,h);
var Ax = 50
var Ay = 50
var Ey = 500
var w = 1250;
var h = 650;
var drawing = Raphael("obrazek",w,h);
var Ax = 50
var function onDragMove(dx,dz) {
this.onDragUpdate(dx - (this.deltax || 0), dz - (this.deltaz || 0));
this.deltax = dx;
this.deltaz = dz;
}
function onDragStart() { this.deltax = this.deltaz = 0; }
function onDragStop() { this.onDragStop(); }
// line 1
var Ax
var line = drawing.path([["M",Ax,Ay],["L",Ax,Ey]]).attr({"stroke-width":3})
line.drag(onDragMove,onDragStart)
line.attr({"cursor":"move"})
line.onDragUpdate = function(dx,dz) {
Ax += dx
line.attr({"path":[["M",Ax,Ay],["L",Ax,Ey]]})
}
and corresponding HTML:
<html>
<head>
<script src="raphael.js"></script>
</head>
<body>
<div id="obrazek">
<script src="ietest.js"></script>
</div>
</body>
</html>
or see the problem in IE9 here and compare it with Chrome:
http://mech.fsv.cvut.cz/~stransky/ietest/ietest.html
Thank in advance for any help.

Your page is missing doctype, so it is rendered in quirks mode. IE9 uses VML instead of SVG in quirks mode, which probably results in slower rendering. Just add this on the first line of your html:
<!DOCTYPE html>
However, your code has some other problems:
Missing semicolons. There is a good explanation of how it may be dangerous.
Variable re-declarations and re-definitions.
When handling rapidly repeating events like mousemove or scroll, it is reasonable to use throttling to avoid redrawing/repainting glitches and performance problems. You can read more about it here. Include the plugin from that site and replace your drag binding with the following:
line.drag($.throttle(30, onDragMove), onDragStart);
In fact, even doing this without specifying the doctype can greatly improve the rendering performance, but there's no reason not to specify it altogether.

Related

Snap.svg setStops causing error

Below is an interactive example attempting to implement setStops to a radial gradient. When the 'setStops' button is clicked an error message occurs: "setStops is not a function"
Am I using this correctly?
<!DOCTYPE HTML>
<html>
<head>
<title>Snap.svg setStops</title>
<script type="text/javascript" src="http://svgDiscovery.com/_SNP/snap.svg-min.js"></script>
</head>
<body>
<svg id=mySVG width=400 height=200></svg>
<br><button onClick=stopsSet()>setStops</button>
<script>
var SNPsvg = Snap("#mySVG");
var radialGradient = SNPsvg.gradient("r(.5,.5,.5,.5)#000-#f00-#fff-green");
var circle = SNPsvg.circle(200,100,50).attr({fill: radialGradient});
//---button---
function stopsSet()
{
radialGradient.setStops("#fff-#000-#f00-#fc0");
circle.attr({fill: radialGradient});
}
</script>
</body>
</html>
Seems to be a bug in snap.svg. The code for linearGradient looks like this:
function gradientLinear(defs, x1, y1, x2, y2) {
var el = Snap._.make("linearGradient", defs);
el.stops = Gstops;
el.addStop = GaddStop;
el.getBBox = GgetBBox;
el.setStops = GsetStops;
but for radialGradients it's this:
function gradientRadial(defs, cx, cy, r, fx, fy) {
var el = Snap._.make("radialGradient", defs);
el.stops = Gstops;
el.addStop = GaddStop;
el.getBBox = GgetBBox;
if (cx != null) {
The code to add the setStops function is missing.
I've created a pull request in Snap.svg to fix this so hopefully one of the maintainers will merge it and include it in a subsequent release. In the meantime you could always make the same change to a local copy of Snap.
I think that's because setStops is only available for a linearGradient and not a radialGradient (not sure if that's by design or not).
If you try gradient("L(0, 0, 100, 100)#000-#f00:25-#fff") I think the error will go away. Naturally that's probably not what you want, but I'm just explaining why I think the error is there.
One thing you can always do with Snap if you get stuck, is use a bit of your own markup if it's not supported direct, and add it in. Eg Snap.parse('Some SVG Markup')
var svgMarkup = Snap.parse('<defs><radialGradient id="exampleGradient"><stop offset="10%" stop-color="gold"/><stop offset="95%" stop-color="green"/></radialGradient></defs>');
SNPsvg.append( svgMarkup );
var radialGradient = SNPsvg.select('#exampleGradient')
var circle = SNPsvg.circle(200,100,50).attr({fill: radialGradient});
Example jsfiddle

multiple canvas layers memory leak

I am trying to add multiple canvas layers into the DOM and am finding that while adding many layers is not an issue if I use .fillText to add content to the layers I suddenly get a lot of memory being used, often to the point that firefox crashes and brings down the entire machine. If anyone out there can see that I am doing something inthe wrong way I would appreciate any advice you might give - thanks tb!
Code below - this is a simplified version of the real thing but demonstrates the issue - if I increase the number of times the loops run I very quickly encounter problems. If I comment out the .fillText code however, I seem to be able to add many layers but as soon as I try and .fillText them my memory use goes through the roof....
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>.</title>
</head>
<body>
<div id="canvasCont"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
$(function () {
var id = 1;
var top = 100;
for ( i = 0; i < 5; i++ ) {
var left = 100;
for ( a = 0; a < 5; a++ ) {
$('#canvasCont').append(
"<canvas width='1300px' height='1300px' style='position: absolute; border: 1px solid black;' id='canvas_" + id + "'>" +
"Your browser does not support canvas" +
"</canvas>"
);
var context = document.getElementById( 'canvas_' + id ).getContext( '2d' );
context.font = "10px Verdana";
context.fillStyle = "red";
context.fillText(
id,
left,
top
);
left += 55;
id++;
}
top = top + 55;
}
}
});
</script>
</body>
</html>
FillText uses A LOT of memory to display, since it's vector-based drawing.
(I would suggest using images, if you intend on displaying always the same text)
I'll also add that you're creating mutliple canvases with quite high resolution, which can be tough for your machine.
In the end I found this library.....paperJS which solved the performance issues with multiple live data points on multiple canvas layers. Highly recommended.

Normalize deviceorientation data

I'm playing with deviceorientation in JavaScript and I noticed some differences between my Ipad (iOS 6.1) and my Nexus7 (Android 4.2.2).
This code does not print the same data with the Ipad and the Nexus7.
<html>
<head/>
<body>
<button id="calibrate">Calibrate</button>
<button id="stop">Stop</button>
<button id="play">Play</button>
<div id="log"><p></p></div>
<script>
var log = document.getElementById('log');
var calibrate = false;
var calibrateG = 0, calibrateB = 0, calibrateA = 0;
var deviceorientation = function(e) {
if (calibrate) {
calibrateG = e.gamma;
calibrateB = e.beta;
calibrateA = e.alpha;
calibrate = false;
}
var gamma = parseInt(e.gamma - calibrateG);
var beta = parseInt(e.beta - calibrateB);
var alpha = parseInt(e.alpha - calibrateA);
var p = document.createElement('p');
p.innerHTML = gamma + ' ' + beta + ' ' + alpha;
log.insertBefore(p, log.firstChild);
}
document.getElementById('stop').onclick = function() {
window.removeEventListener('deviceorientation', deviceorientation);
};
document.getElementById('play').onclick = function() {
window.addEventListener('deviceorientation', deviceorientation);
};
document.getElementById('calibrate').onclick = function() {
calibrate = true;
};
window.addEventListener('deviceorientation', deviceorientation);
</script>
</body>
</html>
At start Android print 0 0 270 and iOS 0 0 0.
Then when I move both in the same way, they don't print the same values.
Can someone explain why, and if there are a way to normalize the data.
UPDATE #1
I already try some calibrations and I care about landscape/portrait.
To reproduce, you can take the code above, put ipad and nexus7 in portrait in front of you.
Calibrate the value of both (first button).
Then take the right corner of the tablet and rotate it until the tablet reaches 90 degrees.
The tablet should be on the left side.
On Android the gamma goes from 0 to -80 and then jump to 270.
On IOS the gamma goes from 0 to -180 without any jump.
Full Tilt JS normalizes the data values between Android and iOS deviceorientation implementations. It also ensures deviceorientation data remains consistent whenever the user rotates their screen.
This article provides a summary of some of the techniques used in the Full Tilt JS library.
Disclaimer: I am the author of both the article and library above. Please give it a try and report any issues directly on the Github project.
If you need all three for an application or game you could prompt the user to ~"hold there device up straight" and record the initial values, then get offsets (deltas) of those values. You could even save that initial calibration to localStorage so it doesn't need to be repeated.
If all you need is landscape or portrait just compare window.innerWidth with window.innerHeight or something equally as trivial.

javascript / jquery / processing.js - how to most effectively create and animate canvas elements

I'm trying to build an application which, based on various user interactions, allows for various ellipse based visuals to be added to the stage and then animated very simply. I've currently got a basic demo set up where javascript / jquery communicates with processing.js, but it just seems like really inefficient code, because processing relies on running a continuous loop in order to draw to the screen. I'm wondering, one, if the way I'm doing it will be effective on a larger scale, and two, if there's a better technology or method to use. I come from a flash background where nothing on screen is changed or drawn/animated unless a function is triggered telling it to animate, which seems sensible. Anyway, here's my code:
HTML / JS:
<!DOCTYPE HTML>
<html lang="en">
<head>
<title>Processing</title>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript"></script>
<script src="js/processing-1.3.6.min.js"></script>
<script src="processing/Tween.lib"></script>
</head>
<body>
<canvas id="circles" data-processing-sources="js/drawCircles.js"></canvas>
<div id="clicker">Click</div>
<script>
window.Processing.data = {};
var dataRef = window.Processing.data;
var animInterval;
dataRef.circleArray = new Array();
$('#clicker').click(function(){
var circle = {};
circle.radius = 50;
dataRef.circleArray.push(circle)
var from = {property: 50};
var to = {property: 75};
jQuery(from).animate(to, {
duration: 300,
step: function() {
for (var i in dataRef.circleArray){
circle.radius = this.property;
}
}
});
})
</script>
</body>
</html>
PROCESSING.JS
// Global variables
float radius = 1.0;
int X, Y;
int nX, nY;
int delay = 16;
// Setup the Processing Canvas
void setup(){
// Fill canvas grey
background( 100 );
size( 200, 200 );
strokeWeight( 10 );
frameRate( 15 );
X = width / 2;
Y = width / 2;
nX = X;
nY = Y;
}
// Main draw loop
void draw(){
var dataRef = window.Processing.data;
for (var i in window.Processing.data.circleArray){
radius = dataRef.circleArray[i].radius;
// Set fill-color to blue
fill( 0, 121, 184 );
// Set stroke-color white
stroke(255);
// Draw circle
ellipse( X+(i*10), Y, radius, radius );
}
}
If you want to control when Processing.js draws to the canvas, you have two options. In both cases, the first thing you'll want to do is get access to the Processing instance:
var p = Processing.instances[0];
Now you can make all the Processing API calls you want from JavaScript. You could call noLoop() in your sketch's setup() function, and then inside your jQuery animation loop you could call p.redraw(), which will animate one frame.
In Processing.js we attach all of the functions to the Processing instance. So another option is creating your own function in the sketch, and call it with:
var p = Processing.instances[0];
p.drawEllipses(radius);
You could even pass the data to it in the function parameters, removing the need for windows.Processing.data.
For what you want to do, you might prefer using another library such as paperjs http://paperjs.org/

Can Javascript access native image size?

I'm writing a jQuery function where I'd like to access both the native size of an image, and the size specified for it on the page. I'd like to set a variable for each.
How is that done?
Modern browsers
When I wrote this answer back in 2009 the browser landscape was much different. Nowadays any reasonably modern browser supports Pim Jager's suggestion using img.naturalWidth and img.naturalHeight. Have a look at his answer.
Legacy answer compatible with super old browsers
// find the element
var img = $('#imageid');
/*
* create an offscreen image that isn't scaled
* but contains the same image.
* Because it's cached it should be instantly here.
*/
var theImage = new Image();
theImage.src = img.attr("src");
// you should check here if the image has finished loading
// this can be done with theImage.complete
alert("Width: " + theImage.width);
alert("Height: " + theImage.height);
This should work:
var img = $('#imageid')[0]; //same as document.getElementById('imageid');
var width = img.naturalWidth;
var height = img.naturalHeight;
The naturalWidth and naturalHeight return the size of the image response, not the display size.
According to Josh' comment this is not supported cross browser, this might be correct, I tested this in FF3
EDIT - new idea... see http://jsbin.com/uzoza
var fixedW = $("#imageToTest").width();
$("#imageToTest").removeAttr("width");
var realW = $("#imageToTest").width();
$("#imageToTest").attr("width", fixedW);
ORIGINAL ANSWER
see How to get image size (height & width) using JavaScript?
var img = $('#imageid');
var width = img.clientWidth;
var height = img.clientHeight;
I'm adding a way to accomplish this and be sure that there is support for all browsers. Pretty much all browsers support naturalWidth and naturalHeight except for Internet Explorer 8 and below.
Since IE 8 and below would return the size of the visible image and not the natural size when using trying to retrieve size values, a small workaround is needed to get the full size dimensions which came from an example by Jack Moore.
function naturalSize(imageid) {
imageid = (imageid.src ? imageid : document.getElementById(imageid));
if (document.documentMode < 9) {
var img = new Image();
img.src = imageid.src;
return {width: img.width,height: img.height};
}
return {width: imageid.naturalWidth,height: imageid.naturalHeight};
}
I set this up to support passing either the image ID value or the name of the ID.
Usage:
<img src="http://c64.exitof99.com/ims/VicGauntlet2013.png" id="some_img" width="400">
naturalSize("some_img");
// Returns: Object {width: 731, height: 387}
Or
<img src="http://c64.exitof99.com/ims/VicGauntlet2013.png"
onclick="aaa=naturalSize(this);alert('Size: '+aaa.width+'x'+aaa.height);">
// Displays: Size: 731x387
Be sure to make sure the image is loaded before calling this, whether by using onload or triggering upon adding it to the DOM.
Tested with:
Windows XP - Firefox 1.5, IE 8
Windows 7 - IE 9, Chrome 56
Android 6.0.1 - Chrome 50
Android 5.1.1 - Opera Mini 7.6, Dolphin 10.3
Full code example:
<!DOCTYPE html>
<html dir="LTR" lang="en"><head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title></title>
</head>
<body class="assignments" onload="run_test();">
<img src="http://c64.exitof99.com/ims/VicGauntlet2013.png" id="some_img" width="400">
<textarea id="outbox"></textarea>
<script type="text/javascript">
function naturalSize(imageid) {
imageid = (imageid.src ? imageid : document.getElementById(imageid));
if (document.documentMode < 9) {
var img = new Image();
img.src = imageid.src;
return {width: img.width,height: img.height};
}
return {width: imageid.naturalWidth,height: imageid.naturalHeight};
}
function run_test() {
var a = naturalSize("some_img");
document.getElementById("outbox").innerHTML = "Size: "+a.width+"x"+a.height;
}
</script>
</body></html>
My solution would be to write a web service that gets/downloads the image, and then gets its resolution and returns it as {width: x,height:y}. Then you call it with $.ajax or equivalent to retrieve this.
Alternatively you could add the image to a hidden div using
// e.g. don't set width and height
$("#hiddendiv").html("<img src='theurl'>");
And then get the div's width/height though I haven't tried it.

Categories