Skip to content

Instantly share code, notes, and snippets.

@gschoppe
Last active January 30, 2021 17:09
Show Gist options
  • Save gschoppe/4a0f04ce04db727b9622a69c5340ada9 to your computer and use it in GitHub Desktop.
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
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