PDA

View Full Version : Simple BoxComponent which encapsulates a Raphael canvas



Animal
9 Sep 2009, 6:56 AM
The latest cross-browser graphics library: http://ajaxian.com/archives/raphael-10-rc-get-your-graphics-on

Simply use this BoxComponent subclass. It exports all the methods of a Raphael canvas, and will participate in a layout:



Ext.ux.Raphael = Ext.extend(Ext.BoxComponent, {
onRender: function(ct) {
var p = this.paper = Raphael(ct.dom), v;
this.el = Ext.get(p.canvas);

// Export all methods from this paper object which will not override our native
// methods like setSize etc.
for (var prop in p) {
v = p[prop];
if (!this[prop] && Object.prototype.hasOwnProperty.call(p, prop) && Ext.isFunction(v)) {
this[prop] = v.createDelegate(p);
}
}

// We always cache our size
this.cacheSizes = true;
},

getWidth: function() {
return this.lastSize.width;
},

getHeight: function() {
return this.lastSize.height;
},

onResize: function(w, h) {
this.paper.setSize(w, h);
}
});

moegal
9 Sep 2009, 7:23 AM
Raphael is very nice. Does it depend on any other libraries like jquery?

Are there any extjs raphael examples?

Marty

mystix
9 Sep 2009, 7:46 AM
neat :)


hey @nige, what's the purpose of this?


Object.prototype.hasOwnProperty.call(p, prop)


why not just


p.hasOwnProperty(prop)

(scratched. see http://www.extjs.com/forum/showthread.php?p=384817#post384817)


[edit]
additionally, would it be more efficient to simply remove p.create before commencing, then re-add it when we're done instead of performing the check once per iteration?


Ext.ux.Raphael = Ext.extend(Ext.BoxComponent, {
onRender: function(ct) {
var p = this.paper = Raphael(ct.dom),
create = p.create,
prop, v;

this.el = Ext.get(p.canvas);

// Export all methods from this paper object which will
// not override our native methods like setSize etc.
delete p.create;
for (prop in p) {
v = p[prop];

if (!this[prop] && Object.prototype.hasOwnProperty.call(p, prop) && Ext.isFunction(v)) {
this[prop] = v.createDelegate(p);
}
}
p.create = create;
},

onResize: function(w, h) {
this.el.dom.setAttribute('width', w);
this.el.dom.setAttribute('height', h);
}
});

(disclaimer: i've not taken a look at the source for Raphael yet, but i'm assuming there are tons of methods in there.)

Animal
9 Sep 2009, 9:39 AM
The returned object is an augmented DOM object. I found problems calling hasOwnProperty directly on it using the normal member notation, so I used the methods directly from the Object prorotype.

Good idea about knocking create out before copying the methods up into the Ext Component.

Animal
9 Sep 2009, 9:42 AM
Raphael is very nice. Does it depend on any other libraries like jquery?

Are there any extjs raphael examples?

Marty


If you can find any Raphael examples anwhere, then just use the code on the instance of Ext.ux.Raphael.

mystix
9 Sep 2009, 9:44 AM
The returned object is an augmented DOM object. I found problems calling hasOwnProperty directly on it using the normal member notation, so I used the methods directly from the Object prorotype.

Good idea about knocking create out before copying the methods up into the Ext Component.

i see. updated the code block above to match.
thanks for the info :)

Animal
19 Sep 2009, 5:17 AM
Here's an example which will drop into examples/anywhere.

The actual graphics drawing code is culled directly from http://dev.opera.com/articles/view/raphael-a-javascript-api-for-svg/ which shows that the Ext Raphael object simply exports all Raphael's functionality while conforming to Ext's requirements for a BoxComponent so that it will participate in Ext layouts.



