function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Tim Guibord 9Tim Guibord 9 

Cannot resize image using canvas in a lightning component with LockerService

We are attempting to resize a image file captured from a mobile device prior to sending to the service to eventually upload to S3.  This was working prior to upgrading the component to API veriosn 40.0.

Component markup:
 
<input id="{!v.name}" 
       type="file" 
       accept="image/*" 
       style="display:none" 
       onchange="{!c.setPhoto}" 
       capture="'environment"/>

Lightning Controller:
 
setPhoto: function (cmp, evt, hlp) {
        if (evt.currentTarget.files) {
            var file = evt.currentTarget.files[0];
            hlp.callImageSelected(cmp, file);
        }
    }

Helper:
 
callImageSelected: function (cmp, file) {
        var canvas = document.createElement('canvas');
        var ctx = canvas.getContext('2d');
        var image = document.createElement('img');

        image.onload = function () {
            var max_size = 600;
            var width = image.width;
            var height = image.height;

            if ((width > height) && (width > max_size)) {
                height *= max_size / width;
                width = max_size;
            } else if (height > max_size) {
                width *= max_size / height;
                height = max_size;
            }

            canvas.width = width;
            canvas.height = height;

            ctx.drawImage(image, 0, 0, width, height);

            var dataUrl = canvas.toDataURL('image/jpeg');
            var imageData = self.dataURLToBlob(dataUrl);

            var imageSelected = cmp.getEvent('imageSelected');
            imageSelected.setParams({
                imageData: imageData,
                imageType: cmp.get('v.imageType')
            });
            imageSelected.fire();
        };

        image.src = URL.createObjectURL(file);
    },

    dataURLToBlob: function (dataURL) {
        var BASE64_MARKER = ';base64,';
        var parts, contentType, raw;
        if (dataURL.indexOf(BASE64_MARKER) == -1) {
            parts = dataURL.split(',');
            contentType = parts[0].split(':')[1];
            raw = parts[1];

            return new Blob([raw], {type: contentType});
        }

        parts = dataURL.split(BASE64_MARKER);
        contentType = parts[0].split(':')[1];
        raw = window.atob(parts[1]);

        var rawLength = raw.length;
        var uInt8Array = new Uint8Array(rawLength);

        _.times(rawLength, function (i) {
            uInt8Array[i] = raw.charCodeAt(i)
        });

        return new Blob([uInt8Array], {type: contentType});
    }

​When we attempt to draw the image to the Canvas context in the helper, it fails with the message:

"Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'"

I am assuming the issue is that the Context object cannot unwrap the proxied HTMLImageElement.
 
Rajiv Penagonda 12Rajiv Penagonda 12
The problem seems to be in helper class line 2 and 4. With Locker Service enabled the document.createElement may not work. You can try aura create component to create the canvas.
 
$A.createComponent(
	'aura:html',
	{ 
		tag : 'canvas',
		HTMLAttributes : {
			width: '300',
			height: '300'
		}
	},
	function (el, status, errorMessage) {
		var container = cmp.find('container');
		container.set('v.body', [ el ]);
	}
);

solution taken from here: https://developer.salesforce.com/forums/?id=9060G000000UVVfQAO
Tim Guibord 9Tim Guibord 9
I attempted this methodology and the result was exactly the same.  I would rather not create a new component merely to have access to a canvas object.  According to the LS API docs, document.createElement should be supported (as well as creating a an Image object via the Browser API):
http://documentation.auraframework.org/lockerApiTest/secureDocument.app?aura.mode=DEV

Both lines 2 and 4 in the helper create a SecureElement (HTMLCanvasElement and HTMLImageElement respectively).  I updated the code to log out what is being created:
 
callImageSelected: function (cmp, file) {
        var canvas = document.createElement('canvas');
        var ctx = canvas.getContext('2d');
        var image = new Image();

        console.log(canvas.toString());
        console.log(ctx.toString());
        console.log(image.toString());

The resulting log messages:

User-added image