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

如何缩放由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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 06:39:28