0

I've been using JS for a long time, but i'm just getting into properly using objects.

I'm creating an image generator using canvas, which you can then download as a png. The user can add text to the image using input boxes. I've made this a JS object so that I can create multiple instances of it on a page. It all works fine, except for my event listener to update the text.

The input boxes are created by parameters passed to the function, and then a keyup event is added so that the canvas can update as the user types. This is all working, except for that fact I don't know how to tell the keyup event what JS Object it should be updating.

Here's my code:

function Canvas_to_img( name, container, image, height, width, content_areas ){
    // Assign params to object
    object = this;
    this.name = name;
    this.container = container;
    this.image = image;
    this.height = height;
    this.width = width;
    this.content_areas = content_areas;

    // Prepare image
    var imageObj = new Image();
    imageObj.src = image;

    // Add canvas div
    container.prepend('<div class="canvas canvas--' + name + '"></div>')

    // Add temp canvas div
    container.append('<div class="canvas-temp canvas-temp--' + name + '"></div>')

    // Add canvas wrapper
    var wrapper = new Concrete.Wrapper({
      container: container.find('.canvas')[0],
      width: width,
      height: height
    });

    // Add BG and Text layers
    var bgLayer = new Concrete.Layer();
    var textLayer = new Concrete.Layer();
    wrapper.add(bgLayer).add(textLayer);

    // Add image to BG layer
    imageObj.onload = function() {
        bgLayer.sceneCanvas.context.drawImage(imageObj,0,0, width, height);
    };

    // Set up input areas and event listeners
    for (key in content_areas){

        for (subkey in content_areas[key].fields){
            name_field = content_areas[key].fields[subkey].name;
            name_layer = content_areas[key].fields[subkey].name;

            // Add input fields for text layer
            container.prepend('<div class="field-set__item">\
                <span class="field-set__label">' + name_field +'</span>\
                <input data-object="'+object+'" data-area="' + key + '" data-name="' + name + '" data-field="' + subkey + '" class="field-set__input ' + name_field + '" id="text1" type="text">\
            </div>');

            $( "."+name_field ).keyup(function() {
                area = $(this).data('area');
                field = $(this).data('field');
                object = $(this).data('object');
                console.log(object);
                value = $(this).val();
                content_areas[key].fields[subkey].name;
                //content_areas[area].fields[field].value = value;
                OBJECTNAME.updateContent(area, field, value)
                OBJECTNAME.updateCanvas(wrapper);
            });
        }
    }

    container.append('<a href="#" id="download--' + name +'" class="button download download--' + name + '">Download</a>');

    $('body').on('click', '.download--' + name, function(e) {
        var dataURL = $('.canvas-temp--fb-banner canvas')[0].toDataURL('image/png');
        $(this).attr('download', 'test.png');
        document.getElementById('download--' + name).href = dataURL;
    });
}

Canvas_to_img.prototype = {
    updateContent: function(area, field, value){
        this.content_areas[area].fields[field].value = value;
    },

    updateCanvas: function(wrapper) {
        $('.canvas-temp--'+this.name+' canvas').remove();
        wrapper.layers[1].destroy();

        var textLayer = new Concrete.Layer();
        wrapper.add(textLayer);

        content_areas = this.content_areas;

        for (key in content_areas){
            textLayer.sceneCanvas.context.fillStyle = content_areas[key].colour;
            textLayer.sceneCanvas.context.font = "bold 24px Open Sans";
            textLayer.sceneCanvas.context.textAlign = content_areas[key].alignment;
            layer_content = '';
            for (subkey in content_areas[key].fields){
                //console.log(content_areas[key].fields[subkey]);
                layer_content += content_areas[key].fields[subkey].prepend + " ";
                layer_content += content_areas[key].fields[subkey].value;
                layer_content += content_areas[key].fields[subkey].append + " ";
                //console.log(layer_content);
            }
            textLayer.sceneCanvas.context.fillText(layer_content, content_areas[key].position_x, content_areas[key].position_y);
        }

        //ctx.fillText($(this).val(), cnvs.width/2, 300);
        // textLayer.sceneCanvas.context.fillText('Test', 325, 300);
         var canvas = wrapper.toCanvas({
            pixelRatio: 1
        });
        //console.log(canvas.canvas);
        $('.canvas-temp--'+this.name).append(canvas.canvas);
    },
    update: function(){

    }
};



