Paulo Almeida
Paulo Almeida

Reputation: 8061

Why are symbols rendered three times in a flot graphic?

I am using the flot library to render a plot where PNG images are rendered as points. It's working, but each PNG image seems to be rendered three times. This is visible on a refresh (the overlap isn't perfect), and I also logged the image's onload function and Firebug tells me that it is being called three times for each graphic. Here is the function that renders the images (counter is the logging variable):

function generateImageFunction (image) {
    return function getImage(ctx, x, y, radius, shadow) {                   
        var img = new Image();
        img.onload = function() {
            console.log(counter++);
            ctx.drawImage(img, x, y, img.width, img.height);    
        }
        img.src = image;
    }
}

data has 13 series, these are just the first two as an example:

var data = [
            { 
             data: [[1, 1]], 
             points: { symbol: generateImageFunction("face-angel.png") }
            }
            { 
             data: [[2, 2]], 
             points: { symbol: generateImageFunction("face-angry.png") }
            }
           ];

My options are these:

var options = {
               series: {
                   points: { show: true },
               },
               xaxis: { min: 0, max: 15},
               yaxis: { show: false, min: 0, max: 15 },
               selection: { mode: "x" }
};

And then I do $.plot($("#placeholder"), data, options);

With the 13 data series, the counter variable goes to 39 when I first load the page. If I zoom in on a region with only one data point, the counter is increased by 3. Can anyone tell me why this is happening, or what can I do investigate further?

Upvotes: 2

Views: 641

Answers (2)

Ryley
Ryley

Reputation: 21226

What your code does is re-download the image every time the point is used by flot. Admittedly, flot probably should not be using it 3 times per $.plot call, but there's not much you can do about that! What you can do is load the image only once!

function generateImageFunction(image) {
    return function getImage(ctx, x, y, radius, shadow) {
        ctx.drawImage(image, x, y, image.width, image.height);
    }
}

//list out all used images here
var symbols = {
    'icon1': 'http://tinychat.com/public/images/star.png',
        'icon2': 'http://www.earthdetails.com/images/icons/X-16-01.gif'
};
var symbol2image = {};//used to reference the downloaded image
var loading = [];//just used in the $.load call

//start loading each image
for (var symbol in symbols) {
    var img = new Image();
    img.src = symbols[symbol];
    symbol2image[symbol] = img;
    loading.push(img);
}

//wait until they are all loaded, then call the plot function
$.apply($, loading).load(function () {

   //setup data/options here, key lines being how you call the new generateImageFunction
                 symbol: generateImageFunction(symbol2image['icon2'])

   //call plot
    $.plot($("#placeholder"), data, options);
});

There is a working example here: http://jsfiddle.net/ryleyb/kEhsQ/2/

Upvotes: 5

DNS
DNS

Reputation: 38189

Unless you override the global or per-series shadowSize option, Flot will draw a shadow beneath each series. The shadow is drawn in two steps (search the code for the plotPoints function to see how this is done) of increasing opacity, to soften its border.

So your function runs once to draw the shadow at alpha 0.1, again to draw it at alpha 0.2, then once more to draw the actual series.

If you don't want this, you can disable the shadow entirely by setting shadowSize to zero. You can also skip drawing the shadow for the points only, by checking the value of the function's 'shadow' parameter; if it's true, as for the two calls used to draw the shadow, then you can just return immediately.

If you go with the latter, then I would definitely also do as @Ryley suggested to avoid actually loading the image multiple times.

Upvotes: 4

Related Questions