-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathangular-canvas-ext.min.js
11 lines (11 loc) · 15.1 KB
/
angular-canvas-ext.min.js
1
2
3
4
5
6
7
8
9
10
11
/** @license
* Angular Canvas Extensions - AngularJS module
*
* Allows to open images in canvas, zoom, pan, crop, resize and download image.
*
* https://github.com/petalvlad/angular-canvas-ext
*
* Copyright (c) 2014 Alex Petropavlovsky <[email protected]>
* Released under the MIT license
*/
var canvasExtModule=angular.module("ap.canvas.ext",[]);canvasExtModule.factory("apBrowserHelper",function(){var uagent=navigator.userAgent.toLowerCase(),browser={},platform={};if(platform.ios=/iphone|ipad|ipod/.test(uagent),platform.ipad=/ipad/.test(uagent),platform.android=/android/.test(uagent),platform.blackberry=/blackberry/.test(uagent),platform.windowsPhone=/iemobile/.test(uagent),platform.mobile=platform.ios||platform.android||platform.blackberry||platform.windowsPhone,platform.desktop=!platform.mobile,browser.firefox=/mozilla/.test(uagent)&&/firefox/.test(uagent),browser.chrome=/webkit/.test(uagent)&&/chrome/.test(uagent),browser.safari=/applewebkit/.test(uagent)&&/safari/.test(uagent)&&!/chrome/.test(uagent),browser.opera=/opera/.test(uagent),browser.msie=/msie/.test(uagent),browser.version="",browser.msie||browser.firefox||browser.chrome||browser.safari||browser.opera||/trident/.test(uagent)&&(browser.msie=!0,browser.version=11),""===browser.version)for(x in browser)if(browser[x]){browser.version=uagent.match(new RegExp("("+x+")( |/)([0-9]+)"))[3];break}return{browser:browser,platform:platform,retina:window.devicePixelRatio>=1.5}}),canvasExtModule.factory("apTypeHelper",function(){function objectType(obj){var text=Function.prototype.toString.call(obj.constructor);return text.match(/function (.*)\(/)[1]}function isInstanceOf(value,type){return value&&"object"==typeof value?objectType(value)===type:!1}function isOneOf(value,types){for(var i=0;i<types.length;i++)if(isInstanceOf(value,types[i]))return!0;return!1}function isNumber(value){return"number"==typeof value}return{isInstanceOf:isInstanceOf,isOneOf:isOneOf,isNumber:isNumber}}),canvasExtModule.factory("apPosition",function(apTypeHelper){function APPosition(x,y){this.x=apTypeHelper.isNumber(x)?x:0,this.y=apTypeHelper.isNumber(y)?y:0}return APPosition.prototype={isValid:function(){return apTypeHelper.isNumber(this.x)&&apTypeHelper.isNumber(this.y)}},APPosition.defaultPosition=function(){return new APPosition},APPosition}),canvasExtModule.factory("apSize",function(apTypeHelper){function APSize(width,height){this.width=apTypeHelper.isNumber(width)?width:0,this.height=apTypeHelper.isNumber(height)?height:0}return APSize.prototype={isValid:function(){return apTypeHelper.isNumber(this.width)&&apTypeHelper.isNumber(this.height)}},APSize.defaultSize=function(){return new APSize},APSize}),canvasExtModule.factory("apFrame",function(apTypeHelper,apPosition,apSize){function APFrame(x,y,width,height){this.origin=new apPosition(x,y),this.size=new apSize(width,height)}return APFrame.prototype={isValid:function(){return this.origin.isValid()&&this.size.isValid()}},APFrame.defaultFrame=function(){return new APFrame},APFrame}),canvasExtModule.factory("apImageHelper",function($rootScope,$q,apBrowserHelper,apTypeHelper,apPosition,apSize,apFrame){function createCanvasContext(width,height){var canvas=document.createElement("canvas");return canvas.width=width,canvas.height=height,canvas.getContext("2d")}function imageToImageData(image){var context=createCanvasContext(image.width,image.height);return context.drawImage(image,0,0),context.getImageData(0,0,context.canvas.width,context.canvas.height)}function imageToCanvas(image){var context=createCanvasContext(image.width,image.height);return context.drawImage(image,0,0),context.canvas}function sameSizeImages(img1,img2){return img1.width===img2.width&&img1.height===img2.height}function imagesDifference(img1,img2,tolerance,strict){var img1Data=img1 instanceof ImageData?img1.data:imageToImageData(img1).data,img2Data=img2 instanceof ImageData?img2.data:imageToImageData(img2).data;tolerance=apTypeHelper.isNumber(tolerance)||12.75;for(var difference=0,i=0;i<img1Data.length;i++)if(img1Data[i]!==img2Data[i]&&Math.abs(img1Data[i]-img2Data[i])>tolerance){if(strict)return 100;difference++}var differencePercent=100*difference/img1Data.length;return differencePercent}function sameImages(img1,img2,tolerance,treshold,strict){return sameSizeImages(img1,img2)?(treshold=apTypeHelper.isNumber(treshold)?treshold:5,difference=imagesDifference(img1,img2,tolerance,strict),treshold>=difference):!1}function loadImageFromUrl(url){var d=$q.defer(),image=new Image;return image.onload=function(){d.resolve(image),$rootScope.$apply()},image.onerror=function(){d.resolve(null),$rootScope.$apply()},image.src=url,d.promise}function loadImagesFromUrls(urls){var promises=[];return angular.forEach(urls,function(url){promises.push(loadImageFromUrl(url))}),$q.all(promises)}function downloadImageHandler(imageDataURI,filename,event){function prevent(){return event.preventDefault(),!1}var dataURI=imageDataURI;if(!dataURI)return prevent();if(platform.ios)return window.win=open(dataURI),prevent();if(browser.msie){var blob=dataURItoBlob(dataURI);return blob&&window.navigator.msSaveOrOpenBlob&&window.navigator.msSaveOrOpenBlob(blob,filename),prevent()}var type=mimetypeOfDataURI(dataURI);return dataURI=dataURI.replace(type,"image/octet-stream"),event.currentTarget.href=dataURI,event.currentTarget.download=filename,!0}function isImageDataURL(s){var regex=/^\s*data:(image\/[a-z]+);base64,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$/i;return s&&!!s.match(regex)}function mimetypeOfDataURI(dataURI){return isImageDataURL(dataURI)?dataURI.split(",")[0].split(":")[1].split(";")[0]:null}function dataURItoBlob(dataURI){if(!isImageDataURL(dataURI))return null;var byteString;byteString=dataURI.split(",")[0].indexOf("base64")>=0?atob(dataURI.split(",")[1]):unescape(dataURI.split(",")[1]);for(var mimeString=mimetypeOfDataURI(dataURI),ab=new ArrayBuffer(byteString.length),ia=new Uint8Array(ab),i=0;i<byteString.length;i++)ia[i]=byteString.charCodeAt(i);return new Blob([ab],{type:mimeString})}function fileToImageDataURI(file,type,quality,callback){if(!(file instanceof Blob||file instanceof File))return void callback(null);type||(type=file.type);var imgSrc=URL.createObjectURL(file);if(imgSrc&&""!==imgSrc){var image=new Image;image.onload=function(){if(platform.ios)getImageOrientation(image,function(orientation){var fixOptions={orientation:orientation,maxWidth:500,maxHeight:500};getCanvasWithFixedImage(image,fixOptions,function(target){callback(canvasToDataURI(target,type,quality))})});else{var ctx=createCanvasContext(image.width,image.height);ctx.drawImage(image,0,0),callback(canvasToDataURI(ctx.canvas,type,quality))}},image.src=imgSrc}else callback(null)}function getImageOrientation(image,callback){EXIF.getData(image,function(){callback(EXIF.getTag(image,"Orientation"))})}function getCanvasWithFixedImage(image,fixOptions,callback){var mpImg=new MegaPixImage(image),canvas=createCanvasContext(image.width,image.height).canvas;mpImg.onrender=function(target){callback(target)},mpImg.render(canvas,fixOptions)}function copyImageData(ctx,src){var dst=ctx.createImageData(src.width,src.height);if(dst.data.set)dst.data.set(src.data);else for(var srcData=src.data,dstData=dst.data,i=0;i<srcData.length;++i)dstData[i]=srcData[i];return dst}function makeFrame(x,y,width,height){return new apFrame(x,y,width,height)}function cropImage(image,frame,maxSize,type,quality){if(!image||!frame)return null;var ctx=createCanvasContext(frame.size.width,frame.size.height);if(image instanceof ImageData){var srcCtx=createCanvasContext(image.width,image.height);srcCtx.putImageData(image,0,0),image=srcCtx.canvas}return ctx.drawImage(image,frame.origin.x,frame.origin.y,frame.size.width,frame.size.height,0,0,frame.size.width,frame.size.height),maxSize&&(frame.size.width>maxSize.width||frame.size.height>maxSize.height)?resizeImage(ctx.canvas,maxSize,type,quality):canvasData(ctx,type,quality)}function resizeImage(image,size,type,quality,fill){if(!(image&&(image instanceof Image||image instanceof ImageData||image instanceof HTMLCanvasElement)))return null;var widthScale=size.width/image.width,heightScale=size.height/image.height,scale=fill?Math.max(widthScale,heightScale):Math.min(widthScale,heightScale),size={width:image.width*scale,height:image.height*scale},dstCtx=createCanvasContext(size.width,size.height);if(image instanceof ImageData){var srcCtx=createCanvasContext(image.width,image.height);srcCtx.putImageData(image,0,0),image=srcCtx.canvas}return dstCtx.drawImage(image,0,0,dstCtx.canvas.width,dstCtx.canvas.height),canvasData(dstCtx,type,quality)}function getImageOffsetLimits(image,scale,size){var imageHalfWidth=image.width/2,imageHalfHeight=image.height/2,boundsHalfWidth=size.width/2,boundsHalfHeight=size.height/2,scaledBoundsHalfWidth=boundsHalfWidth/scale,scaledBoundsHalfHeight=boundsHalfHeight/scale;return{left:-imageHalfWidth+scaledBoundsHalfWidth,right:imageHalfWidth-scaledBoundsHalfWidth,top:-imageHalfHeight+scaledBoundsHalfHeight,bottom:imageHalfHeight-scaledBoundsHalfHeight}}function drawImage(image,scale,offset,ctx){var imageHalfWidth=image.width/2,imageHalfHeight=image.height/2,canvasHalfWidth=ctx.canvas.width/2,canvasHalfHeight=ctx.canvas.height/2,beforeScaleOffset={x:(-imageHalfWidth+offset.x)*scale,y:(-imageHalfHeight+offset.y)*scale},afterScaleOffset={x:canvasHalfWidth/scale,y:canvasHalfHeight/scale};ctx.canvas.width=ctx.canvas.width,ctx.translate(beforeScaleOffset.x,beforeScaleOffset.y),ctx.scale(scale,scale),ctx.translate(afterScaleOffset.x,afterScaleOffset.y),ctx.drawImage(image,0,0,image.width,image.height);var x=imageHalfWidth-canvasHalfWidth/scale-offset.x,y=imageHalfHeight-canvasHalfHeight/scale-offset.y,width=ctx.canvas.width/scale,height=ctx.canvas.height/scale;return makeFrame(x,y,width,height)}function snapImage(image,size,scale,offset){var ctx=createCanvasContext(size.width,size.height);return drawImage(image,scale,offset,ctx),canvasData(ctx)}function canvasToDataURI(canvas,type,quality){return type||(type="image/jpeg"),quality||(quality=1),canvas.toDataURL(type,quality)}function canvasData(ctx,type,quality){return{dataURI:canvasToDataURI(ctx.canvas,type,quality),imageData:ctx.getImageData(0,0,ctx.canvas.width,ctx.canvas.height)}}var browser=apBrowserHelper.browser,platform=apBrowserHelper.platform;return{downloadImageHandler:downloadImageHandler,dataURItoBlob:dataURItoBlob,fileToImageDataURI:fileToImageDataURI,copyImageData:copyImageData,cropImage:cropImage,resizeImage:resizeImage,getImageOffsetLimits:getImageOffsetLimits,drawImage:drawImage,mimetypeOfDataURI:mimetypeOfDataURI,loadImagesFromUrls:loadImagesFromUrls,loadImageFromUrl:loadImageFromUrl,sameImages:sameImages,makeFrame:makeFrame,imageToImageData:imageToImageData,imageToCanvas:imageToCanvas,snapImage:snapImage}}),canvasExtModule.directive("apCanvas",function(apImageHelper){return{restrict:"A",scope:{src:"=",scale:"=?",offset:"=?",zoomable:"=?",mode:"=?",image:"=?",frame:"=?"},link:function($scope,element){function loadImage(){var image=new Image;image.onload=function(){$scope.image=image,$scope.$apply()},image.src=$scope.src}function setScale(scale){isUpdateScale=!0,$scope.scale=scale,isUpdateScale=!1}function updateDefaultScale(){var image=$scope.image,widthScale=canvas.width/image.width,heightScale=canvas.height/image.height;defaultScale="fill"===$scope.mode?Math.max(widthScale,heightScale):"fit"===$scope.mode?Math.min(widthScale,heightScale):1}function updateScale(){setScale(defaultScale)}function drawImage(){!$scope.image||isUpdateScale||isUpdateOffset||(clipToBounds(),$scope.frame=apImageHelper.drawImage($scope.image,$scope.scale,$scope.offset,ctx))}function clipToBounds(){isUpdateOffset=!0;var bounds={width:canvas.width,height:canvas.height},offsetLimits=apImageHelper.getImageOffsetLimits($scope.image,$scope.scale,bounds);$scope.offset.y<offsetLimits.top&&($scope.offset.y=offsetLimits.top),$scope.offset.y>offsetLimits.bottom&&($scope.offset.y=offsetLimits.bottom),$scope.offset.x<offsetLimits.left&&($scope.offset.x=offsetLimits.left),$scope.offset.x>offsetLimits.right&&($scope.offset.x=offsetLimits.right),isUpdateOffset=!1}function getMousePosition(e){var rect=canvas.getBoundingClientRect();return{x:(e.clientX-rect.left)/$scope.scale,y:(e.clientY-rect.top)/$scope.scale}}function setIsMoving(moving,event,position){event.preventDefault(),isMoving=moving,moving&&(previousMousePosition=getMousePosition(position))}function moveTo(e,position){if(isMoving){e.preventDefault();var mousePosition=getMousePosition(position);$scope.offset={x:$scope.offset.x+(mousePosition.x-previousMousePosition.x),y:$scope.offset.y+(mousePosition.y-previousMousePosition.y)},previousMousePosition=mousePosition,$scope.$apply()}}function zoom(e,touch1,touch2){e.preventDefault();var dist=Math.sqrt(Math.pow(touch2.pageX-touch1.pageX,2)+Math.pow(touch2.pageY-touch1.pageY,2));lastZoomDist&&($scope.scale*=dist/lastZoomDist,$scope.$apply()),lastZoomDist=dist}function handleMouseDown(e){setIsMoving(!0,e,e)}function handleTouchStart(e){1===e.targetTouches.length&&setIsMoving(!0,e,e.changedTouches[0])}function handleMouseUp(e){setIsMoving(!1,e)}function handleTouchEnd(e){lastZoomDist=null,setIsMoving(!1,e)}function handleMouseMove(e){moveTo(e,e)}function handleTouchMove(e){if(e.targetTouches.length>=2){var touch1=e.targetTouches[0],touch2=e.targetTouches[1];touch1&&touch2&&zoom(e,touch1,touch2)}else moveTo(e,e.changedTouches[0])}function handleMouseWheel(e){e.wheelDelta>0?$scope.scale*=1.01:$scope.scale/=1.01}var canvas=element[0],ctx=canvas.getContext("2d"),previousMousePosition=null,isMoving=!1,defaultScale=0,isUpdateOffset=!1,isUpdateScale=!1,lastZoomDist=null;$scope.offset||($scope.offset={x:0,y:0}),$scope.mode||($scope.mode="fill"),$scope.$watch(function(){return $scope.src},function(newSrc){console.log("new src "+newSrc),newSrc?loadImage():$scope.image=null}),$scope.$watch(function(){return $scope.image},function(newImage,oldImage){console.log("new image "+newImage),canvas.width=canvas.width,newImage&&(updateDefaultScale(),(oldImage||!$scope.scale)&&updateScale(),drawImage())}),$scope.zoomable&&(canvas.addEventListener("mousedown",handleMouseDown,!1),canvas.addEventListener("mouseup",handleMouseUp,!1),canvas.addEventListener("mouseleave",handleMouseUp,!1),canvas.addEventListener("mousemove",handleMouseMove,!1),canvas.addEventListener("mousewheel",handleMouseWheel,!1),canvas.addEventListener("touchstart",handleTouchStart,!1),canvas.addEventListener("touchend",handleTouchEnd,!1),canvas.addEventListener("touchcancel",handleTouchEnd,!1),canvas.addEventListener("touchleave",handleTouchEnd,!1),canvas.addEventListener("touchmove",handleTouchMove,!1),$scope.$watch(function(){return $scope.scale},function(newScale){newScale&&defaultScale>newScale&&setScale(defaultScale),drawImage()}),$scope.$watch(function(){return $scope.offset},function(){drawImage()}))}}}),canvasExtModule.directive("apFileSrc",function(apImageHelper){return{restrict:"A",scope:{src:"=apFileSrc",onImageSelected:"&?",onImageReady:"&?",mimeType:"=?",quality:"=?"},link:function($scope,element){var updateImageSrc=function(src){console.log("new src "+src),$scope.src=src,$scope.$apply(),$scope.onImageReady&&$scope.onImageReady()};element.bind("change",function(e){console.log("file changed"),$scope.onImageSelected&&$scope.onImageSelected();var file=e.target.files.length?e.target.files[0]:null;file&&apImageHelper.fileToImageDataURI(file,$scope.mimeType,$scope.quality,updateImageSrc)})}}});