ChartJS and OpenFL -- how to create a new Canvas that renders between DisplayObjects?

I’m using haxe-chart to draw graphs and charts in an OpenFL project. This library draws directly to a Canvas, and is generally used like so:

var canvas:CanvasElement = Browser.document.createCanvasElement();
Browser.document.body.appendChild(canvas);
var ctx:CanvasRenderingContext2D = canvas.getContext("2d");
var barChart = new Chart(ctx).Bar(data);

I have some DisplayObjects that should render behind this Chart, and some that should render in front of it, and am having some difficulty accomplishing this.

If I understand the HTML5Renderer and related classes correctly, certain things create a new Canvas, including the creation of a BitmapData. I thought that perhaps I could create a new Bitmap from a new BitmapData, add it to the desired place in the display tree, and use the newly created CanvasRenderingContext2D like so:

var bmd:BitmapData = new BitmapData( 100, 100 );
var bm:Bitmap = new Bitmap( bmd );

// assume this is a DisplayObjectContainer in the desired point in the display tree
this.addChild( bm );

// I exposed __srcContext in lime's ImageBuffer
var ctx = bmd.image.buffer.__srcContext;
var chart = new Chart( ctx ).Line( data );

Ideally I wouldn’t need to expose __srcContext like that, but I didn’t see any existing way to get at the newly created canvas or its context. Unfortunately, even with that hack it still didn’t work, as the ChartJS library throws an exception at runtime (it appears to be trying to access a property of the Canvas’s parentNode, which is null).

Any advice or assistance would be greatly appreciated =)

Does it work if you try openfl test html5 -Ddom and use a new openfl.display.DOMSprite to house the ChartJS canvas?

I had tried the -Ddom flag (without DOMSprite) without success, but didn’t spend much time on it as it created several additional issues that would need to be solved (things positioned and scaled differently, some input troubles, etc – I’m not against solving them if -Ddom is the answer, but figured I’d explore other options first).

I just tried DOMSprite + `-Ddom’ flag:

var bmd:BitmapData = new BitmapData( 100, 100 );
var dspr:DOMSprite = new DOMSprite( bmd.image.buffer.__srcCanvas );
addChild( dspr );
m_ctx = bmd.image.buffer.__srcContext;
m_chart = new chart.Chart(m_ctx).Line( data );

I got the same runtime error: Uncaught TypeError: Cannot read property 'clientWidth' of null. This appears to be coming from this bit in the minified chart.js code:

R=s.getMaximumWidth=function(t){var i=t.parentNode;return i.clientWidth}

which appears to be the minified version of this function (source):

	getMaximumWidth = helpers.getMaximumWidth = function(domNode){
		var container = domNode.parentNode,
		    padding = parseInt(getStyle(container, 'padding-left')) + parseInt(getStyle(container, 'padding-right'));
		// TODO = check cross browser stuff with this.
		return container.clientWidth - padding;
	},

(It’s worth adding that we have a backup plan if needed: remove the background image(s), add the Chart behind the rest of our content, and add the desired background image back in via index.html; just figured it was worth checking to make sure I wasn’t missing anything at the openfl level first :slightly_smiling:)

Or a DOMSprite with a DIV, and then embed a canvas element inside?

Here is a trick to do it, you need to create a bitmapdata from the canvas, and then, use it in a bitmap as a displayobject:

bitmapdata.dispose();
bitmapdata= BitmapData.fromCanvas(htmlCanvasElement);
bitmap.bitmapData= bitmapdata;

I used this way to display a canvas as a displayobject, the canvas has even animation, I’ve refreshed the bitmap about 20 times per second, it worked fine with Chrome, but I had an issue with Safari as here BitmapData.fromCanvas on Safari

1 Like