Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance Issue in iOS #7

Open
nickmurphyorg opened this issue Apr 21, 2014 · 6 comments
Open

Performance Issue in iOS #7

nickmurphyorg opened this issue Apr 21, 2014 · 6 comments

Comments

@nickmurphyorg
Copy link

When I load more than one image with this Library it causes the iPhone's CPU to over-clock. Is there a way to clear the global object or null the canvas?

@vincentjames501
Copy link

+1

@vincentjames501
Copy link

Looks like even after the canvas is removed from the dom the animate function is continuously called and not stopped so resources aren't being freed up properly. The ImgTouchCanvas object needs some sort of destroy function that will stop the animation and unbind both the canvas and window events.

@nickmurphyorg
Copy link
Author

That is what I found out, I tried nulling all of the variables but couldn't get the trash collector to destroy the plugin.

@btsai
Copy link

btsai commented May 13, 2015

Oh, yeah, wow, this is an issue.

@amoerke
Copy link

amoerke commented Oct 15, 2015

Hi there,

at first i would like to thank Rom for sharing this code. It safed me much time! But i ran into this issue too and fixed it as shown below:

I've added a new property stopNow. I also added a new method destruct to set the property. This method has to be called, if you want to clean up and remove the canvas.
After that i've added the code to save the requestAnimationFrameId and if property stopNow is true, cancel the last requestAnimationFrame.

See also code annotations!

/*
=================================
img-touch-canvas - v0.1
http://github.com/rombdn/img-touch-canvas

(c) 2013 Romain BEAUDON
This code may be freely distributed under the MIT License
=================================
*/