<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Raphel</title>
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all-debug.js"></script>
<script type="text/javascript" src="raphael.js"></script>
<script type="text/javascript">
Ext.ux.Raphael = Ext.extend(Ext.BoxComponent, {
onRender: function(ct) {
var p = this.paper = Raphael(ct.dom), v;
this.el = Ext.get(p.canvas);

// Export all methods from this paper object which will not override our native
// methods like setSize etc.
for (var prop in p) {
v = p[prop];
if (!this[prop] && Object.prototype.hasOwnProperty.call(p, prop) && Ext.isFunction(v)) {
this[prop] = v.createDelegate(p);
}
}

// We always cache our size
this.cacheSizes = true;
},

getWidth: function() {
return this.lastSize.width;
},

getHeight: function() {
return this.lastSize.height;
},

onResize: function(w, h) {
this.paper.setSize(w, h);
}
});
Ext.reg('raphael', Ext.ux.Raphael);

Ext.onReady(function() {
var r = new Ext.ux.Raphael(),
sectorsCount = 12,
color = "#000",
width = 15,
r1 = 35,
r2 = 60,
cx = 200,
cy = 200,
sectors = [],
opacity = [],
beta = 2 * Math.PI / sectorsCount,
pathParams = {stroke: color, "stroke-width": width, "stroke-linecap": "round"};

new Ext.Window({
x: 20, y: 20,
title: 'Raphael Test',
height: 432,
width: 414,
layout: 'fit',
items: r
}).show();

// Keep spinner in centre
r.on('resize', function () {
var ncx = r.getWidth() / 2;
var ncy = r.getHeight() / 2;
for (var i = 0; i < sectorsCount; i++) {
sectors[i].translate(ncx - cx, ncy - cy);
}
cx = ncx;
cy = ncy;
});

for (var i = 0; i < sectorsCount; i++) {
var alpha = beta * i - Math.PI / 2,
cos = Math.cos(alpha),
sin = Math.sin(alpha);
opacity[i] = 1 / sectorsCount * i;
sectors[i] = r.path(pathParams)//.attr("stroke", Raphael.getColor())
.moveTo(cx + r1 * cos, cy + r1 * sin)
.lineTo(cx + r2 * cos, cy + r2 * sin);
}

(function ticker() {
opacity.unshift(opacity.pop());
for (var i = 0; i < sectorsCount; i++) {
sectors[i].attr("opacity", opacity[i]);
}
r.safari();
setTimeout(ticker, 1000 / sectorsCount);
})();

function createChart(paper, cx, cy, r, values, labels, stroke) {
var rad = Math.PI / 180;
function sector(cx, cy, r, startAngle, endAngle, params) {
var x1 = cx + r * Math.cos(-startAngle * rad),
x2 = cx + r * Math.cos(-endAngle * rad),
y1 = cy + r * Math.sin(-startAngle * rad),
y2 = cy + r * Math.sin(-endAngle * rad);
return paper.path(params, ["M", cx, cy, " L", x1, y1, " A", r, r, 0, +(endAngle - startAngle > 180), 0, x2, y2, "z"]);
}
var angle = 0,
total = 0,
start = 0,
process = function (j) {
var value = values[j],
angleplus = 360 * value / total,
popangle = angle + (angleplus / 2),
color = "hsb(" + start + ", 1, .5)",
ms = 500,
delta = 30,
bcolor = "hsb(" + start + ", 1, 1)",
p = sector(cx, cy, r, angle, angle + angleplus, {gradient: "90-" + bcolor + "-" + color, stroke: stroke, "stroke-width": 3}),
txt = paper.text(cx + (r + delta + 35) * Math.cos(-popangle * rad), cy + (r + delta) * Math.sin(-popangle * rad), labels[j]).attr({fill: bcolor, stroke: "none", opacity: 0, "font-family": 'Fontin-Sans, Arial', "font-size": "20px"});
p.mouseover(function () {
p.animate({scale: [1.1, 1.1, cx, cy]}, ms, "elastic");
txt.animate({opacity: 1}, ms, "elastic");
}).mouseout(function () {
p.animate({scale: [1, 1, cx, cy]}, ms, "elastic");
txt.animate({opacity: 0}, ms);
});
angle += angleplus;
start += .1;
};
for (var i = 0, ii = values.length; i < ii; i++) {
total += values[i];
}
for (var i = 0; i < ii; i++) {
process(i);
}
};

var chart = new Ext.ux.Raphael(),
values = [],
labels = [];
Ext.getBody().select("tr").each(function (r) {
values.push(Ext.DomQuery.selectNumber("td", r.dom));
labels.push(Ext.DomQuery.selectValue("th", r.dom));
});
new Ext.Window({
title: 'Pie Chart',
height: 500,
width: 500,
layout: 'fit',
items: chart
}).show();
var w = chart.getWidth();
var h = chart.getHeight();
var rad = (Math.min(w, h) - 200) / 2
createChart(chart, w/2, h/2, rad, values, labels, "#fff");

});
</script>
</head>
<body>
<table style="display:none">
<tr>
<th>Ruby</th>
<td>40%</td>
</tr>
<tr>
<th>JavaScript</th>
<td>26%</td>
</tr>
<tr>
<th>Shell</th>
<td>5%</td>
</tr>
<tr>
<th>Python</th>
<td>5%</td>
</tr>
<tr>
<th>PHP</th>
<td>4%</td>
</tr>
<tr>
<th>C</th>
<td>4%</td>
</tr>
<tr>
<th>Perl</th>
<td>3%</td>
</tr>
<tr>
<th>C++</th>
<td>2%</td>
</tr>
<tr>
<th>Java</th>
<td>2%</td>
</tr>
<tr>
<th>Objective-C</th>
<td>2%</td>
</tr>
</table>
</body>
</html>

