const BlockType = require('../../extension-support/block-type'); const ArgumentType = require('../../extension-support/argument-type'); const Color = require('../../util/color'); const cstore = require('./canvasStorage'); const Cast = require('../../util/cast'); const store = new cstore(); /** * Class * @constructor */ class canvas { constructor(runtime) { /** * The runtime instantiating this block package. * @type {runtime} */ this.runtime = runtime; store.attachRuntime(runtime); } static get canvasStorageHeader() { return 'canvases: '; } deserialize(data) { store.canvases = {}; for (const canvas of data) { store.newCanvas(canvas.name, canvas.width, canvas.height, canvas.id); } } serialize() { return store.getAllCanvases() .map(variable => ({ name: variable.name, width: variable.width, height: variable.height, id: variable.id })); } readAsImageElement(src) { return new Promise((resolve, reject) => { const image = new Image(); image.onload = function () { resolve(image); image.onload = null; image.onerror = null; }; image.onerror = function () { reject(new Error('Costume load failed. Asset could not be read.')); image.onload = null; image.onerror = null; }; image.src = src; }); } orderCategoryBlocks(blocks) { const button = blocks[0]; const varBlock = blocks[1]; delete blocks[0]; delete blocks[1]; // create the variable block xml's const varBlocks = store.getAllCanvases().map(canvas => varBlock .replace('{canvasId}', canvas.id)); if (!varBlocks.length) { return [button]; } // push the button to the top of the var list varBlocks .reverse() .push(button); // merge the category blocks and variable blocks into one block list blocks = varBlocks .reverse() .concat(blocks); return blocks; } /** * @returns {object} metadata for this extension and its blocks. */ getInfo() { return { id: 'canvas', name: 'html canvas', color1: '#0069c2', color2: '#0060B4', color3: '#0060B4', isDynamic: true, orderBlocks: this.orderCategoryBlocks, blocks: [ { opcode: 'createNewCanvas', blockType: BlockType.BUTTON, text: 'create new canvas' }, { opcode: 'canvasGetter', blockType: BlockType.REPORTER, arguments: { canvas: { type: ArgumentType.STRING, menu: 'canvas', defaultValue: '{canvasId}' } }, text: '[canvas]' }, { blockType: BlockType.LABEL, text: "config" }, { opcode: 'setGlobalCompositeOperation', text: 'set composite operation of [canvas] to [CompositeOperation]', arguments: { canvas: { type: ArgumentType.STRING, menu: 'canvas', defaultValue: "" }, CompositeOperation: { type: ArgumentType.STRING, menu: 'CompositeOperation', defaultValue: "" } }, blockType: BlockType.COMMAND }, { opcode: 'setSize', text: 'set width: [width] height: [height] of [canvas]', arguments: { canvas: { type: ArgumentType.STRING, menu: 'canvas', defaultValue: "" }, width: { type: ArgumentType.NUMBER, defaultValue: this.runtime.stageWidth }, height: { type: ArgumentType.NUMBER, defaultValue: this.runtime.stageHeight } }, blockType: BlockType.COMMAND }, { opcode: 'setTransparency', text: 'set transparency of [canvas] to [transparency]', arguments: { canvas: { type: ArgumentType.STRING, menu: 'canvas', defaultValue: "" }, transparency: { type: ArgumentType.NUMBER, defaultValue: '0' } }, blockType: BlockType.COMMAND }, { opcode: 'setFill', text: 'set fill color of [canvas] to [color]', arguments: { canvas: { type: ArgumentType.STRING, menu: 'canvas', defaultValue: "" }, color: { type: ArgumentType.COLOR } }, blockType: BlockType.COMMAND }, { opcode: 'setBorderColor', text: 'set border color of [canvas] to [color]', arguments: { canvas: { type: ArgumentType.STRING, menu: 'canvas', defaultValue: "" }, color: { type: ArgumentType.COLOR } }, blockType: BlockType.COMMAND }, { blockType: BlockType.LABEL, text: "drawing" }, { opcode: 'clearCanvas', text: 'clear canvas [canvas]', arguments: { canvas: { type: ArgumentType.STRING, menu: 'canvas', defaultValue: "" } }, blockType: BlockType.COMMAND }, { opcode: 'clearAria', text: 'clear area at x: [x] y: [y] with width: [width] height: [height] on [canvas]', arguments: { canvas: { type: ArgumentType.STRING, menu: 'canvas', defaultValue: "" }, x: { type: ArgumentType.NUMBER, defaultValue: '0' }, y: { type: ArgumentType.NUMBER, defaultValue: '0' }, width: { type: ArgumentType.NUMBER, defaultValue: this.runtime.stageWidth }, height: { type: ArgumentType.NUMBER, defaultValue: this.runtime.stageHeight } }, blockType: BlockType.COMMAND }, '---', { opcode: 'drawRect', text: 'draw rectangle at x: [x] y: [y] with width: [width] height: [height] on [canvas]', arguments: { canvas: { type: ArgumentType.STRING, menu: 'canvas', defaultValue: "" }, x: { type: ArgumentType.NUMBER, defaultValue: '0' }, y: { type: ArgumentType.NUMBER, defaultValue: '0' }, width: { type: ArgumentType.NUMBER, defaultValue: this.runtime.stageWidth }, height: { type: ArgumentType.NUMBER, defaultValue: this.runtime.stageHeight } }, blockType: BlockType.COMMAND }, { opcode: 'drawImage', text: 'draw image [src] at x: [x] y: [y] on [canvas]', arguments: { canvas: { type: ArgumentType.STRING, menu: 'canvas', defaultValue: "" }, x: { type: ArgumentType.NUMBER, defaultValue: '0' }, y: { type: ArgumentType.NUMBER, defaultValue: '0' }, src: { type: ArgumentType.STRING, defaultValue: 'https://studio.penguinmod.com/favicon.ico' } }, blockType: BlockType.COMMAND } ], menus: { canvas: 'getCanvasMenuItems', CompositeOperation: { items: [ { "text": "source-over", "value": "source-over" }, { "text": "source-in", "value": "source-in" }, { "text": "source-out", "value": "source-out" }, { "text": "source-atop", "value": "source-atop" }, { "text": "destination-over", "value": "destination-over" }, { "text": "destination-in", "value": "destination-in" }, { "text": "destination-out", "value": "destination-out" }, { "text": "destination-atop", "value": "destination-atop" }, { "text": "lighter", "value": "lighter" }, { "text": "copy", "value": "copy" }, { "text": "xor", "value": "xor" }, { "text": "multiply", "value": "multiply" }, { "text": "screen", "value": "screen" }, { "text": "overlay", "value": "overlay" }, { "text": "darken", "value": "darken" }, { "text": "lighten", "value": "lighten" }, { "text": "color-dodge", "value": "color-dodge" }, { "text": "color-burn", "value": "color-burn" }, { "text": "hard-light", "value": "hard-light" }, { "text": "soft-light", "value": "soft-light" }, { "text": "difference", "value": "difference" }, { "text": "exclusion", "value": "exclusion" }, { "text": "hue", "value": "hue" }, { "text": "saturation", "value": "saturation" }, { "text": "color", "value": "color" }, { "text": "luminosity", "value": "luminosity" } ] } } }; } createNewCanvas() { const newCanvas = prompt('canvas name?', 'newCanvas'); // if this camvas already exists, remove it to minimize confusion if (!newCanvas) return alert('Canceled') if (store.getCanvasByName(newCanvas)) return; store.newCanvas(newCanvas); vm.emitWorkspaceUpdate(); this.serialize(); } getCanvasMenuItems() { const canvases = store.getAllCanvases(); if (canvases.length < 1) return [{ text: '', value: '' }]; return canvases.map(canvas => ({ text: canvas.name, value: canvas.id })); } canvasGetter(args) { const canvasObj = store.getCanvas(args.canvas); return canvasObj.element.toDataURL(); } setGlobalCompositeOperation(args) { const canvasObj = store.getCanvas(args.canvas); canvasObj.context.globalCompositeOperation = args.CompositeOperation; } setBorderColor(args) { const color = Cast.toString(args.color); const canvasObj = store.getCanvas(args.canvas); canvasObj.context.strokeStyle = color; } setFill(args) { const color = Cast.toString(args.color); const canvasObj = store.getCanvas(args.canvas); canvasObj.context.fillStyle = color; } setSize(args) { const canvasObj = store.getCanvas(args.canvas); canvasObj.element.width = args.width; canvasObj.element.height = args.height; canvasObj.context = canvasObj.element.getContext('2d'); } drawRect(args) { const canvasObj = store.getCanvas(args.canvas); canvasObj.context.fillRect(args.x, args.y, args.width, args.height); } drawImage(args) { return new Promise(resolve => { const canvasObj = store.getCanvas(args.canvas); const image = new Image(); image.onload = () => { canvasObj.context.drawImage(image, args.x, args.y); resolve(); }; image.src = args.src; }); } clearAria(args) { const canvasObj = store.getCanvas(args.canvas); canvasObj.context.clearRect(args.x, args.y, args.width, args.height); } clearCanvas(args) { const canvasObj = store.getCanvas(args.canvas); canvasObj.context.clearRect(0, 0, canvasObj.width, canvasObj.height); } setTransparency(args) { const canvasObj = store.getCanvas(args.canvas); canvasObj.context.globalAlpha = args.transparency / 100; } } module.exports = canvas;