You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Emscripten移植的WebAssembly OpenGL应用向JavaScript传递数据?

在Emscripten中从C++向JavaScript传递事件与数据的解决方案

针对你用Emscripten移植OpenGL/SDL2/ImGui应用时,需要从C++向JS传递按钮点击等事件的需求,以下是几种可靠实现方式,同时解决emscripten_cancel_main_loop导致的冻结问题:

一、直接用EM_ASM系列宏调用JavaScript代码

这是最简单的方式,可在C++事件触发点直接嵌入JS逻辑,或调用网页上的全局函数、触发自定义事件。

C++侧代码(ImGui按钮点击处理)

#include <emscripten.h>
#include <imgui.h>

// 在你的渲染循环中处理ImGui界面
void render_frame() {
    ImGui::Begin("简单对话框");
    
    if (ImGui::Button("OK")) {
        // 传递字符串数据到JS
        const char* action_msg = "用户点击了OK按钮";
        EM_ASM_(
            {
                // 将C++传入的UTF8字符串转为JS字符串
                const msg = UTF8ToString($0);
                // 1. 触发网页自定义事件,让JS监听处理
                window.dispatchEvent(new CustomEvent('wasm-user-action', { detail: msg }));
                // 2. 或者直接调用网页已定义的全局函数
                // window.onWasmAction(msg);
            },
            action_msg // 传递给EM_ASM的参数,$0对应第一个参数
        );
    }
    
    ImGui::End();
    // 其余渲染逻辑...
}

JavaScript侧代码(网页端监听事件)

// 监听C++触发的自定义事件
window.addEventListener('wasm-user-action', (event) => {
    console.log("来自Wasm的操作:", event.detail);
    // 这里可以执行网页逻辑:显示提示、更新DOM、跳转页面等
});

// 或者定义全局函数供C++调用
window.onWasmAction = function(msg) {
    alert("Wasm通知:" + msg);
};

二、注册JavaScript回调函数到C++

如果需要更灵活的双向交互,可以将JS函数注册为C++的回调,在事件触发时直接调用。

JavaScript侧代码(注册回调)

// 定义回调函数
function wasmCallback(data) {
    console.log("回调收到Wasm数据:", data);
    // 处理业务逻辑
}

// 等待Wasm模块初始化完成后注册回调
Module.onRuntimeInitialized = () => {
    Module.registerWasmCallback(wasmCallback);
};

C++侧代码(绑定并调用回调)

#include <emscripten.h>
#include <functional>
#include <imgui.h>

// 存储JS回调的全局变量
std::function<void(const char*)> g_wasm_callback;

// 导出供JS调用的注册函数,必须用EMSCRIPTEN_KEEPALIVE标记
extern "C" {
    EMSCRIPTEN_KEEPALIVE
    void registerWasmCallback(void (*callback)(const char*)) {
        g_wasm_callback = std::function<void(const char*)>(callback);
    }
}

void render_frame() {
    ImGui::Begin("对话框");
    
    if (ImGui::Button("OK")) {
        // 检查回调已注册后调用
        if (g_wasm_callback) {
            g_wasm_callback("OK按钮点击事件");
        }
    }
    
    ImGui::End();
    // 其余渲染逻辑...
}

编译注意事项

编译时需导出注册函数,避免被Emscripten优化掉:

emcc your_code.cpp -o app.html \
    -s EXPORTED_FUNCTIONS=['_registerWasmCallback','_main'] \
    -s EXPORTED_RUNTIME_METHODS=['ccall','cwrap'] \
    # 其余SDL2/ImGui/OpenGL编译参数...

三、解决emscripten_cancel_main_loop导致的冻结问题

直接调用emscripten_cancel_main_loop会强制终止Wasm主循环,未完成资源清理就停止线程,导致界面冻结。正确做法是:

  1. 用全局变量控制循环运行状态
  2. 在循环内检查状态,待资源清理完成后再取消循环
  3. 通知JS循环已停止,由JS处理后续界面逻辑

C++侧代码

#include <emscripten.h>
#include <imgui.h>
#include <SDL.h>

SDL_Window* window = nullptr;
bool is_loop_running = true;

void render_frame() {
    if (!is_loop_running) {
        // 清理ImGui、SDL资源
        ImGui_ImplOpenGL3_Shutdown();
        ImGui_ImplSDL2_Shutdown();
        ImGui::DestroyContext();
        SDL_GL_DeleteContext(SDL_GL_GetCurrentContext());
        SDL_DestroyWindow(window);
        SDL_Quit();
        
        // 现在安全取消主循环
        emscripten_cancel_main_loop();
        
        // 通知JS循环已停止
        EM_ASM_(
            {
                window.dispatchEvent(new CustomEvent('wasm-loop-stopped'));
            }
        );
        return;
    }

    // 处理ImGui与渲染逻辑
    ImGui_ImplSDL2_NewFrame(window);
    ImGui_ImplOpenGL3_NewFrame();
    ImGui::NewFrame();

    ImGui::Begin("退出对话框");
    if (ImGui::Button("确认退出")) {
        // 标记循环停止,而非直接取消
        is_loop_running = false;
    }
    ImGui::End();

    // 渲染输出
    ImGui::Render();
    glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
    SDL_GL_SwapWindow(window);
}

int main() {
    // 初始化SDL、OpenGL、ImGui...
    emscripten_set_main_loop(render_frame, 0, 1);
    return 0;
}

JavaScript侧代码

window.addEventListener('wasm-loop-stopped', () => {
    console.log("Wasm主循环已安全停止");
    // 隐藏Wasm画布或切换到网页其他界面
    document.getElementById('canvas').style.display = 'none';
    document.getElementById('post-wasm-content').style.display = 'block';
});

内容的提问来源于stack exchange,提问作者stevenchucp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 01:36:24