moegal
19 Sep 2009, 8:58 AM
thanks!that is easy.

Marty

shoopnl
13 Dec 2009, 12:09 PM
I tried to execute the last example but it did not work.
Anyway,
I was wondering if it is possible to use the rect function from raphael with this boxcomponent?


// regular rectangle
var c = paper.rect(10, 10, 50, 50);but how do I do that?

I tried something like:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Raphel</title>
<link rel="stylesheet" type="text/css" href="ext-3.0.0/resources/css/ext-all.css" />
<script type="text/javascript" src="ext-3.0.0/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="ext-3.0.0/ext-all-debug.js"></script>
<script type="text/javascript" src="raphael.js"></script>
<script type="text/javascript">

Ext.ux.Raphael = Ext.extend(Ext.BoxComponent, {
onRender: function(ct) {
var p = this.paper = Raphael(ct.dom),
create = p.create,
prop, v;

this.el = Ext.get(p.canvas);

// Export all methods from this paper object which will
// not override our native methods like setSize etc.
delete p.create;
for (prop in p) {
v = p[prop];

if (!this[prop] && Object.prototype.hasOwnProperty.call(p, prop) && Ext.isFunction(v)) {
this[prop] = v.createDelegate(p);
}
}
p.create = create;
},

onResize: function(w, h) {
this.el.dom.setAttribute('width', w);
this.el.dom.setAttribute('height', h);
}
});
Ext.reg('raphael', Ext.ux.Raphael);

Ext.onReady(function() {

var rect_test = new Ext.ux.Raphael(),
rect(10, 10, 50, 50);

new Ext.Window({
x: 20, y: 20,
title: 'Raphael Test',
height: 432,
width: 414,
layout: 'fit',
items: rect_test
}).show();
});
</script>
</head>
<body>

</body>
</html>But it does not work, any hints?

Animal
15 Dec 2009, 12:42 AM
The browser doesn't give you any hints then?

pcr
25 Jun 2010, 2:02 AM
When I try the example I get this error in firebug console:

r.path is not a function
chrome://firebug/content/blank.gif sectors[i] = r.path(pathParams)//.attr("stroke", Raphael.getColor())

its on line 81

VinylFox
25 Jun 2010, 3:21 AM
Have you included the Raphael library?

http://www.raphaeljs.com

pcr
25 Jun 2010, 4:36 AM
So far I not seen any errors in loading files. Raphael is loaded. What version do I have to load??. I loaded 1.4.3 and tried RC1.

pcr
25 Jun 2010, 4:44 AM
On the homepage of Raphael I read about changes in 'r.path'. Maybe this has something to do with the error??

tomalex0
28 Jul 2010, 3:56 AM
Hi i have tried to make the chart working using the code mentioned in this thread, but got error like r.path is undefined. Current version of Raphael is 1.4.7 . Anyone solved the issue?

