如何缩放由Emscripten生成的SDL3应用Canvas画布?
解决Emscripten+SDL3 Canvas无法缩放的问题
这问题我之前折腾过,核心原因是Emscripten搭配SDL渲染时,每帧都会重置Canvas上下文的所有状态——包括你用ctx.scale()设置的变换矩阵。你直接在点击事件里修改上下文,下一秒就被SDL的渲染流程覆盖了,自然看不到效果。下面给你几个靠谱的解决办法:
方法一:修改SDL内部的渲染缩放(推荐,贴合SDL逻辑)
这个方法直接从SDL的渲染流程入手,修改它自己的缩放参数,不会被渲染流程覆盖。
步骤1:修改C++代码
把原来的const float static SCALE = 4.0F;改成全局可变变量,再暴露一个给JS调用的函数来调整缩放:
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ //NOLINT #include <SDL3/SDL.h> #include <SDL3/SDL_main.h> //keep uncommented for release // 把SCALE改成全局可变变量 static float SCALE = 4.0F; const int static WIDTH = 300; const int static HEIGHT = 300; const int static MAX = 255; const char static *MESSAGE = "Hello, World!7"; //NOLINT const float XPOS = 10; const float YPOS = 10; // 针对Emscripten添加交互接口 #ifdef __EMSCRIPTEN__ #include <emscripten.h> #endif // 暴露给JS的函数,用来增加缩放比例 extern "C" { EMSCRIPTEN_KEEPALIVE void increaseCanvasScale() { SCALE *= 1.1F; } } // 剩下的SDL_AppInit、SDL_AppEvent、SDL_AppIterate、SDL_AppQuit代码保持不变 // 注意SDL_AppIterate里依然使用SCALE变量做渲染缩放
步骤2:修改JavaScript代码
在点击事件里调用C++暴露的函数:
console.log("wasm loaded"); const canvas = document.getElementById("canvas"); const canvas2 = document.getElementById("canvas2"); const ctx = canvas.getContext("2d"); const ctx2 = canvas2.getContext("2d"); ctx2.fillStyle = "green"; ctx2.fillRect(10, 10, 110, 110); // 修改canvas的点击事件 canvas.addEventListener('click', function (event) { // 调用C++暴露的函数 Module.increaseCanvasScale(); console.log("canvas clicked, scale increased"); }); canvas2.addEventListener('click', function (event) { ctx2.scale(1.1, 1.1) ctx2.fillStyle = "green"; ctx2.fillRect(10, 10, 110, 110); console.log("canvas2 clicked") }); console.log("checkers_cust.js finished")
这样每次点击Canvas,就会修改SDL内部的缩放参数,SDL每帧渲染时都会用新的比例,效果能稳定生效。
方法二:用CSS缩放Canvas元素(简单粗暴,快速测试)
如果不想改C++代码,直接通过CSS修改Canvas元素的视觉缩放也是可行的——这是纯前端层面的缩放,不影响SDL内部的渲染逻辑:
let scaleFactor = 1; const canvas = document.getElementById("canvas"); canvas.addEventListener('click', function (event) { scaleFactor *= 1.1; // 设置CSS缩放,同时指定原点避免偏移 canvas.style.transform = `scale(${scaleFactor})`; canvas.style.transformOrigin = "top left"; console.log("canvas clicked, CSS scale applied"); });
这个方法的优点是零C++代码修改,缺点是如果后续需要处理鼠标交互,SDL拿到的坐标是原始Canvas的坐标,需要手动转换为缩放后的坐标。
为什么原来的方法无效?
SDL在每次执行SDL_RenderPresent()之前,会重置Canvas上下文的所有状态——包括变换矩阵、画笔颜色、线条宽度等。你在点击事件里设置的ctx.scale(),会被SDL下一次渲染时的重置操作覆盖,所以看不到任何效果。而你的canvas2是完全由自己控制渲染流程,没有自动重置上下文的逻辑,所以scale能累积生效。
内容的提问来源于stack exchange,提问作者user2309803
相关产品推荐
相关产品推荐