var content_areas = {
    1: {
        position_x: 425,
        position_y: 300,
        alignment: 'center',
        colour: '#fff',
        font: 'Open Sans',
        size: '24px',
        weight: 600,
        fields: {
            field_1: {
                name: 'date',
                value: '',
                prepend: '',
                append: ','
            }, 
            field_2: {
                name: 'venue',
                value: '',
                prepend: '',
                append: ''
            }
        }
    }
}

var content_areas_2 = {
    1: {
        position_x: 425,
        position_y: 300,
        alignment: 'center',
        colour: '#fff',
        font: 'Open Sans',
        size: '24px',
        weight: 600,
        fields: {
            field_1: {
                name: 'date',
                value: '',
                prepend: '',
                append: ','
            }, 
            field_2: {
                name: 'venue',
                value: '',
                prepend: '',
                append: ''
            },
            field_3: {
                name: 'time',
                value: '',
                prepend: '',
                append: ''
            }
        }
    }
}

var facebook_banner = new Canvas_to_img('fb-banner', $('.banner-container'), 'http://localhost:1234/images/mainsite5/bb-fb-cover.jpg', 315, 851, content_areas);
var facebook_banner_2 = new Canvas_to_img('fb-banner-2', $('.banner-container-2'), 'http://localhost:1234/images/mainsite5/bb-fb-cover.jpg', 315, 851, content_areas_2);

These are the function calls that need to be associated with an object:

OBJECTNAME.updateContent(area, field, value)
OBJECTNAME.updateCanvas(wrapper);

I probably need to instead make this eventlistener linked to the object itself? I'm not sure how do to that.

Anthony Forloney
  • 90,123
  • 14
  • 117
  • 115
user3429445
  • 91
  • 2
  • 9

2 Answers2

1

updateContent and updateCanvas are prototypes of that objects you create at the bottom of your code. Normally, you would access those with this.updateContent(area, field, value). BUT you are inside a callback function, so this refers to the scope of that function.

Earlier in your code, you set object = this; (why is object global b.t.w.?), so you already store this inside a variable that you should have access to inside the callback. So your code should read:

object.updateContent(area, field, value); , etc // object is your this

Here is another similar but simplified question.

Community
  • 1
  • 1
Hinrich
  • 13,485
  • 7
  • 43
  • 66
  • If I use `object` I just get the last object that is created, so all changes apply to the last image. I tried `this.object = this` but then that is assigned to the object, so I can't refer to it in the callback function. – user3429445 Jun 15 '16 at 13:35
  • That is because object is in the global scope, which is bad anyway. Try `var that = this;` as the first line in the `Canvas_to_img` function. Then you can do that.updateContent(area, field, value); – Hinrich Jun 15 '16 at 15:46
0

I've found it useful to save the instance to the element. You can do this by taking the instance assigned here:

var facebook_banner = new Canvas_to_img('fb-banner', $('.banner-container'), 'http://localhost:1234/images/mainsite5/bb-fb-cover.jpg', 315, 851, content_areas);

And setting it to a data property of its element:

$('.banner-container').data('canvas_to_img', facebook_banner);

Now you can get that instance when you need it:

$('.banner-container').data('canvas_to_img');

Or if you're within the context of that element already:

$(this).data('canvas_to_img');
Eric N
  • 2,136
  • 13
  • 13
  • Thanks man, this has worked and i'm using it now. Appreciate the help. Feels like there should be a way to do this without having to pass the object to the element and back though... – user3429445 Jun 15 '16 at 13:41