canvas-size
Version:
Determine the maximum area, height, width, and custom dimensions of an HTML canvas element.
283 lines (275 loc) • 9.53 kB
JavaScript
/*!
* canvas-size
* v2.0.0
* https://github.com/jhildenbiddle/canvas-size
* (c) 2015-2024 John Hildenbiddle <http://hildenbiddle.com>
* MIT license
*/
function canvasTest(settings) {
const size = settings.sizes.shift();
const width = Math.max(Math.ceil(size[0]), 1);
const height = Math.max(Math.ceil(size[1]), 1);
const fill = [ width - 1, height - 1, 1, 1 ];
const testTimeStart = performance.now();
const isWorker = typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope;
let cropCvs, testCvs;
if (isWorker) {
cropCvs = new OffscreenCanvas(1, 1);
testCvs = new OffscreenCanvas(width, height);
} else {
cropCvs = document.createElement("canvas");
cropCvs.width = 1;
cropCvs.height = 1;
testCvs = document.createElement("canvas");
testCvs.width = width;
testCvs.height = height;
}
const cropCtx = cropCvs.getContext("2d");
const testCtx = testCvs.getContext("2d");
if (testCtx) {
testCtx.fillRect.apply(testCtx, fill);
cropCtx.drawImage(testCvs, width - 1, height - 1, 1, 1, 0, 0, 1, 1);
}
const isTestPass = cropCtx && cropCtx.getImageData(0, 0, 1, 1).data[3] !== 0;
const testTime = parseInt(performance.now() - testTimeStart);
[ cropCvs, testCvs ].forEach((cvs => {
cvs.height = 0;
cvs.width = 0;
}));
if (isWorker) {
postMessage({
width: width,
height: height,
testTime: testTime,
isTestPass: isTestPass
});
if (!isTestPass && settings.sizes.length) {
setTimeout((() => {
canvasTest(settings);
}), 0);
}
} else if (isTestPass) {
settings.onSuccess({
width: width,
height: height,
testTime: testTime
});
} else {
settings.onError({
width: width,
height: height,
testTime: testTime
});
if (settings.sizes.length) {
setTimeout((() => {
canvasTest(settings);
}), 0);
}
}
return isTestPass;
}
const testSizes = {
area: [ 16384, 14188, 11402, 11180, 10836, 8192, 4096, 1 ],
height: [ 8388607, 65535, 32767, 16384, 8192, 4096, 1 ],
width: [ 4194303, 65535, 32767, 16384, 8192, 4096, 1 ]
};
const defaults = {
max: null,
min: 1,
sizes: [],
step: 1024,
useWorker: false,
onError: Function.prototype,
onSuccess: Function.prototype
};
const workerJobs = {};
function createSizesArray(settings) {
const isArea = settings.width === settings.height;
const isWidth = settings.height === 1;
const isHeight = settings.width === 1;
const sizes = [];
if (!settings.width || !settings.height) {
settings.sizes.forEach((testSize => {
const width = isArea || isWidth ? testSize : 1;
const height = isArea || isHeight ? testSize : 1;
sizes.push([ width, height ]);
}));
} else {
const testMin = settings.min || defaults.min;
const testStep = settings.step || defaults.step;
let testSize = Math.max(settings.width, settings.height);
while (testSize >= testMin) {
const width = isArea || isWidth ? testSize : 1;
const height = isArea || isHeight ? testSize : 1;
sizes.push([ width, height ]);
testSize -= testStep;
}
}
return sizes;
}
function handleMethod(settings) {
const isBrowser = typeof window !== "undefined";
const hasPromiseSupport = isBrowser && "Promise" in window;
const hasCanvasSupport = isBrowser && "HTMLCanvasElement" in window;
const hasOffscreenCanvasSupport = isBrowser && "OffscreenCanvas" in window;
const jobID = URL.createObjectURL(new Blob([])).slice(-36);
const totalTimeStart = performance.now();
const {onError: onError, onSuccess: onSuccess, ...settingsWithoutCallbacks} = settings;
const getTotalTime = () => parseInt(performance.now() - totalTimeStart);
let worker = null;
if (!hasCanvasSupport) {
return false;
}
if (settings.useWorker && hasOffscreenCanvasSupport) {
const js = `\n var canvasTest = ${canvasTest.toString()};\n onmessage = function(e) {\n canvasTest(e.data);\n };\n `;
const blob = new Blob([ js ], {
type: "application/javascript"
});
const blobURL = URL.createObjectURL(blob);
worker = new Worker(blobURL);
URL.revokeObjectURL(blobURL);
worker.onmessage = function(e) {
const {width: width, height: height, testTime: testTime, isTestPass: isTestPass} = e.data;
const results = {
width: width,
height: height,
testTime: testTime,
totalTime: getTotalTime()
};
if (isTestPass) {
workerJobs[jobID].onSuccess(results);
delete workerJobs[jobID];
} else {
workerJobs[jobID].onError(results);
}
};
}
if (hasPromiseSupport) {
return new Promise((resolve => {
const promiseSettings = {
...settings,
onError(_ref) {
let {width: width, height: height, testTime: testTime} = _ref;
const results = {
width: width,
height: height,
testTime: testTime,
totalTime: getTotalTime()
};
let isLastTest;
if (settings.sizes.length === 0) {
isLastTest = true;
} else {
const [[lastWidth, lastHeight]] = settings.sizes.slice(-1);
isLastTest = width === lastWidth && height === lastHeight;
}
onError(results);
if (isLastTest) {
resolve({
...results,
success: false
});
}
},
onSuccess(_ref2) {
let {width: width, height: height, testTime: testTime} = _ref2;
const results = {
width: width,
height: height,
testTime: testTime,
totalTime: getTotalTime()
};
onSuccess(results);
resolve({
...results,
success: true
});
}
};
if (worker) {
const {onError: onError, onSuccess: onSuccess} = promiseSettings;
workerJobs[jobID] = {
onError: onError,
onSuccess: onSuccess
};
worker.postMessage(settingsWithoutCallbacks);
} else {
canvasTest(promiseSettings);
}
}));
} else {
if (worker) {
workerJobs[jobID] = {
onError: onError,
onSuccess: onSuccess
};
worker.postMessage(settingsWithoutCallbacks);
} else {
return canvasTest(settings);
}
}
}
const canvasSize = {
maxArea() {
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
const sizes = createSizesArray({
width: options.max,
height: options.max,
min: options.min,
step: options.step,
sizes: [ ...testSizes.area ]
});
const settings = {
...defaults,
...options,
sizes: sizes
};
return handleMethod(settings);
},
maxHeight() {
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
const sizes = createSizesArray({
width: 1,
height: options.max,
min: options.min,
step: options.step,
sizes: [ ...testSizes.height ]
});
const settings = {
...defaults,
...options,
sizes: sizes
};
return handleMethod(settings);
},
maxWidth() {
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
const sizes = createSizesArray({
width: options.max,
height: 1,
min: options.min,
step: options.step,
sizes: [ ...testSizes.width ]
});
const settings = {
...defaults,
...options,
sizes: sizes
};
return handleMethod(settings);
},
test() {
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
const settings = {
...defaults,
...options
};
settings.sizes = [ ...settings.sizes ];
if (settings.width && settings.height) {
settings.sizes = [ [ settings.width, settings.height ] ];
}
return handleMethod(settings);
}
};
export { canvasSize as default };
//# sourceMappingURL=canvas-size.esm.js.map