img-cacher
Version:
Serve images from localStorage – great for offline apps.
151 lines (121 loc) • 5.45 kB
JavaScript
function getSize(img, options) {
var ratio = 1,
width = img.width,
height = img.height;
if (options.fillWidth || options.fillHeight) {
var widthRatio = (typeof options.fillWidth !== 'undefined' ? options.fillWidth : 0) / width,
heightRatio = (typeof options.fillHeight !== 'undefined' ? options.fillHeight : 0) / height;
ratio = widthRatio > heightRatio ? widthRatio : heightRatio;
height = img.height * ratio;
width = img.width * ratio;
} else if (options.maxWidth || options.maxHeight) {
if (options.maxWidth && options.maxHeight) {
ratio = Math.min(options.maxWidth / img.width, options.maxHeight / img.height);
} else if (options.maxWidth) {
ratio = options.maxWidth / img.width;
} else if (options.maxHeight) {
ratio = options.maxHeight / img.height;
}
height = img.height * ratio;
width = img.width * ratio;
} else if (options.width || options.height) {
if (options.width && options.height) {
width = options.width;
height = options.height;
} else if (options.width) {
ratio = options.width / img.width;
width = options.width;
height = img.height * ratio;
} else if (options.height) {
ratio = options.height / img.height;
width = img.width * ratio;
height = options.height;
}
}
return {
width: Math.ceil(width),
height: Math.ceil(height)
};
}
function getCrop(img, options, size) {
var crop = {
x: Math.ceil(typeof options.cropX !== 'undefined' ? options.cropX : (options.cropWidth ? (size.width - options.cropWidth) / -2 : 0)),
y: Math.ceil(typeof options.cropY !== 'undefined' ? options.cropY : (options.cropHeight ? (size.height - options.cropHeight) / -2 : 0)),
width: Math.ceil(size.width),
height: Math.ceil(size.height)
};
if (options.cropWidth) {
if (Math.ceil(options.cropWidth) < crop.width) {
crop.width = Math.ceil(options.cropWidth);
}
if (Math.ceil(options.cropHeight) < crop.height) {
crop.height = Math.ceil(options.cropHeight);
}
}
return {
x: Math.ceil(typeof options.cropX !== 'undefined' ? options.cropX : (options.cropWidth ? (size.width - options.cropWidth) / -2 : 0)),
y: Math.ceil(typeof options.cropY !== 'undefined' ? options.cropY : (options.cropHeight ? (size.height - options.cropHeight) / -2 : 0)),
width: Math.ceil(options.cropWidth ? Math.floor(options.cropWidth, size.width) : size.width),
height: Math.ceil(options.cropHeight ? Math.floor(options.cropHeight, size.height) : size.height)
};
}
// Derived from https://stackoverflow.com/questions/19262141/resize-image-with-javascript-canvas-smoothly
function steppedScale(img, width, step) {
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
oc = document.createElement('canvas'),
octx = oc.getContext('2d');
canvas.width = width;
canvas.height = Math.ceil(canvas.width * img.height / img.width);
if (img.width * step > width) {
var mul = 1 / step,
cur = {
width: Math.ceil(Math.floor(img.width * step)),
height: Math.ceil(Math.floor(img.height * step))
};
oc.width = cur.width;
oc.height = cur.height;
octx.drawImage(img, 0, 0, cur.width, cur.height);
while (cur.width * step > width) {
cur = {
width: Math.floor(cur.width * step),
height: Math.floor(cur.height * step)
};
octx.drawImage(oc, 0, 0, cur.width * mul, cur.height * mul, 0, 0, cur.width, cur.height);
}
ctx.drawImage(oc, 0, 0, cur.width, cur.height, 0, 0, canvas.width, canvas.height);
} else {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
return canvas;
}
var cached = {};
module.exports = function base64Img(img, options, done) {
if (typeof options === 'function') {
done = options;
options = {};
}
var _ = this,
srcWithOptions = _.buildSrc(img.src, options);
if (cached[srcWithOptions]) {
_.log('base64Img', 'fromMemory', img.src, options);
return done( undefined, cached[srcWithOptions] );
}
var croppedCanvas = document.createElement('canvas'),
croppedContext = croppedCanvas.getContext('2d'),
size = getSize(img, options),
crop = getCrop(img, options, size),
bufferCanvas = steppedScale(img, size.width, typeof options.sharpness === 'number' && options.sharpness < 1 ? options.sharpness : 0.5);
console.log('HEREE', size, crop)
croppedCanvas.width = crop.width;
croppedCanvas.height = crop.height;
if (options.bg) {
croppedContext.fillStyle = options.bg;
croppedContext.fillRect(0, 0, crop.width, crop.height);
}
croppedContext.drawImage(bufferCanvas, crop.x * -1, crop.y * -1, crop.width, crop.height, 0, 0, crop.width, crop.height);
croppedContext.imageSmoothingEnabled = true;
cached[srcWithOptions] = croppedCanvas.toDataURL(options.type, options.encoderOptions);
_.log('base64Img', 'noMemory', img.src, options);
done( undefined, cached[srcWithOptions], true );
};