Animal
28 Jul 2010, 4:52 AM
Raphael seems to have changed a lot. path() takes a string now.

But try this:




Ext.ux.Raphael = Ext.extend(Ext.BoxComponent, {
onRender: function(ct) {
var p = this.paper = Raphael(ct.dom), v;
this.el = Ext.get(p.canvas);

// Export all methods from this paper object which will not override our native
// methods like setSize etc.
for (var prop in p) {
v = p[prop];
if (!this[prop] && Ext.isFunction(v)) {
this[prop] = v.createDelegate(p);
}
}

// We always cache our size
this.cacheSizes = true;
},

getWidth: function() {
return this.lastSize.width;
},

getHeight: function() {
return this.lastSize.height;
},

onResize: function(w, h) {
this.paper.setSize(w, h);
}
});
Ext.reg('raphael', Ext.ux.Raphael);

Animal
28 Jul 2010, 4:57 AM
Yes, it's changed a lot.

New example page:



<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Raphel</title>
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base-debug.js"></script>
<script type="text/javascript" src="../../ext-all-debug.js"></script>
<script type="text/javascript" src="raphael-debug.js"></script>
<script type="text/javascript">
Ext.ux.Raphael = Ext.extend(Ext.BoxComponent, {
onRender: function(ct) {
var p = this.paper = Raphael(ct.dom), v;
this.el = Ext.get(p.canvas);

// Export all methods from this paper object which will not override our native
// methods like setSize etc.
for (var prop in p) {
v = p[prop];
if (!this[prop] && Ext.isFunction(v)) {
this[prop] = v.createDelegate(p);
}
}

// We always cache our size
this.cacheSizes = true;
},

getWidth: function() {
return this.lastSize.width;
},

getHeight: function() {
return this.lastSize.height;
},

onResize: function(w, h) {
this.paper.setSize(w, h);
}
});
Ext.reg('raphael', Ext.ux.Raphael);

