【问题标题】:Web workers and Canvas data网络工作者和 Canvas 数据
【发布时间】:2014-05-29 18:48:24
【问题描述】:

我看到了很多关于 web worker 和 <canvas> 数据传递的线程,我不确定我的场景是否可以实现。

我想创建一个小型网站,供用户学习用 Javascript 编写代码。当用户单击按钮时,代码将在 Web Worker 中运行。为了帮助他们,我还创建了“打印”方法(基本上只是将文本添加到 <pre> 元素。

// get code from user and prepend helper "functions"
var userCode = editor.getValue();
var codeWithMessages = "\n\
    function print(data) { postMessage(\"\"+data); } \n\
    function println(data) { postMessage(\"\"+data+\'\\n\'); } \n\
    function alert(data) { postMessage('ALERT'+data); }  \n\
    \n" + userCode;
var codeWithMessagesAndExit = codeWithMessages + "\npostMessage('exit()');";
var blob = new Blob([codeWithMessagesAndExit], {type: 'application/javascript'});
// Obtain a blob URL reference to our worker 'file'.
var blobURL = window.URL.createObjectURL(blob);
outer.worker = new Worker(blobURL);

...
// handle messages by "printing" to the run-output element, or display
// a success message when finishing
outer.worker.addEventListener('message', function (event) {

        if (event.data == "exit()") {
            outer.worker.terminate();
            outer.worker = null;

            outer.codeRunStatus = outer.CODE_RUN_SUCCESS;
            outer.errorMessage = null;
            outer.outputFromLatestRun = $("#run-output").text();
            $("#run-output").append("<span class='code-run-success'>\nKörning klar</span>");
            enableButtons();
        }
        else if (event.data.indexOf("ALERT") == 0) {
            alert(event.data.substring(5));
        }
        else {
            $("#run-output").append(event.data);
        }
    }, false);
// start the worker
outer.worker.postMessage();

现在我想在页面上添加一个画布,以便用户可以编写代码在其上绘图。

var canvas = document.getElementById("user-canvas");
var ctx = canvas.getContext("2d");
var imgData = ctx.getImageData(0,0,canvas.width,canvas.height);
// rest same as above
outer.worker.postMessage(imgData);

我不知道如何从这里开始。理想情况下,用户可以写类似

myCanvas.fillStyle = "rgb(200,0,0)";
myCanvas.fillRect (10, 10, 55, 50);

然后让事件侦听器处理它,就像我对 print() 函数所做的那样。这可能吗?

【问题讨论】:

    标签: javascript canvas web-worker


    【解决方案1】:

    无法从 WebWorker 访问 DOM。

    我能看到的最好的方法是在 webworker 中重新定义这个对象,并定义一个协议来将每个调用传输到一个方法。但是当您需要图像等其他对象时,它就不起作用了。

    工人端:

    var CanvasRenderingContext2D = function() {
        this.fillStyle = "black";
        this.strokeStyle = "black";
        ...
        this.lineWidth = 1.0;
    };
    
    ["fillRect", "strokeRect", "beginPath", ... "rotate", "stroke"].forEach(function(methodName) {
        CanvasRenderingContext2D.prototype[methodName] = function() {
            postMessage({called: methodName, args: arguments, currentObjectAttributes: this});
        };
    });
    
    ...
    
    var myCanvas = new CanvasRenderingContext2D();
    myCanvas.fillStyle = "rgb(200,0,0)";
    myCanvas.fillRect(10, 10, 55, 50);
    

    首页侧:

    var canvas = document.createElement("canvas");
    var context = canvas.getContext("2d");
    var worker = new Worker( ... );
    worker.onMessage = function(event) {
        var data = event.data;
    
        // Refreshing context attributes
        var attributes = data.currentObjectAttributes;
        for(var k in attributes) {
            context[k] = attributes[k];
        }
    
        // Calling method
        var method = context[data.called];
        method.apply(context, data.args);
    };
    

    编辑:

    我尝试将它与您的代码集成(未经测试)。为了整合它,我不得不改变工人发送的消息的结构。

    // get code from user and prepend helper "functions"
    var userCode = editor.getValue();
    var codeWithMessages = '\n\
    function print  (data) { postMessage(["print", data.toString()]); } \n\
    function println(data) { postMessage(["print", data.toString() + "\\n"]); } \n\
    function alert  (data) { postMessage(["alert", data.toString()]); } \n\
    var CanvasRenderingContext2D = function() { \n\
        this.fillStyle   = "black"; \n\
        this.strokeStyle = "black"; \n\
        /* ... */ \n\
        this.lineWidth = 1.0; \n\
    }; \n\
    ["fillRect", "strokeRect", "beginPath", /* ... */ "rotate", "stroke"].forEach(function(methodName) { \n\
        CanvasRenderingContext2D.prototype[methodName] = function() { \n\
            postMessage(["canvas", {called: methodName, args: Array.prototype.slice.call(arguments), currentObjectAttributes: this}]); \n\
        }; \n\
    });\n' + userCode;
    var codeWithMessagesAndExit = codeWithMessages + "\npostMessage(['exit']);";
    var blob = new Blob([codeWithMessagesAndExit], {type: 'application/javascript'});
    // Obtain a blob URL reference to our worker 'file'.
    var blobURL = window.URL.createObjectURL(blob);
    outer.worker = new Worker(blobURL);
    
    ...
    
    // Define your canvas ...
    var canvas = document.createElement("canvas");
    var context = canvas.getContext("2d");
    
    // handle messages by "printing" to the run-output element, or display
    // a success message when finishing
    outer.worker.addEventListener('message', function (event) {
        var method = event.data[0] || null;
        var data   = event.data[1] || null;
    
        if(method == "canvas") {
            // Refreshing context attributes
            var attributes = data.currentObjectAttributes;
            for(var k in attributes) {
                context[k] = attributes[k];
            }
    
            // Calling method
            var method = context[data.called];
            method.apply(context, data.args);
        } else if(method == "exit") {
            outer.worker.terminate();
            outer.worker = null;
    
            outer.codeRunStatus = outer.CODE_RUN_SUCCESS;
            outer.errorMessage = null;
            outer.outputFromLatestRun = $("#run-output").text();
            $("#run-output").append("<span class='code-run-success'>\nKörning klar</span>");
            enableButtons();
        } else if(method == "alert") {
            alert(data);
        } else if(method == "print") {
            $("#run-output").append(data);
        } else {
            $("#run-output").append(event.data);
        }
    }, false);
    // start the worker
    outer.worker.postMessage();
    

    【讨论】:

    • 这看起来像我所追求的。我可以将这些与我当前的 print 函数结合起来吗?我收到Uncaught TypeError: Cannot read property 'apply' of undefined
    • @pg-robban 您可以将其与您的print 结合使用,但需要更改消息的结构。使用最近的浏览器,您可以直接发送结构化对象和数组。关于您的错误,您是否在我的示例中将 context 重命名为 ctx 在您的示例中?我将编辑我的答案以将其与您的代码集成。
    • @pg-robban :我编辑了我的答案。而且我无法从现在的位置访问 pastebin :(
    • 我更改了一些不匹配的引号 - 如果需要,我可以编辑您的帖子。让我们看看:printalertexit 函数在这种结构下工作得很好,现在关于画布:我尝试了你的示例代码 var myCanvas = new CanvasRenderingContext2D(); myCanvas.fillRect(10, 10, 55, 50);,我得到了 Uncaught TypeError: Failed to execute 'fillRect' on 'CanvasRenderingContext2D': 4 arguments required, but only 0 present.
    • @pg-robban 将它添加到else if 应该可以工作,但这意味着它将在每次调用方法时附加到页面。最好将其附加在同一级别而不是创建。
    猜你喜欢
    • 2013-05-18
    • 2016-02-21
    • 2021-11-09
    • 2010-12-24
    • 1970-01-01
    • 2016-06-08
    • 1970-01-01
    • 2015-03-16
    相关资源
    最近更新 更多