UNPKG

react-native-canvas

Version:
338 lines (337 loc) 11.2 kB
var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var WEBVIEW_TARGET = "@@WEBVIEW_TARGET"; var ID = function () { return Math.random().toString(32).slice(2); }; var flattenObjectCopyValue = function (flatObj, srcObj, key) { var value = srcObj[key]; if (typeof value === "function") { return; } if (typeof value === "object" && value instanceof Node) { return; } flatObj[key] = flattenObject(value); }; var flattenObject = function (object) { if (typeof object !== "object" || object === null) { return object; } var flatObject = {}; for (var key in object) { flattenObjectCopyValue(flatObject, object, key); } for (var key in Object.getOwnPropertyNames(object)) { flattenObjectCopyValue(flatObject, object, key); } return flatObject; }; var AutoScaledCanvas = /** @class */ (function () { function AutoScaledCanvas(element) { this.element = element; } AutoScaledCanvas.prototype.toDataURL = function () { var _a; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return (_a = this.element).toDataURL.apply(_a, args); }; AutoScaledCanvas.prototype.autoScale = function () { if (this.savedHeight !== undefined) { this.element.height = this.savedHeight; } if (this.savedWidth !== undefined) { this.element.width = this.savedWidth; } window.autoScaleCanvas(this.element); }; Object.defineProperty(AutoScaledCanvas.prototype, "width", { get: function () { return this.element.width; }, set: function (value) { this.savedWidth = value; this.autoScale(); return value; }, enumerable: false, configurable: true }); Object.defineProperty(AutoScaledCanvas.prototype, "height", { get: function () { return this.element.height; }, set: function (value) { this.savedHeight = value; this.autoScale(); return value; }, enumerable: false, configurable: true }); return AutoScaledCanvas; }()); var toMessage = function (result) { if (result instanceof Blob) { return { type: "blob", payload: btoa(result), meta: {}, }; } if (result instanceof Object) { if (!result[WEBVIEW_TARGET]) { var id = ID(); result[WEBVIEW_TARGET] = id; targets[id] = result; } return { type: "json", payload: flattenObject(result), args: toArgs(flattenObject(result)), meta: { target: result[WEBVIEW_TARGET], constructor: result.__constructorName__ || result.constructor.name, }, }; } return { type: "json", payload: JSON.stringify(result), meta: {}, }; }; /** * Gets the all the args required for creating the object. * Also converts typed arrays to normal arrays. * * For example with ImageData we need a Uint8ClampedArray, * but if we sent it as JSON it will be sent as an object * not an array. So we convert any typed arrays into arrays * first, they will be converted to Uint8ClampedArrays in * `webview-binders.js`. * */ var toArgs = function (result) { var args = []; for (var key in result) { if (result[key] !== undefined && key !== "@@WEBVIEW_TARGET") { if (typedArrays[result[key].constructor.name] !== undefined) { result[key] = Array.from(result[key]); } args.push(result[key]); } } return args; }; /** * Creates objects from args. If any argument have the object * which contains `className` it means we need to convert that * argument into an object. * * We need to do this because when we pass data between the WebView * and RN using JSON, it strips/removes the class data from the object. * So this will raise errors as the WebView will expect arguments to be * of a certain class. * * For example for ImageData we expect to receive * [{className: Uint8ClampedArray, classArgs: [Array(4)]}, 100, 100] * We need to convert the first parameter into an object first. * */ var createObjectsFromArgs = function (args) { var _a; for (var index = 0; index < args.length; index += 1) { var currentArg = args[index]; if (currentArg && currentArg.className !== undefined) { var className = currentArg.className, classArgs = currentArg.classArgs; var object = new ((_a = constructors[className]).bind.apply(_a, __spreadArray([void 0], classArgs, false)))(); args[index] = object; } } return args; }; // const print = (...args) => { // const message = JSON.stringify({ // type: 'log', // payload: args, // }); // window.ReactNativeWebView.postMessage(message); // }; var canvas = document.createElement("canvas"); var autoScaledCanvas = new AutoScaledCanvas(canvas); var targets = { canvas: autoScaledCanvas, context2D: canvas.getContext("2d"), }; var constructors = { Image: Image, Path2D: Path2D, CanvasGradient: CanvasGradient, ImageData: ImageData, Uint8ClampedArray: Uint8ClampedArray, }; var typedArrays = { Uint8ClampedArray: Uint8ClampedArray, }; /** * In iOS 9 constructors doesn't have bind defined which fails * Babel object constructors utility function */ Image.bind = Image.bind || function () { return Image; }; Path2D.bind = Path2D.bind || function () { return Path2D; }; ImageData.bind = ImageData.bind || function () { return ImageData; }; Uint8ClampedArray.bind = Uint8ClampedArray.bind || function () { return Uint8ClampedArray; }; var populateRefs = function (arg) { if (arg && arg.__ref__) { return targets[arg.__ref__]; } return arg; }; document.body.appendChild(canvas); /** * NOTE: Depending on the message type, the message sender will potentially get a callback via * window.ReactNativeWebView.postMessage(...). The postMessage function causes Bus to resolve * a Promise to the caller. * * For example, ctx.fillRect(...) returns a Promise that is then resolved from the code below. * * 'set' is currently the exception - it doesn't resolve at all. * Therefore, Bus should not be saving message ids for 'set' messages. * See the function 'post' in Bus.js. */ function handleMessage(_a) { var _b, _c; var id = _a.id, type = _a.type, payload = _a.payload; switch (type) { case "exec": { var target = payload.target, method = payload.method, args = payload.args; var result = (_b = targets[target])[method].apply(_b, args.map(populateRefs)); var message = toMessage(result); /** * In iOS 9 some classes name are not defined so we compare to * known constructors to find the name. */ if (typeof result === "object" && !message.meta.constructor) { for (var constructorName in constructors) { if (result instanceof constructors[constructorName]) { message.meta.constructor = constructorName; } } } window.ReactNativeWebView.postMessage(JSON.stringify(__assign({ id: id }, message))); break; } case "set": { var target = payload.target, key = payload.key, value = payload.value; targets[target][key] = populateRefs(value); break; } case "construct": { var constructor = payload.constructor, target = payload.id, _d = payload.args, args = _d === void 0 ? [] : _d; var newArgs = createObjectsFromArgs(args); var object = void 0; try { object = new ((_c = constructors[constructor]).bind.apply(_c, __spreadArray([void 0], newArgs, false)))(); } catch (error) { throw new Error("Error while constructing ".concat(constructor, " ").concat(error.message)); } object.__constructorName__ = constructor; var message = toMessage({}); targets[target] = object; window.ReactNativeWebView.postMessage(JSON.stringify(__assign({ id: id }, message))); break; } case "listen": { var types = payload.types, target_1 = payload.target; for (var _i = 0, types_1 = types; _i < types_1.length; _i++) { var eventType = types_1[_i]; targets[target_1].addEventListener(eventType, function (e) { var _a; var message = toMessage({ type: "event", payload: { type: e.type, target: __assign(__assign({}, flattenObject(targets[target_1])), (_a = {}, _a[WEBVIEW_TARGET] = target_1, _a)), }, }); window.ReactNativeWebView.postMessage(JSON.stringify(__assign({ id: id }, message))); }); } break; } } } var handleError = function (err, message) { window.ReactNativeWebView.postMessage(JSON.stringify({ id: message.id, type: "error", payload: { message: err.message, stack: err.stack, }, })); document.removeEventListener("message", handleIncomingMessage); }; function handleIncomingMessage(e) { var data = JSON.parse(e.data); if (Array.isArray(data)) { for (var _i = 0, data_1 = data; _i < data_1.length; _i++) { var message = data_1[_i]; try { handleMessage(message); } catch (err) { handleError(err, message); } } } else { try { handleMessage(data); } catch (err) { handleError(err, data); } } } // iOS window.addEventListener("message", handleIncomingMessage); // Android document.addEventListener("message", handleIncomingMessage);