Created
July 1, 2015 11:40
-
-
Save thomwiggers/51d79e19ca518c7344fa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Javascript Image Editor | |
| * | |
| * Allows to resize images inside the browser. | |
| * | |
| * Supports IE10+ with native support | |
| * | |
| * @author Thom Wiggers | |
| * @copyright InQbator B.V. 2013-2014 | |
| */ | |
| "use strict"; | |
| function ImageEditor (canvas, targetWidth, targetHeight, scale, fallbackmode) { | |
| this.canvas = canvas; | |
| this.canvas.width += 0; | |
| this.width = targetWidth; | |
| this.height = targetHeight; | |
| this.CANVASWIDTH = 800; | |
| this.CANVASHEIGHT = 600; | |
| this.CANVASMARGIN = 20; | |
| this.handleSize = 15; | |
| this.canvas.width = this.CANVASWIDTH + this.CANVASMARGIN; | |
| this.canvas.height = this.CANVASHEIGHT + this.CANVASMARGIN; | |
| this.scale = typeof scale !== 'undefined' ? scale : 1; | |
| // make canvas bigger if necessary | |
| if((targetWidth/this.scale) > this.CANVASWIDTH) { | |
| this.scale *= (targetWidth/this.scale)/this.CANVASWIDTH; | |
| } | |
| if((targetHeight/this.scale) > this.CANVASHEIGHT) { | |
| this.scale *= (targetHeight/this.scale)/this.CANVASHEIGHT; | |
| } | |
| this.imgLeft = null; | |
| this.imgTop = null; | |
| this.imgWidth = null; | |
| this.imgHeight = null; | |
| this.img = null; | |
| this.movingCorner = null; | |
| this.dragging = false; | |
| this.origin = {x:null, y:null}; | |
| // Cache for background | |
| this.cache = {}; | |
| this.fallbackmode = typeof fallbackmode !== 'undefined' ? fallbackmode : false; | |
| this.canvas.addEventListener('mouseup', function(event) { | |
| event.preventDefault(); | |
| this.movingCorner = null; | |
| this.dragging = false; | |
| this.canvas.style.cursor = "inherit"; | |
| this.origin = {x:null, y:null}; | |
| }.bind(this), false); | |
| this.canvas.addEventListener('mousemove', function(event) { | |
| event.preventDefault(); | |
| var xClick = event.pageX - this.canvas.offsetLeft, | |
| yClick = event.pageY - this.canvas.offsetTop; | |
| // if we're not moving... | |
| if(this.movingCorner === null && !this.dragging) { | |
| // half handle size | |
| var hhs = this.handleSize/2; | |
| //op een van de hoekpunten? | |
| if ((Math.abs(this.imgTop - yClick) <= hhs) || | |
| (Math.abs( (this.imgTop + this.imgHeight) - yClick) <= hhs) || | |
| (Math.abs(this.imgLeft-xClick) <= hhs) || | |
| (Math.abs( (this.imgLeft + this.imgWidth) - xClick) <= hhs)) { | |
| this.canvas.style.cursor = "grab"; | |
| } else { | |
| this.canvas.style.cursor = "inherit"; | |
| } | |
| return; | |
| } | |
| // linksboven | |
| if(this.movingCorner === "top left") { | |
| this.imgWidth -= xClick - this.imgLeft; | |
| this.imgLeft = xClick; | |
| this.imgHeight -= yClick - this.imgTop; | |
| this.imgTop = yClick; | |
| } | |
| // linksonder | |
| else if (this.movingCorner === "bottom left") { | |
| this.imgWidth -= xClick - this.imgLeft; | |
| this.imgLeft = xClick; | |
| this.imgHeight = yClick - this.imgTop; | |
| } | |
| // rechtsonder | |
| else if (this.movingCorner === "bottom right") { | |
| this.imgWidth = xClick - this.imgLeft; | |
| this.imgHeight = yClick - this.imgTop; | |
| } | |
| //rechtsboven | |
| else if (this.movingCorner === "top right") { | |
| this.imgWidth = xClick - this.imgLeft; | |
| this.imgHeight -= yClick - this.imgTop; | |
| this.imgTop = yClick; | |
| } else if (this.dragging) { | |
| this.imgLeft -= (this.origin.x - xClick); | |
| this.imgTop -= (this.origin.y - yClick); | |
| this.origin = {x:xClick,y:yClick}; | |
| } | |
| this.redraw(); | |
| }.bind(this), false); | |
| this.canvas.addEventListener('mousedown', function(event) { | |
| event.preventDefault(); | |
| var xClick = event.pageX - this.canvas.offsetLeft, | |
| yClick = event.pageY - this.canvas.offsetTop; | |
| this.origin = {x:xClick,y:yClick}; | |
| var movingCorner = "", | |
| cursor = ""; | |
| // half handle size | |
| var hhs = this.handleSize/2; | |
| //bovenkant of onderkant: | |
| if ((Math.abs(this.imgTop - yClick) <= hhs)) { | |
| movingCorner = "top "; | |
| cursor += "n"; | |
| } else if ((Math.abs( (this.imgTop + this.imgHeight) - yClick) <= hhs)) { | |
| movingCorner = "bottom "; | |
| cursor += "s"; | |
| } | |
| //links of rechts | |
| if((Math.abs(this.imgLeft-xClick) <= hhs)) { | |
| movingCorner += "left"; | |
| cursor += "w-resize"; | |
| } else if((Math.abs( (this.imgLeft + this.imgWidth) - xClick) <= hhs)) { | |
| movingCorner += "right"; | |
| cursor += "e-resize"; | |
| } else { | |
| movingCorner = null; | |
| cursor = "inherit"; | |
| } | |
| this.movingCorner = movingCorner; | |
| this.canvas.style.cursor = cursor; | |
| if(movingCorner === null) { | |
| if ( | |
| ( | |
| (xClick > this.imgLeft && xClick < (this.imgLeft+this.imgWidth)) || | |
| (xClick < this.imgLeft && xClick > (this.imgLeft+this.imgWidth)) | |
| ) && | |
| ( | |
| (yClick > this.imgTop && yClick < (this.imgTop+this.imgHeight)) || | |
| (yClick < this.imgTop && yClick > (this.imgTop+this.imgHeight)) | |
| ) | |
| ) { | |
| this.dragging = true; | |
| this.canvas.style.cursor = "move"; | |
| } | |
| } | |
| }.bind(this), false); | |
| this.canvas.addEventListener('ondrag', function(event) { | |
| event.preventDefault(); | |
| }); | |
| } | |
| /** | |
| * Save the image | |
| * | |
| * @param ajaxCall Callback function to save the image, takes a dataUrl object. | |
| * @param filetype (optional) Filetype to save the file as. Default "image/png" | |
| * @param fallbackCall (optional) Callback function which takes the dimensions | |
| * of the image, the canvas, and the viewport. | |
| */ | |
| ImageEditor.prototype.saveImage = function (ajaxCall, filetype, fallbackCall) { | |
| filetype = typeof filetype !== 'undefined' ? filetype : "image/png"; | |
| var context = this.canvas.getContext("2d"), | |
| viewportLeft = ((this.CANVASWIDTH+this.CANVASMARGIN) - this.width/this.scale) / 2, | |
| viewportTop = ((this.CANVASHEIGHT+this.CANVASMARGIN) - this.height/this.scale) /2, | |
| offsetImageLeft = this.imgLeft - viewportLeft, | |
| offsetImageTop = this.imgTop - viewportTop; | |
| this.canvas.width += 0; | |
| this.drawImage(false); | |
| if((!context.getImageData || this.fallbackmode) && typeof fallbackCall !== 'undefined') { | |
| var canvasData = { | |
| imageCoords: { | |
| top: this.imgTop, | |
| left: this.imgLeft, | |
| width: this.imgWidth, | |
| height: this.imgHeight, | |
| offsetImageTop: offsetImageTop, | |
| offsetImageLeft: offsetImageLeft | |
| }, | |
| settings: { | |
| targetWidth: this.width, | |
| targetHeight: this.height, | |
| scale: this.scale | |
| } | |
| }; | |
| fallbackCall(canvasData); | |
| } else { | |
| var tempCanvas = document.createElement("canvas"), | |
| tempContext = tempCanvas.getContext("2d"); | |
| tempCanvas.width = this.width; | |
| tempCanvas.height = this.height; | |
| console.log(tempCanvas); | |
| tempContext.drawImage(this.img, | |
| offsetImageLeft, | |
| offsetImageTop, | |
| this.imgWidth * this.scale, | |
| this.imgHeight * this.scale | |
| ); | |
| var dataURL = tempCanvas.toDataURL(filetype); | |
| if(dataURL === "data:,") { | |
| alert("Something went wrong while trying to save your image. Please try again"); | |
| return; | |
| } | |
| ajaxCall(dataURL); | |
| } | |
| }; | |
| ImageEditor.prototype.drawImage = function drawImage(decoration) { | |
| decoration = typeof decoration !== 'undefined' ? decoration : true; | |
| var context = this.canvas.getContext("2d"); | |
| context.save(); | |
| var width, height, top, left; | |
| if(this.imgHeight === null || this.imgWidth === null | |
| || this.imgTop === null || this.imgLeft === null) { | |
| width = this.img.width; | |
| height = this.img.height; | |
| if (width > this.CANVASWIDTH) { | |
| height *= this.CANVASWIDTH / width; | |
| width = this.CANVASWIDTH; | |
| } | |
| if (height > this.CANVASHEIGHT) { | |
| width *= this.CANVASHEIGHT / height; | |
| height = this.CANVASHEIGHT; | |
| } | |
| this.imgLeft = left = ((this.CANVASWIDTH - width) /2) + 10; | |
| this.imgTop = top = ((this.CANVASHEIGHT - height) / 2) + 10; | |
| this.imgHeight = height; | |
| this.imgWidth = width; | |
| } else { | |
| width = this.imgWidth; | |
| height = this.imgHeight; | |
| left = this.imgLeft; | |
| top = this.imgTop; | |
| } | |
| try { | |
| context.drawImage(this.img, left, top, width, height); | |
| } catch (error) { | |
| var message = "An error occurred while trying to draw your image!\n"; | |
| message += "Your image might be invalid. Try opening it on your computer " | |
| + "or try again.\n\n Technical details: " + error.description; | |
| } | |
| if(decoration) { | |
| //first draw viewport | |
| this.drawViewport(); | |
| // draw dashed box around image | |
| context.globalAlpha = 1; | |
| context.save(); | |
| context.beginPath(); | |
| context.rect(left,top,width,height); | |
| context.lineWidth = 4; | |
| //Add a placeholder function for browsers that don't have setLineDash() | |
| // in other words: old browsers. setLineDash is only available since | |
| // VERY RECENTLY | |
| if (!context.setLineDash) { | |
| context.setLineDash = function () {} | |
| } | |
| context.setLineDash([10, 4]); | |
| context.strokeStyle = "#000"; | |
| context.stroke(); | |
| context.restore(); | |
| // draw handles | |
| // hs: handlesize, hhs=half hs | |
| var hs = this.handleSize, | |
| hhs = hs/2; | |
| context.save() | |
| context.strokeStyle = "#ddd"; | |
| context.lineWidth = 3; | |
| context.beginPath(); | |
| context.rect(left-hhs,top-hhs,hs,hs); | |
| context.rect(left-hhs,top+height-hhs,hs,hs); | |
| context.rect(left+width-hhs,top-hhs,hs,hs); | |
| context.rect(left+width-hhs,top+height-hhs,hs,hs); | |
| context.fillStyle = '#000'; | |
| context.fill(); | |
| context.stroke(); | |
| context.restore(); | |
| } | |
| } | |
| /** | |
| * Draws a background. Tries to cache it. | |
| */ | |
| ImageEditor.prototype.drawBackground = function () { | |
| //check the cache | |
| if(typeof this.cache['background'] !== 'undefined') { | |
| this.canvas.getContext("2d").drawImage(this.cache['background'], 0, 0); | |
| return; | |
| } | |
| var canvas = document.createElement("canvas"); | |
| canvas.height = this.CANVASHEIGHT + this.CANVASMARGIN; | |
| canvas.width = this.CANVASWIDTH + this.CANVASMARGIN; | |
| var context = canvas.getContext("2d"); | |
| context.globalAlpha = 1; | |
| function drawSquare(initX, initY) { | |
| context.beginPath(); | |
| context.fillStyle = "#656565"; | |
| context.fillRect(initX, initY, 5, 5); | |
| } | |
| for (var i = 0; i < (this.CANVASWIDTH+this.CANVASMARGIN)/10; i++) { | |
| for (var j = 0; j < (this.CANVASHEIGHT+this.CANVASMARGIN)/5; j++) { | |
| if ((j % 2) === 1) { | |
| drawSquare(i * 10, j * 5); | |
| } else { | |
| drawSquare(i * 10 + 5, j * 5); | |
| } | |
| } | |
| } | |
| this.canvas.getContext("2d").drawImage(canvas, 0, 0); | |
| //cache canvas | |
| this.cache['background'] = canvas; | |
| } | |
| /** | |
| * Draws a view port | |
| */ | |
| ImageEditor.prototype.drawViewport = function() { | |
| //check the cache | |
| if(typeof this.cache['viewport'] !== 'undefined') { | |
| this.canvas.getContext("2d").drawImage(this.cache['viewport'], 0, 0); | |
| return; | |
| } | |
| // ok construct a new viewport | |
| var canvas = document.createElement("canvas"); | |
| canvas.width = this.CANVASWIDTH+this.CANVASMARGIN; | |
| canvas.height = this.CANVASHEIGHT+this.CANVASMARGIN; | |
| var context = canvas.getContext("2d"); | |
| context.save(); | |
| context.beginPath(); | |
| context.fillStyle = "#666"; | |
| var height = this.height / this.scale, | |
| width = this.width / this.scale; | |
| var viewportLeft = ((this.CANVASWIDTH+this.CANVASMARGIN) - width) / 2, | |
| viewportRight = ((this.CANVASWIDTH+this.CANVASMARGIN) + width) / 2, | |
| viewportBottom = ((this.CANVASHEIGHT+this.CANVASMARGIN) + height) / 2, | |
| viewportTop = ((this.CANVASHEIGHT+this.CANVASMARGIN) - height) /2; | |
| //draw border | |
| context.strokeStyle = "#000"; | |
| context.lineWidth = "2"; | |
| context.rect(viewportLeft, viewportTop, width, height); | |
| context.stroke(); | |
| //draw gray area | |
| context.globalAlpha = 0.5; | |
| context.fillRect(0, 0, viewportLeft, this.CANVASHEIGHT+this.CANVASMARGIN); | |
| context.fillRect(viewportRight, 0, this.CANVASWIDTH/2, this.CANVASHEIGHT+this.CANVASMARGIN); | |
| context.fillRect(viewportLeft, 0, width, viewportTop); | |
| context.fillRect(viewportLeft, viewportBottom, width, viewportTop); | |
| context.restore() | |
| this.canvas.getContext("2d").drawImage(canvas, 0, 0); | |
| this.cache['viewport'] = canvas; | |
| } | |
| /** | |
| * redraw the image | |
| */ | |
| ImageEditor.prototype.redraw = function () { | |
| this.canvas.width +=0; | |
| this.drawBackground(); | |
| this.drawImage(); | |
| } | |
| /** | |
| * loads an image onto the canvas | |
| * | |
| * @param file file from form. | |
| * @param fallbackhandler (optional) how to handle the fallback. Expects a | |
| * function which when provided with an image | |
| * will return an image URL. | |
| */ | |
| ImageEditor.prototype.loadImage = function(file, fallbackhandler) { | |
| this.img = new Image(); | |
| if ((!window.URL.createObjectURL || this.fallbackmode) && | |
| typeof fallbackhandler !== 'undefined' ) { | |
| this.img.src = fallbackhandler(file); | |
| } else { | |
| this.img.src = window.URL.createObjectURL(file); | |
| } | |
| this.img.onload = function () { | |
| this.redraw(); | |
| }.bind(this); | |
| } | |
| // vim: set ts=4 sw=2 tw=0 et : |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment