CleverCover - How to dress up your Facebook Timeline (V2)

January 01, 1970

He's Alive, ALIVE!

Finally, my new version of CleverCover is available for everyone : CleverCover!

For those who don't know what CleverCover is, you can take a look at the first version here :
It was not very userfriendly, not very modular etc. This time, it looks great. Here's some screen shots in order to explain how it works :

Behavior

Here's the new welcome screen (select the social network you want your cover for) :

And the selection of the image :

Now that every thing is ok, click on "Generate your Cover" and Voila!

In this page, you can resize the picture with the black scroll bar just below the cover, you can also flip your picture from left to right with the little check box, and do not forget to move by dragging the picture!

When finished, click the "Save" button in the top right corner and Abracadabra, here are your pictures!

You can now save both images (left is cover and right is avatar) and put them in your favorite social network.

Here's a list of what I intend to improve next in this script :

  • Add one script for each social network that will automatically put both images in your profile without any action for you except the "Save" button"
  • Allow to rotate the picture
  • Allow to use 2 images instead of just one
  • Allow to over resize the picture (actually you can only resize from the minimum of the required width/height to the exact size of the picture, not over)

If you have ideas to improve it, do not hesitate to leave a comment bellow!

Code

Now let's talk about code!

First, the more important : The cover!

JavaScript - ResizableCanvas class

// Javascript class to implement to add a new part of image
// Required : width/height/id
var ResizableCanvas = (function() {
    var poolEventId = 0;

    return function(params) {

        /*********************
         *   Variables   *
         ********************/
        this._id = params.id;
        this._width = params.width;
        this._height = params.height;
        this._extractWidth = params.extractWidth || params.width;
        this._extractHeight = params.extractHeight || params.height;

        this._links = {};
        this._fullWidth = params.width;
        this._fullHeight = params.height;
        this._left = 0;
        this._top = 0;
        this._right = 0;
        this._bottom = 0;

        this._img = null;
        this._x = 0;
        this._y = 0;
        this._ratio = 100;
        this._minRatio = 0;
        this._opacity = 1;
        this._flipFactor = 1;

        this._events = {};

        /*********************
         *    Methods     *
         ********************/

        // draw the canvas
        this.draw = function(propagate) {
            var _draw = function() {
                this._fixPosition();

                var context = ctx(this._id);
                context.clearRect(0, 0, this._fullWidth, this._fullHeight);
                context.save();
                context.globalAlpha = this._opacity;
                context.scale(this._ratio, this._ratio);

                if(this._flipFactor === -1) {
                    context.translate(this._fullWidth/this._ratio, 0);
                    context.scale(-1, 1);
                }
                context.translate(this._flipFactor*(-this._x-this._left)/this._ratio, (-this._y-this._top)/this._ratio);
                context.drawImage(this._img, 0, 0);

                context.restore();
            };

            if (propagate) {
                this._propagate(_draw, [], true);
            } else {
                _draw.apply(this);
            }
        };

        // init when dom's ready
        this._prepare = function() {
            $(this._id).setAttribute('width', this._width);
            $(this._id).setAttribute('height', this._height);
            $(this._id).style.width = this._width + 'px';
            $(this._id).style.height = this._weight + 'px';
        };
        jQuery(this._prepare.bind(this));

        // link to an other ResizableCanvas
        this.link = function(canvas, params) {
            if(this._links[canvas._id]) {
                return;
            }

            var shared = ["left", "top"];
            for(i in shared) {
                if(params[shared[i]] < 0) {
                    canvas['_' + shared[i]] -= params[shared[i]];
                } else {
                    this['_' + shared[i]] += params[shared[i]] || 0;
                }
            }

            this._links[canvas._id] = canvas;
            canvas._links[this._id] = this;

            // TODO: adapt to multiple links
            this._fullWidth = Math.max(this._width + Math.max(0, canvas._left + canvas._width - this._width), canvas._width + Math.max(0, this._left + this._width - canvas._width));
            this._fullHeight = Math.max(this._height + Math.max(0, canvas._top + canvas._height - this._height), canvas._height + Math.max(0, this._top + this._height - canvas._height));
            this._propagate({
                _fullWidth : this._fullWidth,
                _fullHeight : this._fullHeight
            });
            jQuery(this._prepare.bind(this));
        };

        // propagate a function or params firectly to linked RezisableCanvas
        this._propagate = function(event, params, execute) {
            var values = [];
            if(!event.id) {
                event.id = ++poolEventId;
            } else if(this._events[event.id]) {
                return values;
                // Already done and forwarded
            } else {
                execute = true;
            }

            if(execute) {
                if(jQuery.isFunction(event)) {
                    values.push(event.apply(this, params));
                } else {
                    // Treat the event
                    for(var index in event) {
                        this[index] = event[index];
                    }
                }
            }

            this._events[event.id] = event;

            // Forward
            for(var canvasId in this._links) {
                jQuery.merge(values, this._links[canvasId]._propagate(event, params));
            }
            return values;
        };

        // change canvas picture
        this.setImage = function(url, callback) {
            this._img = loadImg(url, function() {
                var success = true;
                if(this._img.naturalWidth < this._fullWidth || this._img.naturalHeight < this._fullHeight) {
                    alert('This picture is too small. Need at least ' + this._fullWidth + 'x' + this._fullHeight + 'pixels (got ' + this._img.naturalWidth + 'x' + this._img.naturalHeight+').');
                    success = false;
                } else {
                    this._minRatio = Math.max(this._fullWidth / this._img.naturalWidth, this._fullHeight / this._img.naturalHeight) * 100;

                    this._propagate({
                        _img : this._img,
                        _minRatio : this._minRatio
                    });

                    this.changeRatio(this._ratio);
                }(callback || noop)(success);
            }.bind(this), true);
        };

        // Move the picture
        this.move = function(data) {
            this._propagate(function() {
                this._x -= data.dx;
                this._y -= data.dy;
                this.draw(false);
            }, [], true);
        };

        // Correct position while moving/resizing (if at bottom-right corner, resize have to move the picture to the left)
        this._fixPosition = function() {
            this._x = mm(0, this._x, this._flipFactor * (this._img.naturalWidth - this._fullWidth / this._ratio) * this._ratio);
            this._y = mm(0, this._y, (this._img.naturalHeight - this._fullHeight / this._ratio) * this._ratio);
        };

Comments

Lucho: Great app man!

Jérémy: Thanks ;)