(function() {
    var root = this; //global object
    var requestAnimationFrameId = null;
    // Set if clean up is requested
    var stopNow = false;

    var ImgTouchCanvas = function(options) {
        if( !options || !options.canvas || !options.path) {
            throw 'ImgZoom constructor: missing arguments canvas or path';
        }

        this.externalFrameSize = false;
        this.canvas         = options.canvas;
        this.canvas.width   = this.canvas.clientWidth;
        this.canvas.height  = this.canvas.clientHeight;
        this.context        = this.canvas.getContext('2d');

        this.desktop = options.desktop || false; //non touch events

        this.frameSize = {
          height: this.canvas.clientHeight,
          width: this.canvas.clientWidth  
        };

        if(options.framesize){
            this.externalFrameSize = true;
            this.frameSize = options.framesize;
        }

        this.position = {
            x: this.canvas.clientWidth / 2 - this.frameSize.width / 2,
            y: 0
        };
        this.scale = {
            x: 0.5,
            y: 0.5
        };
        this.imgTexture = new Image();
        this.imgTexture.src = options.path;

        this.lastZoomScale = null;
        this.lastX = null;
        this.lastY = null;

        this.mdown = false; //desktop drag

        this.init = false;
        this.checkRequestAnimationFrame();        
        this.requestAnimationFrameId = requestAnimationFrame(this.animate.bind(this));

        this.setEventListeners();
    };

    ImgTouchCanvas.prototype = {
        /**
         * Has to be called, if img-touch-canvas is'nt more needed
         */
        destruct:function(){
            this.stopNow = true;
        },
        animate: function() {
            //set scale such as image cover all the canvas
            if(!this.init) {
                if(this.imgTexture.width) {
                    var scaleRatio = null;
                    if(this.frameSize.width > this.frameSize.height) {
                        scaleRatio = this.frameSize.width / (this.imgTexture.width);
                    }
                    else {
                        scaleRatio = this.frameSize.height / this.imgTexture.height;
                    }

                    this.scale.x = scaleRatio;
                    this.scale.y = scaleRatio;
                    this.init = true;
                }
            }

            this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);

            this.context.drawImage(
                this.imgTexture, 
                this.position.x, this.position.y, 
                this.scale.x * (this.imgTexture.width), 
                this.scale.y * this.imgTexture.height);

            // Cancel if requested
            this.requestAnimationFrameId = requestAnimationFrame(this.animate.bind(this));
            if(this.stopNow){
                window.cancelAnimationFrame(this.requestAnimationFrameId);
            }
        },


        gesturePinchZoom: function(event) {
            var zoom = false;

            if( event.targetTouches.length >= 2 ) {
                var p1 = event.targetTouches[0];
                var p2 = event.targetTouches[1];
                var zoomScale = Math.sqrt(Math.pow(p2.pageX - p1.pageX, 2) + Math.pow(p2.pageY - p1.pageY, 2)); //euclidian distance

                if( this.lastZoomScale ) {
                    zoom = zoomScale - this.lastZoomScale;
                }

                this.lastZoomScale = zoomScale;
            }    

            return zoom;
        },

        doZoom: function(zoom) {
            if(!zoom) return;

            //new scale
            var currentScale = this.scale.x;
            var newScale = this.scale.x + zoom/100;


            //some helpers
            var deltaScale = newScale - currentScale;
            var currentWidth    = (this.imgTexture.width * this.scale.x);
            var currentHeight   = (this.imgTexture.height * this.scale.y);
            var deltaWidth  = this.imgTexture.width*deltaScale;
            var deltaHeight = this.imgTexture.height*deltaScale;


            //by default scale doesnt change position and only add/remove pixel to right and bottom
            //so we must move the image to the left to keep the image centered
            //ex: coefX and coefY = 0.5 when image is centered <=> move image to the left 0.5x pixels added to the right
            var canvasmiddleX = this.canvas.clientWidth / 2;
            var canvasmiddleY = this.canvas.clientHeight / 2;
            var xonmap = (-this.position.x) + canvasmiddleX;
            var yonmap = (-this.position.y) + canvasmiddleY;
            var coefX = -xonmap / (currentWidth);
            var coefY = -yonmap / (currentHeight);
            var newPosX = this.position.x + deltaWidth*coefX;
            var newPosY = this.position.y + deltaHeight*coefY;

            //edges cases
            var newWidth = currentWidth + deltaWidth;
            var newHeight = currentHeight + deltaHeight;

            if(this.externalFrameSize){
                if( newWidth < this.frameSize.width && this.frameSize.width > this.frameSize.height) return;
                /*if( newPosX > 0 ) { newPosX = 0; }
                if( newPosX + newWidth < this.frameSize.width ) { newPosX = this.frameSize.width - newWidth;}*/

                if( newHeight < this.frameSize.height && this.frameSize.width < this.frameSize.height) return;
                if( newPosY > 0 ) { newPosY = 0; }
                if( newPosY + newHeight < this.frameSize.height ) { newPosY = this.frameSize.height - newHeight; }
            }else{
                var newWidth = currentWidth + deltaWidth;
                var newHeight = currentHeight + deltaHeight;

                if( newWidth < this.canvas.clientWidth ) return;
                if( newPosX > 0 ) { newPosX = 0; }
                if( newPosX + newWidth < this.canvas.clientWidth ) { newPosX = this.canvas.clientWidth - newWidth;}

                if( newHeight < this.canvas.clientHeight ) return;
                if( newPosY > 0 ) { newPosY = 0; }
                if( newPosY + newHeight < this.canvas.clientHeight ) { newPosY = this.canvas.clientHeight - newHeight; }
            }


            //finally affectations
            this.scale.x    = newScale;
            this.scale.y    = newScale;
            this.position.x = newPosX;
            this.position.y = newPosY;
        },

        doMove: function(relativeX, relativeY) {
            if(this.lastX && this.lastY) {
              var deltaX = relativeX - this.lastX;
              var deltaY = relativeY - this.lastY;
              var currentWidth = (this.imgTexture.width * this.scale.x);
              var currentHeight = (this.imgTexture.height * this.scale.y);

              this.position.x += deltaX;
              this.position.y += deltaY;


              //edge cases
              if(this.externalFrameSize){
                  if( this.position.x < 0 && currentWidth < this.canvas.clientWidth ) {
                    this.position.x = 0;
                  }
                  else if( this.position.x + currentWidth > this.canvas.clientWidth && currentWidth < this.canvas.clientWidth ) {
                    this.position.x = this.canvas.clientWidth - currentWidth;
                  }
                  if( this.position.y > 0 ) {
                    this.position.y = 0;
                  }
                  else if( this.position.y + currentHeight < this.canvas.clientHeight ) {
                    this.position.y = this.canvas.clientHeight - currentHeight;
                  }
              }else{
                  if( this.position.x > 0 ) {
                    this.position.x = 0;
                  }
                  else if( this.position.x + currentWidth < this.canvas.clientWidth ) {
                    this.position.x = this.canvas.clientWidth - currentWidth;
                  }
                  if( this.position.y > 0 ) {
                    this.position.y = 0;
                  }
                  else if( this.position.y + currentHeight < this.canvas.clientHeight ) {
                    this.position.y = this.canvas.clientHeight - currentHeight;
                  }
              }
            }

            this.lastX = relativeX;
            this.lastY = relativeY;
        },

        setEventListeners: function() {
            // touch
            this.canvas.addEventListener('touchstart', function(e) {
                this.lastX          = null;
                this.lastY          = null;
                this.lastZoomScale  = null;
            }.bind(this));

            this.canvas.addEventListener('touchmove', function(e) {
                e.preventDefault();

                if(e.targetTouches.length == 2) { //pinch
                    this.doZoom(this.gesturePinchZoom(e));
                }
                else if(e.targetTouches.length == 1) {
                    var relativeX = e.targetTouches[0].pageX - this.canvas.getBoundingClientRect().left;
                    var relativeY = e.targetTouches[0].pageY - this.canvas.getBoundingClientRect().top;                
                    this.doMove(relativeX, relativeY);
                }
            }.bind(this));

            if(this.desktop) {
                // keyboard+mouse
                window.addEventListener('keyup', function(e) {
                    if(e.keyCode == 187 || e.keyCode == 61) { //+
                        this.doZoom(5);
                    }
                    else if(e.keyCode == 54) {//-
                        this.doZoom(-5);
                    }
                }.bind(this));

                window.addEventListener('mousedown', function(e) {
                    this.mdown = true;
                    this.lastX = null;
                    this.lastY = null;
                }.bind(this));

                window.addEventListener('mouseup', function(e) {
                    this.mdown = false;
                }.bind(this));

                window.addEventListener('mousemove', function(e) {
                    var relativeX = e.pageX - this.canvas.getBoundingClientRect().left;
                    var relativeY = e.pageY - this.canvas.getBoundingClientRect().top;

                    if(e.target == this.canvas && this.mdown) {
                        this.doMove(relativeX, relativeY);
                    }

                    if(relativeX <= 0 || relativeX >= this.canvas.clientWidth || relativeY <= 0 || relativeY >= this.canvas.clientHeight) {
                        this.mdown = false;
                    }
                }.bind(this));
            }
        },

        checkRequestAnimationFrame: function() {
            var lastTime = 0;
            var vendors = ['ms', 'moz', 'webkit', 'o'];
            for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
                window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
                window.cancelAnimationFrame = 
                  window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
            }

            if (!window.requestAnimationFrame) {
                window.requestAnimationFrame = function(callback, element) {
                    var currTime = new Date().getTime();
                    var timeToCall = Math.max(0, 16 - (currTime - lastTime));
                    var id = window.setTimeout(function() { callback(currTime + timeToCall); }, 
                      timeToCall);
                    lastTime = currTime + timeToCall;
                    return id;
                };
            }

            if (!window.cancelAnimationFrame) {
                window.cancelAnimationFrame = function(id) {
                    clearTimeout(id);
                };
            }
        }
    };

    root.ImgTouchCanvas = ImgTouchCanvas;
}).call(this);

@faizlongbow
Copy link

how to add close button in the javascript to close the images?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants