Last active
January 30, 2021 17:09
-
-
Save gschoppe/4a0f04ce04db727b9622a69c5340ada9 to your computer and use it in GitHub Desktop.
A simple JS class to manage client-side cropping. ImageScaler supports setting a focal point for the image, as well as selecting a zoom level for the crop
This file contains 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
function ImageScaler(canvas, focus, zoom) { | |
if (canvas) { | |
this.canvas = canvas; | |
} else { | |
this.canvas = document.createElement("CANVAS"); | |
} | |
if (focus) { | |
this.focus = focus; | |
} else { | |
this.focus = {x:.5,y:.5}; | |
} | |
if (zoom) { | |
this.zoom = zoom; | |
} else { | |
this.zoom = 1; | |
} | |
// PUBLIC METHODS | |
// Load Image Content from DataURL (like from filereader) | |
this.loadFromDataURL = function(dataURL) { | |
var self = this; | |
return new Promise(function(resolve, reject) { | |
self.tempImg = document.createElement("IMG"); | |
self.imageToCanvas = function() { | |
this.canvas.width = self.tempImg.width; | |
this.canvas.height = self.tempImg.height; | |
var ctx = self.canvas.getContext("2d"); | |
ctx.drawImage(self.tempImg,0,0); | |
self.tempImg.src = ''; // Assist GC | |
self.tempImg = null; | |
resolve(self); | |
}; | |
self.tempImg.onload = self.imageToCanvas.bind(self); | |
self.tempImg.src = dataURL; | |
}); | |
}; | |
// attempt to match crop position to percent x/y | |
this.setFocalPoint = function(x,y) { | |
var parsePosition = function(word) { | |
switch (word) { | |
case 'top': | |
case 'left': | |
return 0; | |
case 'middle': | |
case 'center': | |
return 0.5; | |
case 'bottom': | |
case 'right': | |
return 1; | |
default: | |
return parseFloat(word); | |
} | |
} | |
this.focus = { | |
x: parsePosition(x), | |
y: parsePosition(y) | |
}; | |
}; | |
// this is the inverse multiplier used on | |
// proportionate dimensions | |
this.setZoom = function(zoom) { | |
zoom = parseFloat(zoom); | |
if (zoom < 1) { | |
zoom = 1; | |
} | |
this.zoom = zoom; | |
}; | |
// crop, based on focal position and zoom | |
this.crop = function(w, h) { | |
w = parseInt(w); | |
h = parseInt(h); | |
var trim = this.calcTrimmedDims(w, h); | |
console.log(trim); | |
var pos = this.calcCropPos(trim.w, trim.h); | |
var cropped = document.createElement("CANVAS"); | |
cropped.width = w; | |
cropped.height = h; | |
var ctx = cropped.getContext("2d"); | |
ctx.drawImage(this.canvas, pos.x, pos.y, trim.w, trim.h, 0, 0, w, h); | |
return new ImageScaler(cropped); | |
}; | |
// returns a dataurl for the image in a specific type | |
this.toDataURL = function(type, encoderOptions) { | |
if (!type) { | |
type = "image/png"; | |
} | |
return this.canvas.toDataURL(type, encoderOptions); | |
}; | |
// passes a blob of mimeType to the callback function sent to it | |
this.toBlob = function(callback, mimeType, qualityArgument) { | |
return this.canvas.toBlob(callback, mimeType, qualityArgument); | |
}; | |
// outputs an x by x pixel dataURL to store in databases for preloading a blurred version | |
this.toBlurURL = function(w) { | |
if (w && parseInt(w)) { | |
w = parseInt(w); | |
} else { | |
w = 3; | |
} | |
var blur = document.createElement("CANVAS"); | |
blur.width = w; | |
blur.height = w; | |
var ctx = blur.getContext("2d"); | |
ctx.drawImage(this.canvas, 0, 0, this.canvas.width, this.canvas.height, 0, 0, w,w); | |
var dataURL = blur.toDataURL("image/png"); | |
ctx = null; | |
blur = null; | |
return dataURL; | |
}; | |
// PRIVATE METHODS | |
this.calcTrimmedDims = function(w,h) { | |
if (w <= 0 || h <= 0) { | |
throw new Exception("Dimensions must be positive"); | |
} | |
var newRatio = w/h; | |
if ( !this.canvas.width || !this.canvas.height ) { | |
throw new Exception("Canvas must have dimensions"); | |
} | |
var trim = { | |
w: this.canvas.width, | |
h: this.canvas.height | |
}; | |
var origRatio = trim.w / trim.h; | |
if (origRatio > newRatio) { | |
trim.w = Math.min(trim.h * newRatio, trim.h); | |
} else { | |
trim.h = Math.min(trim.w / newRatio, trim.w); | |
} | |
if (this.zoom > 1) { | |
trim.w = Math.max(trim.w / this.zoom, 1); | |
trim.h = Math.max(trim.h / this.zoom, 1); | |
} | |
return trim; | |
}; | |
this.calcCropPos = function(w, h) { | |
if (w <= 0 || h <= 0) { | |
throw new Exception("Dimensions must be positive"); | |
} | |
var newRatio = w/h; | |
if ( !this.canvas.width || !this.canvas.height ) { | |
throw new Exception("Canvas must have dimensions"); | |
} | |
var pos = { | |
x: this.canvas.width * this.focus.x - w/2, | |
y: this.canvas.height * this.focus.y - h/2 | |
}; | |
pos.x = Math.min(pos.x, this.canvas.width - w); | |
pos.y = Math.min(pos.y, this.canvas.height - h); | |
pos.x = Math.max(pos.x, 0); | |
pos.y = Math.max(pos.y, 0); | |
return pos; | |
}; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment