UNPKG

canvas-size

Version:

Determine the maximum area, height, width, and custom dimensions of an HTML canvas element.

283 lines (275 loc) 9.53 kB
/*! * 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