Ext.onReady(function() {
var r = new Ext.ux.Raphael(),
sectorsCount = 12,
color = "#000",
width = 15,
r1 = 35,
r2 = 60,
cx = 200,
cy = 200,
sectors = [],
opacity = [],
beta = 2 * Math.PI / sectorsCount,
pathParams = {stroke: color, "stroke-width": width, "stroke-linecap": "round"};

new Ext.Window({
x: 20, y: 20,
title: 'Raphael Test',
height: 432,
width: 414,
layout: 'fit',
items: r
}).show();

// Keep spinner in centre
r.on('resize', function () {
var ncx = r.getWidth() / 2;
var ncy = r.getHeight() / 2;
for (var i = 0; i < sectorsCount; i++) {
sectors[i].translate(ncx - cx, ncy - cy);
}
cx = ncx;
cy = ncy;
});

for (var i = 0; i < sectorsCount; i++) {
var alpha = beta * i - Math.PI / 2,
cos = Math.cos(alpha),
sin = Math.sin(alpha);
opacity[i] = 1 / sectorsCount * i;
sectors[i] = r.path([["M", cx + r1 * cos, cy + r1 * sin], ["L", cx + r2 * cos, cy + r2 * sin]]).attr(pathParams);
if (color == "rainbow") {
sectors[i].attr("stroke", Raphael.getColor());
}
}

(function ticker() {
opacity.unshift(opacity.pop());
for (var i = 0; i < sectorsCount; i++) {
sectors[i].attr("opacity", opacity[i]);
}
r.safari();
setTimeout(ticker, 1000 / sectorsCount);
})();

function createChart(paper, cx, cy, r, values, labels, stroke) {
var rad = Math.PI / 180;
function sector(cx, cy, r, startAngle, endAngle, params) {
var x1 = cx + r * Math.cos(-startAngle * rad),
x2 = cx + r * Math.cos(-endAngle * rad),
y1 = cy + r * Math.sin(-startAngle * rad),
y2 = cy + r * Math.sin(-endAngle * rad);
return paper.path(["M", cx, cy, "L", x1, y1, "A", r, r, 0, +(endAngle - startAngle > 180), 0, x2, y2, "z"]).attr(params);
}
var angle = 0,
total = 0,
start = 0,
process = function (j) {
var value = values[j],
angleplus = 360 * value / total,
popangle = angle + (angleplus / 2),
color = "hsb(" + start + ", 1, .5)",
ms = 500,
delta = 30,
bcolor = "hsb(" + start + ", 1, 1)",
p = sector(cx, cy, r, angle, angle + angleplus, {gradient: "90-" + bcolor + "-" + color, stroke: stroke, "stroke-width": 3}),
txt = paper.text(cx + (r + delta + 35) * Math.cos(-popangle * rad), cy + (r + delta) * Math.sin(-popangle * rad), labels[j]).attr({fill: bcolor, stroke: "none", opacity: 0, "font-family": 'Fontin-Sans, Arial', "font-size": "20px"});
p.mouseover(function () {
p.animate({scale: [1.1, 1.1, cx, cy]}, ms, "elastic");
txt.animate({opacity: 1}, ms, "elastic");
}).mouseout(function () {
p.animate({scale: [1, 1, cx, cy]}, ms, "elastic");
txt.animate({opacity: 0}, ms);
});
angle += angleplus;
start += .1;
};
for (var i = 0, ii = values.length; i < ii; i++) {
total += values[i];
}
for (var i = 0; i < ii; i++) {
process(i);
}
};

var chart = new Ext.ux.Raphael(),
values = [],
labels = [];
Ext.getBody().select("tr").each(function (r) {
values.push(Ext.DomQuery.selectNumber("td", r.dom));
labels.push(Ext.DomQuery.selectValue("th", r.dom));
});
new Ext.Window({
title: 'Pie Chart',
height: 500,
width: 500,
layout: 'fit',
items: chart
}).show();
var w = chart.getWidth();
var h = chart.getHeight();
var rad = (Math.min(w, h) - 200) / 2
createChart(chart, w/2, h/2, rad, values, labels, "#fff");


cm = new Ext.menu.Menu({
items: [{
text: 'Option One'
}, {
text: 'Option Two'
}]
});
chart.el.on("contextmenu", function(e) {
e.stopEvent();
cm.showAt(e.getXY());
});

});
</script>
</head>
<body>
<table style="display:none">
<tr>
<th>Ruby</th>
<td>40%</td>
</tr>
<tr>
<th>JavaScript</th>
<td>26%</td>
</tr>
<tr>
<th>Shell</th>
<td>5%</td>
</tr>
<tr>
<th>Python</th>
<td>5%</td>
</tr>
<tr>
<th>PHP</th>
<td>4%</td>
</tr>
<tr>
<th>C</th>
<td>4%</td>
</tr>
<tr>
<th>Perl</th>
<td>3%</td>
</tr>
<tr>
<th>C++</th>
<td>2%</td>
</tr>
<tr>
<th>Java</th>
<td>2%</td>
</tr>
<tr>
<th>Objective-C</th>
<td>2%</td>
</tr>
</table>
</body>
</html>

tomalex0
28 Jul 2010, 5:35 AM
Thanks it works fine

Animal
28 Jul 2010, 5:40 AM
Well I just randomly ***ed around with it for 5 minutes! Wasn't rocket science.

lorezyra
23 Aug 2010, 4:47 AM
Hey Animal! (Oh dear great kami-sama)
Any way we can get this great code as an example on the EXTjs examples/demo page? It would help a lot of newbies out... (myself excluded)

Animal
23 Aug 2010, 4:50 AM
Paste it into a file in examples/anywhere

Run it.

VinylFox
23 Aug 2010, 5:02 AM
Not sure if this helps, but I threw together a basic example along with some modifications to the base class that Animal created.

http://github.com/VinylFox/ExtJS.ux.RaphaelChart/tree/master/examples/raphael/

Animal
23 Aug 2010, 5:10 AM
Don't forget, the Panel needs layout: 'fit' to ensure that the Raphael BoxComponent is sized to exactly fit.

AlxH
25 Aug 2010, 10:23 AM
Raphael is really nice. But the downside is that is has no scoped eventhandlers. :-(

Animal
25 Aug 2010, 10:30 AM
Use Function.createDelegate